sheleg-design-skill 1.23.0 → 1.24.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 (46) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/README.md +5 -4
  3. package/bin/cli.js +5 -3
  4. package/cursor/rules/sheleg-design.mdc +5 -1
  5. package/kits/roster/.design-sync/config.json +14 -0
  6. package/kits/roster/.design-sync/conventions.md +62 -0
  7. package/kits/roster/README.md +45 -0
  8. package/kits/roster/package.json +29 -0
  9. package/kits/roster/src/Button.md +19 -0
  10. package/kits/roster/src/Button.tsx +33 -0
  11. package/kits/roster/src/Card.md +12 -0
  12. package/kits/roster/src/Card.tsx +24 -0
  13. package/kits/roster/src/Chip.md +11 -0
  14. package/kits/roster/src/Chip.tsx +25 -0
  15. package/kits/roster/src/Eyebrow.md +16 -0
  16. package/kits/roster/src/Eyebrow.tsx +16 -0
  17. package/kits/roster/src/FaqList.md +12 -0
  18. package/kits/roster/src/FaqList.tsx +30 -0
  19. package/kits/roster/src/Heading.md +20 -0
  20. package/kits/roster/src/Heading.tsx +19 -0
  21. package/kits/roster/src/IndustryColumn.md +25 -0
  22. package/kits/roster/src/IndustryColumn.tsx +32 -0
  23. package/kits/roster/src/LogoTile.md +16 -0
  24. package/kits/roster/src/LogoTile.tsx +29 -0
  25. package/kits/roster/src/Rule.md +11 -0
  26. package/kits/roster/src/Rule.tsx +18 -0
  27. package/kits/roster/src/Stat.md +12 -0
  28. package/kits/roster/src/Stat.tsx +17 -0
  29. package/kits/roster/src/StepCard.md +19 -0
  30. package/kits/roster/src/StepCard.tsx +38 -0
  31. package/kits/roster/src/index.ts +25 -0
  32. package/kits/roster/src/styles.css +604 -0
  33. package/kits/roster/tsconfig.json +15 -0
  34. package/package.json +2 -2
  35. package/plugins/sheleg-design/.claude-plugin/plugin.json +2 -2
  36. package/plugins/sheleg-design/commands/sheleg-design.md +1 -1
  37. package/plugins/sheleg-design/skills/sheleg-design/DESIGN_SYNC_BRIDGE.md +1 -1
  38. package/plugins/sheleg-design/skills/sheleg-design/MOBILE_SURFACES.md +1 -1
  39. package/plugins/sheleg-design/skills/sheleg-design/SKILL.md +8 -5
  40. package/plugins/sheleg-design/skills/sheleg-design/SURFACE_COMPOSITION.md +3 -3
  41. package/plugins/sheleg-design/skills/sheleg-design/styles/manpage.md +7 -0
  42. package/plugins/sheleg-design/skills/sheleg-design/styles/pigeonhole.md +29 -9
  43. package/plugins/sheleg-design/skills/sheleg-design/styles/roster.md +425 -0
  44. package/plugins/sheleg-design/skills/sheleg-design/styles/scoreboard.md +19 -3
  45. package/plugins/sheleg-design/skills/sheleg-design/styles/showroom.md +12 -3
  46. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/roster.css +216 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,98 @@ All notable changes to this project are documented in this file. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions
