trykeyline 0.1.0 → 0.1.2

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.
Files changed (73) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/LICENSE +0 -16
  3. package/bin/keyline.mjs +2 -1
  4. package/package.json +1 -1
  5. package/plugins/keyline/.claude-plugin/plugin.json +1 -1
  6. package/plugins/keyline/plugin.json +22 -0
  7. package/plugins/keyline/skills/keyline/SKILL.md +22 -32
  8. package/plugins/keyline/skills/keyline/commands/audit.md +8 -3
  9. package/plugins/keyline/skills/keyline/commands/critique.md +3 -26
  10. package/plugins/keyline/skills/keyline/commands/init.md +5 -4
  11. package/plugins/keyline/skills/keyline/commands/polish.md +2 -2
  12. package/plugins/keyline/skills/keyline/commands/refine.md +6 -16
  13. package/plugins/keyline/skills/keyline/commands/sync.md +1 -3
  14. package/plugins/keyline/skills/keyline/commands/ux-laws.md +26 -0
  15. package/plugins/keyline/skills/keyline/domains/color/deriving-a-ramp.md +1 -1
  16. package/plugins/keyline/skills/keyline/domains/color/guide.md +10 -46
  17. package/plugins/keyline/skills/keyline/domains/color/status-and-exceptions.md +2 -2
  18. package/plugins/keyline/skills/keyline/domains/depth/elevation-recipes.md +2 -8
  19. package/plugins/keyline/skills/keyline/domains/depth/guide.md +12 -41
  20. package/plugins/keyline/skills/keyline/domains/layout/guide.md +31 -56
  21. package/plugins/keyline/skills/keyline/domains/layout/measure-and-rhythm.md +16 -16
  22. package/plugins/keyline/skills/keyline/domains/layout/radius-lines-and-layers.md +2 -2
  23. package/plugins/keyline/skills/keyline/domains/motion/guide.md +12 -47
  24. package/plugins/keyline/skills/keyline/domains/motion/patterns.md +1 -1
  25. package/plugins/keyline/skills/keyline/domains/motion/performance.md +1 -1
  26. package/plugins/keyline/skills/keyline/domains/patterns/guide.md +20 -62
  27. package/plugins/keyline/skills/keyline/domains/patterns/iteration.md +1 -9
  28. package/plugins/keyline/skills/keyline/domains/type/guide.md +13 -37
  29. package/plugins/keyline/skills/keyline/domains/type/ladders.md +1 -1
  30. package/plugins/keyline/skills/keyline/domains/type/type-audit.md +5 -8
  31. package/plugins/keyline/skills/keyline/glass/color/assets/color-tokens.css +34 -165
  32. package/plugins/keyline/skills/keyline/glass/color/assets/theme.ts +10 -70
  33. package/plugins/keyline/skills/keyline/glass/color/guide.md +3 -30
  34. package/plugins/keyline/skills/keyline/glass/color/intent-ramp.md +2 -34
  35. package/plugins/keyline/skills/keyline/glass/color/theming.md +5 -72
  36. package/plugins/keyline/skills/keyline/glass/depth/assets/GlassPanel.tsx +14 -121
  37. package/plugins/keyline/skills/keyline/glass/depth/assets/glass.css +29 -188
  38. package/plugins/keyline/skills/keyline/glass/depth/depth-cues.md +1 -16
  39. package/plugins/keyline/skills/keyline/glass/depth/guide.md +4 -73
  40. package/plugins/keyline/skills/keyline/glass/depth/performance-and-fallbacks.md +3 -11
  41. package/plugins/keyline/skills/keyline/glass/layout/adaptive-presentations.md +5 -91
  42. package/plugins/keyline/skills/keyline/glass/layout/assets/SectionNav.tsx +3 -6
  43. package/plugins/keyline/skills/keyline/glass/layout/assets/layout-tokens.css +11 -138
  44. package/plugins/keyline/skills/keyline/glass/layout/guide.md +10 -15
  45. package/plugins/keyline/skills/keyline/glass/layout/measure-and-rhythm.md +4 -6
  46. package/plugins/keyline/skills/keyline/glass/layout/radius-and-elevation.md +3 -2
  47. package/plugins/keyline/skills/keyline/glass/motion/assets/DropMenu.tsx +4 -6
  48. package/plugins/keyline/skills/keyline/glass/motion/assets/motion-presets.ts +10 -184
  49. package/plugins/keyline/skills/keyline/glass/motion/assets/motion-tokens.css +21 -105
  50. package/plugins/keyline/skills/keyline/glass/motion/easing-and-duration.md +2 -31
  51. package/plugins/keyline/skills/keyline/glass/motion/guide.md +10 -41
  52. package/plugins/keyline/skills/keyline/glass/motion/reduced-motion.md +1 -7
  53. package/plugins/keyline/skills/keyline/glass/motion/scroll-performance.md +3 -32
  54. package/plugins/keyline/skills/keyline/glass/motion/shared-element.md +1 -1
  55. package/plugins/keyline/skills/keyline/glass/motion/sound.md +10 -80
  56. package/plugins/keyline/skills/keyline/glass/type/assets/prose.tsx +8 -43
  57. package/plugins/keyline/skills/keyline/glass/type/assets/type-tokens.css +49 -86
  58. package/plugins/keyline/skills/keyline/glass/type/guide.md +5 -7
  59. package/plugins/keyline/skills/keyline/glass/type/long-form-prose.md +9 -74
  60. package/plugins/keyline/skills/keyline/glass/type/scale.md +2 -6
  61. package/plugins/keyline/skills/keyline/glass/type/weight-and-tracking.md +4 -20
  62. package/plugins/keyline/skills/keyline/scripts/check/css.mjs +5 -14
  63. package/plugins/keyline/skills/keyline/scripts/check/design.mjs +3 -1
  64. package/plugins/keyline/skills/keyline/scripts/check/files.mjs +70 -26
  65. package/plugins/keyline/skills/keyline/scripts/check/markup.mjs +3 -3
  66. package/plugins/keyline/skills/keyline/scripts/check/rules.mjs +9 -1
  67. package/plugins/keyline/skills/keyline/scripts/check.mjs +6 -3
  68. package/plugins/keyline/skills/keyline/scripts/contrast.mjs +2 -0
  69. package/plugins/keyline/skills/keyline/scripts/scan.mjs +29 -45
  70. package/plugins/keyline/skills/keyline/system/design-md.md +4 -4
  71. package/plugins/keyline/skills/keyline/system/guide.md +4 -8
  72. package/plugins/keyline/skills/keyline/system/presets.md +16 -16
  73. package/plugins/keyline/skills/keyline/system/roles.md +8 -8
@@ -6,12 +6,7 @@ The constraint also makes the few places motion *is* spent land hard, because no
6
6
 
7
7
  ## Before you start
8
8
 
9
- Read `DESIGN.md` at the project root: `## Profile` for the preset and any areas on another one, `## Motion` for the project's name for each role and the decisions already made. Reason in roles; write code in the project's names.
10
-
11
- - **No `DESIGN.md`:** run `/keyline init` for this domain only, do the task, then recommend the full setup.
12
- - **A role you need has no row:** reuse a value already doing that job; otherwise add one in the project's naming style and record it in `DESIGN.md`.
13
- - **The task would break a law or move a dial off its preset:** say so and ask; if the user confirms, record it with its reason under `## Decisions`.
14
- - **Preset `glass`:** the values are in `DESIGN.md`; `glass/motion/guide.md` has the exact recipes behind them.
9
+ Reads `DESIGN.md` `## Motion`.
15
10
 
