ucode-agent 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,237 +1,300 @@
1
1
  ---
2
2
  name: ui-ux
3
- description: How to design and build an interface that looks deliberate rather than generated — direction, tokens, layout, states, motion, accessibility. Loads itself for any work with a user interface in it.
4
- auto: app, apps, ui, ux, website, web app, webapp, web page, webpage, landing page, dashboard, frontend, front-end, interface, css, tailwind, html, react, vue, svelte, nextjs, next.js, redesign, restyle, responsive, dark mode, ugly, styling, stylesheet, mockup, prototype
3
+ description: Design and build interfaces at the level of a senior product designer who also ships the code — direction, type, colour, layout, components, states, motion, accessibility and performance, verified on screen. Loads itself for any work with a user interface in it.
4
+ auto: app, apps, ui, ux, website, web app, webapp, web page, webpage, landing page, dashboard, frontend, front-end, interface, css, tailwind, html, react, vue, svelte, nextjs, next.js, shadcn, redesign, restyle, responsive, dark mode, ugly, styling, stylesheet, mockup, prototype, component, components, polished, beautiful, good looking, visually
5
5
  ---
6
6
 
7
- # Interfaces
7
+ # Interfaces, done properly
8
8
 
9
9
  The house style of a language model is a centred column, a purple-to-blue
10
- gradient, three equal cards and a lot of empty space. It is recognisable on
11
- sight, and everyone has now seen it a thousand times. Your job is to not
12
- produce it.
10
+ gradient, three identical cards, Inter at every size, and a lot of empty space.
11
+ Everyone has seen it a thousand times, and it reads as generated on sight.
12
+ This skill exists to stop you producing it.
13
13
 
14
- This is not decoration applied at the end. It is the order the work happens in:
15
- direction, then structure, then tokens, then states, then verification.
14
+ Work in this order and do not skip ahead. Most bad interfaces are good CSS
15
+ applied to an undecided design.
16
16
 
17
- ## 1. Decide the direction before writing any CSS
17
+ 1. Direction 2. Structure 3. Tokens 4. Components 5. States 6. Motion
18
+ 7. Accessibility and performance 8. Look at it 9. Report
18
19
 
19
- Answer these to yourself in one line each, then build to the answers:
20
-
21
- 1. **Job** — what does this screen actually do?
22
- 2. **Who** — who opens it, how often, and what do they need first?
23
- 3. **Tone** — pick one and commit: utilitarian, editorial, technical, playful,
24
- industrial, calm, dense. "Modern and clean" is not a tone, it is a way of
25
- avoiding the question.
26
- 4. **One memorable detail** — a colour, a texture, a typographic move, a single
27
- interaction. Exactly one. It is the difference between a design and a
28
- template.
29
-
30
- ## 2. Pick the mode from the surface, not the product
31
-
32
- The mode names what success looks like for the person in front of it. It
33
- decides how much the interface is allowed to perform.
34
-
35
- - **Operate** — they are completing a task. App UI, dashboards, editors,
36
- admin, settings, tools. Scannability, consistency and speed beat expression
37
- every time. The personality lives in precise details, not in the hero.
38
- - **Persuade** — they are deciding whether to act. Landing pages, pricing,
39
- marketing. Here the design *is* the product; earn the attention.
40
- - **Read** — they are trying to understand something. Docs, articles, guides.
41
- Structure for comprehension first, then make reading pleasant enough to stay.
42
- - **Experience** — they are looking at the work itself. Portfolios, galleries.
43
- The artifact leads from the first screen and the interface gets out of the way.
20
+ ---
44
21
 
