sheleg-design-skill 1.51.1 → 1.53.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.
Files changed (62) hide show
  1. package/CHANGELOG.md +142 -0
  2. package/README.md +5 -4
  3. package/bin/cli.js +5 -1
  4. package/cursor/rules/sheleg-design.mdc +7 -0
  5. package/kits/deskmate/src/styles.css +89 -26
  6. package/kits/onionskin/src/styles.css +19 -2
  7. package/kits/test-drive/.design-sync/config.json +14 -0
  8. package/kits/test-drive/.design-sync/conventions.md +40 -0
  9. package/kits/test-drive/README.md +34 -0
  10. package/kits/test-drive/package.json +29 -0
  11. package/kits/test-drive/src/Annotation.md +13 -0
  12. package/kits/test-drive/src/Annotation.tsx +20 -0
  13. package/kits/test-drive/src/Button.md +23 -0
  14. package/kits/test-drive/src/Button.tsx +33 -0
  15. package/kits/test-drive/src/Card.md +14 -0
  16. package/kits/test-drive/src/Card.tsx +24 -0
  17. package/kits/test-drive/src/Chip.md +13 -0
  18. package/kits/test-drive/src/Chip.tsx +25 -0
  19. package/kits/test-drive/src/Delta.md +11 -0
  20. package/kits/test-drive/src/Delta.tsx +23 -0
  21. package/kits/test-drive/src/DemoFrame.md +19 -0
  22. package/kits/test-drive/src/DemoFrame.tsx +25 -0
  23. package/kits/test-drive/src/Empty.md +13 -0
  24. package/kits/test-drive/src/Empty.tsx +21 -0
  25. package/kits/test-drive/src/Field.md +18 -0
  26. package/kits/test-drive/src/Field.tsx +38 -0
  27. package/kits/test-drive/src/Heading.md +13 -0
  28. package/kits/test-drive/src/Heading.tsx +19 -0
  29. package/kits/test-drive/src/Machine.md +18 -0
  30. package/kits/test-drive/src/Machine.tsx +26 -0
  31. package/kits/test-drive/src/Marker.md +11 -0
  32. package/kits/test-drive/src/Marker.tsx +12 -0
  33. package/kits/test-drive/src/NavBar.md +14 -0
  34. package/kits/test-drive/src/NavBar.tsx +20 -0
  35. package/kits/test-drive/src/Rule.md +11 -0
  36. package/kits/test-drive/src/Rule.tsx +18 -0
  37. package/kits/test-drive/src/Stat.md +18 -0
  38. package/kits/test-drive/src/Stat.tsx +17 -0
  39. package/kits/test-drive/src/ThinkingDots.md +15 -0
  40. package/kits/test-drive/src/ThinkingDots.tsx +19 -0
  41. package/kits/test-drive/src/index.ts +33 -0
  42. package/kits/test-drive/src/styles.css +833 -0
  43. package/kits/test-drive/tsconfig.json +15 -0
  44. package/package.json +3 -3
  45. package/plugins/sheleg-design/.claude-plugin/plugin.json +2 -2
  46. package/plugins/sheleg-design/commands/sheleg-design.md +2 -2
  47. package/plugins/sheleg-design/skills/sheleg-design/DESIGN_SYNC_BRIDGE.md +1 -1
  48. package/plugins/sheleg-design/skills/sheleg-design/FIGMA_BRIDGE.md +2 -2
  49. package/plugins/sheleg-design/skills/sheleg-design/MOBILE_SURFACES.md +1 -1
  50. package/plugins/sheleg-design/skills/sheleg-design/SKILL.md +3 -3
  51. package/plugins/sheleg-design/skills/sheleg-design/STYLE_PACK_INDEX.md +3 -2
  52. package/plugins/sheleg-design/skills/sheleg-design/SURFACE_COMPOSITION.md +8 -5
  53. package/plugins/sheleg-design/skills/sheleg-design/styles/STYLE_PACK_TEMPLATE.md +1 -1
  54. package/plugins/sheleg-design/skills/sheleg-design/styles/datasheet.md +5 -1
  55. package/plugins/sheleg-design/skills/sheleg-design/styles/deskmate.md +61 -18
  56. package/plugins/sheleg-design/skills/sheleg-design/styles/scoreboard.md +6 -1
  57. package/plugins/sheleg-design/skills/sheleg-design/styles/showroom.md +5 -1
  58. package/plugins/sheleg-design/skills/sheleg-design/styles/tenor.md +1 -1
  59. package/plugins/sheleg-design/skills/sheleg-design/styles/test-drive.md +454 -0
  60. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/deskmate.css +68 -10
  61. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/onionskin.css +19 -2
  62. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/test-drive.css +389 -0
