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.
- package/README.md +44 -21
- package/package.json +3 -4
- package/skills/ai-features/SKILL.md +140 -0
- package/skills/build-app/SKILL.md +137 -57
- package/skills/code-review/SKILL.md +69 -24
- package/skills/debug/SKILL.md +73 -31
- package/skills/performance/SKILL.md +84 -0
- package/skills/refactor/SKILL.md +72 -0
- package/skills/security/SKILL.md +110 -0
- package/skills/ui-ux/SKILL.md +256 -193
- package/skills/write-tests/SKILL.md +72 -34
- package/src/core/loop.js +3 -2
- package/src/core/provider.js +21 -19
- package/src/core/version.js +16 -0
- package/src/ui/screen.js +1187 -1067
- package/ucode.js +3 -4
package/skills/ui-ux/SKILL.md
CHANGED
|
@@ -1,237 +1,300 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ui-ux
|
|
3
|
-
description:
|
|
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
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
46
|
-
|
|
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
|
|
63
|
+
## 3. Tokens: set them once, never use a raw value again
|
|
49
64
|
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
56
|
-
--
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
--text-
|
|
61
|
-
|
|
62
|
-
--
|
|
63
|
-
--
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
--
|
|
67
|
-
--
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
--
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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:
|
|
80
|
-
--
|
|
81
|
-
--
|
|
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
|
-
|
|
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-
|
|
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.
|
|
92
|
-
|
|
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:
|
|
137
|
+
*, *::before, *::after { animation-duration: 1ms !important; transition-duration: 1ms !important; }
|
|
95
138
|
}
|
|
96
139
|
```
|
|
97
140
|
|
|
98
|
-
**
|
|
99
|
-
|
|
100
|
-
- **
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
**
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
- **
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
- **
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
-
|
|
135
|
-
|
|
136
|
-
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
157
|
-
padding:
|
|
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
|
|
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
|
-
.
|
|
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
|
-
|
|
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
|
-
|
|
180
|
-
|
|
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
|
-
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
-
|
|
195
|
-
-
|
|
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
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
- Three identical feature cards
|
|
205
|
-
|
|
206
|
-
- The hero-metric template: big number, small label, three
|
|
207
|
-
- A tracked uppercase eyebrow over every section
|
|
208
|
-
|
|
209
|
-
-
|
|
210
|
-
-
|
|
211
|
-
|
|
212
|
-
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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.
|