Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 68 additions & 5 deletions .agents/design/chatgpt-reference/chatgpt-design-tokens.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,13 +278,67 @@ ui-sans-serif, -apple-system, "system-ui", "Segoe UI", Helvetica, "Apple Color E

## Shadows

### Composer Shadow (Dark Mode)
> Confirmed via Chrome DevTools, 2026-02-20

### Composer Shadow — `shadow-short` Tailwind Utility

ChatGPT defines a custom Tailwind 4 utility `shadow-short` with **mode-aware values**. The shadow uses an **inverted edge strategy**: light mode uses an outer dark edge, dark mode uses an inset white glow.

#### Light Mode

```css
.shadow-short {
--tw-shadow:
0px 4px 4px 0px var(--tw-shadow-color, var(--shadow-color-1, #0000000a)),
0px 0px 1px 0px var(--tw-shadow-color, var(--shadow-color-2, #0000009e));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow),
var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
```

Computed values:
- **Layer 1 (drop):** `rgba(0, 0, 0, 0.04) 0px 4px 4px 0px` — subtle 4px depth shadow
- **Layer 2 (edge):** `rgba(0, 0, 0, 0.62) 0px 0px 1px 0px` — **outer** dark 1px edge (acts as visible border)

#### Dark Mode

```css
rgba(0, 0, 0, 0.1) 0px 4px 12px 0px,
rgba(255, 255, 255, 0.2) 0px 0px 1px 0px inset
.shadow-short:where(.dark, .dark *):not(:where(.dark .light, .dark .light *)) {
--tw-shadow:
0px 4px 12px 0px var(--tw-shadow-color, var(--shadow-color-1, #0000001a)),
inset 0px 0px 1px 0px var(--tw-shadow-color, var(--shadow-color-2, #fff3));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow),
var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
```

Computed values:
- **Layer 1 (drop):** `rgba(0, 0, 0, 0.1) 0px 4px 12px 0px` — deeper 12px depth shadow (3x blur vs light)
- **Layer 2 (edge):** `rgba(255, 255, 255, 0.2) 0px 0px 1px 0px inset` — **inset** white glow (luminous inner edge)

#### Mode Comparison

| Property | Light Mode | Dark Mode |
|----------|-----------|-----------|
| Drop shadow blur | 4px | 12px |
| Drop shadow opacity | 4% black | 10% black |
| Drop shadow offset | 0 4px | 0 4px |
| Edge shadow direction | **outer** | **inset** |
| Edge shadow color | 62% black | 20% white |
| Edge shadow blur | 1px | 1px |
| CSS border | `0px` (none) | `0px` (none) |
| Focus state change | None | None |
| Responsive change | None | None |
| Transition includes shadow | No | No |

#### Key Insight

The perceived "border" on the composer is NOT a CSS border — `border-width` is `0px` in both modes. The edge is created entirely by `box-shadow`:
- Light: outer dark shadow against white bg = visible edge
- Dark: inset white glow against dark bg = luminous edge

This is more physically natural than a solid CSS border, creating a card-like depth in dark mode and a crisp paper-edge feel in light mode.

### Header Shadow

```css
Expand All @@ -297,13 +351,22 @@ var(--sharp-edge-top-shadow): 0 1px 0 var(--border-sharp)

### Chat Composer (Input Area)

> Confirmed via Chrome DevTools, 2026-02-20

| Property | Value |
|----------|-------|
| Container border radius | 28px (superellipse via `corner-superellipse/1.1`) |
| Container padding | 10px (`p-2.5`) |
| Container background (dark) | `#303030` (`--bg-secondary`) |
| Container background (light) | `#fff` (`--bg-primary`) |
| Container shadow | `rgba(0,0,0,0.1) 0 4px 12px, rgba(255,255,255,0.2) 0 0 1px inset` |
| Container shadow (light) | `rgba(0,0,0,0.04) 0 4px 4px, rgba(0,0,0,0.62) 0 0 1px` |
| Container shadow (dark) | `rgba(0,0,0,0.1) 0 4px 12px, inset rgba(255,255,255,0.2) 0 0 1px` |
| Container shadow utility | `shadow-short` (custom Tailwind class, see Shadows section) |
| Container border | `0px` — **no CSS border**; edge is purely shadow-based |
| Container border-color (set but unused) | Light: `rgba(13,13,13,0.05)` / Dark: `rgba(255,255,255,0.05)` |
| Container transition | `transition-colors 0.2s ease-in-out` (motion-safe only; does NOT include box-shadow) |
| Container backdrop-filter | `none` — no frosted glass effect |
| Container outline (focus) | `none` — no visual change on focus |
| Input element | ProseMirror (contenteditable div) |
| Input padding | 0px 0px 16px (bottom only) |
| Input font size | 16px |
Expand Down Expand Up @@ -410,5 +473,5 @@ ChatGPT supports multiple accent color themes (visible in "mini" mode):
4. **Token aliasing** — Semantic tokens reference base scale tokens (e.g., `--text-accent: var(--blue-200)`)
5. **Theme-aware accent** — Accent colors swap between light (50–100 range) and dark (600–800 range) via selector-scoped tokens
6. **Pill buttons** — CTAs use extreme border-radius (`1.67772e+07px` / effectively `9999px`) for pill shape
7. **Inset shadow + outer shadow** — Composer combines outer depth shadow with subtle inset glow for a raised, tactile feel
7. **Mode-aware shadow edges** — Composer uses an inverted edge strategy: light mode has an **outer** dark 1px shadow (62% black) as a border substitute; dark mode has an **inset** white 1px glow (20% white). No CSS `border` is used. The drop shadow also scales between modes (4px/4% light vs 12px/10% dark). Defined via custom `shadow-short` Tailwind utility.
8. **Tailwind 4** — Uses Tailwind CSS v4 with CSS-based configuration and `var()` references in utility classes
9 changes: 9 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,15 @@ Use `ultrathink` for complex architectural decisions.