16
11
  ## Laws
17
12
 
@@ -21,7 +16,7 @@ Read `DESIGN.md` at the project root: `## Profile` for the preset and any areas
21
16
 
22
17
  **Paused means still.** A loop reporting a live state — level bars on a recorder, a stroke travelling round a card while something works — stops or settles low when that state pauses; still moving, it claims the thing is still listening. Only a progress indicator loops through a wait, because waiting is its state. On hover or focus a live indicator morphs in place into the action that ends it — bars into a stop square — so the control's next move appears where the eye already is.
23
18
 
24
- Hover travel answers nothing either: a card rising under the pointer claims it moved, or can be picked up, and a grid of them rising in turn reads as a marketing page. Hover is a wash; how far, if at all, a preset lets a hovered thing travel is a dial.
19
+ Hover travel answers nothing either: a card rising under the pointer claims it moved, or can be picked up. Hover is a wash; how far, if at all, a preset lets a hovered thing travel is a dial (`domains/motion/ladder-and-curves.md`).
25
20
 
26
21
  → Live indicators, pausing and the morph to their action: `domains/motion/patterns.md`
27
22
 
@@ -38,7 +33,7 @@ Hover travel answers nothing either: a card rising under the pointer claims it m
38
33
  | `duration.overlay` | A large region entering or collapsing — a sheet, a panel, a reveal |
39
34
  | `duration.theme` | Everything on the page at once |
40
35
 
41
- An element tied to a larger change takes its tier: a disclosure chevron runs at `duration.popover` and lands with the panel opening below it. Hover is no exception: a wash across a large card at `duration.micro`'s 80ms looks instant, not smooth, where 200ms in and 250ms out reads as a fade; rows swept in bulk keep the short tier (Dials). `duration.route` is the one tier size doesn't set; law 3 caps it. The numbers are dials; the ordering is not.
36
+ An element tied to a larger change takes its tier: a disclosure chevron runs at `duration.popover` and lands with the panel opening below it. Hover is no exception: a wash across a large card takes a longer tier than a row swept in bulk (Dials). `duration.route` is the one tier size doesn't set; law 3 caps it. The numbers are dials; the ordering is not.
42
37
 
43
38
  **`duration.theme` runs only when the page itself switches theme**, under a class held on the root for the length of the switch. Everyday colour transitions leave out the page background and are suspended during a theme change: otherwise an OS switch by media query, which has no class to hold, cross-fades every control at its own duration while the page around it snaps.
44
39
 
@@ -78,7 +73,7 @@ Springs are wrong for discrete flips with no travel, anything that must land in
78
73
 
79
74
  ### 7. Exits get out of the way
80
75
 
81
- **Closing is faster than opening, flat, and eases in — no spring, no overshoot, and it travels less far.** Opening answers the user's intent and can afford character; closing comes after they've moved on, so a bounce reads as reluctance and stands in their way, and a disappearing element needn't travel the full distance. In the Glass preset a menu opens on a spring of about 400ms and closes on a 130ms `ease-in` tween. The law is for things leaving; a hover wash fading behind a pointer that has already moved on stands in nobody's way, so its leave may run a little longer than its arrival, and a sweep across a board fades rather than strobes.
76
+ **Closing is faster than opening, flat, and eases in — no spring, no overshoot, and it travels less far.** Opening answers the user's intent and can afford character; closing comes after they've moved on, so a bounce reads as reluctance and stands in their way, and a disappearing element needn't travel the full distance. In the Glass preset a menu opens on a spring of about 400ms and closes on a 130ms `ease-in` tween. The law is for things leaving; a hover wash fading behind a pointer that has already moved on stands in nobody's way, so its leave may run longer than its arrival.
82
77
 
83
78
  ### 8. Where identity persists, the element travels
84
79
 
@@ -92,29 +87,19 @@ A view switch keeps the reader's place (layout law 9), so **measure the reading
92
87
 
93
88
  **Where a group shows both a current item and the one under the pointer — a nav, a segmented control — one highlight travels with the pointer and comes to rest on the current item, rather than a second treatment appearing beside the first.** What current looks like is colour law 7's, never re-picked here, and weight never changes on hover (type law 5). Previewing a choice and reporting one are the same gesture a beat apart, so they share one moving shape (law 8): two signals each doing half the job, never one doing both. Outside such a group, hover is a wash (the dials). Nothing is filled because it looks better.
94
89
 
95
- A filled icon is the same outline with its interior painted, cross-faded in place at `duration.popover` on `ease.colour`, so the silhouette never moves. Marks that can't take it — a brand's official solid mark, a chevron with no interior, a lone toggle with nothing to be picked out from — signal another way. **Fill that distinguishes nothing is just weight.** Nor is a step from `text.secondary` to `text.primary` a signal; the colour guide covers why.
90
+ A filled icon is the same outline with its interior painted, cross-faded in place at `duration.popover` on `ease.colour`, so the silhouette never moves. Marks that can't take it — a brand's official solid mark, a chevron with no interior, a lone toggle with nothing to be picked out from — signal another way. **Fill that distinguishes nothing is just weight.** Nor is a step from `text.secondary` to `text.primary` a signal (colour law 6).
96
91
 
97
92
  ### 10. Reduced motion is a branch with a substitute
98
93
 
99
94
  **Every animated component reads `prefers-reduced-motion` and takes its own reduced path that still communicates the change; a global off switch is a regression.** The preference exists because large-area and accelerating motion causes nausea and dizziness, not to remove visible state: `* { animation: none !important }` strips the signals with the decoration, and a menu appears with no sign it opened.
100
95
 
101
- | Original | Reduced path |
102
- |---|---|
103
- | Spring slide, re-targeting highlight | A flat tween at `duration.state`; the move stays legible |
104
- | Scale or squash entrance | A plain fade |
105
- | Blur cross-fade | A cross-fade; the blur is the trigger, not the fade |
106
- | Reveal-on-scroll, route fade, ambient loop | Off, with content in its final visible state |
107
- | Theme cross-fade, programmatic smooth scroll | Instant |
108
- | Press scale at `duration.instant` | Kept: too small and short to matter |
109
- | Set piece | Skipped entirely, not softened |
110
-
111
96
  Imperative APIs — `scrollIntoView`, `scrollTo`, `Element.animate`, a GSAP timeline, a canvas loop — don't read the preference; pass it in. Motion's `MotionConfig reducedMotion="user"` and React Spring's `skipAnimation` are a floor, not the plan.
112
97
 
113
98
  → Every substitute in CSS, Motion, GSAP and React Spring, and how to test: `domains/motion/guards.md`
114
99
 
115
100
  ### 11. Motion never owns whether content is visible
116
101
 
