sheleg-design-skill 1.52.0 → 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 (57) hide show
  1. package/CHANGELOG.md +51 -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/test-drive/.design-sync/config.json +14 -0
  6. package/kits/test-drive/.design-sync/conventions.md +40 -0
  7. package/kits/test-drive/README.md +34 -0
  8. package/kits/test-drive/package.json +29 -0
  9. package/kits/test-drive/src/Annotation.md +13 -0
  10. package/kits/test-drive/src/Annotation.tsx +20 -0
  11. package/kits/test-drive/src/Button.md +23 -0
  12. package/kits/test-drive/src/Button.tsx +33 -0
  13. package/kits/test-drive/src/Card.md +14 -0
  14. package/kits/test-drive/src/Card.tsx +24 -0
  15. package/kits/test-drive/src/Chip.md +13 -0
  16. package/kits/test-drive/src/Chip.tsx +25 -0
  17. package/kits/test-drive/src/Delta.md +11 -0
  18. package/kits/test-drive/src/Delta.tsx +23 -0
  19. package/kits/test-drive/src/DemoFrame.md +19 -0
  20. package/kits/test-drive/src/DemoFrame.tsx +25 -0
  21. package/kits/test-drive/src/Empty.md +13 -0
  22. package/kits/test-drive/src/Empty.tsx +21 -0
  23. package/kits/test-drive/src/Field.md +18 -0
  24. package/kits/test-drive/src/Field.tsx +38 -0
  25. package/kits/test-drive/src/Heading.md +13 -0
  26. package/kits/test-drive/src/Heading.tsx +19 -0
  27. package/kits/test-drive/src/Machine.md +18 -0
  28. package/kits/test-drive/src/Machine.tsx +26 -0
  29. package/kits/test-drive/src/Marker.md +11 -0
  30. package/kits/test-drive/src/Marker.tsx +12 -0
  31. package/kits/test-drive/src/NavBar.md +14 -0
  32. package/kits/test-drive/src/NavBar.tsx +20 -0
  33. package/kits/test-drive/src/Rule.md +11 -0
  34. package/kits/test-drive/src/Rule.tsx +18 -0
  35. package/kits/test-drive/src/Stat.md +18 -0
  36. package/kits/test-drive/src/Stat.tsx +17 -0
  37. package/kits/test-drive/src/ThinkingDots.md +15 -0
  38. package/kits/test-drive/src/ThinkingDots.tsx +19 -0
  39. package/kits/test-drive/src/index.ts +33 -0
  40. package/kits/test-drive/src/styles.css +833 -0
  41. package/kits/test-drive/tsconfig.json +15 -0
  42. package/package.json +2 -2
  43. package/plugins/sheleg-design/.claude-plugin/plugin.json +2 -2
  44. package/plugins/sheleg-design/commands/sheleg-design.md +2 -2
  45. package/plugins/sheleg-design/skills/sheleg-design/DESIGN_SYNC_BRIDGE.md +1 -1
  46. package/plugins/sheleg-design/skills/sheleg-design/FIGMA_BRIDGE.md +2 -2
  47. package/plugins/sheleg-design/skills/sheleg-design/MOBILE_SURFACES.md +1 -1
  48. package/plugins/sheleg-design/skills/sheleg-design/SKILL.md +2 -2
  49. package/plugins/sheleg-design/skills/sheleg-design/STYLE_PACK_INDEX.md +3 -2
  50. package/plugins/sheleg-design/skills/sheleg-design/SURFACE_COMPOSITION.md +4 -4
  51. package/plugins/sheleg-design/skills/sheleg-design/styles/STYLE_PACK_TEMPLATE.md +1 -1
  52. package/plugins/sheleg-design/skills/sheleg-design/styles/datasheet.md +5 -1
  53. package/plugins/sheleg-design/skills/sheleg-design/styles/scoreboard.md +6 -1
  54. package/plugins/sheleg-design/skills/sheleg-design/styles/showroom.md +5 -1
  55. package/plugins/sheleg-design/skills/sheleg-design/styles/tenor.md +1 -1
  56. package/plugins/sheleg-design/skills/sheleg-design/styles/test-drive.md +454 -0
  57. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/test-drive.css +389 -0
package/CHANGELOG.md CHANGED
@@ -6,6 +6,57 @@ 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
+
9
60
  ## [1.52.0] - 2026-08-27
10
61
 