45
- A tool's landing page is still Persuade. A dashboard is still Operate however
46
- beautiful the brand is. Never put a marketing hero on top of a working tool.
22
+ ## 1. Decide the direction before any code
23
+
24
+ Write these down in one line each, then build to them:
25
+
26
+ - **Job** — what does this screen do, in one sentence a user would say?
27
+ - **Who and when** — who opens it, how often, on what device, in what light?
28
+ A tool opened forty times a day and a page seen once need opposite things.
29
+ - **Mode** — pick one:
30
+ - **Operate**: completing a task (apps, dashboards, tools, settings). Speed,
31
+ scannability and consistency beat expression. Personality lives in details.
32
+ - **Persuade**: deciding whether to act (landing, pricing, marketing). The
33
+ design is the product; it has to earn attention in one screen.
34
+ - **Read**: understanding something (docs, articles). Measure, rhythm and
35
+ hierarchy first; decoration last.
36
+ - **Experience**: looking at the work itself (portfolio, gallery). The
37
+ content leads; the interface gets out of the way.
38
+ - **Tone** — one word you commit to: clinical, warm, editorial, technical,
39
+ playful, industrial, calm, dense. "Modern and clean" is not a tone.
40
+ - **The one memorable thing** — a colour, a type move, a texture, a single
41
+ interaction. Exactly one. It is the difference between a design and a theme.
42
+
43
+ A dashboard stays Operate however loud the brand is. A tool's landing page is
44
+ still Persuade. Never put a marketing hero on top of a working tool.
45
+
46
+ ## 2. Structure: hierarchy before decoration
47
+
48
+ - Decide what the eye lands on first, second, third. Build that with **size,
49
+ weight, colour and position** before reaching for a box, a border or a card.
50
+ Three levels — primary, secondary, muted — is usually all a screen needs.
51
+ - **The primary thing gets disproportionate size.** If a score, a total or a
52
+ status is the point of the screen, make it unmistakably larger than
53
+ everything around it — not 10% bigger, three or four times bigger.
54
+ - Align to a grid and share edges. Ragged left edges are the single most common
55
+ reason a page feels amateur. Pick a max content width (e.g. 1120px for apps,
56
+ 68ch for prose) and hold it.
57
+ - Group by proximity: space **inside** a group must be smaller than space
58
+ **between** groups, or the grouping reads wrong however good the rest is.
59
+ - One job per element. A card that is a link, a form and a menu is three cards.
60
+ - Put actions where the eye already is: primary action at the end of the flow it
61
+ completes, destructive actions separated from safe ones.
47
62
 
48
- ## 3. Tokens first, then never a raw value again
63
+ ## 3. Tokens: set them once, never use a raw value again
49
64
 
50
- Set these at the top and use them everywhere. One-off hard-coded values are
51
- exactly how a design drifts out of alignment with itself.
65
+ Everything below is a starting point to adjust, not a look to ship unchanged.
66
+ The palette in particular must be re-picked for the product's tone.
52
67
 