117
- **Any style that hides content until an animation runs must resolve to visible whenever that animation can't run** — no JavaScript, no observer, reduced motion, print, or an element too tall to trip its trigger. The failure is not a plainer page but a correct document the reader can't see. Two such bugs on the Glass preset's reference site left content at opacity 0: an observer threshold of 0.1 that no section over ten viewports tall could reach, and an externally added `in` class the framework rewrote out of a state-dependent `className`.
102
+ **Any style that hides content until an animation runs must resolve to visible whenever that animation can't run** — no JavaScript, no observer, reduced motion, print, or an element too tall to trip its trigger. The failure is not a plainer page but a correct document the reader can't see. The usual causes: an observer threshold a very tall section can't reach, and a class added outside the framework, which a re-render rewrites away.
118
103
 
119
104
  So reveals are one-shot, with a zero threshold and a root margin, re-observed on client navigation, marked by a data attribute the framework doesn't own, and reset by a `<noscript>` style and a print rule. A collapsible animates its height but stays mounted (layout law 11).
120
105
 
@@ -124,7 +109,7 @@ So reveals are one-shot, with a zero threshold and a root margin, re-observed on
124
109
 
125
110
  ### 13. Animate what the compositor can; escalate one element, never a view
126
111
 
127
- **Animate `transform` and `opacity`, list every transitioned property by name, justify any layout property you animate, and when the DOM can't carry a transition, move one decorative element to the GPU — never the view.** `transition: all` catches properties you never meant, including layout ones a parent adds later. Animating `width` is defensible only where nothing reflows, as on one absolutely positioned element outside the flow; otherwise use a transform, or the `0fr`↔`1fr` grid track to "animate to auto". A transform on an ancestor weakens `backdrop-filter` beneath it, so the moving wrapper and the translucent surface are separate elements (the depth guide has the material side).
112
+ **Animate `transform` and `opacity`, list every transitioned property by name, justify any layout property you animate, and when the DOM can't carry a transition, move one decorative element to the GPU — never the view.** `transition: all` catches properties you never meant, including layout ones a parent adds later. Animating `width` is defensible only where nothing reflows, as on one absolutely positioned element outside the flow; otherwise use a transform, or the `0fr`↔`1fr` grid track to "animate to auto". The moving wrapper and a translucent surface are separate elements (depth law 9).
128
113
 
129
114
  GPU escalation is for one travelling element with no semantics of its own, never a view of tiles, whose semantics a canvas can't carry and whose textures cost hundreds of megabytes; its setup returns `null` rather than throwing, the path reduced motion and no-WebGL already take.
130
115
 
@@ -144,6 +129,7 @@ GPU escalation is for one travelling element with no semantics of its own, never
144
129
 
145
130
  | Dial | editorial | product | expressive |
146
131
  |---|---|---|---|
132
+ | `duration.instant` | 0 | 0 | 0 |
147
133
  | `duration.micro` | 100ms | 80ms | 120ms |
148
134
  | `duration.state` | 150ms | 100ms | 200ms |
149
135
  | `duration.popover` | 200ms | 150ms | 280ms |
@@ -198,37 +184,16 @@ Collect evidence, then report:
198
184
 
199
185
  Then tab through with reduced motion on, and with JavaScript off, for anything that never appears or appears with no sign it opened. Check hover states on *leave* as well as enter; the leave state is where transition-property mistakes live.
200
186
 
201
- Report each breach as one line under `## Open issues`, with the law, the evidence and the reach: *"Law 11 — `.fade-up` has no `<noscript>` reset; content blank with JS off. 23 components."*
202
-
203
187
  ## Writing back to DESIGN.md
204
188
 
205
189
  Under `## Motion`: ladder rows (role, name here, value, used for); curves; springs by character; the reduced-motion policy in a sentence; each off-ladder entry as a row in the **Named exemptions** table (exemption, values, why — its law 4 class and where it plays), which the detector reads; the sound policy, if any.
206
190
 
207
- Under `## Decisions`, dated, with a reason: any dial moved off the preset (*"`duration.state` 150ms, not 100ms: the ledger's hover rows felt abrupt in testing"*), a set piece added, sound turned on, a law knowingly broken.
208
-
209
- Under `## Open issues`: each audit breach, one line with its evidence, removed when fixed; don't fix beyond the task unasked.
191
+ Under `## Decisions`, besides moved dials: a set piece added, sound turned on.
210
192
 
211
193
  ## Checklist
212
194
 
213
- - [ ] Every animation answers "what changed?"; only an ongoing state loops, and it holds still when that state pauses.
214
195
  - [ ] Live indicators morph in place to their action on hover and focus.
215
- - [ ] `duration.*` tiers strictly ascend; the area changed sets each animation's tier, hover included.
216
- - [ ] Hover on cards is a wash at the dial's in · out, with no travel unless the preset allows it.
217
- - [ ] Press lands on `pointerdown` at `duration.instant`; anything wider than about 200px presses with `scale(0.98)` or a 1px translate; nothing repeated waits; `duration.route` ≤ `duration.overlay`.
218
- - [ ] `duration.theme` runs only under the page's own switch class; everyday colour transitions leave out the page background and are suspended while the theme changes.
219
- - [ ] Each off-ladder duration is classed subject, set piece or ambient in `DESIGN.md`.
220
- - [ ] One `ease.standard`; colour and opacity on `ease.colour`; no overshoot on a filter or floored property.
221
- - [ ] Springs are `spring.<character>`, only where things re-target or are grabbed, opacity split out.
222
- - [ ] Exits are shorter than entrances, flat, on `ease.exit`, no spring.
223
- - [ ] Shared elements re-target, never remount, and skip their mount animation; layout switches keep the reading position.
224
- - [ ] In a group with a current item, one highlight travels between hovered and current; current looks as colour law 7 says; no weight change on hover; marks that can't take a fill signal another way.
225
- - [ ] Each animated component has a reduced path that still shows the change; imperative APIs get the preference.
226
- - [ ] Hidden-until-animated content survives no JS, no observer, reduced motion, print, tall elements and re-renders.
227
- - [ ] Scroll work is passive, rAF-collapsed and hysteretic, and never hides what touch can't restore.
228
- - [ ] Only `transform` and `opacity` animate, each property named; GPU escalation is one element and fails to `null`.
229
- - [ ] Hover-opened elements get a close grace or bridge and cleared timers; focus reveals what hover does.
230
- - [ ] Set pieces play once a session, gate before first paint, record "seen" on completion, never block the skip link.
231
- - [ ] Durations, curves and springs match the preset, or a `## Decisions` entry says why not.
232
- - [ ] Reveal travel, press feedback and route transitions match the preset.
233
- - [ ] Set pieces and ambient loops are within the preset's allowance; a loop nothing can pause off screen is finite or gone.
196
+ - [ ] `duration.theme` runs only under the page's own switch class; everyday colour transitions leave out the page background.
197
+ - [ ] Shared elements skip their mount animation; layout switches keep the reading position.
198
+ - [ ] Set pieces record "seen" on completion and never block the skip link.
234
199
  - [ ] Sound only if the preset allows it, off by default behind a visible, persisted toggle.
@@ -126,7 +126,7 @@ A wash, with the leave on the resting rule and the arrival on the hovered one, s
126
126
  .card:hover { background-color: var(--surface-hover); transition-duration: <hover in>; }