package/CHANGELOG.md CHANGED
@@ -6,6 +6,148 @@ follow [SemVer](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.53.0] - 2026-08-27
10
+
11
+ ### The thirty-seventh pack — the visitor is handed the keys
12
+
13
+ **`test-drive`**, extracted from a revenue-analytics product's front page and named per
14
+ ADR-0001 for the register it encodes: a page that sells by handing over the running
15
+ product — the set piece is the live app inside drawn browser chrome, and a founder's
16
+ hand (a script face, a yellow marker, a drawn arrow) annotates the tour.
17
+
18
+ ### Added
19
+
20
+ - `styles/test-drive.md` — widened, all thirteen headings; `Themes: light + dark — a
21
+ full theme twin`; three forks written from both sides: against `datasheet` (whose
22
+ live instrument reads *the visitor*, where this one hands over *the product's own
23
+ workspace*), against `showroom` (a still exhibit against a running machine), and
24
+ against `scoreboard` (one public total against a dashboard's vital signs on the
25
+ same warm paper and orange-red family).
26
+ - `styles/tokens/test-drive.css` — measured off the render, not the stylesheet, and
27
+ the reference is the trap the skeleton's rule 7 describes: its DaisyUI theme
28
+ declares a teal accent that paints **nothing** (the area-weighted census found it
29
+ on zero elements), so the acting coral is the brand and the teal ships nowhere.
30
+ The coral splits into two tokens because the reference's own CTA fails AA —
31
+ `#ffffff` on `#e16540` is 3.42:1 — so `--accent` lights, fills and blinks while
32
+ `--action` `#c04a28` carries every body-size word at 4.94:1 under white. The lit
33
+ control recipe (contact shadow, hue ring, glow, inner bevel) is read byte-for-byte
34
+ out of the reference's authored `.btn-primary`/`.btn-simple` classes, in both
35
+ themes. The machine ladder (`--machine-*`, `#101010`–`#202020`) is quarantined and
36
+ ignores the theme.
37
+ - `kits/test-drive/` — the six-name spine plus `DemoFrame`, `Annotation`, `Marker`,
38
+ `Delta`, `Machine`+`Caret`, `ThinkingDots`, `Field`, `NavBar`, `Empty`. Rendered at
39
+ 1440, 768 and an emulated 390 before shipping (CONTRIBUTING step 8): the render
40
+ caught the nav computing 66px against a stated 65 (a content-box border), the link
41
+ row overflowing a 390px page at 428px, and the touch floor missing — all three
42
+ fixed pre-tag. The stat row is a container (`container-type: inline-size`),
43
+ stepping 2 → 4 → 7 columns by its own width.
44
+ - `test/scenarios.md` T35 — `test-drive` against `datasheet`, the live-instrument
45
+ fork, with its negative branch per standing instruction 4.
46
+ - ADR-0001 gains its fourteenth application: `test-drive`, with `showfloor`,
47
+ `sandbox`, `marginalia` and `demo-day` weighed and rejected on the ADR's own
48
+ criteria.
49
+
50
+ ### Changed
51
+
52
+ - `datasheet`, `showroom`, `scoreboard` — each gains the reciprocal fork clause
53
+ naming `test-drive`.
54
+ - Counted claims moved with the tree: thirty-seven packs and kits across `SKILL.md`,
55
+ the index, `SURFACE_COMPOSITION.md` (the accent is `--accent` in 34 of the 37),
56
+ `FIGMA_BRIDGE.md` (twelve of the thirty-seven ship a twin), `MOBILE_SURFACES.md`,
57
+ `DESIGN_SYNC_BRIDGE.md`, the template's theme split (12/16/9), README, DOCMAP,
58
+ both installers and all three manifests.
59
+
60
+ ## [1.52.0] - 2026-08-27
61
+
62
+ ### The front door is the designs
63
+
64
+ Opening the published catalogue used to mean reading three tiles and clicking one before
65
+ seeing a single pack. It now opens **on the packs**, behind a tab strip of three real
66
+ screens: **Designs** (the front door, and it is the gallery), **Audit** and **Method** —
67
+ the prose that used to occupy the index. `packs.html` stays as a canonical alias for the
68
+ URL the gallery lived at, because two URLs serving one gallery is duplicate content.
69
+
70
+ The tabs are links to pages rather than panels toggled in one document, and that is the
71
+ SEO decision, not a shortcut: one URL per screen, every screen fully in the HTML, nothing
72
+ behind a click a crawler has to execute. `rel="prefetch"` buys the tab feel back.
73
+
74
+ ### What the SEO pass changed
75
+
76
+ - **Structured data, with one publisher everybody points at.** A `CollectionPage` whose
77
+ `mainEntity` is an `ItemList` of all thirty-six packs, each with its own anchor
78
+ (`/#pack-<name>`), so an answer engine can lift the list without running the filter JS;
79
+ a `SoftwareApplication` on Method; `BreadcrumbList` on both sub-pages; and an
80
+ `Organization` node referenced by `@id` from every page. The family site shipped the
81
+ opposite of that for two releases — a publisher node nothing pointed at.
82
+ - **A `404.html`**, a three-URL sitemap that lists only indexable screens, `llms.txt`
83
+ rewritten for the new shape, and `max-image-preview:large`, `color-scheme`,
84
+ `theme-color`, `og:site_name` and `og:locale` on every page.
85
+ - **Two defects the pass found in the old pages:** `method.html` was emitting its
86
+ canonical and description **twice** (the shared `HEAD` and `page_meta` both fired), and
87
+ the published dark-field count was **wrong** — `gallery.py` could not parse
88
+ `oklch()`, so `briefing-room`'s near-black field read as light and the site, its
89
+ `llms.txt` and its gallery all said five dark packs where six ship.
90
+
91
+ ### `deskmate`, corrected by its own audit
92
+
93
+ A blind read of the pack (T34a, which also routed correctly and rejected `tenor` on the
94
+ quoted clause) found nine defects. The important one is a colour pairing no gate can see:
95
+ `--surface` stays white on the dusk surface while `--ink` inverts to white, so **a card
96
+ that painted its heading in `--ink` painted white on white** inside an inverted section.
97
+ The pack now ships `--on-surface` and `--on-surface-soft` — words on a card take those,
98
+ words on the field take `--ink` — and the kit consumes them.
99
+
100
+ Also fixed: the focus ring shipped derived-only, so a browser without `color-mix()` got
101
+ **no ring at all** (literal first now, derived inside `@supports`, the mechanism
102
+ `showroom` established); the slab's inset was 40px in the pack and 80px in the token
103
+ layer; the gutter ramp stated four steps and shipped two tokens; the measure stated in
104
+ prose (62ch/68ch) had no token; the entrance duration's two measurements (0.64s declared,
105
+ 0.7s computed) were never reconciled; "the smallest radius that ships" and "every control
106
+ is 56px" were both contradicted by the pack's own body; and the provenance promise was
107
+ broader than the layer delivers.
108
+
109
+ One finding was rejected: `deskmate` is absent from `MOTION_DOCTRINE.md`'s ceiling
110
+ passage, and that passage is a named selection whose home the gate declares to be the
111
+ pack's own Register.
112
+
113
+ ### `onionskin` gets the same focus-ring guard
114
+
115
+ The audit that read `deskmate` measured the whole library for the same shape: three
116
+ packs derive a focus ring, and only `showroom` guarded it. `onionskin`'s ring was
117
+ `color-mix()`-only, so a browser without `color-mix()` painted **no ring at all** — the
118
+ one degradation both packs say they may not ship. The literal now sits in `:root` and the
119
+ derived form in an `@supports` block; no computed value changed, and its kit's token
120
+ block was resynced with it.
121
+
122
+ ### A count nothing could check, and now can
123
+
124
+ `SURFACE_COMPOSITION.md` claimed `--accent` in **twenty-nine** of the packs at a true
125
+ **thirty-three**, and named two of the three packs that call the accent something else —
126
+ omitting `babylove` entirely, so a reader following that sentence would look for a token
127
+ that pack does not have. `validate_counted_claims` cannot read a count whose noun is
128
+ "packs that declare a token", so `validate_token_population_counts` now derives both that
129
+ figure and the `@role non-text:` population from the token layers, and refuses a pack
130
+ that declares neither an `--accent` nor an `@role accent:` marker. The same sweep found
131
+ `SKILL.md` still saying "the twenty-nine".
132
+
133
+ ### The scenario that produced all of this, and the half of it that did not run
134
+
135
+ `T34` is the behavioural scenario shipped with `deskmate`, and 1.52.0 is the first
136
+ release where one of these branches was actually run rather than filed. **`T34a`
137
+ passed**: it routed to the pack, quoted the deciding clause, rejected `tenor` on the
138
+ clause that rules it out, and stayed inside the bundle directory. It then did the half
139
+ these scenarios have always asked for and never exercised — read the chosen pack in full
140
+ and report defects with quotes — and returned nine, eight of which are fixed above.
141
+
142
+ **`T34b`, the negative branch, terminated on an account spend limit before reporting a
143
+ verdict, and is not counted as a result.** A positive-only pass cannot detect the failure
144
+ that branch exists to detect. `test/scenarios.md` records both outcomes.
145
+
146
+ ### Ratchets
147
+
148
+ `validate.py` 5047 → 5060, `validate_palette.py` 3013 → 3023, `computed_at_least`
149
+ 686 → 691. Thirteen of the first delta are the new gate.
150
+
9
151
  ## [1.51.1] - 2026-08-27