5
5
  follow [SemVer](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [1.24.0] - 2026-08-13
8
+
9
+ A seventeenth style pack, whose whole argument is other people's names — and a reference
10
+ whose `h1` is one pixel wide.
11
+
12
+ ### Added
13
+
14
+ - **`roster`** — the seventeenth pack, extracted from `babylovegrowth.ai` off the
15
+ server-rendered HTML of `/en` (1,415,414 bytes), its two shipped stylesheets (466,577
16
+ bytes, 410 custom properties) and then off **computed styles on the live page** at
17
+ 1440×900, 768×1168 and 390×790 — **5,936 rendered elements** at the widest. A white
18
+ field in a faint grid of squares, hairlines instead of shadows, the pill as the most
19
+ frequent shape, and one orange that may never carry a word. For products whose argument
20
+ is *who already carries them*: AI-search and GEO visibility, SEO and content platforms,
21
+ agencies, marketplaces. Widened contract, a light-only token layer, a full reference kit,
22
+ and reciprocal forks written into `scoreboard`, `showroom`, `pigeonhole` and `manpage`.
23
+ - **The fork against `scoreboard` is the pack's reason to exist.** Both serve growth, ads
24
+ and SEO products, so the category cannot decide it — the **kind of proof** can. One is
25
+ built around a figure that ticks up; the other around a name that appears. Ask what the
26
+ page loses if you delete its proof, and the giveaway is that `roster` sets its largest
27
+ figure (*4,000+*) in the same 16px eyebrow as everything else.
28
+ - **`RESOLVED`, a fourth provenance family.** The reference computes its neutrals in
29
+ `lab()`, which the palette gate refuses on purpose, so each was resolved to the sRGB the
30
+ browser actually paints — **34 distinct values**, each painted into a 1×1 canvas and read
31
+ back as bytes. They turn out to be Tailwind v4's defaults: 7,234 borders at
32
+ `lab(91.6229 …)` = `#e5e7eb`. The bespoke layer beside them is four near-blacks and two
33
+ oranges wide, and the pack ships one of each with the criterion written at the
34
+ declaration.
35
+ - **`IndustryColumn`, the signature element** — a pill-labelled column of other companies'
36
+ marks, hairline-divided, six across at 1440 — plus `LogoTile`, `Eyebrow` and `StepCard`.
37
+ Both container breakpoints are **derived from the component's own geometry** (220px for
38
+ the column's two-up mark grid, 640px for the step card's split) rather than carried over
39
+ from the viewport, which is what B-032 exists to prevent.
40
+
41
+ ### Fixed
42
+
43
+ - **The pack refuses the reference's heading structure.** Its `h1` is `.sr-only` — 1×1px,
44
+ white, `clip-path: inset(50%)` — and the visible 68px line is a `<span>`, while all
45
+ sixteen `h2`s are small orange eyebrows. So the document outline says *eyebrow* where the
46
+ page says *section head*, and the largest text on the page is not a heading at all. This
47
+ pack teaches the opposite and `manpage` now carries the fork from the other end.
48
+ - **The accent may not carry a word.** `#fa5c12` is 3.18:1 on white; white on `#f25533` at
49
+ the nav pill's 16px/600 is **3.43:1**. The pack ships `--accent-ink` at 4.52:1 for
50
+ anything read, keeps the accent for fills and large text, and makes the primary button
51
+ black — 19.66:1, which is what the reference itself does for its hero.
52
+ - **The dominant secondary ink fails on its own band**: `#6a7282` is 4.84:1 on white and
53
+ **4.35:1** on the `#f0f3f8` band it is painted on. `--ink-soft` is the darkened form and
54
+ clears both.
55
+ - **No `--warn` is shipped.** The reference paints no amber anywhere, so one here would be
56
+ invented rather than selected. Stated in the Palette instead of filled in.
57
+
58
+ ### Notes
59
+
60
+ - **Reduced motion covers six of the reference's animations out of roughly twenty.** Its
61
+ branch names classes one by one, leaving `arrow-nudge`, `skeleton-blink`, both spinners,
62
+ `settings-ripple`, `meta-preview-float` and the accordions running — the opposite failure
63
+ to `pigeonhole`'s reference, which collapsed everything with one `*` rule and strobed its
64
+ marquee by doing so. This pack collapses unconditionally and pauses its two floats in the
65
+ component layer.
66
+ - **A claim the measurement refused.** The hero sets a third party's wordmark inline after
67
+ the word *from*; it was sampled seven times across 5.4 seconds with no change, so the
68
+ pack specifies **one mark chosen per page** and makes no claim that it rotates.
69
+ - Gates: validate 1938 → 2066, palette 906 → 956, sloplint 436 → 450, floors raised with
70
+ the reason.
71
+
72
+ ## [1.23.1] - 2026-08-13
73
+
74
+ Two of the seven answers 1.23.0 shipped told an implementer to do something the same
75
+ release had just described as impossible.
76
+
77
+ ### Fixed
78
+
79
+ - **`pigeonhole` contradicted the SELF category it shipped beside.** Its new Responsive
80
+ answer said the labelled row and the FAQ "take `container-type: inline-size` on their
81
+ wrapper" — but the row's axis and the list's columns are properties of the row and the
82
+ `<dl>` **themselves**, so neither can query its own width. That is exactly the SELF case
83
+ the skeleton now defines. The corrected answer is the useful one: both live inside a
84
+ list the consumer already owns, so **the consumer's list is the container** — a
85
+ component library may ask for `container-type` on it and may not wrap someone's markup
86
+ to get it. Descendants inside them (the preview's truncation, the date's visibility)
87
+ are ordinary CONTAINER cases and need nothing from the consumer.
88
+ - **`showroom` named a wrapper its kit does not ship.** "container-type on the table
89
+ wrapper" became the **specimen frame**, which is the component that actually holds the
90
+ table (`Specimen`), so the data row and the column header query something that exists.
91
+
92
+ ### Notes
93
+
94
+ Found by checking every component named in the seven new answers against the components
95
+ its kit actually ships — a check worth running whenever pack prose starts naming parts.
96
+ Five of the seven were already right; `maquette`'s "agent prompt" is named by the pack and
97
+ absent from the kit, which is a pre-existing pack/kit gap rather than this release's.
98
+
7
99
  ## [1.23.0] - 2026-08-13