127
127
  ```
128
128
 
129
- No `translate`, no shadow change, one named property; the bundle stays where the depth guide put it. Rows keep `duration.micro` both ways.
129
+ Why a wash, and the timings: `domains/motion/ladder-and-curves.md`.
130
130
 
131
131
  ## Press
132
132
 
@@ -97,7 +97,7 @@ const update = () => {
97
97
 
98
98
  The same works on columns with `min-width: 0`. Where supported, `interpolate-size: allow-keywords` lets `height: auto` transition directly.
99
99
 
100
- **Transforms and `backdrop-filter`.** A transform on an ancestor creates a new containing block and weakens or breaks the blur beneath it, so the moving wrapper and the translucent surface are different elements; the depth guide covers how much the tint carries while it animates.
100
+ **Transforms and `backdrop-filter`.** The moving wrapper and a translucent surface are different elements (depth law 9).
101
101
 
102
102
  **`will-change`** only on an element about to animate, removed after; left on everything, it pins memory for layers that never move.
103
103
 
@@ -6,12 +6,7 @@ The laws hold under every preset. How much keyboard, how much is remembered and
6
6
 
7
7
  ## Before you start
8
8
 
9
- Read `DESIGN.md` at the project root: `## Profile` for the preset and any areas on another one, `## Patterns` for the conventions already chosen — the product's nouns, its destructive pattern, its shortcut map, which views are remembered — and `## Decisions`. Write copy in the project's nouns.
10
-
11
- - **No `DESIGN.md`:** run `/keyline init` for the profile and preset only, do the task, then add a `## Patterns` section and recommend the full setup.
12
- - **A pattern you need has no entry:** find the nearest one already in the code and reuse it; otherwise define it once and record it under `## Patterns`.
13
- - **The task would break a law or move a dial off its preset:** say so and ask; if the user confirms, record it with its reason under `## Decisions`.
14
- - **Preset `glass`:** it carries no pattern values; use the dials of the preset nearest the reader.
9
+ Reads `DESIGN.md` `## Patterns`: the product's nouns, its destructive pattern, its shortcut map, which views are remembered. Write copy in the project's nouns.
15
10
 
16
11
  Boundaries: how a label or figure is set is the type guide's; what colour danger, brand and status take, the colour guide's; where things sit and which views fork, the layout guide's; what a dialog or card is built from, the depth guide's; how long anything takes to move, the motion guide's. This guide decides the flow and the words, and names the owner where the rest is someone else's.
17
12
 
@@ -19,15 +14,15 @@ Boundaries: how a label or figure is set is the type guide's; what colour danger
19
14
 
20
15
  ### 1. A label is as short as its action, in sentence case
21
16
 
22
- **A label names the action in the fewest words that still say it: a button reads its verb — "Send", "Save changes", "Record" — not the sentence around it ("Record a voice note").** Labels are scanned, not read; every extra word moves the verb away from where the eye lands, and the tail is the first thing to truncate. Short is not narrow: a label covers everything behind it, so an item that takes questions, bug reports and requests is "Help & feedback", not "Request a feature". Add an object only where two verbs on one screen would otherwise collide ("Save draft" beside "Publish"); a confirm button repeats its dialog's verb ("Delete project"), never "OK" or "Yes".
17
+ **A label names the action in the fewest words that still say it: a button reads its verb — "Send", "Save changes", "Record" — not the sentence around it ("Record a voice note").** Labels are scanned, not read; every extra word moves the verb away from where the eye lands, and the tail is the first thing to truncate. A confirm button repeats its dialog's verb, never "OK" or "Yes".
23
18
 
24
- **Sentence case everywhere** — buttons, menu items, dialog titles, field and column names, tabs, toasts, and labels the code generates from keys (`dueDate` → "Due date"). Title case marks every word as a name, and uppercase strips word shapes; acronyms, proper nouns and third-party product names stay as written.
19
+ **Sentence case everywhere**, labels the code generates from keys included. Title case marks every word as a name, and uppercase strips word shapes. The one display exception is an `expressive` eyebrow, written in sentence case and set in capitals by `text-transform` (type law 7).
25
20
 
26
21
  ### 2. A row's second line says what it is and where it's from
27
22
 
28
23
  **Under a title, the second line names the record's type and origin in the product's own nouns — "Task from Riverside", "Note from site walk" — never an event ("Issue added to Riverside").** The title says which one; the reader's next questions are what kind and from where, and those let them triage without opening it. Event phrasing describes something that happened to the record once, in a verb nobody filters by, and often swaps the noun, so the reader wonders whether an "issue" here is a "task" everywhere else. **One noun per kind of thing across the product**, listed in `DESIGN.md`. Time, owner and status take their own slots — a column, a meta line, a glyph — not the sentence. The title stays the strongest line in the row (the type guide, the colour guide).
29
24
 
30
- → Before-and-after labels, button verbs, generated labels, toasts and error copy: `domains/patterns/copy.md`
25
+ → Before-and-after labels, when a label takes an object, generated labels and their acronyms, toasts and error copy: `domains/patterns/copy.md`
31
26
 
32
27
  ### 3. Destructive and session-ending actions look alike, and come last
33
28
 
@@ -35,11 +30,7 @@ Boundaries: how a label or figure is set is the type guide's; what colour danger
35
30
 
36
31
  ### 4. A confirmation says what is lost, in the system's own dialog
37
32
 
38
- **A confirmation states in plain words what will be lost, uses the product's standard dialog, and asks for proof of intent in proportion to the loss.** The dialog is the system's own (depth law 1). Proportion:
39
-
40
- - **Reversible** — no dialog; do it, and offer Undo in the toast.
41
- - **Irreversible, one item** — a dialog whose button repeats the verb.
42
- - **Irreversible and large** — a project, an account, a workspace — "Type DELETE to confirm", compared case-insensitively, the input focused on open (not Cancel), the action disabled until it matches, "Deleting…" while it runs, and the dialog held open until it succeeds or says why it failed.
33
+ **A confirmation states in plain words what will be lost, uses the product's standard dialog, and asks for proof of intent in proportion to the loss.** The dialog is the system's own (depth law 1). Proof runs from none for the reversible, which gets Undo, to a typed word for a project, an account or a workspace (→ `domains/patterns/actions-and-forms.md`).
43
34
 
44
35
  **One pattern everywhere**: deleting a project and deleting an account read the same. On close, focus goes back to what opened it (depth law 12).
45
36
 
@@ -65,7 +56,7 @@ The conversation carries no avatar per message: the reader's turns sit right-ali
65
56
 
66
57
  **A launcher offers only the choices the task has, and a toolbar or a long list holds back what isn't needed every time.** Every option on the path of the most frequent action is something to read before starting: a record launcher asks one question with two answers — which project, or a personal note — not a form. Toolbar filters are icons with tooltips, not a text label on every control. A long admin list groups into collapsed bars, closed by default, each with a summary of what the reader would open it to check ("Admins · 3 of 12 with access"), and a search opens every group it matches.
67
58
 