10
152
 
11
153
  ### `deskmate`, the thirty-sixth pack — a colleague, quoted
package/README.md CHANGED
@@ -31,7 +31,7 @@ problem — invented colors, six accent hues, dark mode retrofitted later.
31
31
 
32
32
  This skill is the taste layer. It gives a coding agent **one motion
33
33
  methodology** for cinematic, scroll-driven pages, **a motion doctrine** that
34
- decides whether to animate before it decides how, and **thirty-six locked style
34
+ decides whether to animate before it decides how, and **thirty-seven locked style
35
35
  packs** with ready-made design tokens, so what it builds reads as one system
36
36
  instead of a pile of effects.
37
37
 
@@ -76,6 +76,7 @@ into the cinematic layer, and says so in its own *Motion flavor* section.
76
76
  | `rimlight` | a white field with a cool grey act separator and one near-black act, a grotesque for every sentence and a **monospace for every piece of chrome**, square on 84% of its elements, tracked negative at every size and with **no bold anywhere** — and an elevation made of **coloured light**: a sixteen-layer rig, six layers lit and ten held at alpha 0, thrown from below-left onto one control per viewport | **a studio's own front door and the pages that sell what it makes** — design and engineering agencies, product studios, service and case-study pages (standalone) |
77
77
  | `onionskin` | a white technical sheet at **96.5% zero radius** — the squarest page in the library — where **two bases do all the work and everything quiet is one of them at an alpha**: text dims through the ink, structure through a navy that is never a word, so there is no grey ramp at all. A dot grid, dashed hairlines, three faces with one job each, and an **11px working size** | **developer and AI infrastructure whose front page is a working document** — memory and context engines, retrieval services, agent runtimes, evaluation and observability surfaces (standalone) |
78
78
  | `deskmate` | a warm beige working day lit from **one source above the top edge** — every gradient origin in the reference sits at or above `y=0` — where a single four-stop ramp (peach, lilac, violet, deep navy) washes the field, fills a panel and fills one word of a heading, and everything a hand touches is a 56px pill against 32px slabs | **products sold as a colleague rather than a tool** — AI employees and chat-native agents, assistants living in Slack or Teams, delegation and approval surfaces (standalone) |
79
+ | `test-drive` | warm paper and white cards where **the live product runs inside drawn browser chrome** — traffic-light dots, a real address, the engine on — one coral in two tokens (the brand hue lights, fills and blinks; a darker step carries every body-size word), every control lit by a four-layer shadow in its own hue, a handwritten face and a yellow marker annotating from the margin | **self-serve SaaS sold by handing over the running product** — analytics, dashboards, developer tools with a visible console, indie SaaS (standalone) |
79
80
  | `outrank` | white field with one violet that carries text in both directions, a 5px light ring standing in for a button shadow, and two type families split by how long a thing is read | SEO and growth SaaS, agent-run back-office — the pack for a product that ships a landing and a dashboard at once |
