jig-ui 0.16.0 → 0.17.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/CHANGELOG.md +65 -0
- package/README.md +7 -6
- package/dist/index.js +730 -235
- package/examples/A-01.html +7 -0
- package/examples/A-02.html +7 -0
- package/examples/A-03.html +7 -0
- package/examples/A-04.html +7 -0
- package/examples/A-05.html +7 -0
- package/examples/A-06.html +7 -0
- package/examples/A-07.html +7 -0
- package/examples/A-08.html +7 -0
- package/examples/A-09.html +7 -0
- package/examples/A-10.html +7 -0
- package/examples/A-135.html +7 -0
- package/examples/A-136.html +7 -0
- package/examples/A-137.html +7 -0
- package/examples/A-138.html +7 -0
- package/examples/A-139.html +7 -0
- package/examples/A-140.html +7 -0
- package/examples/A-141.html +7 -0
- package/examples/A-142.html +7 -0
- package/examples/A-143.html +7 -0
- package/examples/A-144.html +7 -0
- package/examples/A-146.html +7 -0
- package/examples/A-58.html +7 -0
- package/examples/A-59.html +7 -0
- package/examples/A-60.html +7 -0
- package/examples/A-67.html +7 -0
- package/examples/B-105.html +7 -0
- package/examples/B-106.html +7 -0
- package/examples/B-11.html +7 -0
- package/examples/B-12.html +7 -0
- package/examples/B-13.html +7 -0
- package/examples/B-14.html +7 -0
- package/examples/B-15.html +7 -0
- package/examples/B-16.html +7 -0
- package/examples/B-17.html +7 -0
- package/examples/B-75.html +7 -0
- package/examples/B-76.html +7 -0
- package/examples/B-77.html +7 -0
- package/examples/B-78.html +7 -0
- package/examples/C-18.html +7 -0
- package/examples/C-19.html +7 -0
- package/examples/C-20.html +7 -0
- package/examples/C-21.html +7 -0
- package/examples/C-22.html +11 -0
- package/examples/C-49.html +7 -0
- package/examples/C-50.html +7 -0
- package/examples/C-66.html +7 -0
- package/examples/C-68.html +7 -0
- package/examples/D-111.html +7 -0
- package/examples/D-112.html +7 -0
- package/examples/D-114.html +7 -0
- package/examples/D-115.html +7 -0
- package/examples/D-23.html +11 -0
- package/examples/D-24.html +7 -0
- package/examples/D-25.html +7 -0
- package/examples/D-26.html +7 -0
- package/examples/D-27.html +7 -0
- package/examples/D-69.html +7 -0
- package/examples/D-70.html +7 -0
- package/examples/D-71.html +7 -0
- package/examples/D-72.html +7 -0
- package/examples/D-96.html +7 -0
- package/examples/E-116.html +7 -0
- package/examples/E-28.html +7 -0
- package/examples/E-29.html +7 -0
- package/examples/E-30.html +7 -0
- package/examples/E-31.html +7 -0
- package/examples/E-32.html +7 -0
- package/examples/E-33.html +11 -0
- package/examples/E-34.html +7 -0
- package/examples/E-35.html +7 -0
- package/examples/E-51.html +7 -0
- package/examples/E-52.html +7 -0
- package/examples/E-61.html +7 -0
- package/examples/E-62.html +7 -0
- package/examples/E-63.html +7 -0
- package/examples/E-64.html +7 -0
- package/examples/E-65.html +7 -0
- package/examples/E-73.html +7 -0
- package/examples/E-74.html +7 -0
- package/examples/E-91.html +7 -0
- package/examples/E-92.html +7 -0
- package/examples/E-93.html +7 -0
- package/examples/E-94.html +7 -0
- package/examples/E-95.html +7 -0
- package/examples/F-100.html +7 -0
- package/examples/F-101.html +7 -0
- package/examples/F-102.html +7 -0
- package/examples/F-103.html +7 -0
- package/examples/F-104.html +7 -0
- package/examples/F-113.html +7 -0
- package/examples/F-147.html +7 -0
- package/examples/F-36.html +7 -0
- package/examples/F-37.html +7 -0
- package/examples/F-38.html +7 -0
- package/examples/F-39.html +7 -0
- package/examples/F-40.html +7 -0
- package/examples/F-41.html +11 -0
- package/examples/F-97.html +7 -0
- package/examples/F-98.html +7 -0
- package/examples/F-99.html +7 -0
- package/examples/G-145.html +7 -0
- package/examples/G-42.html +7 -0
- package/examples/G-43.html +10 -0
- package/examples/G-44.html +7 -0
- package/examples/H-117.html +11 -0
- package/examples/H-119.html +13 -0
- package/examples/H-45.html +13 -0
- package/examples/H-46.html +10 -0
- package/examples/H-47.html +15 -0
- package/examples/H-48.html +8 -0
- package/examples/I-118.html +7 -0
- package/examples/I-53.html +7 -0
- package/examples/I-54.html +7 -0
- package/examples/I-55.html +7 -0
- package/examples/I-56.html +7 -0
- package/examples/I-57.html +7 -0
- package/examples/I-79.html +7 -0
- package/examples/I-80.html +7 -0
- package/examples/I-81.html +7 -0
- package/examples/I-82.html +7 -0
- package/examples/I-83.html +7 -0
- package/examples/I-84.html +7 -0
- package/examples/I-85.html +7 -0
- package/examples/I-86.html +7 -0
- package/examples/I-87.html +7 -0
- package/examples/I-88.html +7 -0
- package/examples/I-89.html +7 -0
- package/examples/I-90.html +7 -0
- package/examples/J-120.html +7 -0
- package/examples/J-121.html +7 -0
- package/examples/J-122.html +7 -0
- package/examples/J-123.html +7 -0
- package/examples/J-124.html +8 -0
- package/examples/J-125.html +10 -0
- package/examples/J-126.html +9 -0
- package/examples/J-127.html +10 -0
- package/examples/K-128.html +9 -0
- package/examples/K-129.html +7 -0
- package/examples/K-130.html +7 -0
- package/examples/K-131.html +11 -0
- package/examples/K-132.html +7 -0
- package/examples/K-133.html +8 -0
- package/examples/K-134.html +9 -0
- package/examples/README.md +44 -0
- package/package.json +3 -2
- package/rules/00-anti-patterns.md +67 -0
- package/rules/01-modes.md +1 -1
- package/rules/03-patterns.md +6 -6
- package/rules/04-principles.md +12 -12
- package/rules/05-copy.md +3 -0
- package/rules.index.json +101 -0
- package/templates/COMMAND.md.tmpl +10 -3
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
One file per rule, `<ID>.html`, showing what the rule forbids and what it asks for
|
|
4
|
+
instead. A rule describes a failure in words; a reader recognises it by sight, and
|
|
5
|
+
most of these failures are things people have seen a hundred times without a name
|
|
6
|
+
for them. The example is the picture beside the name.
|
|
7
|
+
|
|
8
|
+
## Shape
|
|
9
|
+
|
|
10
|
+
```html
|
|
11
|
+
<!-- A-58 · Decorative styling that implies meaning -->
|
|
12
|
+
<figure data-example="dont">
|
|
13
|
+
…the failure, as small as it can be and still be recognised…
|
|
14
|
+
</figure>
|
|
15
|
+
<figure data-example="do">
|
|
16
|
+
…the same content, done the way the rule asks…
|
|
17
|
+
</figure>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- **Exactly one `dont` and one `do`.** The `do` shows the same content, not a
|
|
21
|
+
different, easier one. A fix that changes the content proves nothing.
|
|
22
|
+
- **Self-contained.** Styles are `style` attributes on the elements. No `<style>`,
|
|
23
|
+
`<script>`, `<link>`, event handler, or external URL. A consumer renders a figure
|
|
24
|
+
in a sandboxed frame or an inert box, and it has to look the same in both.
|
|
25
|
+
- **Small.** A figure is drawn for a box about 320 by 180 pixels. It exaggerates
|
|
26
|
+
just enough to be recognised, and no more.
|
|
27
|
+
- **Raw values are fine here.** An example has to show violet, a glow, a 44px
|
|
28
|
+
radius. It is a specimen of the failure, not a component, so the token rule
|
|
29
|
+
that governs a real page does not apply inside it.
|
|
30
|
+
- **The `do` follows every rule**, not only its own. A fix that commits a
|
|
31
|
+
different failure teaches the second one.
|
|
32
|
+
|
|
33
|
+
## Not for everything visible
|
|
34
|
+
|
|
35
|
+
Some rules are about code, metadata or behaviour and have no look. Their example
|
|
36
|
+
shows what a person meets because of it: the search result a missing description
|
|
37
|
+
produces, the tab a link hands over, the field a password manager cannot fill.
|
|
38
|
+
Where even that is impossible, the figure shows the code, set as code.
|
|
39
|
+
|
|
40
|
+
## Checked
|
|
41
|
+
|
|
42
|
+
`packages/cli/test/examples.test.ts` fails when a rule has no example, when a file
|
|
43
|
+
has other than one `dont` and one `do`, or when a figure carries anything that
|
|
44
|
+
would not render the same in a sandbox.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jig-ui",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "A design system for coding agents.
|
|
3
|
+
"version": "0.17.0",
|
|
4
|
+
"description": "A design system for coding agents. 143 numbered UI rules, brand x mode design tokens, and an installer for Claude Code, Codex, Cursor and opencode.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
12
12
|
"rules",
|
|
13
|
+
"examples",
|
|
13
14
|
"tokens",
|
|
14
15
|
"templates",
|
|
15
16
|
"references",
|
|
@@ -39,6 +39,7 @@ These are the strongest defaults in a model's training data and the fastest way
|
|
|
39
39
|
✅ Opaque `--color-bg-raised` with a `--color-stroke-weak` edge. Use translucency only over media, and only when legibility is verified against the worst frame.
|
|
40
40
|
Both styles make sufficient contrast and clear hierarchy structurally difficult — neumorphism in particular defines every element with shadow alone, which fails at 3:1 almost by construction. Trend styles also age badly: the more of them a product carries, the more precisely it is dated. Minimal styling that foregrounds content lasts longer.
|
|
41
41
|
Experiment freely — but not where it costs legibility or excludes people.
|
|
42
|
+
**Everywhere is its own failure.** A frosted panel over a video is a layering decision; frosted cards, a frosted nav, frosted buttons and glowing glass borders on a flat page are decoration applied to everything, and they solve no layering problem at all.
|
|
42
43
|
|
|
43
44
|
### A-58 Decorative styling that implies meaning
|
|
44
45
|
❌ List items in assorted colours chosen for variety; a decorative icon beside a heading that looks pressable; a heading coloured and underlined though it is not a link
|
|
@@ -66,6 +67,7 @@ Where several already apply — a table's rows are aligned, alike, and close —
|
|
|
66
67
|
### A-06 The three-column feature grid reflex
|
|
67
68
|
❌ Icon-in-rounded-square + heading + two lines, three across, for any set of three things
|
|
68
69
|
✅ Let the content pick the layout. Three items of unequal weight are a list, not a grid.
|
|
70
|
+
**The same reflex at any count.** Six or nine cards, each an icon, a title and a line, identical in size and shape, give every point the same weight whether it deserves it or not, and make the titles the only thing a reader can tell apart, so they skim all of them and remember none. When one point matters most, make it bigger, put it first, or give it the space the others do not get. Group related ones; drop the ones that are filler.
|
|
69
71
|
|
|
70
72
|
### A-07 Oversized radius everywhere
|
|
71
73
|
❌ One radius applied to cards, buttons, inputs and badges alike, regardless of element size
|
|
@@ -84,6 +86,61 @@ In `operator`, `--radius-surface` also selects `sm`, so cards, buttons and input
|
|
|
84
86
|
❌ Lorem ipsum, "Acme Inc", `https://example.com`, stock avatars left in
|
|
85
87
|
✅ Real content, or clearly marked `TODO:` that fails a build check. Placeholder text that survives to review costs a reviewer more than it saved you.
|
|
86
88
|
|
|
89
|
+
### A-135 A kicker above every heading
|
|
90
|
+
❌ A small uppercase, letter-spaced line over each section heading: "FEATURES" over "What you get", "HOW IT WORKS" over "Three steps to launch"
|
|
91
|
+
✅ One heading that says it. If the kicker repeats the heading, delete it; if it adds something, work those words into the heading or the line under it.
|
|
92
|
+
A kicker is one more line to read before the one that matters, and on every section it stops marking anything. It survives from print, where a section label helped a reader flip to a page. On a screen the heading is already the label.
|
|
93
|
+
|
|
94
|
+
### A-136 An eyebrow chip over the headline
|
|
95
|
+
❌ A rounded pill above the hero headline: "✦ Introducing v2", "New: AI-powered", with a border or tint that makes it look pressable
|
|
96
|
+
✅ Put the news in the headline or the line under it. If the chip links somewhere, make it a link that reads as one.
|
|
97
|
+
A pill is the shape of a control and of a filter, so people try to press it. When it does nothing, the page has taught them its shapes lie; when it does something, the most important action on the page is dressed as a label.
|
|
98
|
+
|
|
99
|
+
### A-137 Cream and beige by reflex
|
|
100
|
+
❌ A warm off-white page (`#f5f0e8`, `#faf7f2`), beige cards and a brown-grey text colour, chosen because the brief said "warm", "editorial" or nothing at all
|
|
101
|
+
✅ The brand file's neutrals, or a decision recorded in `DECISIONS.md`. Cream is a fine answer to a question someone asked; it is a default when nobody did.
|
|
102
|
+
Cream replaced violet (`A-01`) as the colour a model reaches for when asked to look tasteful. The failure is the same one: a palette nobody chose, which makes unrelated products look alike and dates every page that carries it.
|
|
103
|
+
|
|
104
|
+
### A-138 An italic serif display headline
|
|
105
|
+
❌ An oversized headline set in an italic serif, often with one word picked out: "Beautifully *crafted*", "Work, *reimagined*"
|
|
106
|
+
✅ Type chosen for this product's voice (`DECISIONS.md`), set upright unless the italic means something. Emphasis comes from the words.
|
|
107
|
+
It is the shortcut to an editorial look, and it has been taken so often that it now reads as the absence of a type decision rather than the presence of one.
|
|
108
|
+
|
|
109
|
+
### A-139 A side-tab accent border
|
|
110
|
+
❌ A thick coloured stripe down one side of an ordinary card, list item or panel
|
|
111
|
+
✅ No stripe. Where the stripe would mean something, a status, a warning, the current item, say it with a label or an icon and text, and keep the stripe only as a second signal.
|
|
112
|
+
A coloured edge on one side is the shape of an alert. Put on a card with nothing to announce, it makes the card look like a warning, and a page of them makes a real warning impossible to see.
|
|
113
|
+
|
|
114
|
+
### A-140 A thick coloured border on a rounded element
|
|
115
|
+
❌ A 2px or wider coloured outline around a card or button with a large radius
|
|
116
|
+
✅ A hairline in `--color-stroke-weak`, or no border and a surface step. If the element must stand out, change its surface, not its outline.
|
|
117
|
+
A heavy outline on a curve becomes the most visible shape in the element, and the eye traces it instead of reading what is inside.
|
|
118
|
+
|
|
119
|
+
### A-141 Cards inside cards
|
|
120
|
+
❌ A card holding a panel holding a card, each with its own border, padding and background
|
|
121
|
+
✅ One container at most, and the groups inside it made with space, type and a divider (`A-67`).
|
|
122
|
+
Every layer adds padding and an edge around the same content, and takes width from it. Three layers deep, the content has the least room on the page and the chrome has the most.
|
|
123
|
+
|
|
124
|
+
### A-142 The soft rounded card
|
|
125
|
+
❌ A 1px hairline and a wide soft shadow on the same card, a large radius, a pale tint: the card that holds every section of a generated page
|
|
126
|
+
✅ Choose one edge: a hairline, or a surface step (`A-08`). Keep the radius to the element's size (`A-07`).
|
|
127
|
+
An edge and a shadow both draw the same boundary, so the card has two outlines. Repeated down the page, the cards stop grouping anything and become the page's texture.
|
|
128
|
+
|
|
129
|
+
### A-143 A decorative grid or stripe background
|
|
130
|
+
❌ Faint grid lines, dot grids or repeating diagonal stripes filling the space behind content
|
|
131
|
+
✅ A plain surface. Keep grids for what is measured or placed on them: a canvas, a map, a chart.
|
|
132
|
+
A grid says "this is a workspace". Behind a paragraph it is noise at the one contrast level that makes text harder to read without being visible enough to mean anything.
|
|
133
|
+
|
|
134
|
+
### A-144 Dark mode with glowing accents
|
|
135
|
+
❌ Coloured glows on borders, buttons and headings of a dark page: `box-shadow: 0 0 24px #22d3ee`, neon outlines, a halo behind the hero
|
|
136
|
+
✅ Dark mode from the token ramp (`C-21`, `C-66`), with the brand colour spent where it means something (`I-56`).
|
|
137
|
+
Glow is contrast added without meaning. On a dark page everything that glows competes to be first, and the one element that needs attention looks like all the rest.
|
|
138
|
+
|
|
139
|
+
### A-146 Numbered section labels
|
|
140
|
+
❌ "01", "02", "03" in small type beside section headings that are not a sequence
|
|
141
|
+
✅ Number only a real order: steps to follow, a ranking, a procedure. Anything else keeps its heading and no number.
|
|
142
|
+
A number promises an order. A reader told there are three steps looks for what comes after the third, and a page that numbers its sections only because they are sections has promised something it does not deliver.
|
|
143
|
+
|
|
87
144
|
---
|
|
88
145
|
|
|
89
146
|
## B. Typography
|
|
@@ -503,6 +560,11 @@ Where people must *browse* to decide, split the list into two dependent fields
|
|
|
503
560
|
iOS Safari zooms the whole page when a field whose text is below 16px takes focus, and it does not zoom back out when the field loses it. The reader is left with a form wider than the screen, scrolling sideways to find the next field (`D-115`).
|
|
504
561
|
Setting `maximum-scale=1` on the viewport to stop it is not the fix. That disables pinch zoom for everyone, which is an accessibility failure in its own right.
|
|
505
562
|
|
|
563
|
+
### F-147 Asking for more than the task needs
|
|
564
|
+
❌ A sign-up that asks for a phone number, company size, job title and "how did you hear about us" before the person can try anything
|
|
565
|
+
✅ Ask for what this step needs to work. Every other field is optional and says so, or it moves to the moment it becomes useful.
|
|
566
|
+
Each field is a question someone has to answer and a reason to leave. The ones the product does not need yet are also the ones people fill with nonsense, so the data they were meant to collect is worse than none.
|
|
567
|
+
|
|
506
568
|
## G. Motion
|
|
507
569
|
|
|
508
570
|
### G-42 Entrance animation on everything
|
|
@@ -517,6 +579,11 @@ Setting `maximum-scale=1` on the viewport to stop it is not the fix. That disabl
|
|
|
517
579
|
|
|
518
580
|
**A consequence worth stating: motion is never the sole signal, for the same reason colour is not (`C-20`).** Honouring the reduced-motion path removes the animation, so any state that was communicated by movement alone is communicated to that user by nothing at all. A field that only shakes on a bad password has no error state under reduced motion. Pair the motion with text, an icon, or a colour change that survives without it.
|
|
519
581
|
|
|
582
|
+
### G-145 A pulsing status dot
|
|
583
|
+
❌ A green dot that pulses or pings beside "All systems operational", "Live", "Online"
|
|
584
|
+
✅ A still dot and the word. Motion is for a state that is changing now: a sync in progress, a recording under way.
|
|
585
|
+
A pulse says "look here, something is happening". On a status that has not changed in a week it is a false alarm on a loop, and it trains people to ignore the one indicator whose job is to be noticed when it matters (`G-43` applies too: under reduced motion it must still read).
|
|
586
|
+
|
|
520
587
|
### G-44 Durations too long
|
|
521
588
|
❌ 500ms+ on UI feedback
|
|
522
589
|
✅ 100–200ms for state change, up to 300ms for larger transitions. If it can be perceived as waiting, it is too slow.
|
package/rules/01-modes.md
CHANGED
|
@@ -190,7 +190,7 @@ Attempting to vary these by mode is a category error:
|
|
|
190
190
|
- **Accessibility floors.** Contrast, focus indication, target size, semantic markup. Identical in all three. `operator` being dense does not license a 24px tap target or a 3:1 body contrast.
|
|
191
191
|
- **Brand identity.** Palette, typeface, logo, voice.
|
|
192
192
|
- **State completeness.** Every mode renders loading, empty, error and disabled.
|
|
193
|
-
- **The anti-pattern file.** All
|
|
193
|
+
- **The anti-pattern file.** All 125 rules in it apply everywhere.
|
|
194
194
|
|
|
195
195
|
---
|
|
196
196
|
|
package/rules/03-patterns.md
CHANGED
|
@@ -479,7 +479,7 @@ Compose it for the phone first. Mobile navigation is a different control — not
|
|
|
479
479
|
|
|
480
480
|
Not a component. The procedure for structuring any screen, before styling anything.
|
|
481
481
|
|
|
482
|
-
### Step 1
|
|
482
|
+
### Step 1: Group
|
|
483
483
|
|
|
484
484
|
Four tools, weakest to strongest. Use the weakest that works (`A-67`):
|
|
485
485
|
|
|
@@ -492,7 +492,7 @@ Four tools, weakest to strongest. Use the weakest that works (`A-67`):
|
|
|
492
492
|
|
|
493
493
|
Combine them and the container usually becomes unnecessary — a table's rows are already aligned, alike and close. Break continuity deliberately to mark the end of a group, or to interrupt a list with something that is not part of it.
|
|
494
494
|
|
|
495
|
-
### Step 2
|
|
495
|
+
### Step 2: Order by importance
|
|
496
496
|
|
|
497
497
|
Six variables carry hierarchy: **size**, **colour**, **contrast**, **spacing**, **position**, **depth**. The procedure:
|
|
498
498
|
|
|
@@ -504,11 +504,11 @@ Position does more than it looks: people best recall the **first and last** item
|
|
|
504
504
|
|
|
505
505
|
Give elements *similar* prominence where they should be read as a pair — matching a label's weight to its icon's balances them instead of letting one shout.
|
|
506
506
|
|
|
507
|
-
### Step 3
|
|
507
|
+
### Step 3: Space from the inside out
|
|
508
508
|
|
|
509
509
|
Start at XS on the innermost rectangle and step up moving outward (`D-69`). Between two options, take the larger.
|
|
510
510
|
|
|
511
|
-
### Step 4
|
|
511
|
+
### Step 4: Align to a grid
|
|
512
512
|
|
|
513
513
|
Main containers align to a 12-column grid; small elements *inside* them do not — those use the spacing options.
|
|
514
514
|
|
|
@@ -516,7 +516,7 @@ Main containers align to a 12-column grid; small elements *inside* them do not
|
|
|
516
516
|
- **Gutters** fixed, narrower than columns, and kept empty. `--grid-gutter`.
|
|
517
517
|
- **Margins** keep content off the screen edge, wider on large screens. `--grid-margin`.
|
|
518
518
|
|
|
519
|
-
### Step 5
|
|
519
|
+
### Step 5: The squint test
|
|
520
520
|
|
|
521
521
|
Blur the design, zoom out, or step back. You should still be able to tell what the screen is for and which element matters most. If everything reads at one weight the hierarchy has failed; if elements smear together the white space is too tight.
|
|
522
522
|
|
|
@@ -524,7 +524,7 @@ Blur the design, zoom out, or step back. You should still be able to tell what t
|
|
|
524
524
|
|
|
525
525
|
**When nothing can render it, use the analogue:** if all type were one size and one colour, would the layout still communicate its order? If the hierarchy depends entirely on type styling, it is too weak. The analogue is a fallback, not an equal — a reading of the source is not a look at the page.
|
|
526
526
|
|
|
527
|
-
### Step 6
|
|
527
|
+
### Step 6: Decide how it collapses
|
|
528
528
|
|
|
529
529
|
A spec writes a composition per size. This step is what happens **between** them:
|
|
530
530
|
the same content, arranged for less room. Six rules, and the first is the one
|
package/rules/04-principles.md
CHANGED
|
@@ -15,7 +15,7 @@ If you reach for Part 2 often, the rules in `00`–`03` are underspecified and t
|
|
|
15
15
|
|
|
16
16
|
# Part 1 · Frames
|
|
17
17
|
|
|
18
|
-
## R-01 · Frame 1
|
|
18
|
+
## R-01 · Frame 1: Minimise usability risk
|
|
19
19
|
|
|
20
20
|
**Ask: who could struggle with this, and why?**
|
|
21
21
|
|
|
@@ -35,7 +35,7 @@ The risk is rarely to the median user. It falls on people with reduced vision, l
|
|
|
35
35
|
|
|
36
36
|
**Floor:** WCAG 2.1 level AA. Meeting AA is the starting point, not the achievement.
|
|
37
37
|
|
|
38
|
-
## R-02 · Frame 2
|
|
38
|
+
## R-02 · Frame 2: Every detail has a reason you can state
|
|
39
39
|
|
|
40
40
|
**Ask: why this way rather than another way?**
|
|
41
41
|
|
|
@@ -45,7 +45,7 @@ This is the test every rule in this system had to pass, and it is why the token
|
|
|
45
45
|
|
|
46
46
|
**Use it like this:** when you make a call the rules do not cover, state the reason in one line. If you cannot, you are guessing — and a guess should be surfaced as a question, not shipped as a decision (Tiebreaker 5).
|
|
47
47
|
|
|
48
|
-
## R-03 · Frame 3
|
|
48
|
+
## R-03 · Frame 3: Minimise interaction cost
|
|
49
49
|
|
|
50
50
|
**Ask: what does this cost the user, counted?**
|
|
51
51
|
|
|
@@ -59,7 +59,7 @@ Three reliable reductions:
|
|
|
59
59
|
|
|
60
60
|
**Use it like this:** count before and after, and state it. "3 clicks + 1 scroll → 2 clicks" is reviewable. "Improved the UX" is not. See `P-10`.
|
|
61
61
|
|
|
62
|
-
## R-04 · Frame 4
|
|
62
|
+
## R-04 · Frame 4: Minimise cognitive load
|
|
63
63
|
|
|
64
64
|
**Ask: how much thinking does this require that is not the user's actual task?**
|
|
65
65
|
|
|
@@ -73,7 +73,7 @@ Attention spent decoding the interface is unavailable for the work. Reliable red
|
|
|
73
73
|
|
|
74
74
|
**Use it like this:** when something feels heavy but no rule is broken, the load is usually ungrouped information or an unnecessary decision. Split it or remove it. A long form becomes steps; a wide table becomes fewer default columns; six equal options become two recommended and four behind "more".
|
|
75
75
|
|
|
76
|
-
## R-05 · Frame 5
|
|
76
|
+
## R-05 · Frame 5: Optimise for the common path
|
|
77
77
|
|
|
78
78
|
**Ask: what are most people here to do?**
|
|
79
79
|
|
|
@@ -91,31 +91,31 @@ Effort should follow it. Make the common task excellent before making the rare o
|
|
|
91
91
|
|
|
92
92
|
Seven. Each resolves a specific conflict in a specific direction. A principle that does not tell you what to give up is decoration.
|
|
93
93
|
|
|
94
|
-
## R-06 · Tiebreaker 1
|
|
94
|
+
## R-06 · Tiebreaker 1: Prefer the loud failure
|
|
95
95
|
|
|
96
96
|
**Between silent failure and visible failure, choose visible.**
|
|
97
97
|
|
|
98
98
|
A form that discards a submission and shows success is worse than one that errors. A page serving stale data without saying so is worse than a slow one. Silent failure is the most expensive class of defect, because the cost is paid by someone who never finds out.
|
|
99
99
|
|
|
100
|
-
## R-07 · Tiebreaker 2
|
|
100
|
+
## R-07 · Tiebreaker 2: Never destroy on suspicion
|
|
101
101
|
|
|
102
102
|
**When the system suspects input is wrong, mark it and hold it. Do not discard it.**
|
|
103
103
|
|
|
104
104
|
Spam scores, validation failures, duplicate detection — all heuristics, all wrong sometimes. Hold the item, record why, let a person decide. Applies equally to the user's typing: never clear a form, drop a draft, or overwrite without a copy.
|
|
105
105
|
|
|
106
|
-
## R-08 · Tiebreaker 3
|
|
106
|
+
## R-08 · Tiebreaker 3: Recoverable beats correct
|
|
107
107
|
|
|
108
108
|
**Between preventing a mistake and allowing it to be undone, choose undo.**
|
|
109
109
|
|
|
110
110
|
Prevention charges every user friction on every interaction to guard against a rare error. Recovery costs nothing until the error happens. Exception: genuinely irreversible operations, which confirm — and in `operator`, confirm by typing.
|
|
111
111
|
|
|
112
|
-
## R-09 · Tiebreaker 4
|
|
112
|
+
## R-09 · Tiebreaker 4: Optimise for who is actually there
|
|
113
113
|
|
|
114
114
|
**When density and legibility conflict, decide by the user's real conditions, not by preference.**
|
|
115
115
|
|
|
116
116
|
A first-time visitor on mobile data in bright sun and an operator at a large display for eight hours need opposite things. Mode encodes this. When the mode is genuinely unclear, ask — do not average, because the average serves neither.
|
|
117
117
|
|
|
118
|
-
## R-10 · Tiebreaker 5
|
|
118
|
+
## R-10 · Tiebreaker 5: Restraint is the default
|
|
119
119
|
|
|
120
120
|
**When a decision has not been made, ship the plainer thing and surface the question.**
|
|
121
121
|
|
|
@@ -125,13 +125,13 @@ An invented accent, a decorative animation, a gradient filling an empty space
|
|
|
125
125
|
|
|
126
126
|
**Ceiling: restraint applies to decoration, never to information.** Minimal is not the same as simple. A sparse interface that has dropped labels, selected states or visible actions is harder to use than a busier one that keeps them — it just photographs better. Strip styling freely; never strip the answers to *what is this*, *which one is selected*, and *what can I do next* (`E-63`).
|
|
127
127
|
|
|
128
|
-
## R-11 · Tiebreaker 6
|
|
128
|
+
## R-11 · Tiebreaker 6: The platform before the framework
|
|
129
129
|
|
|
130
130
|
**When the browser can already do it, use the browser.**
|
|
131
131
|
|
|
132
132
|
`<dialog>`, `<details>`, `position: sticky`, `:has()`, container queries, native form validation, `popover`. Platform features carry accessibility, keyboard handling and state management that a reimplementation gets wrong and then needs maintaining.
|
|
133
133
|
|
|
134
|
-
## R-12 · Tiebreaker 7
|
|
134
|
+
## R-12 · Tiebreaker 7: Match the codebase before matching this document
|
|
135
135
|
|
|
136
136
|
**When local convention conflicts with these rules, local convention wins.**
|
|
137
137
|
|
package/rules/05-copy.md
CHANGED
|
@@ -54,6 +54,7 @@ Someone who reads only the heading still gets the point. Someone who needs the d
|
|
|
54
54
|
✅ "Beautiful waterfront location", "Fast check-in experience", "Free secure parking"
|
|
55
55
|
A heading must carry its own meaning. People scan headings and skip the supporting text, and screen reader users routinely pull up a list of every heading on a page to navigate — a list of one-word labels tells them nothing.
|
|
56
56
|
Break long passages into groups with a descriptive heading each, rather than one unbroken block.
|
|
57
|
+
**The same holds for a page's headline.** "Build the future of work", "Your all-in-one platform", "Where ideas come to life" could sit above any product, which means they say nothing about this one. A headline names what the product does and for whom: "Search your logs by asking in plain English". If it would still be true after swapping in a competitor's name, rewrite it.
|
|
57
58
|
|
|
58
59
|
### I-82 Uneven text length across parallel elements
|
|
59
60
|
❌ Three feature columns of two, four and three lines
|
|
@@ -89,6 +90,7 @@ Whichever you choose, be consistent across sibling elements — a list where thr
|
|
|
89
90
|
Interface text is read in fragments, at a glance, in a space someone else's content has to fit too. An em dash is a pause the reader has to interpret: it stands in for a comma, a colon, a bracket or a full stop, and which one it is only becomes clear after reading past it. The punctuation that says exactly one thing is faster.
|
|
90
91
|
It is also the clearest tell of machine-written copy. Generated text reaches for the em dash far more often than a person does, and readers have learned to notice. Copy that reads as generated is copy the reader trusts less, whatever it says.
|
|
91
92
|
This is about interface strings: labels, buttons, headings, errors, empty states, help text, and the prose a page ships. Markdown counts where a framework renders it as a page, which is most of them (`src/content`, `content/`, MDX routes). A repository document does not: `README.md`, `CHANGELOG.md`, `AGENTS.md` and their kin are written for whoever works on the code, and so are your commit messages and this file.
|
|
93
|
+
A verbatim quotation keeps its own punctuation. Text a page quotes from another source (a document, a spec, a person), marked as a quotation with `<blockquote>` or `<q>`, is that source's words, and changing them to satisfy this rule would misquote it. The page's own copy around the quotation, and any heading or label it gives it, is still held to the rule.
|
|
92
94
|
The en dash keeps its one job: ranges, where it is read as "to" (`2–10 seats`, `Mon–Fri`). That is not a pause, and it is not affected.
|
|
93
95
|
|
|
94
96
|
### I-87 Inconsistent vocabulary
|
|
@@ -111,6 +113,7 @@ Users assume different words mean different things, because in a well-built inte
|
|
|
111
113
|
Screen reader users pull up a list of every link on a page; a list of "learn more" is useless. Sighted users scanning have to read the surrounding text to work out where each one goes. Three identical links also imply one destination.
|
|
112
114
|
"Click here" is worse still: it explains a mechanism people already understand, and it is wrong for anyone on touch, keyboard or voice.
|
|
113
115
|
Often the cleanest fix is to drop the link and make the **heading** the link.
|
|
116
|
+
**Buttons too.** "Get started", "Learn more", "Try it free" on every call to action say that something happens, not what. Name the outcome: "Create a workspace", "Search your first log file", "Book a 20-minute demo". A button whose label would fit any product is a button the reader has to decode.
|
|
114
117
|
|
|
115
118
|
### I-57 Actions and text centred by default
|
|
116
119
|
❌ Centred buttons and centred body text as a general habit
|
package/rules.index.json
CHANGED
|
@@ -912,5 +912,106 @@
|
|
|
912
912
|
"severity": "note",
|
|
913
913
|
"since": "0.14.0",
|
|
914
914
|
"pass": "code"
|
|
915
|
+
},
|
|
916
|
+
{
|
|
917
|
+
"id": "A-135",
|
|
918
|
+
"bucket": "hybrid",
|
|
919
|
+
"severity": "warning",
|
|
920
|
+
"since": "0.17.0",
|
|
921
|
+
"detector": "heading-kicker",
|
|
922
|
+
"pass": "code"
|
|
923
|
+
},
|
|
924
|
+
{
|
|
925
|
+
"id": "A-136",
|
|
926
|
+
"bucket": "hybrid",
|
|
927
|
+
"severity": "warning",
|
|
928
|
+
"since": "0.17.0",
|
|
929
|
+
"detector": "eyebrow-chip",
|
|
930
|
+
"pass": "code"
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
"id": "A-137",
|
|
934
|
+
"bucket": "hybrid",
|
|
935
|
+
"severity": "warning",
|
|
936
|
+
"since": "0.17.0",
|
|
937
|
+
"detector": "cream-palette",
|
|
938
|
+
"pass": "code"
|
|
939
|
+
},
|
|
940
|
+
{
|
|
941
|
+
"id": "A-138",
|
|
942
|
+
"bucket": "hybrid",
|
|
943
|
+
"severity": "warning",
|
|
944
|
+
"since": "0.17.0",
|
|
945
|
+
"detector": "italic-serif-display",
|
|
946
|
+
"pass": "code"
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
"id": "A-139",
|
|
950
|
+
"bucket": "hybrid",
|
|
951
|
+
"severity": "warning",
|
|
952
|
+
"since": "0.17.0",
|
|
953
|
+
"detector": "side-tab-border",
|
|
954
|
+
"pass": "code"
|
|
955
|
+
},
|
|
956
|
+
{
|
|
957
|
+
"id": "A-140",
|
|
958
|
+
"bucket": "hybrid",
|
|
959
|
+
"severity": "warning",
|
|
960
|
+
"since": "0.17.0",
|
|
961
|
+
"detector": "rounded-accent-border",
|
|
962
|
+
"pass": "code"
|
|
963
|
+
},
|
|
964
|
+
{
|
|
965
|
+
"id": "A-141",
|
|
966
|
+
"bucket": "judgment",
|
|
967
|
+
"severity": "warning",
|
|
968
|
+
"since": "0.17.0",
|
|
969
|
+
"pass": "screen"
|
|
970
|
+
},
|
|
971
|
+
{
|
|
972
|
+
"id": "A-142",
|
|
973
|
+
"bucket": "judgment",
|
|
974
|
+
"severity": "warning",
|
|
975
|
+
"since": "0.17.0",
|
|
976
|
+
"pass": "screen"
|
|
977
|
+
},
|
|
978
|
+
{
|
|
979
|
+
"id": "A-143",
|
|
980
|
+
"bucket": "hybrid",
|
|
981
|
+
"severity": "warning",
|
|
982
|
+
"since": "0.17.0",
|
|
983
|
+
"detector": "decorative-grid-background",
|
|
984
|
+
"pass": "code"
|
|
985
|
+
},
|
|
986
|
+
{
|
|
987
|
+
"id": "A-144",
|
|
988
|
+
"bucket": "hybrid",
|
|
989
|
+
"severity": "warning",
|
|
990
|
+
"since": "0.17.0",
|
|
991
|
+
"detector": "glow-accent",
|
|
992
|
+
"pass": "code"
|
|
993
|
+
},
|
|
994
|
+
{
|
|
995
|
+
"id": "G-145",
|
|
996
|
+
"bucket": "hybrid",
|
|
997
|
+
"severity": "warning",
|
|
998
|
+
"since": "0.17.0",
|
|
999
|
+
"detector": "pulsing-dot",
|
|
1000
|
+
"pass": "code"
|
|
1001
|
+
},
|
|
1002
|
+
{
|
|
1003
|
+
"id": "A-146",
|
|
1004
|
+
"bucket": "hybrid",
|
|
1005
|
+
"severity": "warning",
|
|
1006
|
+
"since": "0.17.0",
|
|
1007
|
+
"detector": "numbered-section-labels",
|
|
1008
|
+
"pass": "code"
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
"id": "F-147",
|
|
1012
|
+
"bucket": "judgment",
|
|
1013
|
+
"severity": "warning",
|
|
1014
|
+
"since": "0.17.0",
|
|
1015
|
+
"pass": "screen"
|
|
915
1016
|
}
|
|
916
1017
|
]
|
|
@@ -965,9 +965,13 @@ every rule.
|
|
|
965
965
|
probe in the browser and save what it returns beside the verdicts:
|
|
966
966
|
|
|
967
967
|
```
|
|
968
|
-
# If a browser is on this machine, one command renders and records
|
|
968
|
+
# If a browser is on this machine, one command renders and records every width:
|
|
969
969
|
{{scripts_path}} probe --run <page> --save <surface>
|
|
970
970
|
|
|
971
|
+
# A built site that links its styles from the site root (/assets/site.css) must be
|
|
972
|
+
# served, or nothing loads. Name the build directory and the CLI serves it:
|
|
973
|
+
{{scripts_path}} probe --run dist/pricing/index.html --serve dist --save <surface>
|
|
974
|
+
|
|
971
975
|
# Otherwise, at each width: open the page in whatever browser you have, evaluate
|
|
972
976
|
# what `{{scripts_path}} probe` prints, and pipe exactly what it returned into:
|
|
973
977
|
{{scripts_path}} probe --save <surface>
|
|
@@ -976,7 +980,8 @@ probe in the browser and save what it returns beside the verdicts:
|
|
|
976
980
|
`--run` drives a headless Chrome, Chromium or Edge it finds for itself — a
|
|
977
981
|
project with Playwright or Puppeteer already has one — and needs nothing
|
|
978
982
|
installed. The Stop hook runs it too, before it judges a review, so a render is
|
|
979
|
-
not a step anyone can forget.
|
|
983
|
+
not a step anyone can forget. Do not copy the build and rewrite its links to make
|
|
984
|
+
a file load: `--serve` shows the page a browser actually gets.
|
|
980
985
|
|
|
981
986
|
**The CLI writes the probe file, you do not.** `--save` reads the probe's output,
|
|
982
987
|
stamps it with the page's checksum, and stores it. That is what makes the file a
|
|
@@ -1182,7 +1187,9 @@ Not a command to run. In Claude Code, `install` adds a Stop hook that runs
|
|
|
1182
1187
|
`check` error or warning, or a critique's verdict files fail `verdicts`, it refuses
|
|
1183
1188
|
to let you stop and hands you the failures as the reason. **Fix what it names.** Do
|
|
1184
1189
|
not edit verdict files to satisfy it, and do not report the work as done while it is
|
|
1185
|
-
blocking.
|
|
1190
|
+
blocking. It also refuses when a critique's verdict files changed after `critique`
|
|
1191
|
+
wrote them, in a session that did not run `critique`: a fix is judged by the next
|
|
1192
|
+
review, never by the builder editing the last one.
|
|
1186
1193
|
|
|
1187
1194
|
A warning you are sure is right as it stands, because the detector guessed and the
|
|
1188
1195
|
guess is wrong here, is waived on its own line or the line above, in the file's
|