68
- **Hick's law** is why: the time to choose grows with the number of choices, so every option shown up front taxes the choice the reader came to make. **Progressive disclosure** is the cure — the common path visible, the rest one step away (layout law 11 keeps it in the markup). **Miller's law** says why grouping helps: people hold only a few chunks in mind at once, so a flat run of a dozen nav items or a 16-digit code reads as noise, where three groups of four, or `4242 4242 4242 4242`, read at a glance. Chunk long lists and long strings; don't treat "seven" as a cap that cuts what the product needs.
59
+ **Hick's law** is why, and **progressive disclosure** the cure — the common path visible, the rest one step away (layout law 11 keeps it in the markup). By **Miller's law**, chunk long lists and long strings — three groups of four nav items, `4242 4242 4242 4242` — but don't treat "seven" as a cap that cuts what the product needs.
69
60
 
70
61
  ### 9. An empty state offers the first action
71
62
 
@@ -99,17 +90,13 @@ Whether the view and span are remembered is layout law 9; list each page that re
99
90
 
100
91
  **What an element looks like says what it does, and it does that: a play icon opens a video, a link goes somewhere, and anything that doesn't act doesn't react.** Landing pages and articles are where these promises break, because they are built to look a certain way before anyone decides what each piece does, and a reader who clicks a dead logo or a silent play icon stops trusting the rest. A logo that isn't a link takes no hover state; an email address is a `mailto:` link; "Copy link" is a button, not a link to the page already open; link text names its destination ("Pricing", not "Learn more"). A call to action names what happens next — "Start free", "Book a demo" — so two on one page can't be mistaken for each other, as "Get started" beside "Start free" can. A subscribe or sign-up field has a visible label and says what will arrive and how often.
101
92
 