53
68
  ```css
54
69
  :root {
55
- --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
56
- --mono: ui-monospace, SFMono-Regular, "Cascadia Code", Consolas, monospace;
57
-
58
- /* One scale. Nothing between the steps. */
59
- --text--1: .8125rem; --text-0: 1rem; --text-1: 1.25rem;
60
- --text-2: 1.5rem; --text-3: 2rem; --text-4: 2.75rem;
61
-
62
- --s1: .25rem; --s2: .5rem; --s3: .75rem; --s4: 1rem;
63
- --s5: 1.5rem; --s6: 2rem; --s7: 3rem; --s8: 4rem;
64
-
65
- /* Neutrals carry a hue. Flat #808080 grey is what makes a UI look dead. */
66
- --bg: #fbfaf9; --surface: #ffffff; --line: #e6e2dd;
67
- --ink: #17161a; --ink-2: #55525c; --ink-3: #8a8792;
68
-
69
- --accent: #2f6fe0; --accent-ink: #ffffff; --accent-soft: #eaf1fe;
70
- --danger: #b42318; --ok: #217a4b; --warn: #b25e09;
71
-
72
- --radius: 10px; --radius-sm: 6px;
73
- --shadow: 0 1px 2px rgb(20 18 24 / .05), 0 8px 24px rgb(20 18 24 / .07);
70
+ /* Type a real scale, fluid between mobile and desktop. */
71
+ --font-sans: "Inter Tight", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
72
+ --font-display: var(--font-sans);
73
+ --font-mono: ui-monospace, "Cascadia Code", "JetBrains Mono", Consolas, monospace;
74
+
75
+ --text-xs: .75rem;
76
+ --text-sm: .875rem;
77
+ --text-md: 1rem;
78
+ --text-lg: clamp(1.125rem, 1rem + .4vw, 1.25rem);
79
+ --text-xl: clamp(1.375rem, 1.1rem + .9vw, 1.75rem);
80
+ --text-2xl: clamp(1.75rem, 1.3rem + 1.6vw, 2.5rem);
81
+ --text-3xl: clamp(2.25rem, 1.5rem + 3vw, 3.75rem);
82
+ --text-hero: clamp(3rem, 2rem + 5vw, 6rem);
83
+
84
+ /* Space one scale, nothing in between. */
85
+ --s-1: .25rem; --s-2: .5rem; --s-3: .75rem; --s-4: 1rem; --s-5: 1.5rem;
86
+ --s-6: 2rem; --s-7: 3rem; --s-8: 4rem; --s-9: 6rem; --s-10: 8rem;
87
+
88
+ /* Colour neutrals carry a hue; flat #808080 grey is what makes a UI look dead.
89
+ OKLCH so lightness steps are perceptually even. */
90
+ --bg: oklch(98.5% .004 90);
91
+ --surface: oklch(100% 0 0);
92
+ --surface-2: oklch(96.5% .006 90);
93
+ --line: oklch(90% .008 90);
94
+ --ink: oklch(22% .01 90);
95
+ --ink-2: oklch(45% .012 90);
96
+ --ink-3: oklch(60% .01 90);
97
+
98
+ --accent: oklch(58% .19 255);
99
+ --accent-ink: oklch(99% 0 0);
100
+ --accent-soft: oklch(95% .03 255);
101
+
102
+ --good: oklch(62% .16 150); --good-soft: oklch(95% .04 150);
103
+ --warn: oklch(72% .16 70); --warn-soft: oklch(96% .05 80);
104
+ --bad: oklch(58% .21 25); --bad-soft: oklch(95% .04 25);
105
+
106
+ --radius-sm: 6px; --radius: 10px; --radius-lg: 16px; --radius-full: 999px;
107
+ --shadow-sm: 0 1px 2px oklch(20% .01 90 / .06);
108
+ --shadow: 0 1px 2px oklch(20% .01 90 / .05), 0 8px 24px oklch(20% .01 90 / .08);
109
+ --shadow-lg: 0 2px 4px oklch(20% .01 90 / .06), 0 24px 48px oklch(20% .01 90 / .14);
74
110
  --focus: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);
111
+
112
+ --ease-out: cubic-bezier(.22, 1, .36, 1);
113
+ --dur-1: 120ms; --dur-2: 200ms; --dur-3: 320ms;
75
114
  }
76
115
 
77
116
  @media (prefers-color-scheme: dark) {
78
117
  :root {
79
- --bg: #101014; --surface: #17171c; --line: #2a2a33;
80
- --ink: #f2f1f5; --ink-2: #b3b0bd; --ink-3: #807d8a;
81
- --accent: #6fa4ff; --accent-ink: #0f1016; --accent-soft: #16203a;
118
+ --bg: oklch(16% .008 260); --surface: oklch(20% .01 260);
119
+ --surface-2: oklch(24% .012 260); --line: oklch(30% .012 260);
120
+ --ink: oklch(95% .005 260); --ink-2: oklch(76% .01 260); --ink-3: oklch(60% .01 260);
121
+ --accent: oklch(70% .16 255); --accent-ink: oklch(18% .02 260); --accent-soft: oklch(28% .06 255);
82
122
  }
83
123
  }
84
124
 
85
- * { box-sizing: border-box; }
125
+ *, *::before, *::after { box-sizing: border-box; }
126
+ html { -webkit-text-size-adjust: 100%; }
86
127
  body {
87
128
  margin: 0; background: var(--bg); color: var(--ink);
88
- font: var(--text-0)/1.6 var(--font);
89
- -webkit-font-smoothing: antialiased;
129
+ font: var(--text-md)/1.6 var(--font-sans);
130
+ -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
90
131
  }
91
- h1, h2, h3 { margin: 0; line-height: 1.15; letter-spacing: -.02em; }
92
- :focus-visible { outline: none; box-shadow: var(--focus); }
132
+ h1, h2, h3 { margin: 0; line-height: 1.1; letter-spacing: -.02em; text-wrap: balance; }
133
+ p { text-wrap: pretty; }
134
+ :focus-visible { outline: none; box-shadow: var(--focus); border-radius: var(--radius-sm); }
135
+ .num { font-variant-numeric: tabular-nums; }
93
136
  @media (prefers-reduced-motion: reduce) {
94
- *, *::before, *::after { animation: none !important; transition: none !important; }
137
+ *, *::before, *::after { animation-duration: 1ms !important; transition-duration: 1ms !important; }
95
138
  }
96
139
  ```