8
100
 
9
101
  A component library that sized itself by the screen, and a contract bullet nobody had
package/README.md CHANGED
@@ -11,7 +11,7 @@ problem — invented colors, six accent hues, dark mode retrofitted later.
11
11
 
12
12
  This skill is the taste layer. It gives a coding agent **one motion
13
13
  methodology** for cinematic, scroll-driven pages, **a motion doctrine** that
14
- decides whether to animate before it decides how, and **sixteen locked style
14
+ decides whether to animate before it decides how, and **seventeen locked style
15
15
  packs** with ready-made design tokens, so what it builds reads as one system
16
16
  instead of a pile of effects.
17
17
 
@@ -61,6 +61,7 @@ into the cinematic layer, and says so in its own *Motion flavor* section.
61
61
  | `datasheet` | an off-white spec sheet ruled with dashed page guides, a live instrument built from hairline cells at radius zero, one vivid orange, Inter over JetBrains Mono, concentric radii from 16 to 2, and a dark alarm state the instrument enters when it detects the reader is hiding | B2B SaaS whose product is a verdict about the visitor, the request or the device — fraud and bot detection, device intelligence, identity and verification, API products sold on their payload |
62
62
  | `manpage` | cream paper under the reader's own system monospace — zero webfont bytes for the display face — a 48px display that never grows louder, a 576px argument column, coral label chips that are real `<h2>`s, `└` tree glyphs in their own grid column, and one dark code frame as the focal point | developer products whose buyer reads code — APIs, SDKs, CLIs, MCP servers, developer infrastructure, where the honest hero is the call itself |
63
63
  | `pigeonhole` | a white field ruled by hairlines, a display face that never passes weight 400 with exactly one italic word, and **nine categories in which a hue is the category**, drawn from an eleven-ramp pastel system — each rendered as a two-layer chip, 8px outside and 7px inside, whose label word is mandatory because the hue cannot carry the meaning alone | products whose job is to sort the reader's incoming mess into named categories — email triage, ticket routing, notification digests, file organisers, CRM inboxes |
64
+ | `roster` | a white field in a faint grid of squares, hairlines instead of shadows, a display set in the **body** face while the section heads take another, and one orange that may never carry a word | products whose argument is **who already carries them** — AI-search and GEO visibility, SEO and content platforms, agencies, marketplaces, integration-led tooling |
64
65
 