102
- **Conventions are promises too** (Jakob's law): readers spend most of their time in other products and arrive expecting those to hold here. The logo goes home, search sits behind ⌘K or a magnifier, the account is at the end of the navigation, a link is underlined or obviously a link. Break a convention only for a gain the reader will feel on first use, and never for novelty alone.
93
+ **Conventions are promises too** (Jakob's law): the logo goes home, search sits behind ⌘K or a magnifier, the account is at the end of the navigation, a link is underlined or obviously a link. Break a convention only for a gain the reader will feel on first use, and never for novelty alone.
103
94
 
104
95
  → Calls to action, link text and sign-up lines: `domains/patterns/copy.md`
105
96
 
106
97
  ### 15. Every action answers at once, and anything slow says what it's doing
107
98
 
108
- **An input is acknowledged within 100ms — a press state, a toggle flipping, a row moving — and any result that takes longer than about 400ms shows what is happening in the place it was asked for.** Under 400ms (the Doherty threshold) the reader and the product keep pace and attention holds; past it, people start to wonder whether the click took, and click again. So the acknowledgement is instant and local (press feedback is the motion guide's), the slow part runs in the background where it can (law 11), and the wait is honest where it can't:
109
-
110
- - **Past ≈ 400ms**, the control says it's working — "Saving…" in the button's own label, the button held at its width.
111
- - **Past ≈ 1s**, the region that will change shows its shape — a skeleton of the rows coming, not a centred spinner over a blank page.
112
- - **Past ≈ 10s**, progress is determinate — a count, a step, a bar — with a way to cancel or leave and be told when it's done.
99
+ **An input is acknowledged within 100ms — a press state, a toggle flipping, a row moving — and any result that takes longer than about 400ms shows what is happening in the place it was asked for.** 400ms is the Doherty threshold. The acknowledgement is instant and local (press feedback is the motion guide's), the slow part runs in the background where it can (law 11), and the wait is honest where it can't, its form set by how long it runs (→ `domains/patterns/actions-and-forms.md`, pending states).
113
100
 
114
101
  **The loop closes where it opened**: the result appears where the action was taken, and what changed is visible without reading a toast. A save that succeeds silently, a filter that applies off screen, or an error reported in a corner the reader isn't looking at breaks the loop, and readers repeat the action to find out what happened.
115
102
 
@@ -117,13 +104,13 @@ Whether the view and span are remembered is layout law 9; list each page that re
117
104
 
118
105
  **Accept what people type in any reasonable form and store one canonical form; show choices rather than ask for things from memory; and let the product carry the complexity the reader would otherwise carry.** Three laws meet here:
119
106
 
120
- - **Postel's law** — be liberal in what you accept. A phone number with spaces, a date as "12 Mar", an email with a trailing space or capitals, a pasted card number with dashes: all are valid input, normalised on blur, never rejected for format. Errors are for what's actually wrong, and say how to fix it.
121
- - **Recognition over recall** — seeing beats remembering. Recent and frequent items lead a picker; a command palette lists what it can do before the reader types; labels stay visible above fields (a placeholder that vanishes on typing is recall); a filter shows its current value, not just its name.
122
- - **Tesler's law** — every task carries some complexity that can't be removed, only moved. Move it into the product: default to the likely answer, infer what can be inferred (the country from the postcode, the time zone from the device), and never ask for what the product already knows.
107
+ - **Postel's law** — spaces, dashes, case and a trailing space are valid input, normalised, never rejected for format. Errors are for what's actually wrong, and say how to fix it.
108
+ - **Recognition over recall** — recent and frequent items lead a picker; a command palette lists what it can do before the reader types; labels stay visible above fields; a filter shows its current value, not just its name.
109
+ - **Tesler's law** — default to the likely answer, infer what can be inferred, and never ask for what the product already knows.
123
110
 
124
111
  ### 17. A flow shows how near the end is, and the end is designed
125
112
 
126
- **A task of several steps shows where the reader is and how much is left, and its last screen is built as carefully as its first.** People speed up as a goal comes into view (the goal-gradient effect), so a visible "Step 2 of 3" or a checklist with items already ticked pulls them through, where an unmarked sequence of screens feels endless; count a step the reader has already done — the account they just made — as progress. **An experience is remembered by its most intense moment and its end** (the peak-end rule), not its average: a smooth setup that ends on a blank dashboard, or a purchase that ends on "Success", is remembered as flat. The end says what happened in the product's nouns, shows the result, and offers the next action; a failure at the peak — a payment declined, an upload lost — gets the most careful copy in the product, never a code.
113
+ **A task of several steps shows where the reader is and how much is left, and its last screen is built as carefully as its first.** A visible count pulls the reader through (the goal-gradient effect), and the end is what is remembered (the peak-end rule): it says what happened in the product's nouns, shows the result, and offers the next action. A failure at the peak — a payment declined, an upload lost — gets the most careful copy in the product, never a code.
127
114
 
128
115
  → Pending states, skeletons, forgiving inputs and progress in code: `domains/patterns/actions-and-forms.md`
129
116
 
@@ -133,7 +120,7 @@ Whether the view and span are remembered is layout law 9; list each page that re
133
120
  |---|---|---|---|
134
121
  | Label length | 1–3 words | 1–2 words, verb first | A call to action may run to a short phrase |
135
122
  | Keyboard | Tab order, skip link, Escape | Palette, list movement, single-key actions, shown in menus | Tab order and Escape |
136
- | View memory | Reset on arrival | Remembered per page on daily working pages, switch in the header (layout law 9) | Reset on arrival |
123
+ | View memory | Reset on arrival | Remembered per page on daily working pages (layout law 9) | Reset on arrival |
137
124
  | Optimistic updates | Rare | Default for moves, toggles, renames, reorders | Rare |
138
125
  | Proof of intent for large deletes | Typed word | Typed word | Typed word |
139
126
  | Empty-state copy | One sentence and the action | One line and the action | A headline, a line and the action |
@@ -144,22 +131,11 @@ The `glass` preset carries no pattern dials; take them from the preset nearest t
144
131
 
145
132
  **Keyboard.** Product readers repeat; editorial and expressive readers arrive, read and leave, so a palette there is a feature nobody discovers. Every preset keeps the floor: everything reachable by Tab, Escape closing one layer.
146
133
 
147
- **View memory.** A working reader returns to the same collection daily and set its view for a reason; a visiting reader didn't, and should see the collection as designed.
148
-
149
134
  **Toasts.** A toast confirms what the reader can't see happen. A move they watched needs none; a save that failed, an undo window, or a result on another page does.
150
135
 
151
136
  ## Iterating on feedback
152
137
 
153
- Most product feedback is one step: "one step lighter", "25% smaller", "to 16px". The craft is making exactly that change, everywhere it applies, and writing it down.
154
-
155
- 1. **Make the change asked, at the size asked.** Move one step on the existing ladder; don't re-pick the system. The count per row stays unless more per row was asked for.
156
- 2. **After any change to a shared rule, sweep the product** — every route, every component on that rule — **and report what moved.** Partial updates are the commonest bug: some screens took the new value, some didn't. Ask before calling a page done, and never "fix" a page the owner has said is theirs.
157
- 3. **Record the decision** under `## Decisions` (format: `system/design-md.md`).
158
- 4. **Study reference products for how information is structured, not how it looks**: where icons are and aren't, where tags appear, how tables are built, what is boxed and what isn't, what aligns with what.
159
- 5. **Wireframe a new page type before building it**, and share the wireframe.
160
- 6. **Review on a preview harness**: the real app on mock data, every route reachable without a backend, so a whole-product sweep or audit takes minutes.
161
-
162
- → The sweep, the report, the log entry, a reference-study worksheet and the harness: `domains/patterns/iteration.md`
138
+ Most product feedback is one step: "one step lighter", "25% smaller", "to 16px". Make exactly that change, at the size asked, sweep everywhere the rule reaches and report what moved, then record it under `## Decisions` (→ `domains/patterns/iteration.md`: the step table, the sweep, references and the preview harness). Wireframe a new page type before building it, and share the wireframe.
163
139
 
164
140
  ## Auditing existing code
165
141
 
@@ -183,34 +159,16 @@ Most product feedback is one step: "one step lighter", "25% smaller", "to 16px".
183
159
  | 16 | Inputs that reject spaces, dashes, case or a trailing space; placeholders as labels; pickers with no recent items; questions the product could answer itself | Field, the input it rejects or the question it needn't ask |
184
160
  | 17 | Multi-step flows with no position or count; a last screen that says only "Success" or "Done"; failure copy with a code and no next step | Flow, step, and its end state |
185
161
 
186
- Click through every flow once with the keyboard alone, and once with the network throttled to failure. Report one line per finding with its evidence under `## Open issues`, and fix nothing unless asked.
162
+ Click through every flow once with the keyboard alone, and once with the network throttled to failure.
187
163
 
188
164
  ## Writing back to DESIGN.md
189
165
 
190
166
  - **`## Patterns`**: the product's nouns, one per kind of thing; the second-line formula; the destructive items and the proof each needs; the shortcut map; which pages remember their view and which default wins; the agent-step conventions.
191
- - **Decisions**, dated, marked by who decided, with the reason: any dial moved off the preset, a law knowingly broken, or a change a law forced that a later session might undo (marked *law*).
192
- - **Open issues** for every law the code breaks, with evidence, removed when the fix lands.
193
167
 
194
168
  ## Checklist
195
169
 
196
- - [ ] Labels are as short as their action, read the verb, and are sentence case, generated ones included.
197
- - [ ] Row second lines name type and origin in the product's nouns; one noun per kind of thing.
198
- - [ ] Every destructive or session-ending item shares one treatment, last in its menu, with no icon; nothing else borrows it.
199
- - [ ] Confirmations use the system dialog, say what is lost, scale proof to the loss, show progress and return focus.
200
- - [ ] Agent steps are cards with name, arguments and result; approvals are inline; no per-message avatars; the panel is non-modal.
201
- - [ ] Streaming text separates committed from forming, follows only while the reader is at the bottom, and is still when paused.
202
- - [ ] Frequent actions have keys; every drag has a keyboard path; Escape closes one layer; single keys never fire in inputs.
203
- - [ ] Launchers offer only the task's choices; toolbar filters are icons with tooltips; long lists group and collapse.
204
- - [ ] Empty user data offers the first action; filtered-empty names the filter.
205
- - [ ] Staged work has a board in workflow order beside its list; remembered views follow layout law 9 and are listed under `## Patterns`.
206
- - [ ] Moves are optimistic with a visible rollback; sends, payments and deletes are not.
207
- - [ ] Dashboards run overview, trends, action items, wireframed first, scoped views on the global pipeline.
208
- - [ ] Forms lead with the deciding field; errors replace hints; sign-in is a form alone in its column, its route an `expressive` area if the art moves.
209
- - [ ] Every element does what its form promises: play icons play, inert logos don't react, email addresses are `mailto:` links, copy controls are buttons, calls to action and link text name what happens next, sign-up fields are labelled and say what arrives and how often.
210
- - [ ] Conventions readers know from other products hold, unless breaking one is a gain felt on first use.
211
- - [ ] Every input is acknowledged at once; past ≈ 400ms the control says it's working, past ≈ 1s the region shows its shape, past ≈ 10s progress is determinate and cancellable; results appear where they were asked for.
212
- - [ ] Inputs accept any reasonable form and normalise it; labels stay visible; pickers lead with recent items; the product never asks for what it already knows.
213
- - [ ] Multi-step flows show position and what's left; the last screen names what happened, shows it and offers the next action; failures at the peak get the most careful copy.
214
- - [ ] Long lists and long strings are chunked; launchers and toolbars show the common path and disclose the rest.
215
- - [ ] Feedback was applied at the size asked, swept product-wide, reported, and recorded as a dated decision.
216
- - [ ] Dials match the preset, or the difference is under `## Decisions`.
170
+ - [ ] Large deletes show "Deleting…" and hold the dialog open until done; focus returns to the opener.
171
+ - [ ] Filtered-empty names the filter and offers to clear it.
172
+ - [ ] Sends, payments and deletes are never optimistic.
173
+ - [ ] A sign-in route whose art moves is listed as an `expressive` area.
174
+ - [ ] Long lists and long strings are chunked.
@@ -35,15 +35,7 @@ Card title weight 600 → 500 (role `type.card-title`):
35
35
 
36
36
  ## The decision log
37
37
 
38
- Every decision goes under `## Decisions` in `DESIGN.md`, in the format `system/design-md.md` sets.
39
-
40
- ```markdown
41
- - 2026-10-28 — Card titles at 500, not 600: "unnecessarily heavy" next to the pane title. (User decision.)
42
- - 2026-10-21 — Boards remember their view per page; folders always open on folders. (User decision.)
43
- - 2026-10-14 — More colour, each hue with one job: type, status, area, person. (Client feedback.)
44
- ```
45
-
46
- Read the log before changing anything it covers. A reviewer's instinct to "fix" a decided value is how a product oscillates between two versions.
38
+ Every decision goes under `## Decisions` in `DESIGN.md`, in the format and with the rules `system/design-md.md` sets.
47
39
 
48
40
  ## Studying a reference
49
41
 
@@ -6,12 +6,7 @@ The organising idea is **a size exists because a role needs it** — not because
6
6
 
7
7
  ## Before you start
8
8
 
9
- Read `DESIGN.md` at the project root: `## Profile` for the preset and any areas on another one, `## Type` for the project's name for each role and the decisions already made. Reason in roles; write code in the project's names.
10
-
11
- - **No `DESIGN.md`:** run `/keyline init` for this domain only, do the task, then recommend the full setup.
12
- - **A role you need has no row:** reuse a value already doing that job; otherwise add one in the project's naming style and record it in `DESIGN.md`.
13
- - **The task would break a law or move a dial off its preset:** say so and ask; if the user confirms, record it with its reason under `## Decisions`.
14
- - **Preset `glass`:** the values are in `DESIGN.md`; `glass/type/guide.md` has the exact recipes behind them.
9
+ Reads `DESIGN.md` `## Type`.
15
10
 
16
11
  ## Laws
17
12
 
@@ -23,13 +18,13 @@ The ladder has a floor as well as a ceiling. Below the readable floor type is re
23
18
 
24
19
  Type inside artwork — an illustration or diagram, an SVG or an HTML mock marked `role="img"` — is exempt from the ladder, as colour law 1 exempts colour there; any label in it a reader must read still clears the readable floor.
25
20
 
26
- Cut downward, checking what each remaining step was holding up: removing a 30px Glass article title took three more sizes with it, two of which existed only to sit between it and the body.
21
+ Cut downward, checking what each remaining step was holding up: removing one size often takes others that existed only to sit beside it.
27
22
 
28
23
  → Per-preset ladders, and adding a role without growing one: `domains/type/ladders.md`
29
24
 
30
25
  ### 2. A scale is only a system if every template is on it
31
26
 
32
- **No template opens off the ladder, and templates of one kind open alike; a page type with its own hierarchy is opting out of the system, not expressing importance.** Glass articles once opened at 30px with 19px section headings while every index opened at 18px, and a case study felt like a different site. Readers feel the opt-out as inconsistency before they notice sizes; the fix was the site's existing ladder, not a compromise. An article may open above an index — its title starts a piece, an index's labels a list (the ceiling dial) — but every article opens at one size, and every index at another.
27
+ **No template opens off the ladder, and templates of one kind open alike; a page type with its own hierarchy is opting out of the system, not expressing importance.** Readers feel the opt-out as inconsistency before they notice sizes, and the fix is the existing ladder, not a compromise. An article may open above an index — its title starts a piece, an index's labels a list (the ceiling dial) — but every article opens at one size, and every index at another.
33
28
 
34
29
  Areas on another preset are declared by path in `DESIGN.md`, not drifted into. Inside one every template shares its ladder, and roles crossing the boundary — navigation, `type.ui`, `type.meta`, the footer — keep one set of values, so the chrome doesn't change between marketing site and app.
35
30
 
@@ -37,7 +32,7 @@ The same holds for one element: a pane title at 14/600 where every other page's
37
32
 
38
33
  ### 3. A tier never outranks the heading it sits under
39
34
 
40
- **Nothing inside a section — a lead, a pull quote, a keyline, a note — may be larger, heavier or louder than the heading that owns it.** A Glass keyline at 17px under a 16px heading read as paragraphs shouting over their titles; an in-body heading larger than the page's title inverts the document. Check role pairs — `type.lead` against `type.heading`, `type.heading` against `type.title` — since a violation only shows where the two meet.
35
+ **Nothing inside a section — a lead, a pull quote, a keyline, a note — may be larger, heavier or louder than the heading that owns it.** A keyline a pixel larger than its heading reads as paragraphs shouting over their titles; an in-body heading larger than the page's title inverts the document. Check role pairs — `type.lead` against `type.heading`, `type.heading` against `type.title` — since a violation only shows where the two meet.
41
36
 
42
37
  `type.display`, the one role above `type.title`, obeys it too: one loudest moment, nothing under it competing. Two are two pages' worth of hierarchy on one.
43
38
 
@@ -59,7 +54,7 @@ In a row or card the rule runs the other way: **its title is the darkest thing i
59
54
 
60
55
  A further role there needs an unused axis, or it isn't a role.
61
56
 
62
- The corollary matters more: **when you remove a signal, audit the ones you left in.** The Glass pull quote's 2px border at ten percent black, invisible on white, survived only while the quote was two steps larger than the body; when the size step went, it had to become a real bar.
57
+ The corollary matters more: **when you remove a signal, audit the ones you left in.** A pull quote's faint border, invisible on white, survives only while the quote is larger than the body; when the size step goes, it has to become a real bar.
63
58
 
64
59
  Between sizes too, size is the weakest signal in the 11–20px band: a 13 → 14 → 20 ladder at one weight reads flat. **A step up in rank moves weight and tracking with it** — headings tighten as they grow while labels stay at zero — never size alone.
65
60
 
@@ -73,7 +68,7 @@ Between sizes too, size is the weakest signal in the 11–20px band: a 13 → 14
73
68
 
74
69
  A second family earns its place with a second register — literal text in a code face, a brand display voice, interface text beside a serif reading face — never because "headings should feel different", which size, weight, leading and tracking handle. A code face for a handful of spans is a system stack, not a download; IDs, codes and times rarely need even that — tabular, slashed-zero figures (law 10) set them apart in the text face.
75
70
 
76
- Weight is the cheapest second channel for state: colour alone fails WCAG 1.4.1, so an active row adds `weight.strong` to the colour colour law 7 gives it — only on states that don't follow the pointer (active, current, selected, not hover), since a weight change alters width and shifts neighbours. **Weight carries one state per product:** if unread rows step up beside their dot, current and selected use a fill, a marker or tier, or the step means nothing.
71
+ Weight is the cheapest second channel for state (colour law 6): an active row adds `weight.strong` to the colour colour law 7 gives it — only on states that don't follow the pointer (active, current, selected, not hover), since a weight change alters width and shifts neighbours. **Weight carries one state per product:** if unread rows step up beside their dot, current and selected use a fill, a marker or tier, or the step means nothing.
77
72
 
78
73
  ### 6. Leading follows the job
79
74
 
@@ -126,7 +121,7 @@ The mapping covers user-agent styles: `<cite>`, `<blockquote>`, `<b>`, `<th>` an
126
121
  | Dial | editorial | product | expressive |
127
122
  |---|---|---|---|
128
123
  | `type.body` size | 16–18px | 14px | 17–20px |
129
- | `type.ui` size | body − 1–2px | 13px (12px in dense tables) | 15–16px |
124
+ | `type.ui` size | body − 1–2px | 13px (12px in dense tables) | 15–16px; on any preset, inputs go to 16px on touch widths only |
130
125
  | Ceiling: `type.title` ÷ `type.body` | ≤ 2.25 for an article's or page's title; index and chrome titles may sit lower | ≤ 1.6 | ≤ 2.5 |
131
126
  | `type.display` | none | none | one per page, derived from the measure; ≥ 1.5 × `type.title` at every width (× `type.heading` where the display is the `h1`) |
132
127
  | Distinct sizes above the floor (excluding display and code) | ≤ 7 | ≤ 7 | ≤ 8 |
@@ -214,38 +209,19 @@ Evidence per law:
214
209
  - **Loading (12).** Third-party font hosts, `swap` without a metric-matched fallback, weights requested but unused.
215
210
  - **Icons (13).** Icon sizes and strokes against the text beside them; sibling controls that differ.
216
211
 
217
- Report one line per finding, with evidence, in `DESIGN.md` `## Open issues`: *"`type.lead` (`.intro`) is 20px under an 18px `type.heading` on `app/blog/[slug]` — outranks its heading."* Don't fix while auditing unless asked.
218
-
219
- **Sweep after every type decision** (the sweep: `domains/patterns/iteration.md`). Raw sizes hide a partial type update from grep, so walk every route rendered against the `## Type` table and migrate raw sizes to role tokens as you go. → `domains/type/type-audit.md`
212
+ **Sweep after every type decision** (`domains/patterns/iteration.md`; its type steps, `domains/type/type-audit.md`).
220
213
 
221
214
  ## Writing back to DESIGN.md
222
215
 
223
216
  - **`## Type`** — families; a row per role in use (project name, size, leading, tracking, weight, tier); project roles under their canonical parent with their separating axis; each weight's job, and with three the size × use table; each role's icon size. Keep existing names; mark new ones `proposed`.
224
- - **`## Decisions`** — every dial moved off the preset, dated, with its reason (a third weight and the table that needed it, a raised ceiling and its role, italic for a true italic); any law knowingly broken, with the user's confirmation.
225
- - **`## Open issues`** — each violation with its evidence, removed when fixed.
217
+ - **`## Decisions`** — a third weight and the table that needed it, a raised ceiling and its role, italic for a true italic.
226
218
 
227
219
  Write back whenever the ladder changes shape — a role, size, weight or family added or dropped. A size without a row is how a ladder turns back into a pile of values.
228
220
 
229
221
  ## Checklist
230
222
 
231
- - [ ] Every size maps to a named role in `DESIGN.md`; no unclaimed base size.
232
- - [ ] One ladder per area; roles crossing a boundary keep one set of values.
233
- - [ ] Nothing outranks its heading; no in-body heading outranks `type.title`; at most one `type.display` per page.
234
- - [ ] Each role sharing a size owns a signal no other role there uses; each rank up moves weight or tracking too.
235
- - [ ] One element, one set of values on every page; row and card titles are their darkest text.
236
- - [ ] Every weight and family has a job; no weight changes on hover.
237
- - [ ] Weight lifts one step, never on neighbours; one weight per set of titles; at most one state on weight.
238
- - [ ] Active and selected states carry a signal besides colour.
239
- - [ ] Display, reading and UI each use their own leading curve; fixed-height single-line UI uses 1.
240
- - [ ] Tracking is in `em`: negative only above the text range, positive on tiny text and capitals, zero on body.
241
- - [ ] UI and reading copy differ in leading, measure and tier; in-product help text is reading copy.
242
- - [ ] `font-synthesis: none`; `<strong>` is `weight.strong`; no UA italic or bold leaks past the mapping.
243
- - [ ] Aligned and live numbers are tabular, and the family ships `tnum`.
244
- - [ ] Imported prose: one scoped class, existing roles, first-child margin reset, wide content scrolling in its own box.
245
- - [ ] Fonts self-hosted, `swap`, metric-matched fallback; only used weights requested.
246
- - [ ] Icons match the size and stroke of their text; inputs are 16px on touch widths.
247
- - [ ] After a type change, every route swept rendered; no raw sizes left.
248
- - [ ] Nothing a user must read sits below the readable floor.
249
- - [ ] Dials match the preset: body size, ceiling ratio, weight and family counts, leading ranges, tracking at the top.
223
+ - [ ] No unclaimed base size; row and card titles are their darkest text.
224
+ - [ ] Weight lifts one step, never on neighbours; one weight per set of titles; at most one state on weight; no weight change on hover.
225
+ - [ ] Imported prose resets its first child's margin and scrolls wide content in its own box.
226
+ - [ ] Inputs are 16px on touch widths; nothing a user must read sits below the readable floor.
250
227
  - [ ] Display derived from the measure; every fluid size clamped with a `rem` intercept; no text sized in `vw` alone.
251
- - [ ] Italic, case (generated labels included), numeral defaults and the unit match the preset or a recorded decision.
@@ -79,7 +79,7 @@ The recorded third weight, for a dense screen where most lines share two or thre
79
79
  | 13 | a row's second line, a card's time | nav items, header-bar page title, tabs, buttons, table header labels | — |
80
80
  | 12 | timestamps, hints, captions | field labels, a group label's count | — |
81
81
 
82
- Its rules: nothing inside a row, card or folder goes above 500 but the state weight; lift by one step, never on two neighbouring lines; every title in a set takes one weight. A board's card titles sit a step lighter than a list's row titles: a board shows many titles at once, and at 500 the whole board reads heavy.
82
+ Where this table is recorded it replaces the ladder's weight column: nav items, tabs and buttons sit lifted throughout, and the current one is marked by its fill (colour law 7), not by a weight. Its rules: nothing inside a row, card or folder goes above 500 but the state weight; lift by one step, never on two neighbouring lines; every title in a set takes one weight. A board's card titles sit a step lighter than a list's row titles: a board shows many titles at once, and at 500 the whole board reads heavy.
83
83
 
84
84
  Expect the first pass to be a step too heavy everywhere. Correct it one step at a time per context, then sweep (`type-audit.md`) — a partial step-down is worse than none.
85
85
 
@@ -80,11 +80,8 @@ Use the project's names, and keep the list beside the theme tokens so a new role
80
80
 
81
81
  ## The sweep
82
82
 
83
- After any change to a role — a size, a weight step, a tracking value:
84
-
85
- 1. **Change the token, not the instances.** If the change needs instance edits, those instances weren't on the role; migrate them first.
86
- 2. **Grep for the old value** in the role's raw forms (`font-semibold`, `text-[14px]`, `font-weight: 600`) and decide each hit.
87
- 3. **Run the rendered inventory on every route**, not the route the change was asked about.
88
- 4. **Check the generated strings** for case while you're there (patterns law 1): field names, column headers, tool labels, dialog titles and toasts that arrive in Title Case from a schema or a config.
89
- `rg -n "(label|title|header|name):\s*['\"][A-Z][a-z]+ [A-Z][a-z]+" src`
90
- 5. **Report what moved**, by role and by page, and anything left on the old value with its reason, so the change can be checked rather than trusted.
83
+ The sweep is `domains/patterns/iteration.md`'s. For a type role, two steps of its own:
84
+
85
+ - **Grep for the old value** in the role's raw forms (`font-semibold`, `text-[14px]`, `font-weight: 600`) and decide each hit; raw sizes are where a partial update hides.
86
+ - **Check the generated strings** for case while you're there (patterns law 1): field names, column headers, tool labels, dialog titles and toasts that arrive in Title Case from a schema or a config.
87
+ `rg -n "(label|title|header|name):\s*['\"][A-Z][a-z]+ [A-Z][a-z]+" src`