80
81
  | `orchard` | warm oat field of rounded slabs, sage brand + one candy-orange action, rounded geometric display, soft-3D pills built from inset light | friendly consumer biotech, DTC wellness, testing kits & supplements |
81
82
  | `field-notes` | warm green-cast paper ruled by hairlines, one rust accent, a hero that dissolves into the page instead of ending, numbered mono eyebrows, crop marks, colour that encodes how a claim is known | open-source & developer tools sold on auditability — code intelligence, provenance, evals, agent memory |
@@ -176,7 +177,7 @@ skills.
176
177
  | `DESIGN_SYNC_BRIDGE.md` | The Claude Design contract: what a pack sends to claude.ai/design and in what shape, the rule for each of the four reference types, and the border motion does not cross |
177
178
  | `FIGMA_BRIDGE.md` | The design↔code contract: how a pack's tokens map onto Figma variable collections and modes, how to implement a design without importing raw values, and what cannot cross the border |
178
179
  | `AI_PRODUCT_PATTERNS.md` | The surfaces a model drives: the five states of a call, streaming instead of spinners, latency, provenance and uncertainty, agent confirmations, and the bans that keep it honest |
179
- | `styles/*.md` | The thirty-six style packs — palette, type, texture, motion tokens, motifs, bans, and the traps each one carries |
180
+ | `styles/*.md` | The thirty-seven style packs — palette, type, texture, motion tokens, motifs, bans, and the traps each one carries |
180
181
  | `styles/tokens/*.css` | The ready-made token layer per pack, copied verbatim instead of transcribed (`workbench` and `field-notes` each ship a light `:root` plus a `data-theme="dark"` twin) |
181
182
  | `styles/STYLE_PACK_TEMPLATE.md` | The pack contract as a skeleton, so a new style is authored against the same headings rather than improvised |
182
183
 
@@ -247,7 +248,7 @@ cd ./ds-workbench && npm install && npm run build
247
248
  then `/design-sync` in that directory, from Claude Code. Three layers cross: the
248
249
  pack's **bans** as the design system's own README, `styles.css` built from
249
250
  `tokens/<pack>.css` verbatim, and the components — a six-name spine that is
250
- identical in all thirty-six kits, so switching packs swaps identity rather than API,
251
+ identical in all thirty-seven kits, so switching packs swaps identity rather than API,
251
252
  plus each pack's signature parts. **Motion does not cross**, exactly as it does
252
253
  not cross into Figma: a kit is the static half of a pack, and saying so is what
253
254
  stops an agent inventing motion to fill the silence.
@@ -297,7 +298,7 @@ a pack's four widened sections used to make two gates *quieter* and still green.
297
298
  One honest limit: the npx installer is checked by asserting its runtime bundle
298
299
  walker exists, not by reading a file list — it has none by design. What proves
299
300
  it ships the right files is CI, which installs the bundle through **both**
300
- installers and `diff -r`s the result against the source, then builds all thirty-six
301
+ installers and `diff -r`s the result against the source, then builds all thirty-seven
301
302
  kits.
302
303
 
303
304
  `test/scenarios.md` (T1–T34) is the behavioral harness: fresh subagents given a
package/bin/cli.js CHANGED
@@ -234,7 +234,7 @@ ${c("bold", "What it installs")}
234
234
  DESIGN_SYNC_BRIDGE.md the Claude Design contract (what a pack sends, and
235
235
  what does not cross)
236
236
  AI_PRODUCT_PATTERNS.md chat / agent / streaming surfaces (honest state)
237
- styles/ thirty-six style packs — instrument-console (dark console),
237
+ styles/ thirty-seven style packs — instrument-console (dark console),
238
238
  editorial-luxury (warm editorial), workbench (light/dark
239
239
  product UI), briefing-room (dark 16:9 presentation deck),
240
240
  atrium (warm cream consumer health), patchbay (dark live
@@ -250,6 +250,10 @@ ${c("bold", "What it installs")}
250
250
  size), deskmate (warm beige lit from above the top
251
251
  edge, one four-stop ramp, 56px pills against 32px
252
252
  slabs, and a framed transcript as the set piece),
253
+ test-drive (warm paper where the live product runs
254
+ inside drawn browser chrome, controls lit by a
255
+ four-layer shadow in their own hue, and a
256
+ handwritten hand annotating the tour),
253
257
  babylove (white +
254
258
  one orange, seven tokens over Tailwind defaults),
255
259
  outrank (white +
@@ -125,6 +125,13 @@ slabs, elevation is a field step with two shadows on the whole page, and the set
125
125
  piece is a framed transcript whose quoted chat client keeps its own face and colours
126
126
  under a --quoted-* namespace, for products sold as a colleague rather than a tool:
127
127
  AI employees and chat-native agents);
128
+ test-drive — warm paper and white cards where the set piece is the live product
129
+ running inside drawn browser chrome with traffic-light dots, one coral in two tokens
130
+ (the brand hue lights, fills and blinks; a darker step carries every body-size word),
131
+ every control lit by a four-layer shadow in its own hue, a handwritten face and a
132
+ yellow marker annotating from the margin, machine surfaces quarantined in their own
133
+ dark ladder that ignores the theme, for self-serve SaaS sold by handing over the
134
+ running product;
128
135
  otherwise follow the contract below (self-contained on purpose).
129
136
 
130
137
  ## Whether to animate at all — before how
@@ -58,6 +58,13 @@
58
58
  rules and it bars, and an icon that must be READ takes --ink-soft instead. */
59
59
  --ink-faint: #9693a3;
60
60
  --on-ink: #ffffff; /* 17.36:1 on --ink */