See `.agents/workflows/development-cycle.md` for details.

## Pull Requests

When creating a pull request:

1. **Always fetch first** — Run `git fetch origin` before comparing branches
2. **Compare against remote** — Use `origin/main` (not local `main`) for diffs and commit logs. Local `main` may be stale.
3. **Verify commit count** — Run `git log origin/main..HEAD --oneline` and confirm the number matches what GitHub will show
4. **Scope the description** — The PR body must reflect only the commits unique to the branch, not the full history since an outdated local ref

---

*~200 lines. For detailed patterns, see `.cursor/rules/` and `.agents/skills/`.*
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ When debugging issues:
- **Auth**: Uses Clerk for authentication; avoid touching `middleware.ts` without review
- **File Storage**: Uses Convex storage for file uploads
- **Model terminology**: See `.agents/context/glossary.md` for precise definitions
- **Pull requests**: Always `git fetch origin` first and compare against `origin/main`, never local `main`. Local main can be arbitrarily stale, causing commit/diff inflation in PR descriptions.

---

Expand Down
2 changes: 1 addition & 1 deletion app/components/chat-input/chat-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ export function ChatInput({
onClick={() => textareaRef.current?.focus()}
>
<PromptInput
className="bg-popover relative z-10 p-0 pt-1 shadow-xs backdrop-blur-xl"
className="relative z-10 p-0 pt-1"
maxHeight={200}
value={localValue}
onValueChange={handleValueChange}
Expand Down
4 changes: 2 additions & 2 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@
--sidebar-border: oklch(0.92 0.004 286.32);
--sidebar-ring: oklch(0.705 0.015 286.067);

/* Composer surface — ChatGPT-aligned shadow tokens */
/* Composer surface */
--composer-shadow-drop: rgba(0, 0, 0, 0.04);
--composer-shadow-edge: rgba(0, 0, 0, 0.62);
--composer-shadow-drop-offset: 0px 4px 4px 0px;
Expand All @@ -278,7 +278,7 @@
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.985 0 0);
--primary-foreground: oklch(0.21 0.006 285.885);
--secondary: oklch(0.32 0 0);
--secondary: oklch(0.30 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.274 0.006 286.033);
--muted-foreground: oklch(0.705 0.015 286.067);
Expand Down
2 changes: 1 addition & 1 deletion components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"

const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm font-medium disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
"inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm font-medium disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
Expand Down
2 changes: 1 addition & 1 deletion components/ui/prompt-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ function PromptInput({
>
<div
className={cn(
"border-input bg-background cursor-text rounded-3xl border p-2 shadow-xs",
"bg-[var(--composer-bg)] cursor-text rounded-[28px] p-2 shadow-composer",
className
)}
onClick={() => {
Expand Down
10 changes: 5 additions & 5 deletions components/ui/prompt-suggestion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function PromptSuggestion({
if (!isHighlightMode) {
return (
<Button
variant={variant || "outline"}
variant={variant || "secondary"}
size={size || "lg"}
className={cn("rounded-full", className)}
{...props}
Expand All @@ -42,8 +42,8 @@ function PromptSuggestion({
variant={variant || "ghost"}
size={size || "sm"}
className={cn(
"w-full cursor-pointer justify-start rounded-xl py-2",
"hover:bg-accent",
"w-full justify-start rounded-xl py-2",
"hover:bg-secondary",
className
)}
{...props}
Expand All @@ -63,8 +63,8 @@ function PromptSuggestion({
variant={variant || "ghost"}
size={size || "sm"}
className={cn(
"w-full cursor-pointer justify-start gap-0 rounded-xl py-2",
"hover:bg-accent",
"w-full justify-start gap-0 rounded-xl py-2",
"hover:bg-secondary",
className
)}
{...props}
Expand Down
13 changes: 0 additions & 13 deletions lib/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
Brain01Icon,
Note01Icon,
PaintBrush01Icon,
ChartUpIcon,
Search01Icon,
Expand Down Expand Up @@ -57,18 +56,6 @@ export const SUGGESTIONS: Array<{
],
icon: Brain01Icon,
},
{
label: "Writing",
highlight: "Write",
prompt: `Write`,
items: [
"Write a professional email to follow up on a job application",
"Write documentation for my open-source project",
"Write a blog post explaining machine learning basics",
"Write a compelling product description for my startup",
],
icon: Note01Icon,
},
{
label: "Research",
highlight: "Research",
Expand Down