97
140
 
98
- **Swap the four palette lines to change direction, and keep everything else:**
99
-
100
- - **Paper** `--bg:#faf9f7; --surface:#fff; --ink:#1a1815; --accent:#b45309`.
101
- Warm, calm, for reading and for tools used daily.
102
- - **Console** `--bg:#0d1117; --surface:#161b22; --ink:#e6edf3; --accent:#2f81f7`
103
- with `--line:#30363d`. Technical and dense, for dashboards and dev tools.
104
- - **Editorial** `--bg:#fffdf8; --surface:#fff; --ink:#141414; --accent:#c2410c`
105
- plus a serif on headings only. For content and landing pages.
106
-
107
- One direction, all the way through. Half Console and half Editorial reads as a
108
- mistake, because it is one.
109
-
110
- **The rules behind the tokens**, for when you need a value that is not in them:
111
-
112
- - **Spacing** every value from the scale. Space *inside* a group must be
113
- smaller than the space *around* it, or the grouping reads wrong however good
114
- the rest is.
115
- - **Type** one family for UI, at most one more for display. Body 15–17px,
116
- line-height 1.5–1.65, measure capped at 65–75ch. Headings tighter: 1.1–1.25,
117
- and `letter-spacing: -.02em` above 28px.
118
- - **Colour** one accent hue, one neutral ramp, semantic red/amber/green. Never
119
- pure `#000` on pure `#fff`.
120
- - **Radius** pick one and derive: inputs and buttons 6–8, cards 10–12,
121
- pills 999. Four unrelated radii look like an accident.
122
- - **Depth**borders and background steps first, shadows last, and only for
123
- things that genuinely float: menus, modals, toasts. A shadow on every card
124
- flattens the hierarchy instead of building it.
125
- - **Dark mode** — swap the variables. Never invert. Surfaces get *lighter* as
126
- they rise, and pure white on near-black is too harsh: use around 90%.
127
-
128
- ## 4. Layout
129
-
130
- - Build hierarchy with size, weight and colour before reaching for a box.
131
- Three levels — primary, secondary, muted is usually all you need.
132
- - Align to a grid and share edges. Ragged left edges are the single most
133
- common reason a page feels amateur.
134
- - Full width is not a layout. Constrain content to what the content needs: a
135
- table wants width, prose does not.
136
- - One job per element. A card that is a link, a form and a menu is three cards.
137
-
138
- ## 5. States are most of the work
139
-
140
- An interface that only handles the happy path is a mockup. For every screen:
141
-
142
- - **Empty** first run, nothing there yet. Say what this is and how to make
143
- the first one. Never a blank box.
144
- - **Loading** — skeletons shaped like the real content, or a spinner on the
145
- control that was pressed. Do not blank the page.
146
- - **Error** — what failed and what they can do about it. Keep their input.
147
- - **Partial** — one row failed and the rest loaded.
148
-
149
- And for every interactive element: `:hover`, `:focus-visible`, `:active`,
150
- `:disabled`, and the selected state. A control with only a default state is
151
- unfinished, not minimal. `outline: none` with no replacement focus ring is a
152
- bug, not a style choice.
141
+ **The rules behind the tokens**, for when you need a value that is not there:
142
+
143
+ - **Type.** One family for UI, at most one more for display. Body 15–17px at
144
+ 1.5–1.65 line height, measure 60–75ch. Headings 1.05–1.2 line height with
145
+ negative tracking above 28px. Weight does more than size for mid-level
146
+ hierarchy: 600 for labels that matter, 400 for body, 500 for UI controls.
147
+ Use `tabular-nums` anywhere numbers line up or change in place.
148
+ - **Pick a typeface with intent.** Inter everywhere is the generated look. Good
149
+ free choices through `next/font/google` or Google Fonts: *Inter Tight,
150
+ Geist, Manrope, DM Sans, Plus Jakarta Sans, Instrument Sans, Space Grotesk,
151
+ IBM Plex Sans* for UI; *Fraunces, Instrument Serif, Bricolage Grotesque,
152
+ Newsreader* for display. Load only the weights you use, `display: swap`.
153
+ - **Colour.** One accent hue, one neutral ramp tinted slightly toward it, and
154
+ semantic good/warn/bad that mean one thing each. Colour is for meaning and
155
+ emphasis, not decoration. Never pure `#000` on pure `#fff`. Count the hue
156
+ families at the end: more than one accent plus the semantics means the
157
+ palette got away from you.
158
+ - **Radius.** Pick one base and derive: controls 6–8, cards 10–16, pills full.
159
+ Four unrelated radii look like an accident.
160
+ - **Depth.** Borders and background steps first; shadows only for things that
161
+ genuinely float (menus, popovers, modals, toasts). A shadow on every card
162
+ flattens the hierarchy it was meant to create.
163
+ - **Dark mode.** Swap variables, never invert. Surfaces get *lighter* as they
164
+ rise. Text around 92–95% lightness, not pure white. Re-check every accent for
165
+ contrast in dark most need to get lighter.
166
+
167
+ ### With Tailwind and shadcn/ui
168
+
169
+ - shadcn ships with a neutral slate look. **Re-theme it** in `globals.css` by
170
+ setting its CSS variables (`--background`, `--foreground`, `--primary`,
171
+ `--muted`, `--accent`, `--destructive`, `--border`, `--ring`, `--radius`) to
172
+ your palette. Shipping the default theme is shipping someone else's design.
173
+ - Use components for behaviour and accessibility (Dialog, Popover, Tabs,
174
+ Select, Tooltip, Toast/Sonner), then style them to the direction. Do not wrap
175
+ every region of the page in a `Card` that is the three-card look again.
176
+ - Extend the Tailwind theme with your tokens rather than scattering arbitrary
177
+ values (`text-[17px]`, `mt-[13px]`). Arbitrary values are the raw values this
178
+ section forbids, in a different syntax.
179
+ - Icons: `lucide-react`, one stroke width throughout, sized to the text beside
180
+ them (16px with 14–15px text, 20px with 16–18px). Never emoji as UI icons.
181
+
182
+ ## 4. Components, with every state built in
183
+
184
+ Every interactive element needs **default, hover, focus-visible, active,
185
+ disabled**, and where it applies **selected, loading, error**. A control with
186
+ only a default state is unfinished, not minimal.
153
187
 