11
62
  ### The front door is the designs
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
@@ -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
+ ```
@@ -0,0 +1,20 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface AnnotationProps {
4
+ /** `accent` writes in --action; default writes in --ink-mute. */
5
+ tone?: 'mute' | 'accent';
6
+ children: ReactNode;
7
+ className?: string;
8
+ }
9
+
10
+ export function Annotation({ tone = 'mute', children, className }: AnnotationProps) {
11
+ return (
12
+ <span
13
+ className={['td-annotation', `td-annotation--${tone}`, className]
14
+ .filter(Boolean)
15
+ .join(' ')}
16
+ >
17
+ {children}
18
+ </span>
19
+ );
20
+ }
@@ -0,0 +1,23 @@
1
+ ---
2
+ category: Actions
3
+ ---
4
+
5
+ Every control is an 8px-radius block at `--control-h` (48px) with `--control-px` of
6
+ horizontal padding, 16px at `--weight-control`. `sm` is `--control-h-sm` (32px) — the
7
+ nav's measured pair, and the reason `--tap-min` exists as a correction.
8
+
9
+ `primary` fills with `--action`, labels in `--on-action` at 4.94:1 on `#c04a28`, and is
10
+ lit by `--lit-action` — the four-layer coral recipe. Hover moves the fill to
11
+ `--action-hover`, the resolved form of the reference's own
12
+ `color-mix(in oklab, fill 90%, #000)`. `secondary` is `--surface` with a `--hairline`
13
+ border and `--lit-quiet`; hover brightens the glow to `--lit-quiet-hover`, and the
14
+ control itself does not move. `ghost` is transparent until hover fills it with
15
+ `--hairline`.
16
+
17
+ Press scales to `--press-scale` (0.95) over `--dur-press`. Focus is one mechanism: a
18
+ `--focus-w` outline in `--focus-color` at `--focus-offset`.
19
+
20
+ ```tsx
21
+ <Button>Add my website</Button>
22
+ <Button variant="secondary" size="sm">Log in</Button>
23
+ ```
@@ -0,0 +1,33 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface ButtonProps {
4
+ /** `primary` is the accent fill — at most one per view. */
5
+ variant?: 'primary' | 'secondary' | 'ghost';
6
+ size?: 'sm' | 'md' | 'lg';
7
+ disabled?: boolean;
8
+ onClick?: () => void;
9
+ children: ReactNode;
10
+ className?: string;
11
+ }
12
+
13
+ export function Button({
14
+ variant = 'primary',
15
+ size = 'md',
16
+ disabled = false,
17
+ onClick,
18
+ children,
19
+ className,
20
+ }: ButtonProps) {
21
+ return (
22
+ <button
23
+ type="button"
24
+ className={['td-btn', `td-btn--${variant}`, `td-btn--${size}`, className]
25
+ .filter(Boolean)
26
+ .join(' ')}
27
+ disabled={disabled}
28
+ onClick={onClick}
29
+ >
30
+ {children}
31
+ </button>
32
+ );
33
+ }
@@ -0,0 +1,14 @@
1
+ ---
2
+ category: Surfaces
3
+ ---
4
+
5
+ A card is `--surface` at `--r-card` with `--ring-card` — a 1px 6% ring plus a soft
6
+ 1–2px drop, no border property, and never the lit shadow, which belongs to controls.
7
+ The title is `--t-h3` at `--weight-display`; meta sits right-aligned in `--ink-mute`.
8
+ Body copy holds `--measure-body`.
9
+
10
+ Cards do not hover, do not nest, and never paint a `--machine-*` token.
11
+
12
+ ```tsx
13
+ <Card title="Web analytics" meta="live">…</Card>
14
+ ```
@@ -0,0 +1,24 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface CardProps {
4
+ title?: string;
5
+ /** Right-aligned metadata on the title row: a count, an id, a timestamp. */
6
+ meta?: string;
7
+ children: ReactNode;
8
+ className?: string;
9
+ }
10
+
11
+ export function Card({ title, meta, children, className }: CardProps) {
12
+ const head = title !== undefined || meta !== undefined;
13
+ return (
14
+ <section className={['td-card', className].filter(Boolean).join(' ')}>
15
+ {head && (
16
+ <div className="td-card__head">
17
+ {title !== undefined && <h3 className="td-card__title">{title}</h3>}
18
+ {meta !== undefined && <span className="td-card__meta">{meta}</span>}
19
+ </div>
20
+ )}
21
+ <div className="td-card__body">{children}</div>
22
+ </section>
23
+ );
24
+ }
@@ -0,0 +1,13 @@
1
+ ---
2
+ category: Data
3
+ ---
4
+
5
+ A pill at `--r-badge` on `--surface` with a `--hairline` border, `--t-label` at
6
+ `--weight-control`. `accent` fills with `--accent-wash` and writes in `--action`;
7
+ `selected` moves the border and label to `--action`. A chip never carries a status
8
+ colour — states live in `Delta` and the word beside it.
9
+
10
+ ```tsx
11
+ <Chip>codefa.st</Chip>
12
+ <Chip tone="accent" selected>Last 30 days</Chip>
13
+ ```
@@ -0,0 +1,25 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface ChipProps {
4
+ children: ReactNode;
5
+ selected?: boolean;
6
+ tone?: 'neutral' | 'accent';
7
+ className?: string;
8
+ }
9
+
10
+ export function Chip({ children, selected = false, tone = 'neutral', className }: ChipProps) {
11
+ return (
12
+ <span
13
+ className={[
14
+ 'td-chip',
15
+ `td-chip--${tone}`,
16
+ selected ? 'td-chip--selected' : undefined,
17
+ className,
18
+ ]
19
+ .filter(Boolean)
20
+ .join(' ')}
21
+ >
22
+ {children}
23
+ </span>
24
+ );
25
+ }
@@ -0,0 +1,11 @@
1
+ ---
2
+ category: Signature
3
+ ---
4
+
5
+ A vital-sign's movement: the number first, the arrow second, the hue third — `--good`
6
+ up, `--danger` down, and never the colour alone, which is why the arrow ships inside
7
+ the component. Sits in a `Stat`'s source slot.
8
+
9
+ ```tsx
10
+ <Stat value="83%" label="Bounce rate" source={<Delta direction="up">1%</Delta>} />
11
+ ```
@@ -0,0 +1,23 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface DeltaProps {
4
+ /** The arrow is the second encoding; the number beside it is the first. */
5
+ direction: 'up' | 'down';
6
+ children: ReactNode;
7
+ className?: string;
8
+ }
9
+
10
+ export function Delta({ direction, children, className }: DeltaProps) {
11
+ return (
12
+ <span
13
+ className={['td-delta', `td-delta--${direction}`, className]
14
+ .filter(Boolean)
15
+ .join(' ')}
16
+ >
17
+ {children}
18
+ <span aria-hidden="true" className="td-delta__arrow">
19
+ {direction === 'up' ? '\u2191' : '\u2193'}
20
+ </span>
21
+ </span>
22
+ );
23
+ }
@@ -0,0 +1,19 @@
1
+ ---
2
+ category: Signature
3
+ ---
4
+
5
+ **The signature element.** Browser chrome at `--r-frame` in `--machine-chrome` — three
6
+ traffic-light dots in the measured `--frame-dot-*` hues, the real address centered in
7
+ `--on-machine` — around a running interior on `--surface`, with `--shadow-frame`
8
+ under it. The window is concentric: `calc(var(--r-frame) - var(--frame-inset))`.
9
+
10
+ The interior must run — an iframe of the live product, or a self-narrating demo built
11
+ from `Machine`, `Caret` and `ThinkingDots`. A frozen screenshot inside this chrome is
12
+ the one thing the pack bans outright. Anchor an `Annotation` outside the frame's top
13
+ corner to say what the reader should notice.
14
+
15
+ ```tsx
16
+ <DemoFrame url="https://datafa.st/codefa.st">
17
+ <iframe src="/demo" title="Live demo" />
18
+ </DemoFrame>
19
+ ```
@@ -0,0 +1,25 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface DemoFrameProps {
4
+ /** The address shown in the chrome bar — a real one; the frame quotes a live app. */
5
+ url: string;
6
+ /** The running interior — an iframe, or a self-narrating demo. Never a still. */
7
+ children: ReactNode;
8
+ className?: string;
9
+ }
10
+
11
+ export function DemoFrame({ url, children, className }: DemoFrameProps) {
12
+ return (
13
+ <figure className={['td-frame', className].filter(Boolean).join(' ')}>
14
+ <div className="td-frame__chrome">
15
+ <span className="td-frame__dots" aria-hidden="true">
16
+ <i className="td-frame__dot td-frame__dot--red" />
17
+ <i className="td-frame__dot td-frame__dot--amber" />
18
+ <i className="td-frame__dot td-frame__dot--green" />
19
+ </span>
20
+ <span className="td-frame__url">{url}</span>
21
+ </div>
22
+ <div className="td-frame__window">{children}</div>
23
+ </figure>
24
+ );
25
+ }
@@ -0,0 +1,13 @@
1
+ ---
2
+ category: Surfaces
3
+ ---
4
+
5
+ An empty screen is a state, not an absence: a `--surface` card holding one 16px/500
6
+ line, one sentence in `--ink-mute`, and one quiet control that starts the demo — this
7
+ pack always has a demo to offer. A zero in a metric renders as a figure in its tile,
8
+ the way the reference's dashboard ships "Online 0".
9
+
10
+ ```tsx
11
+ <Empty title="No data yet" hint="Add your website to start the tour."
12
+ action={<Button variant="secondary">Try the live demo</Button>} />
13
+ ```
@@ -0,0 +1,21 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface EmptyProps {
4
+ /** One line in --ink, 16px/500. */
5
+ title: string;
6
+ /** One sentence in --ink-mute. */
7
+ hint?: string;
8
+ /** One quiet control that starts the demo — this pack always has one to offer. */
9
+ action?: ReactNode;
10
+ className?: string;
11
+ }
12
+
13
+ export function Empty({ title, hint, action, className }: EmptyProps) {
14
+ return (
15
+ <div className={['td-empty', className].filter(Boolean).join(' ')}>
16
+ <span className="td-empty__title">{title}</span>
17
+ {hint !== undefined && <span className="td-empty__hint">{hint}</span>}
18
+ {action !== undefined && <span className="td-empty__action">{action}</span>}
19
+ </div>
20
+ );
21
+ }
@@ -0,0 +1,18 @@
1
+ ---
2
+ category: Actions
3
+ ---
4
+
5
+ A `--control-h` input on `--surface` with a `--hairline-strong` border at
6
+ `--r-control`, placeholder in `--ink-mute`. Focus is the pack's one focus contract.
7
+ Invalid moves the border to `--danger` **and** shows the message — never the colour
8
+ alone.
9
+
10
+ The hero's joined pair wraps a `Field` and a `Button` in `td-join`: one 8px outer
11
+ radius, square seam edges — the measured shape of the reference's URL capture.
12
+
13
+ ```tsx
14
+ <div className="td-join">
15
+ <Field placeholder="website.com" />
16
+ <Button>Add my website</Button>
17
+ </div>
18
+ ```
@@ -0,0 +1,38 @@
1
+ export interface FieldProps {
2
+ label?: string;
3
+ placeholder?: string;
4
+ value?: string;
5
+ onChange?: (value: string) => void;
6
+ invalid?: boolean;
7
+ /** Shown under the field when invalid — the colour never carries the state alone. */
8
+ message?: string;
9
+ className?: string;
10
+ }
11
+
12
+ export function Field({
13
+ label,
14
+ placeholder,
15
+ value,
16
+ onChange,
17
+ invalid = false,
18
+ message,
19
+ className,
20
+ }: FieldProps) {
21
+ return (
22
+ <label className={['td-field', className].filter(Boolean).join(' ')}>
23
+ {label !== undefined && <span className="td-field__label">{label}</span>}
24
+ <input
25
+ className={['td-field__input', invalid ? 'td-field__input--invalid' : undefined]
26
+ .filter(Boolean)
27
+ .join(' ')}
28
+ placeholder={placeholder}
29
+ value={value}
30
+ aria-invalid={invalid || undefined}
31
+ onChange={(e) => onChange?.(e.target.value)}
32
+ />
33
+ {invalid && message !== undefined && (
34
+ <span className="td-field__message">{message}</span>
35
+ )}
36
+ </label>
37
+ );
38
+ }
@@ -0,0 +1,13 @@
1
+ ---
2
+ category: Foundations
3
+ ---
4
+
5
+ One face announces everything: DM Sans at `--weight-display` (800), tracked
6
+ `--track-display` (−0.025em) at every display size. Level 1 is the display — 36px
7
+ below 48rem, 60px from it, stepping rather than flowing, at `--lh-display` (1). Level
8
+ 2 steps 30 → 48; level 3 holds 36. The hand (`Annotation`) never takes a heading's
9
+ job.
10
+
11
+ ```tsx
12
+ <Heading level={1}>Revenue-first analytics</Heading>
13
+ ```
@@ -0,0 +1,19 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface HeadingProps {
4
+ /** 1 = display (60px), 2 = section (48px), 3 = card head (36px). */
5
+ level?: 1 | 2 | 3;
6
+ children: ReactNode;
7
+ className?: string;
8
+ }
9
+
10
+ export function Heading({ level = 2, children, className }: HeadingProps) {
11
+ const Tag = `h${level}` as 'h1' | 'h2' | 'h3';
12
+ return (
13
+ <Tag
14
+ className={['td-heading', `td-heading--${level}`, className].filter(Boolean).join(' ')}
15
+ >
16
+ {children}
17
+ </Tag>
18
+ );
19
+ }