65
66
  Each pack locks palette, type, texture, motion tokens, signature motifs and
66
67
  bans — and ships a `tokens/<pack>.css` to copy verbatim, so the agent never
@@ -135,7 +136,7 @@ skills.
135
136
  | `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 |
136
137
  | `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 |
137
138
  | `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 |
138
- | `styles/*.md` | The sixteen style packs — palette, type, texture, motion tokens, motifs, bans, and the traps each one carries |
139
+ | `styles/*.md` | The seventeen style packs — palette, type, texture, motion tokens, motifs, bans, and the traps each one carries |
139
140
  | `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) |
140
141
  | `styles/STYLE_PACK_TEMPLATE.md` | The pack contract as a skeleton, so a new style is authored against the same headings rather than improvised |
141
142
 
@@ -206,7 +207,7 @@ cd ./ds-workbench && npm install && npm run build
206
207
  then `/design-sync` in that directory, from Claude Code. Three layers cross: the
207
208
  pack's **bans** as the design system's own README, `styles.css` built from
208
209
  `tokens/<pack>.css` verbatim, and the components — a six-name spine that is
209
- identical in all sixteen kits, so switching packs swaps identity rather than API,
210
+ identical in all seventeen kits, so switching packs swaps identity rather than API,
210
211
  plus each pack's signature parts. **Motion does not cross**, exactly as it does
211
212
  not cross into Figma: a kit is the static half of a pack, and saying so is what
212
213
  stops an agent inventing motion to fill the silence.
@@ -252,7 +253,7 @@ a pack's four widened sections used to make two gates *quieter* and still green.
252
253
  One honest limit: the npx installer is checked by asserting its runtime bundle
253
254
  walker exists, not by reading a file list — it has none by design. What proves
254
255
  it ships the right files is CI, which installs the bundle through **both**
255
- installers and `diff -r`s the result against the source, then builds all sixteen
256
+ installers and `diff -r`s the result against the source, then builds all seventeen
256
257
  kits.
257
258
 
258
259
  `test/scenarios.md` (T1–T19) 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/ sixteen style packs — instrument-console (dark console),
237
+ styles/ seventeen 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), orchard (friendly
@@ -249,7 +249,9 @@ ${c("bold", "What it installs")}
249
249
  manpage (a developer landing page set in the reader's
250
250
  own system monospace, cream paper, coral label chips),
251
251
  pigeonhole (a white sorting wall whose nine pastel
252
- categories are a filing scheme, one hue each) —
252
+ categories are a filing scheme, one hue each),
253
+ roster (a white wall of other people's marks: the proof
254
+ is a name rather than a number) —
253
255
  plus a ready-made token CSS per pack and
254
256
  STYLE_PACK_TEMPLATE.md for authoring more
255
257
  `);
@@ -331,7 +333,7 @@ function main() {
331
333
  ` ${c("dim", "SKILL.md")} the agent skill\n` +
332
334
  ` ${c("dim", "SHELEG_DESIGN.md")} the full reference\n` +
333
335
  ` ${c("dim", "MOTION_DOCTRINE.md")} whether to animate at all — read before any animation\n` +
334
- ` ${c("dim", "styles/")} style packs + token CSS (instrument-console / editorial-luxury / workbench / briefing-room / atrium / orchard / field-notes / cyclorama / showroom / blueprint / prism / maquette / scoreboard / datasheet / manpage / pigeonhole)\n\n` +
336
+ ` ${c("dim", "styles/")} style packs + token CSS (instrument-console / editorial-luxury / workbench / briefing-room / atrium / orchard / field-notes / cyclorama / showroom / blueprint / prism / maquette / scoreboard / datasheet / manpage / pigeonhole / roster)\n\n` +
335
337
  `Your Cursor / Claude agent can now discover the skill and build\n` +
336
338
  `cinematic, scroll-driven pages — or style product UI (dashboards, admin,\n` +
337
339
  `internal tools) from a standalone pack — on its principles.\n\n` +
@@ -39,7 +39,11 @@ monospace on cream paper, coral label chips that are real headings and a
39
39
  dark code frame as the argument, for APIs, SDKs and CLIs; pigeonhole — a
40
40
  white sorting wall whose nine pastel categories are a taxonomy rather than
41
41
  a mood, each a two-layer chip whose label word is mandatory, for products
42
- that file the reader's incoming mess into named categories);
42
+ that file the reader's incoming mess into named categories; roster — a white
43
+ field in a faint grid of squares whose whole argument is other people's
44
+ marks, with client logotypes in pill-labelled industry columns and an
45
+ engine's wordmark inside the headline, for products sold on who already
46
+ carries them);
43
47
  otherwise follow the contract below (self-contained on purpose).
44
48
 
45
49
  ## Whether to animate at all — before how
@@ -0,0 +1,14 @@
1
+ {
2
+ "pkg": "@sheleg-design/roster",
3
+ "globalName": "ShelegRoster",
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,62 @@
1
+ # Roster — the design contract
2
+
3
+ The reference is <https://www.babylovegrowth.ai/en>, measured 2026-08-13 off computed
4
+ styles at 1440×900 (5,936 rendered elements), 768×1168 and 390×790. The full pack is
5
+ `styles/roster.md`; this file is what a design agent must not get wrong.
6
+
7
+ ## The one thing this pack is
8
+
9
+ **The proof is a name, not a number.** A white field in a faint grid of squares, hairlines
10
+ instead of shadows, and an argument made entirely of other people's marks: an engine's
11
+ wordmark inside the headline, client logotypes in six pill-labelled industry columns, a
12
+ review score somebody else computed.
13
+
14
+ ## Non-negotiable
15
+
16
+ - **Never white on the accent below large text.** `#fa5c12` is 3.18:1 on white and the
17
+ reference's own nav pill puts white on `#f25533` at 16px/600 — **3.43:1**, its one clear
18
+ failure. The accent is a fill and a large-text colour; `--accent-ink` (4.52:1) carries a
19
+ word.
20
+ - **The primary action is black.** `--cta` at 19.66:1, which is what the reference itself
21
+ uses for the hero.
22
+ - **No elevation system.** The reference's shadow slot holds an all-transparent ring
23
+ composite on 101 elements. Separation is hairline (`--rule`, on 7,234 borders) and pill.
24
+ - **The display is set in the body face** at 68px, and the section heads in the *other*
25
+ family at 52px. That inversion is the pack.
26
+ - **Body weight is 300.** A very long page (13,627px at 1440) stays light because its
27
+ paragraphs are.
28
+ - **The display steps, it does not slide** — 36 / 60 / 68 — because neither stylesheet
29
+ contains a `clamp()`.
30
+ - **Status never by colour alone.** `--accent` and `--danger` are 10.2 apart at full
31
+ colour against a hard floor of 10; a danger state always carries its word or icon.
32
+ - **`:focus-visible` is `--accent-ink`**, never `--accent`.
33
+
34
+ ## Banned
35
+
36
+ - White on the accent at body size. A shadow used as elevation. A scroll clock — the
37
+ reference has none: zero `animation-timeline`, `scroll-behavior: auto`.
38
+ - **An invented `--warn`.** The reference paints no amber anywhere.
39
+ - **A hidden `h1`.** The reference's is `.sr-only` at 1×1px and all sixteen of its `h2`s
40
+ are eyebrows; this kit keeps the outline and the page in agreement.
41
+ - **A tidied roster.** No equalising logo sizes, no single-colour tinting, no dividerless
42
+ wall. Greyscale at rest is the only normalisation.
43
+ - **Pixel numerals.** The biggest figure on the reference is set in a 16px eyebrow; a
44
+ number as the subject is `scoreboard`, not this.
45
+ - A dark theme. None was measured.
46
+
47
+ ## Container queries, not viewport ones
48
+
49
+ `IndustryColumn` and `StepCard` each set `container-type: inline-size`, and their
50
+ breakpoints are derived from their own geometry rather than carried over: 220px for the
51
+ column's two-up mark grid, 640px for the step card's split. Only the display's three
52
+ steps stay viewport rules, and they are marked `PAGE` at the block.
53
+
54
+ ## Motion
55
+
56
+ Entrance and hover, plus two floats at 5.5s and 6.5s offset by a second so the pair never
57
+ syncs. Every transition runs on `--ease`. Under `prefers-reduced-motion` the durations
58
+ collapse in the token layer and the floats are **paused** in the component layer — a
59
+ duration cannot stop an infinite animation, it strobes it.
60
+
61
+ The reference's own reduced-motion branch names six classes and leaves roughly fourteen
62
+ animations running. Do not copy that shape.
@@ -0,0 +1,45 @@
1
+ # @sheleg-design/roster
2
+
3
+ The React reference kit for the SHELEG **Roster** style pack — a white field in a faint
4
+ grid of squares whose argument is other people's names: an engine's wordmark inside the
5
+ headline, client logotypes in six pill-labelled industry columns, a review score somebody
6
+ else computed.
7
+
8
+ It is generated from the pack, not authored beside it: `src/styles.css` opens with
9
+ `styles/tokens/roster.css` byte for byte, and the rules the design agent must obey are in
10
+ [`.design-sync/conventions.md`](./.design-sync/conventions.md).
11
+
12
+ ```bash
13
+ npm install && npm run build
14
+ ```
15
+
16
+ Then run `/design-sync` in Claude Code from this directory to push it to
17
+ claude.ai/design.
18
+
19
+ ## The spine, and this pack's five
20
+
21
+ `Button`, `Card`, `Chip`, `Stat`, `Heading` and `Rule` are identical in name, props and
22
+ types across every SHELEG kit — switching packs swaps identity, not API.
23
+
24
+ | Component | What it is |
25
+ |---|---|
26
+ | `IndustryColumn` | a pill-labelled column of other companies' marks, hairline-divided — the **signature element** |
27
+ | `LogoTile` | one third-party mark, greyscale at rest, full colour on hover, and it never moves |
28
+ | `Eyebrow` | the only tracked type in the pack, and a `<p>` rather than an `<h2>` |
29
+ | `StepCard` | copy beside a product shot, with a two-digit number in the accent |
30
+ | `FaqList` | a `<dl>` whose answers are always visible |
31
+
32
+ ## Why the accent never carries a word
33
+
34
+ Both of the reference's oranges fail as a text or label colour: `#fa5c12` is 3.18:1 on
35
+ white, and white on `#f25533` at the nav pill's 16px/600 is **3.43:1**. The kit ships
36
+ `--accent-ink` at 4.52:1 for anything that has to be read, keeps the accent for fills and
37
+ large text, and makes the primary button black — which is what the reference itself does
38
+ for its hero, at 19.66:1.
39
+
40
+ ## Two breakpoints, both derived
41
+
42
+ `IndustryColumn` goes two-up to one-up below **220px of its own width**; `StepCard`
43
+ collapses its split below **640px of its own**. Neither number is the viewport breakpoint
44
+ the reference used, because a column that is 300px wide on a 1440px screen needs the
45
+ narrow layout and a viewport query cannot see that.
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@sheleg-design/roster",
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,19 @@
1
+ ---
2
+ category: Actions
3
+ ---
4
+
5
+ The primary action is **black**, and that is the reference's own choice rather than
6
+ this pack's invention: of its two CTAs, white on `--cta` measures 19.66:1 and white on
7
+ the accent 3.43:1. The page had already answered the question.
8
+
9
+ The `secondary` variant is the accent fill and it carries **`--ink`, not white**. An
10
+ accent button that must be light-on-dark has to reach large text first (≥24px, or
11
+ ≥18.66px bold); below that the contrast is not there.
12
+
13
+ `:focus-visible` is `--accent-ink`, never `--accent` — 3.18:1 is too little for a ring
14
+ a keyboard user has to find.
15
+
16
+ ```tsx
17
+ <Button>Start 3-day trial</Button>
18
+ <Button variant="secondary">Try free</Button>
19
+ ```
@@ -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={['ro-btn', `ro-btn--${variant}`, `ro-btn--${size}`, className]
25
+ .filter(Boolean)
26
+ .join(' ')}
27
+ disabled={disabled}
28
+ onClick={onClick}
29
+ >
30
+ {children}
31
+ </button>
32
+ );
33
+ }
@@ -0,0 +1,12 @@
1
+ ---
2
+ category: Surfaces
3
+ ---
4
+
5
+ `--surface` at `--radius-card`, one `--rule` hairline, and **no shadow** — the
6
+ reference's shadow slot holds an all-transparent ring composite on 101 elements, so
7
+ this pack has no elevation system at all. `:hover` moves the hairline to
8
+ `--rule-strong`; nothing lifts.
9
+
10
+ ```tsx
11
+ <Card title="Backlinks" meta="weekly">Placements you did not have to pitch.</Card>
12
+ ```
@@ -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={['ro-card', className].filter(Boolean).join(' ')}>
15
+ {head && (
16
+ <div className="ro-card__head">
17
+ {title !== undefined && <h3 className="ro-card__title">{title}</h3>}
18
+ {meta !== undefined && <span className="ro-card__meta">{meta}</span>}
19
+ </div>
20
+ )}
21
+ <div className="ro-card__body">{children}</div>
22
+ </section>
23
+ );
24
+ }
@@ -0,0 +1,11 @@
1
+ ---
2
+ category: Data
3
+ ---
4
+
5
+ A small bordered label. `accent` carries `--accent-ink` on `--surface` rather than a
6
+ tinted fill, because the accent is a fill colour and not a text colour.
7
+
8
+ ```tsx
9
+ <Chip>Keyword Discovery</Chip>
10
+ <Chip tone="accent" selected>User Prompts</Chip>
11
+ ```
@@ -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
+ 'ro-chip',
15
+ `ro-chip--${tone}`,
16
+ selected ? 'ro-chip--selected' : undefined,
17
+ className,
18
+ ]
19
+ .filter(Boolean)
20
+ .join(' ')}
21
+ >
22
+ {children}
23
+ </span>
24
+ );
25
+ }
@@ -0,0 +1,16 @@
1
+ ---
2
+ category: Foundations
3
+ ---
4
+
5
+ The small tracked line above a section head — `--track-eyebrow` 0.4px is the only
6
+ tracked type in the pack — in `--accent-ink` rather than `--accent`, because 3.18:1
7
+ is not enough for 16px text.
8
+
9
+ It renders a `<p>`. The reference marks all sixteen of its eyebrows as `<h2>`, which
10
+ is how a document outline ends up saying "eyebrow" where the page says "section
11
+ head".
12
+
13
+ ```tsx
14
+ <Eyebrow>How it works</Eyebrow>
15
+ <Heading level={2}>Organic growth made simple</Heading>
16
+ ```
@@ -0,0 +1,16 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface EyebrowProps {
4
+ children: ReactNode;
5
+ className?: string;
6
+ }
7
+
8
+ /**
9
+ * The small tracked line above a section head. It is a `<p>`, never an `<h2>`: the
10
+ * reference marks all sixteen of its eyebrows as level-two headings and hides its
11
+ * real `h1`, so its document outline says "eyebrow" where the page says "section
12
+ * head". This pack keeps the outline and the page in agreement.
13
+ */
14
+ export function Eyebrow({ children, className }: EyebrowProps) {
15
+ return <p className={['ro-eyebrow', className].filter(Boolean).join(' ')}>{children}</p>;
16
+ }
@@ -0,0 +1,12 @@
1
+ ---
2
+ category: Data
3
+ ---
4
+
5
+ A `<dl>`, always open, hairline between rows. The reference animates its accordion
6
+ over 0.7s (`--dur-accordion`); this component does not collapse at all, because an
7
+ answer a crawler cannot read without running JavaScript is an answer that is not
8
+ there.
9
+
10
+ ```tsx
11
+ <FaqList entries={[{ q: 'Does it work for local SEO?', a: 'Yes.' }]} />
12
+ ```
@@ -0,0 +1,30 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface FaqEntry {
4
+ q: string;
5
+ a: ReactNode;
6
+ }
7
+
8
+ export interface FaqListProps {
9
+ entries: FaqEntry[];
10
+ className?: string;
11
+ }
12
+
13
+ /**
14
+ * A definition list, always open. There is deliberately no `collapsed` prop: the
15
+ * reference ships seven `dt`/`dd` pairs in served HTML and no `<details>`, and an
16
+ * answer a crawler cannot read without running JavaScript is an answer that is
17
+ * not there.
18
+ */
19
+ export function FaqList({ entries, className }: FaqListProps) {
20
+ return (
21
+ <dl className={['ro-faq', className].filter(Boolean).join(' ')}>
22
+ {entries.map(({ q, a }) => (
23
+ <div className="ro-faq__row" key={q}>
24
+ <dt className="ro-faq__q">{q}</dt>
25
+ <dd className="ro-faq__a">{a}</dd>
26
+ </div>
27
+ ))}
28
+ </dl>
29
+ );
30
+ }
@@ -0,0 +1,20 @@
1
+ ---
2
+ category: Foundations
3
+ ---
4
+
5
+ Level 1 is the display and it is set in the **body** face at `--size-display`; level 2
6
+ is the section head and it is set in `--font-head`. That inversion is the pack: the
7
+ display face only ever appears at 52px, and the body face has to hold a poster.
8
+
9
+ Level 1 steps at breakpoints — 36px, 60px, 68px — because **neither of the
10
+ reference's stylesheets contains a single `clamp()`**, and a fluid ramp here would be
11
+ an invented value.
12
+
13
+ **Use the level for the level.** The reference hides its real `h1` in an `.sr-only`
14
+ span and marks all sixteen of its eyebrows as `h2`, so its outline says "eyebrow"
15
+ where the page says "section head". This kit keeps the outline and the page in
16
+ agreement.
17
+
18
+ ```tsx
19
+ <Heading level={1}>Grow organic traffic on autopilot</Heading>
20
+ ```
@@ -0,0 +1,19 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface HeadingProps {
4
+ /** 1 = page title (28px), 2 = section (20px), 3 = card title (15px). */
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={['ro-heading', `ro-heading--${level}`, className].filter(Boolean).join(' ')}
15
+ >
16
+ {children}
17
+ </Tag>
18
+ );
19
+ }
@@ -0,0 +1,25 @@
1
+ ---
2
+ category: Signature
3
+ ---
4
+
5
+ **The pack's signature element.** A `--radius-pill` label over a column of other
6
+ companies' marks, divided from its neighbours by a 1px `--rule` — six across at 1440,
7
+ three at 768, two below, and the divider survives every step because the divider is
8
+ what makes it a wall rather than a pile.
9
+
10
+ It sets `container-type: inline-size`, so the marks inside answer to the column's own
11
+ width. Below **220px** the two-up grid goes to one, and that number is derived rather
12
+ than carried over from a viewport: two tiles at the `--tile` ceiling of 40px plus the
13
+ 24px gap plus 24px of padding each side is 152px, and a logotype is wider than its
14
+ tile, so the pair crowds at roughly 220.
15
+
16
+ **Do not tidy the roster.** Equalising the logos' optical weights, tinting them to one
17
+ colour, or dropping the dividers each turn it into a decoration. Greyscale at rest is
18
+ the only normalisation allowed, and `LogoTile` does that.
19
+
20
+ ```tsx
21
+ <IndustryColumn label="SaaS & Tech">
22
+ <LogoTile label="ConsentStack"><img src="/consentstack.svg" alt="" /></LogoTile>
23
+ <LogoTile label="Yardstick"><img src="/yardstick.svg" alt="" /></LogoTile>
24
+ </IndustryColumn>
25
+ ```