61
+ /* THE RULE THIS PACK CANNOT BE READ WITHOUT: --ink is for words on --bg, and
62
+ --on-surface is for words on --surface. On the light field the two are the same
63
+ value and the distinction looks decorative. On the dusk surface it is the whole
64
+ difference: --surface stays white there, measured, so a card that paints its text
65
+ in --ink paints white on white. Every component that sits on a card takes these. */
66
+ --on-surface: #1a182b; /* 17.36:1 on --surface */
67
+ --on-surface-soft: #716f7e; /* 4.91:1 on --surface */
61
68
 
62
69
  /* ---- The accent: it writes, it fills, and it needed no correction ---- */
63
70
  /* MEASURED. #6e47ff is the reference's purple-500 and its only functional accent:
@@ -121,9 +128,18 @@
121
128
  /* MEASURED — `ring-3` is 3px, and the ring's alpha IS its specification: 50% behind
122
129
  a control, 40% behind a field. Composited over white #ffffff the ring is
123
130
  #b6a3ff at 2.17:1, which is why the control's own border moves colour in the
124
- same state. */
125
- --focus-ring: 0 0 0 3px color-mix(in srgb, var(--accent) 50%, transparent);
126
- --focus-ring-field: 0 0 0 3px color-mix(in srgb, var(--accent) 40%, transparent);
131
+ same state.
132
+
133
+ THE LITERAL IS HERE AND THE DERIVED FORM IS IN THE `@supports` BLOCK AT THE END OF
134
+ THIS FILE, and the order is not a style preference. A custom property accepts
135
+ almost any token sequence, so `--focus-ring: color-mix(…)` parses even where
136
+ `color-mix()` is unsupported and wins the cascade; the invalidity surfaces only
137
+ where it is substituted, as invalid-at-computed-value-time, and by then the literal
138
+ is gone and `var(--focus-ring)` resolves to `unset` — an INVISIBLE FOCUS RING, on
139
+ the one degradation this pack says it may not ship. `showroom` is the worked
140
+ example this copies. */
141
+ --focus-ring: 0 0 0 3px rgba(110, 71, 255, 0.5);
142
+ --focus-ring-field: 0 0 0 3px rgba(110, 71, 255, 0.4);
127
143
 
128
144
  /* ---- Radii: a pill for the hand, a slab for the eye ----------------- */
129
145
  /* MEASURED at 1440: 977 of 1,191 visible elements compute 0px, 52 take the 32px
@@ -160,14 +176,20 @@
160
176
  --space-28: 112px; /* MEASURED — the section's own vertical rhythm */
161
177
 
162
178
  --page-max: 90rem; /* 1440px — MEASURED */
163
- --gutter: 20px; /* MEASURED — and it steps 40, 56, 80 outward */
164
- --gutter-wide: 80px; /* MEASURED at 1440 */
179
+ /* MEASURED, and the ramp ships as four tokens rather than as a sentence: the page's
180
+ gutter steps 20 → 40 → 56 → 80 across the reference's own breakpoints, and a step a
181
+ consumer cannot reach through a variable is a step the pack did not ship. */
182
+ --gutter: 20px;
183
+ --gutter-2: 40px; /* from 40rem */
184
+ --gutter-3: 56px; /* from 48rem */
185
+ --gutter-wide: 80px; /* from 64rem, and the value at 1440 */
165
186
  --control-h: 56px; /* MEASURED — the button and the field are the same height */
166
187
  --control-h-sm: 40px; /* MEASURED — the navigation's own pair, and see --tap-min */
167
188
  --control-px: 40px; /* MEASURED — 24px on the small size */
168
189
  /* The navigation slab is not a control and does not take a control's height:
169
- MEASURED at 65px with a 30px bottom radius at 1440, and 57px at the narrow
170
- width, inset 80px from each side of the page and 12px below 40rem. */
190
+ MEASURED at 65px with a 30px bottom radius at 1440 and 57px at the narrow width,
191
+ inset 40px from each side of the viewport and 12px below 40rem. That inset is the
192
+ SLAB's, and it is not the page's gutter — the gutter is 80px at the same width. */
171
193
  --slab-h: 65px;
172
194
  --slab-h-sm: 57px;
173
195
  --r-slab: 30px;
@@ -204,6 +226,13 @@
204
226
  --t-display-mid: 4.5rem; /* 72px from 40rem */
205
227
  --t-display-sm: 4rem; /* 64px below 40rem */
206
228
 
229
+ /* The reference holds its lede in a grid column and sets `max-width: none` on both
230
+ the headline and the paragraph, so these two are a PACK DECISION rather than a
231
+ measurement: the measure the reference implies, stated in `ch` so it survives the
232
+ face substitution below. */
233
+ --measure-lede: 62ch;
234
+ --measure-body: 68ch;
235
+
207
236
  --lh-display: 1.1; /* MEASURED — 88px on 80px */
208
237
  --lh-body: 1.4; /* MEASURED — 22.4px on 16px */
209
238
  --lh-ui: 1.5;
@@ -230,7 +259,12 @@
230
259
  --dur-fast: 0.15s; /* a colour, a border or an opacity */
231
260
  --dur-base: 0.2s; /* MEASURED — 23 elements */
232
261
  --dur-slow: 0.3s; /* MEASURED — 9 elements, a disclosure opening */
233
- --dur-enter: 0.64s; /* MEASURED — the reference's own enter-duration token */
262
+ /* MEASURED, and the two halves of the measurement disagree by 60ms: the reference
263
+ DECLARES `--enter-duration` as .6s, .64s and .68s across three sections, while the
264
+ transition on the 19 entering elements COMPUTES 0.7s. The pack ships the declared
265
+ middle value, because that is the number the reference's own author chose and the
266
+ 0.7s is one section's instance of it. */
267
+ --dur-enter: 0.64s;
234
268
  --stagger: 60ms; /* MEASURED — its enter-delay steps 0, 40, 60, 80, 120ms */