154
188
  ```css
155
189
  .btn {
156
- font: 500 var(--text-0)/1 var(--font);
157
- padding: var(--s3) var(--s5);
190
+ display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
191
+ min-height: 44px; padding: 0 var(--s-5);
192
+ font: 500 var(--text-md)/1 var(--font-sans);
158
193
  border: 1px solid transparent; border-radius: var(--radius-sm);
159
194
  background: var(--accent); color: var(--accent-ink);
160
- cursor: pointer; transition: filter .15s, transform .05s;
161
- }
162
- .btn:hover { filter: brightness(1.08); }
163
- .btn:active { transform: translateY(1px); }
164
- .btn:disabled { opacity: .45; cursor: not-allowed; filter: none; }
165
- .btn--quiet { background: transparent; color: var(--ink); border-color: var(--line); }
166
- .btn--quiet:hover { background: var(--accent-soft); }
167
-
168
- .input {
169
- width: 100%; padding: var(--s3) var(--s4);
170
- font: var(--text-0) var(--font); color: var(--ink);
171
- background: var(--surface);
172
- border: 1px solid var(--line); border-radius: var(--radius-sm);
195
+ cursor: pointer; transition: filter var(--dur-1), transform var(--dur-1) var(--ease-out);
173
196
  }
174
- .input::placeholder { color: var(--ink-3); }
197
+ .btn:hover { filter: brightness(1.07); }
198
+ .btn:active { transform: translateY(1px) scale(.99); }
199
+ .btn:disabled, .btn[aria-busy="true"] { opacity: .5; cursor: not-allowed; filter: none; }
200
+ .btn--quiet { background: transparent; color: var(--ink); border-color: var(--line); }
201
+ .btn--quiet:hover { background: var(--surface-2); }
175
202
  ```
176
203
 
177
- ## 6. Motion
204
+ - **Buttons:** verb labels ("Analyze label", not "Submit"). One primary per
205
+ view. Loading state replaces the label's icon with a spinner and keeps the
206
+ width fixed so nothing jumps.
207
+ - **Inputs:** a visible `<label>` always — a placeholder is not a label. Help
208
+ text below, errors below in `--bad` with an icon, `aria-invalid` and
209
+ `aria-describedby` wired up. Validate on blur, re-validate on input once
210
+ an error is showing, never shout on the first keystroke.
211
+ - **File upload:** a real drop zone *and* a click target, keyboard operable,
212
+ showing accepted types and max size before the user tries. Show the chosen
213
+ file (thumbnail for images), let them replace or remove it, validate type and
214
+ size on the client before sending anything.
215
+ - **Scores and metrics:** the number large, with its scale ("7.4 / 10"), a
216
+ label that says what it measures, and a colour band (good/warn/bad) that is
217
+ never the *only* signal — pair it with a word ("Good", "Moderate", "Poor").
218
+ - **Lists of issues or findings:** most severe first; each with the thing, the
219
+ value, why it matters, in one or two lines. If there are none, say so plainly
220
+ once — do not render an empty section header.
221
+ - **Tables:** right-align numbers, left-align text, tabular numerals, sticky
222
+ header on long tables, row hover, and a real empty state.
223
+
224
+ ## 5. States are most of the work
178
225
 
179
- One authored moment, not effects scattered everywhere, and not the same
180
- entrance animation on every section. 120–200ms, on `transform`, `opacity`,
181
- `filter` and colour only — never on `height` or `width`. Ease out from a state
182
- that is already visible. Honour `prefers-reduced-motion` every time.
226
+ An interface that only handles the happy path is a mockup. For every screen
227
+ and every async action:
183
228
 
184
- ## 7. Non-negotiable
229
+ - **Empty / first run** — what this is, and the one action that starts it.
230
+ Illustrated or typographic, never a blank rectangle.
231
+ - **Loading** — skeletons shaped like the real content, or a progress
232
+ indicator on the control that was pressed. Say what is happening if it takes
233
+ more than a second ("Reading the label…"). Never blank the page.
234
+ - **Success** — the result, with a clear next action (try another, share, copy).
235
+ - **Error** — what failed in plain words, what to do next, and the user's input
236
+ preserved. Distinguish "you can fix this" (wrong file type) from "we failed"
237
+ (network, model error) — they need different words and different actions.
238
+ - **Partial** — some parts loaded, one failed; show what worked.
239
+ - **Edge content** — the longest realistic name, a value of zero, a missing
240
+ field, a thousand rows. Design for them, do not discover them.
241
+
242
+ Announce async results to screen readers with an `aria-live="polite"` region.
243
+
244
+ ## 6. Motion
185
245
 
186
- - Contrast 4.5:1 for body text and placeholders, 3:1 for large text and for
187
- the borders of controls.
188
- - Every control reachable and operable by keyboard, in the order it appears.
189
- - Labels on inputs. A placeholder is not a label.
190
- - Touch targets 44px.
191
- - `<button>` for actions, `<a href>` for navigation. Never a `<div>` with an
192
- onClick.
193
- - `aria-label` on any icon-only button; real `alt` text on meaningful images.
194
- - Nothing shifts as content loads: reserve the space, set image dimensions.
195
- - 16px minimum body text on mobile, and `<meta name="viewport" content="width=device-width, initial-scale=1">`.
246
+ One authored moment, not effects scattered everywhere. For a result screen that
247
+ might be the score counting up and the findings staggering in 40–60ms apart.
248
+ Everything else: 120–200ms, ease-out, on `transform`, `opacity`, `filter` and
249
+ colour only never animate `width`, `height`, `top` or `left`. Content must
250
+ be readable with motion off; honour `prefers-reduced-motion` every time.
251
+
252
+ ## 7. Accessibility and performance — non-negotiable
253
+
254
+ - Contrast: 4.5:1 body text and placeholders, 3:1 large text, icons and control
255
+ borders. `--ink-3` on `--bg` is for hints only.
256
+ - Keyboard: every control reachable in visual order, visible focus ring,
257
+ Escape closes overlays, focus returns to the trigger.
258
+ - Semantics: `<button>` for actions, `<a href>` for navigation, landmarks
259
+ (`header`, `main`, `nav`), one `<h1>`, headings in order.
260
+ - Targets 44×44px on touch. `aria-label` on icon-only buttons. Real `alt` text.
261
+ - Viewport meta, 16px minimum input text on mobile (iOS zooms below that).
262
+ - No layout shift: reserve space for images and async content, set image
263
+ dimensions, use `next/image` in Next.js, `font-display: swap`.
264
+ - Ship less JS: server components by default in Next.js, `"use client"` only on
265
+ the parts that are interactive.
196
266
 