235
269
  --enter-y: 16px; /* MEASURED — the entrance travels 8px or 16px, never further */
236
270
 
@@ -294,6 +328,10 @@
294
328
  a muted icon here anyway, because its role is set by the light block. */
295
329
  --ink-faint: #9693a3;
296
330
  --on-ink: #150079;
331
+ /* A card on this surface stays WHITE — that is the measurement — so these two do NOT
332
+ invert with the field. They are the light block's values, and that is the point. */
333
+ --on-surface: #1a182b; /* 17.36:1 on --surface */
334
+ --on-surface-soft: #716f7e; /* 4.91:1 on --surface */
297
335
 
298
336
  --accent: #d2c6ff; /* 10.14:1 on --bg — SELECTED, the reference's own on-dusk eyebrow */
299
337
  --on-accent: #150079; /* 10.14:1 on --accent */
@@ -307,8 +345,12 @@
307
345
  --hairline-quiet: color-mix(in srgb, #ffffff 15%, var(--bg));
308
346
 
309
347
  --focus-color: #bcaaff; /* 7.90:1 on --bg, over the 3:1 mark floor */
310
- --focus-ring: 0 0 0 3px color-mix(in srgb, #8b6cff 50%, transparent);
311
- --focus-ring-field: 0 0 0 3px color-mix(in srgb, #8b6cff 40%, transparent);
348
+ /* MEASURED — the reference's on-dusk focus outline is its purple-400, and it is a
349
+ token here so the ring below derives from something rather than from a loose hex.
350
+ Literal first, derived in the `@supports` block, for the reason stated above. */
351
+ --focus-ring-base: #8b6cff;
352
+ --focus-ring: 0 0 0 3px rgba(139, 108, 255, 0.5);
353
+ --focus-ring-field: 0 0 0 3px rgba(139, 108, 255, 0.4);
312
354
  --gradient-word-fallback: #d2c6ff; /* 10.14:1 on --bg */
313
355
 
314
356
  /* DERIVED, and remapped rather than inherited: the light set measures 2.6-4.1:1 on
@@ -343,6 +385,22 @@
343
385
  }
344
386
  }
345
387
 
388
+ /* THE LITERALS ABOVE ARE THE FALLBACK, AND THEY ONLY WORK FROM IN HERE. The derived
389
+ form tracks the accent — change `--accent` and the ring follows — which is what a
390
+ literal cannot do; the literal exists so that a browser without `color-mix()` still
391
+ paints a ring at all. Both fields get one. */
392
+ @supports (color: color-mix(in srgb, red, red)) {
393
+ :root {
394
+ --focus-ring: 0 0 0 3px color-mix(in srgb, var(--accent) 50%, transparent);
395
+ --focus-ring-field: 0 0 0 3px color-mix(in srgb, var(--accent) 40%, transparent);
396
+ }
397
+
398
+ [data-surface="dusk"] {
399
+ --focus-ring: 0 0 0 3px color-mix(in srgb, var(--focus-ring-base) 50%, transparent);
400
+ --focus-ring-field: 0 0 0 3px color-mix(in srgb, var(--focus-ring-base) 40%, transparent);
401
+ }
402
+ }
403
+
346
404
  /* Body copy here is weight 500 on 146 nodes, and an unset `font-weight` gives 400 —
347
405
  one step light across the whole page. The UA's own 700 for `strong` and `b` is a
348
406
  step the other way and lands on the display's heading weight, so emphasis is pinned
@@ -367,10 +425,15 @@ b {
367
425
  /* PAGE — the page's own gutter, on the element a consumer never nests: a
368
426
  container query here would ask a component's box how wide the page is. */
369
427
  @media (min-width: 40rem) {
370
- .dm-page { padding-inline: var(--space-10); }
428
+ .dm-page { padding-inline: var(--gutter-2); }
371
429
  }
372
- /* PAGE — same rule, the wide step. */
373
- @media (min-width: 80rem) {
430
+ /* PAGE — the middle step. */
431
+ @media (min-width: 48rem) {
432
+ .dm-page { padding-inline: var(--gutter-3); }
433
+ }
434
+
435
+ /* PAGE — the wide step, and the value measured at 1440. */
436
+ @media (min-width: 64rem) {
374
437
  .dm-page { padding-inline: var(--gutter-wide); }
375
438
  }
376
439
 
@@ -486,15 +549,15 @@ b {
486
549
  font-size: var(--t-h3);
487
550
  font-weight: var(--weight-strong);
488
551
  letter-spacing: var(--track-display);
489
- color: var(--ink);
552
+ color: var(--on-surface);
490
553
  }
491
554
 
492
555
  .dm-card__meta {
493
556
  font-size: var(--t-sm);
494
- color: var(--ink-soft);
557
+ color: var(--on-surface-soft);
495
558
  }
496
559
 
497
- .dm-card__body { color: var(--ink-soft); font-size: var(--t-body); line-height: var(--lh-body); }
560
+ .dm-card__body { color: var(--on-surface-soft); font-size: var(--t-body); line-height: var(--lh-body); max-width: var(--measure-body); }
498
561
 
499
562
  /* ---- Chip ------------------------------------------------------------ */
500
563
  .dm-chip {
@@ -508,7 +571,7 @@ b {
508
571
  background-color: var(--surface);
509
572
  font-size: var(--t-sm);
510
573
  font-weight: var(--weight-body);
511
- color: var(--ink);
574
+ color: var(--on-surface);
512
575
  transition: border-color var(--dur-fast) var(--ease);
513
576
  }
514
577
 
@@ -617,7 +680,7 @@ b {
617
680
  margin-bottom: var(--space-2);
618
681
  font-size: var(--t-body);
619
682
  font-weight: var(--weight-body);
620
- color: var(--ink);
683
+ color: var(--on-surface);
621
684
  }
622
685
 
623
686
  .dm-field__input {
@@ -632,11 +695,11 @@ b {
632
695
  font-family: var(--font-body);
633
696
  font-size: var(--t-body);
634
697
  font-weight: var(--weight-regular);
635
- color: var(--ink);
698
+ color: var(--on-surface);
636
699
  transition: border-color var(--dur-fast) var(--ease);
637
700
  }
638
701
 
639
- .dm-field__input::placeholder { color: var(--ink-soft); }
702
+ .dm-field__input::placeholder { color: var(--on-surface-soft); }
640
703
 
641
704
  .dm-field__input:focus-visible {
642
705
  outline: none;
@@ -665,7 +728,7 @@ b {
665
728
  font-size: var(--t-sm);
666
729
  }
667
730
 
668
- .dm-slab__brand { color: var(--ink); font-weight: var(--weight-strong); }
731
+ .dm-slab__brand { color: var(--on-surface); font-weight: var(--weight-strong); }
669
732
  .dm-slab__nav { display: flex; gap: var(--space-2); margin-inline-end: auto; }
670
733
 
671
734
  .dm-slab__nav a {
@@ -674,7 +737,7 @@ b {
674
737
  min-height: var(--tap-min);
675
738
  padding: var(--space-2) var(--space-3);
676
739
  border-radius: var(--r-pill);
677
- color: var(--ink);
740
+ color: var(--on-surface);
678
741
  text-decoration: none;
679
742
  transition: background-color var(--dur-fast) var(--ease);
680
743
  }
@@ -791,10 +854,10 @@ b {
791
854
  font-size: var(--t-h3);
792
855
  font-weight: var(--weight-strong);
793
856
  letter-spacing: var(--track-display);
794
- color: var(--ink);
857
+ color: var(--on-surface);
795
858
  }
796
859
 
797
- .dm-qcard__body { color: var(--ink-soft); font-size: var(--t-body); line-height: var(--lh-body); }
860
+ .dm-qcard__body { color: var(--on-surface-soft); font-size: var(--t-body); line-height: var(--lh-body); }
798
861
 
799
862
  /* The card's halves answer to the CARD's box, not to the screen: at 34rem of
800
863
  container the artefact sits beside the sentence instead of under it. */
@@ -812,8 +875,8 @@ b {
812
875
  text-align: center;
813
876
  }
814
877
 
815
- .dm-empty__title { margin: 0; font-size: var(--t-body); font-weight: var(--weight-body); color: var(--ink); }
816
- .dm-empty__detail { margin: var(--space-2) 0 0; font-size: var(--t-body); color: var(--ink-soft); }
878
+ .dm-empty__title { margin: 0; font-size: var(--t-body); font-weight: var(--weight-body); color: var(--on-surface); }
879
+ .dm-empty__detail { margin: var(--space-2) 0 0; font-size: var(--t-body); color: var(--on-surface-soft); }
817
880
  .dm-empty__action { margin-top: var(--space-6); }
818
881
 
819
882
  /* ---- Skeleton -------------------------------------------------------- */
@@ -108,8 +108,15 @@
108
108
  --shadow-hair: 0 1px 0 0 color-mix(in srgb, var(--ink) 6%, transparent); /* MEASURED */
109
109
  --shadow-lift: 0 32px 64px -24px color-mix(in srgb, var(--ink) 28%, transparent); /* MEASURED — once */
110
110
 
111
- --focus-ring: 0 0 0 4px color-mix(in srgb, var(--accent) 10%, transparent),
112
- 0 4px 10px -3px color-mix(in srgb, var(--accent) 45%, transparent); /* MEASURED */
111
+ /* MEASURED, and declared LITERAL FIRST with the derived form in the `@supports` block
112
+ at the end of this file. The order is the guard, not a style: a custom property
113
+ accepts almost any token sequence, so a `color-mix()` value parses even where
114
+ `color-mix()` is unsupported and wins the cascade; it fails only at substitution, by
115
+ which point the literal is gone and `var(--focus-ring)` resolves to `unset` — an
116
+ invisible focus ring. Added 1.52.0; `showroom` is the pack that established the
117
+ mechanism and `deskmate` carries the same guard. */
118
+ --focus-ring: 0 0 0 4px rgba(5, 98, 239, 0.1),
119
+ 0 4px 10px -3px rgba(5, 98, 239, 0.45);
113
120
  --focus-w: 2px;
114
121
  --focus-offset: 2px;
115
122
  --focus-color: var(--accent); /* 5.25:1 on --bg, over the 3:1 mark floor */
@@ -262,6 +269,16 @@
262
269
  }
263
270
  }
264
271
 
272
+ /* THE LITERALS ABOVE ARE THE FALLBACK, AND THEY ONLY WORK FROM IN HERE. The derived form
273
+ tracks `--accent`; the literal exists so a browser without `color-mix()` still paints a
274
+ ring at all. The dark band inherits this ring, and its lighter accent with it. */
275
+ @supports (color: color-mix(in srgb, red, red)) {
276
+ :root {
277
+ --focus-ring: 0 0 0 4px color-mix(in srgb, var(--accent) 10%, transparent),
278
+ 0 4px 10px -3px color-mix(in srgb, var(--accent) 45%, transparent);
279
+ }
280
+ }
281
+
265
282
  /* The pack bans bold, and a ban the UA overrides is not a ban: `strong` and `b` are 700
266
283
  by default in every browser. 500 is the heaviest weight this page spends at scale, so
267
284
  emphasis here is the medium weight — or, better, the mono. */
@@ -0,0 +1,14 @@
1
+ {
2
+ "pkg": "@sheleg-design/test-drive",
3
+ "globalName": "ShelegTestDrive",
4
+ "shape": "package",
5
+ "buildCmd": "npm run build",
6
+ "srcDir": "src",
7
+ "tsconfig": "tsconfig.json",
8
+ "cssEntry": "src/styles.css",
9
+ "docsDir": "src",
10
+ "readmeHeader": ".design-sync/conventions.md",
11
+ "guidelinesGlob": [
12
+ "guidelines/*.md"
13
+ ]
14
+ }
@@ -0,0 +1,40 @@
1
+ # Test-drive — conventions for a design agent
2
+
3
+ This kit is the built form of the Test-drive style pack. Read these before generating
4
+ anything with it; they are the pack's bans, stated where a design tool will see them.
5
+
6
+ ## Colour
7
+
8
+ - **One coral, two tokens.** `--accent` (#e16540) lights controls, fills bars and
9
+ blinks carets; it carries text only at display sizes. Every body-size coral word is
10
+ `--action` (#c04a28). Swapping them ships the reference's own AA failure.
11
+ - **The declared teal is dead.** The reference's theme names an accent it never
12
+ paints; nothing here may resurrect it.
13
+ - **Money is coral, traffic is blue** (`--chart-money`, `--chart-traffic`), and the
14
+ blue never carries a word.
15
+ - **Status is never colour alone** — a state takes the word or the glyph beside it;
16
+ `Delta` ships the arrow for exactly this reason.
17
+
18
+ ## Geometry
19
+
20
+ - A control is 8px (`--r-control`), a card 16px (`--r-card`), the frame 20.8px
21
+ (`--r-frame`), the badge a pill. The frame's window is concentric:
22
+ `calc(var(--r-frame) - var(--frame-inset))`.
23
+ - The nav is 65px, static, unfrosted, in the field's own colour.
24
+
25
+ ## Elevation
26
+
27
+ - A card is ringed (`--ring-card`); a control is lit (`--lit-action` /
28
+ `--lit-quiet`); the frame gets the one big drop (`--shadow-frame`). There is no
29
+ fourth shadow.
30
+
31
+ ## The machine
32
+
33
+ - Terminal, CLI and agent surfaces take `--machine-*` and ignore the theme. Their
34
+ tokens never reach a card, and the caret never leaves them.
35
+
36
+ ## Motion
37
+
38
+ - 0.2s for anything a pointer caused; press scales to 0.95 over 0.15s; demos narrate
39
+ themselves on double-digit clocks. Nothing is scroll-driven. Under reduced motion a
40
+ demo shows its final frame, not its hidden first one.
@@ -0,0 +1,34 @@
1
+ # @sheleg-design/test-drive
2
+
3
+ The React reference kit for the **Test-drive** style pack — warm paper and white cards
4
+ where the set piece is the live product running inside drawn browser chrome, and a
5
+ founder's hand annotates the tour.
6
+
7
+ The pack document is the source of truth for every value here:
8
+ `plugins/sheleg-design/skills/sheleg-design/styles/test-drive.md`. This kit is what
9
+ those values look like when they are built, and `src/styles.css` opens with the pack's
10
+ token layer copied byte for byte — never transcribed.
11
+
12
+ ```bash
13
+ npm install && npm run build # tsc only; there is no bundler in this kit
14
+ ```
15
+
16
+ ## What ships
17
+
18
+ **The spine**, identical in name, props and types across every SHELEG kit, so switching
19
+ packs swaps identity rather than API: `Button`, `Card`, `Chip`, `Stat`, `Heading`,
20
+ `Rule`.
21
+
22
+ **The signature**, this pack's own: `DemoFrame` (the browser chrome around a running
23
+ interior — the signature element), `Annotation` (the hand), `Marker` (the
24
+ highlighter), `Delta` (a vital sign's movement), `Machine` + `Caret` (the quarantined
25
+ terminal), `ThinkingDots` (the machine's loader), `Field` (and the `td-join` pair),
26
+ `NavBar`, `Empty`.
27
+
28
+ ## The three rules a generator loses first
29
+
30
+ 1. The coral splits: `--accent` lights, fills and blinks; `--action` carries every
31
+ body-size word. `#ffffff` on `#e16540` is 3.42:1 — that is why both exist.
32
+ 2. The lit shadow belongs to controls; a card takes `--ring-card` and nothing else.
33
+ 3. The `DemoFrame` interior runs. A screenshot inside the chrome is `showroom`'s
34
+ exhibit, not this pack's proof.
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@sheleg-design/test-drive",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "src"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc -p tsconfig.json"
21
+ },
22
+ "peerDependencies": {
23
+ "react": ">=18"
24
+ },
25
+ "devDependencies": {
26
+ "typescript": "^5.6.0",
27
+ "@types/react": "^18.3.0"
28
+ }
29
+ }
@@ -0,0 +1,13 @@
1
+ ---
2
+ category: Signature
3
+ ---
4
+
5
+ The founder's hand: `--font-hand` (Fuzzy Bubbles) at `--t-label`, in `--ink-mute` or
6
+ — for an offer — `--action`. It annotates from the margin, always beside or above the
7
+ thing it points at. It never announces: no headings, no controls, no body copy in the
8
+ hand.
9
+
10
+ ```tsx
11
+ <Annotation>Interactive demo</Annotation>
12
+ <Annotation tone="accent">2 months free</Annotation>
13
+ ```