197
267
  ## 8. Do not
198
268
 
199
- These are the defaults of the category rather than laws a brief can earn any
200
- of them — but reaching for one *because it was the first thing to hand* means
201
- you were not deciding.
202
-
203
- - Purple-to-blue gradient headers. Gradient text. Glassmorphism as decoration.
204
- - Three identical feature cards with a lorem sentence each. Same-size icon +
205
- heading + text cards used as the whole page structure. Nested cards.
206
- - The hero-metric template: big number, small label, three supporting stats.
207
- - A tracked uppercase eyebrow over every section, or 01 / 02 / 03 section
208
- numbers where the order carries no information.
209
- - Emoji as interface icons. Use an icon set or well-drawn inline SVG.
210
- - Monospace as a costume for "technical" when there is no code or data in it.
211
- - A modal for something that needs neither interruption nor protected focus.
212
- - Centring everything. Long centred paragraphs are genuinely harder to read.
213
- - Inventing a component library when the project already has one. Look for
214
- existing components, tokens and utilities first, and use them.
215
- - Placeholder copy. Write the real words — they are part of the design.
216
-
217
- ## 9. Before you say it is done
218
-
219
- Open it and look at it. Then walk this list and fix what fails. The last three
220
- are the ones that get skipped, so do not skip them:
221
-
222
- 1. **375px wide.** No horizontal scrollbar, nothing overlapping, nothing cut
223
- off. If you wrote no media query at all, you have not done this.
224
- 2. **The longest realistic string** in every label and every cell. Does the row
225
- hold, or does one long title break the layout?
226
- 3. **Empty data.** Is there a real empty state, or a blank rectangle?
227
- 4. **Keyboard only.** Tab through everything. Can you see where you are at
228
- every step?
229
- 5. **Count the accent hues.** More than one family means the palette got away
230
- from you. Put it back.
231
- 6. **Contrast.** `--ink-3` on `--bg` is for hints, never for anything that has
232
- to be read.
233
-
234
- Then say which of these you actually checked and what you found. Do not claim
235
- it works on mobile if you never made it narrow. If it runs in a browser, start
236
- it with `run_command` and `background: true` and open it before you call it
237
- finished.
269
+ Defaults of the category. A brief can earn any of them; reaching for one
270
+ because it was first to hand means you were not deciding.
271
+
272
+ - Purple-to-blue gradients, gradient text, glassmorphism as decoration,
273
+ glowing blobs in the background.
274
+ - Three identical feature cards; icon + heading + sentence cards as the whole
275
+ page; cards inside cards.
276
+ - The hero-metric template: big number, small label, three stats in a row.
277
+ - A tracked uppercase eyebrow over every section; 01/02/03 section numbers.
278
+ - Emoji as icons. Monospace as a costume. Centred long paragraphs.
279
+ - The default shadcn slate theme, unchanged.
280
+ - Lorem ipsum, "Feature 1", "John Doe", placeholder images. Write the real copy
281
+ it is part of the design.
282
+ - A modal for anything that does not need to interrupt.
283
+
284
+ ## 9. Look at it, then report
285
+
286
+ Run it (`npm run dev` starts in the background and returns the URL) and check:
287
+
288
+ 1. **375px wide** — no horizontal scroll, nothing overlapping or clipped. If you
289
+ wrote no responsive rules at all, you have not done this.
290
+ 2. **1440px wide** — the content has a max width and does not stretch into
291
+ unreadable lines.
292
+ 3. **Longest realistic content** in every label, cell and card.
293
+ 4. **Every state** empty, loading, success, error reachable and designed.
294
+ 5. **Keyboard only** tab through everything; focus always visible.
295
+ 6. **Contrast** of body text, muted text and the accent on its background.
296
+ 7. **Hue count** one accent family plus semantics.
297
+ 8. **The first thing the eye lands on** is the thing that matters most.
298
+
299
+ Then say which of these you actually checked and what you found. Never claim it
300
+ works on mobile if you never made it narrow.