sheleg-design-skill 1.46.0 → 1.48.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 (71) hide show
  1. package/CHANGELOG.md +177 -0
  2. package/README.md +6 -4
  3. package/bin/cli.js +6 -2
  4. package/cursor/rules/sheleg-design.mdc +10 -2
  5. package/kits/nameplate/.design-sync/config.json +14 -0
  6. package/kits/nameplate/.design-sync/conventions.md +74 -0
  7. package/kits/nameplate/README.md +44 -0
  8. package/kits/nameplate/package.json +29 -0
  9. package/kits/nameplate/src/Button.md +23 -0
  10. package/kits/nameplate/src/Button.tsx +33 -0
  11. package/kits/nameplate/src/Card.md +14 -0
  12. package/kits/nameplate/src/Card.tsx +24 -0
  13. package/kits/nameplate/src/Chip.md +15 -0
  14. package/kits/nameplate/src/Chip.tsx +25 -0
  15. package/kits/nameplate/src/Eyebrow.md +18 -0
  16. package/kits/nameplate/src/Eyebrow.tsx +20 -0
  17. package/kits/nameplate/src/Frame.md +14 -0
  18. package/kits/nameplate/src/Frame.tsx +15 -0
  19. package/kits/nameplate/src/Heading.md +14 -0
  20. package/kits/nameplate/src/Heading.tsx +19 -0
  21. package/kits/nameplate/src/Plate.md +24 -0
  22. package/kits/nameplate/src/Plate.tsx +23 -0
  23. package/kits/nameplate/src/PlateBand.md +20 -0
  24. package/kits/nameplate/src/PlateBand.tsx +19 -0
  25. package/kits/nameplate/src/Rule.md +13 -0
  26. package/kits/nameplate/src/Rule.tsx +18 -0
  27. package/kits/nameplate/src/Stat.md +14 -0
  28. package/kits/nameplate/src/Stat.tsx +17 -0
  29. package/kits/nameplate/src/Wave.md +17 -0
  30. package/kits/nameplate/src/Wave.tsx +19 -0
  31. package/kits/nameplate/src/index.ts +25 -0
  32. package/kits/nameplate/src/styles.css +762 -0
  33. package/kits/nameplate/tsconfig.json +15 -0
  34. package/kits/patchbay/.design-sync/config.json +14 -0
  35. package/kits/patchbay/.design-sync/conventions.md +38 -0
  36. package/kits/patchbay/package.json +14 -0
  37. package/kits/patchbay/src/Button.md +17 -0
  38. package/kits/patchbay/src/Button.tsx +33 -0
  39. package/kits/patchbay/src/Card.md +18 -0
  40. package/kits/patchbay/src/Card.tsx +23 -0
  41. package/kits/patchbay/src/Chip.md +15 -0
  42. package/kits/patchbay/src/Chip.tsx +21 -0
  43. package/kits/patchbay/src/Diagram.md +39 -0
  44. package/kits/patchbay/src/Diagram.tsx +91 -0
  45. package/kits/patchbay/src/Heading.md +15 -0
  46. package/kits/patchbay/src/Heading.tsx +13 -0
  47. package/kits/patchbay/src/Rule.md +12 -0
  48. package/kits/patchbay/src/Rule.tsx +8 -0
  49. package/kits/patchbay/src/Stat.md +17 -0
  50. package/kits/patchbay/src/Stat.tsx +26 -0
  51. package/kits/patchbay/src/StatusDot.md +19 -0
  52. package/kits/patchbay/src/StatusDot.tsx +15 -0
  53. package/kits/patchbay/src/index.ts +8 -0
  54. package/kits/patchbay/src/styles.css +272 -0
  55. package/kits/patchbay/tsconfig.json +15 -0
  56. package/package.json +2 -2
  57. package/plugins/sheleg-design/.claude-plugin/plugin.json +2 -2
  58. package/plugins/sheleg-design/commands/sheleg-design.md +2 -2
  59. package/plugins/sheleg-design/skills/sheleg-design/DESIGN_SYNC_BRIDGE.md +1 -1
  60. package/plugins/sheleg-design/skills/sheleg-design/FIGMA_BRIDGE.md +2 -2
  61. package/plugins/sheleg-design/skills/sheleg-design/MOBILE_SURFACES.md +1 -1
  62. package/plugins/sheleg-design/skills/sheleg-design/MOTION_DOCTRINE.md +31 -0
  63. package/plugins/sheleg-design/skills/sheleg-design/SKILL.md +5 -3
  64. package/plugins/sheleg-design/skills/sheleg-design/SURFACE_COMPOSITION.md +5 -5
  65. package/plugins/sheleg-design/skills/sheleg-design/styles/STYLE_PACK_TEMPLATE.md +3 -2
  66. package/plugins/sheleg-design/skills/sheleg-design/styles/nameplate.md +421 -0
  67. package/plugins/sheleg-design/skills/sheleg-design/styles/patchbay.md +418 -0
  68. package/plugins/sheleg-design/skills/sheleg-design/styles/roster.md +9 -0
  69. package/plugins/sheleg-design/skills/sheleg-design/styles/tenor.md +1 -1
  70. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/nameplate.css +309 -0
  71. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/patchbay.css +137 -0
package/CHANGELOG.md CHANGED
@@ -6,6 +6,183 @@ follow [SemVer](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.48.0] - 2026-08-24
10
+
11
+ ### Nameplate, the thirty-third pack — the proof is a borrowed name
12
+
13
+ Extracted from `brandpush.co` by enumerating its sitemap (20 URLs, 12 distinct pages),
14
+ fetching every page `robots.txt` permits, reading the **11 stylesheets the site itself
15
+ authors** — 500,051 bytes, 20,240 lines, 136 custom properties — and then reading
16
+ **computed styles on the live page** through CDP at 1440×900 (1,251 visible of 5,694
17
+ elements), 768×1168 and a device-emulated 390×844. Vendor layers were excluded from every
18
+ count: the site also loads Bootstrap 4.6.2, a purchased *ave* theme, a *frest* admin theme
19
+ and three Font Awesome versions, and counting those reads Bootstrap's defaults as the
20
+ brand's decisions.
21
+
22
+ What the pack takes is a **rationed round shape**. 1,091 of the 1,251 rendered elements sit
23
+ at zero radius — 87% of the page — and the pill is 52 of them, spent on one thing: a white
24
+ 1px-bordered plate carrying somebody else's publication name as 15px/500 type rather than
25
+ as a logo. Thirty names set as type read as a list a visitor can check; thirty logotypes
26
+ read as thirty typefaces. Around it, one family at four weights with the **body at weight
27
+ 500** (172 nodes at 1440, 459 at 768, 182 at 390 — the dominant weight at every width), a
28
+ display face tracked −0.02em, and two uppercase registers tracked 0.06em and 0.175em.
29
+
30
+ **The declared token layer disagreed with the render, and this is the sharpest case in the
31
+ library.** The site declares `--primary: #84B761` three times — a green nothing paints — and
32
+ its real primary action is a gradient, whose element carries a *transparent*
33
+ `background-color`. A colour census reading `background-color` alone finds the blue cookie
34
+ banner and reports it as the brand. Both facts were caught by rendering rather than by
35
+ reading, which is why the pack's Origin records the method and not just the address.
36
+
37
+ **Five corrections travel with the pack, each with its number at the declaration.** The
38
+ primary action is white on a coral ramp at 2.90:1 and 3.19:1, 3.04:1 at the midpoint — under
39
+ AA and under the 3:1 large-text floor, with a 12px uppercase label on it; `--action` holds
40
+ both hues in OKLab and moves lightness until white clears 4.5:1 along the whole ramp. The
41
+ body grey is 4.74:1 on white and **4.42:1 on the cool slab the hero stands on** — the
42
+ correction a white-background-only check never finds. The focus ring is
43
+ `rgba(5,135,237,0.35)`, which composites to `#d3dff9` at 1.34:1, and all three of its alpha
44
+ variants miss the 3:1 non-text floor on both fields; the hue was never the problem, so the
45
+ ring keeps the measured colour and drops the alpha. Two greys carry live text at 2.46:1 and
46
+ 2.54:1 across 46 nodes. And 87 of 137 visible interactive elements at 390 are under 44px
47
+ tall, so `--tap-min` is a floor.
48
+
49
+ **The status set was searched, not chosen, and the palette gate is why.** With a coral action
50
+ in play a conventional red `--danger` lands 2.93 OKLab units from it and is
51
+ indistinguishable under all three dichromacies, so danger is held at the reference's red hue
52
+ and taken deep to `#900004`. And `--info` may not be the reference's blue, because that blue
53
+ is already `--accent`: the two came out 7.5 units apart, which the gate refuses outright —
54
+ one colour cannot be both a link and a state. Info is the reference's cyan instead. All
55
+ fifteen pairs across the four states, the accent and the action clear 15 units at full
56
+ colour and 8 under protanopia, deuteranopia and tritanopia.
57
+
58
+ **The first pack in four not to move a ratio pin.** `outrank`, `babylove` and `patchbay` each
59
+ raised `unresolved_at_most` or `unguarded_at_most`, because a pack that documents what it
60
+ *refused* carries ratios whose subject it deliberately does not ship. This pack carries the
61
+ same kind of evidence and states each claim with its subject and its partner on one line, so
62
+ all 34 of its ratios are **computed** and both ceilings held; `computed_at_least` rises
63
+ 536 → 570 to lock that in. Three claims did land unpairable on the first run and every one
64
+ was a line break — `--on-action`'s own name resolves as `--action`, which is a gradient and
65
+ pairs with nothing — and a fourth was a table row stating a ratio with no partner named. All
66
+ four were rewritten rather than pinned.
67
+
68
+ A fifth standalone pack pins its own motion ceiling: **nameplate at 4**, the same as `roster`
69
+ and for the same reason. 1,057 of the 1,251 visible elements compute
70
+ `transition-duration: 0s`, `animation-timeline` appears zero times in 20,240 lines, and the
71
+ whole budget is an entrance reveal, a 2px hover lift whose shadow grows in the same 200ms,
72
+ and a 140ms press. The reference also ships a real reduced-motion contract — 23 blocks across
73
+ 7 of its 11 authored stylesheets, with the reveal layer restoring `visibility` and `opacity`
74
+ so content is never locked behind an animation that did not run. That shape is kept without
75
+ correction; it is the one thing here worth copying as measured.
76
+
77
+ **Rendering the kit found two defects three green gates could not**, which is the second
78
+ consecutive release where the browser beat the ratchet. The plate came out **78px against a
79
+ stated 50** — the kit declared no `box-sizing`, and Chrome's UA stylesheet gives `button`
80
+ `border-box` while giving an anchor `content-box`, so the button beside it kept its promise
81
+ and the plate silently grew by its own padding. And body copy rendered at **weight 400
82
+ against the pack's central claim of 500**, because `--weight-body` existed and no component
83
+ consumed it — a token that is decorative rather than binding. Both fixed and re-measured at
84
+ 50px and 500. A gate reads structure, not layout.
85
+
86
+ **The fork against `roster` is recorded in both directions**, because the two share one
87
+ sentence — the proof is a name — and nothing else. `roster` proves by *who already uses us*
88
+ and makes the pill its most frequent shape (102 elements) with a body at weight 300 on a
89
+ grid-textured white field; nameplate proves by *who will carry you*, is square on 87% of its
90
+ elements, and sets its body at 500 on an untextured slab. `ADR-0001` gains a further
91
+ application: the pack is named for the register, with `masthead`, `newsstand`, `clipping` and
92
+ `hallmark` weighed and rejected on its own criteria — the last of those for trademark
93
+ surface, which is the clause `datasheet` was chosen under.
94
+
95
+ Ships `styles/nameplate.md` on the widened thirteen-heading contract with `Themes:` and
96
+ `Rank:` declared, `styles/tokens/nameplate.css`, the `kits/nameplate` reference kit (the
97
+ six-name spine plus `Plate`, `PlateBand`, `Wave`, `Frame` and `Eyebrow`), routing in every
98
+ enumeration surface, and the `.cursor` mirror.
99
+
100
+ ## [1.47.0] - 2026-08-22
101
+
102
+ ### `patchbay` — the thirty-second pack, and the first read off a page with no tokens at all
103
+
104
+ Measured 2026-08-22 off <https://nautilustrader.io/> with `getComputedStyle` on the running
105
+ page. Named for its register under ADR-0001 — named ports, drawn cords, live signal — not for
106
+ the source brand.
107
+
108
+ **The reference declares zero custom properties.** It is Material UI with Emotion, so there was
109
+ no author-side vocabulary to lift and every value in the token layer was read off a painted
110
+ element. Two of the pack's findings come straight out of that: the MUI theme's own defaults
111
+ leak through where a variant was not overridden, so the page's **59.2px hero is tracked
112
+ +0.555px** — positive tracking on a display line, two selectors away from designed styles that
113
+ track −0.3px — and **fourteen text nodes render in Roboto**, which is not one of the three fonts
114
+ the page loads.
115
+
116
+ **The signature element is a live architecture diagram, and it is measured rather than
117
+ admired.** 21 cords carrying 32 particles, distributed 12 / 7 / 2 at one, two and three
118
+ particles each. Every multi-particle cord divides its own period evenly: the seven pairs are
119
+ offset by exactly half their duration — 1.25s on a 2.5s cord, all seven — and both triples by a
120
+ third, at 0.4 / 1.2333 / 2.0667 and 0.5 / 1.3333 / 2.1667. Consecutive cords start 0.1s apart,
121
+ so the board has no visible beginning. Each cord is an inline `<svg>` with a path, an endpoint
122
+ dot, an arrowhead marker and one `<circle>` per particle driven by `<animateMotion>` — no
123
+ JavaScript, no library, one element per dot.
124
+
125
+ Three corrections the arithmetic produced:
126
+
127
+ * **The reference paints two muted greys and only one of them is legible.** `#848895` is
128
+ 4.86:1; `#686B75`, in 13 text nodes, is **3.23:1**. There is no room for a corrected third
129
+ step below 4.86, so the pack ships **one** muted tier and says which token replaces the other
130
+ in each of its two uses.
131
+ * **The diagram's group labels are 2.70:1** — 9px mono uppercase at 30% white, the least
132
+ legible text on the page. Corrected to 50%, which is 5.19:1 and which the reference already
133
+ uses for its own diagram legend further down. The ladder already had the step.
134
+ * **The status set is derived and its constraint is unusual.** The accent is a mint-cyan, so
135
+ the ordinary dark-UI green collides with it under dichromacy: `instrument-console`'s `#46D39A`
136
+ separates from this accent by **3.51** under simulated CVD against a floor of 8. Every
137
+ candidate that clears the floor is light, which is why `--ok` sits above `--warn` and
138
+ `--danger` in luminance rather than beside them.
139
+
140
+ ### The reduced-motion doctrine gained the three holes this page measures
141
+
142
+ `MOTION_DOCTRINE.md` §9 has always said an animation without a reduced-motion path is a bug.
143
+ What it did not say is that the usual one-rule remedy — zero every duration — **cannot reach
144
+ three layers**, all three of them live on this one reference:
145
+
146
+ * **It cannot stop SMIL.** `<animateMotion>` is not a CSS animation and does not read
147
+ `animation-duration`. All 32 particles keep moving with the preference on. `pauseAnimations()`
148
+ is the fix and it has to be wired by hand; `kits/patchbay/src/Diagram.tsx` wires it.
149
+ * **It cannot stop JavaScript.** The reference's reveal writes `opacity` and `transform` inline
150
+ per scroll frame across 48 wrappers, so with the preference on the content is **still hidden
151
+ until scrolled**.
152
+ * **It teleports a loop that does not end where it began.** Collapsing to `.01ms` jumps to the
153
+ final keyframe. That is safe here only because all four ambient light loops are written with
154
+ `0%` and `100%` identical — so write perpetual loops that way and the cheap remedy stops being
155
+ a gamble.
156
+
157
+ ### What the reference gets wrong, kept because a pack that only admires is not evidence
158
+
159
+ * **`0+` renders on a live page, in two separate sections.** The count-up animates from zero and
160
+ has no state for *the fetch did not return*, so the failure renders as a precise, confident,
161
+ wrong number under `GITHUB STARS`, `DOWNLOADS`, `DISCORD MEMBERS` and `TESTING`. `Stat` in the
162
+ kit refuses: an empty value renders the skeleton and announces itself, never a zero.
163
+ * **The reveal freezes on a programmatic jump.** Measured at eight scroll positions from 700px
164
+ to 1700px reached with `scrollTo`: the target stayed `opacity: 0` at every one and the section
165
+ never appeared. Reached by wheel, it reveals normally — which is what makes it a deep-link,
166
+ anchor and print-stylesheet defect rather than a broken page.
167
+
168
+ ### Three numbers that were already stale beside this change
169
+
170
+ Found while registering the pack, none of them this run's regression: the slash command said
171
+ **"the twenty-nine are"** over a list of thirty-one — invisible to the count gate because the
172
+ numeral named no noun, which is the exact failure the skeleton's rule 6 warns about, so it now
173
+ names one; `SURFACE_COMPOSITION.md` said **sixteen** token layers declare `@role non-text:` and
174
+ the tree says nineteen; and `FIGMA_BRIDGE.md` said **ten** packs ship a theme twin against a
175
+ derived eleven. The widened remainder in `SKILL.md` went stale for the **third consecutive
176
+ release** and the gate caught it, which is what that check was written for.
177
+
178
+ ### ADR-0001 now records that it was broken twice
179
+
180
+ `outrank` and `babylove` both shipped on 2026-08-21 carrying their source brand as the pack
181
+ name, and neither run added an entry. Recorded rather than left to be rediscovered — a register
182
+ showing an unbroken chain of eleven applications was making a claim the library does not
183
+ support. **Neither is renamed:** the ADR's own Consequences make a pack name a public API across
184
+ four channels, which is what makes renaming after release a breaking change.
185
+
9
186
  ## [1.46.0] - 2026-08-22
10
187
 
11
188
  ### `babylove` — the thirty-first pack, and the other end of the same question
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 **thirty-one locked style
14
+ decides whether to animate before it decides how, and **thirty-three 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
 
@@ -51,6 +51,8 @@ into the cinematic layer, and says so in its own *Motion flavor* section.
51
51
  | `briefing-room` | dark 16:9 deck: one blue hue top to bottom (OKLCH), mono slide furniture, 1-bit dithered art, claims as titles | investor & board decks, technical briefings, talks published as a page |
52
52
  | `atrium` | warm cream daylight field with no dark bands, one terracotta accent, light serif with italic asides, fluted-glass hero over photography | consumer health, longevity & diagnostics, wellness, premium care, high-trust DTC |
53
53
  | `babylove` | white with one orange in six steps and nothing else declared — seven tokens over Tailwind's defaults, two card levels nested at 16 and 8px, no shadow and no dark theme | SEO and AI-visibility SaaS with a long time-to-value — the minimal-system answer to the same brief `outrank` answers maximally |
54
+ | `patchbay` | near-black under a faint 40px grid, one mint-cyan doing every functional job, elevation made of 8% hairlines with no shadow anywhere, and a live architecture diagram whose cords carry SMIL particles | engines, message buses, pipelines, schedulers and open-source front doors — anything whose argument is an architecture rather than a promise |
55
+ | `nameplate` | a cool near-white slab under a page that is **square on 87% of its elements** — 1,091 of 1,251 measured — where the one round shape is reserved for a white 1px-bordered pill carrying somebody else's publication name **as type rather than as a logo**, one family with the body at **weight 500**, and two uppercase registers tracked 0.06em and 0.175em | **pages whose argument is that named third parties will vouch for you** — press and media placement, PR distribution, trust marks and badges, certification, review aggregation and "as featured in" surfaces (standalone) |
54
56
  | `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 |
55
57
  | `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 |
56
58
  | `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 |
@@ -151,7 +153,7 @@ skills.
151
153
  | `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 |
152
154
  | `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 |
153
155
  | `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 |
154
- | `styles/*.md` | The thirty-one style packs — palette, type, texture, motion tokens, motifs, bans, and the traps each one carries |
156
+ | `styles/*.md` | The thirty-three style packs — palette, type, texture, motion tokens, motifs, bans, and the traps each one carries |
155
157
  | `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) |
156
158
  | `styles/STYLE_PACK_TEMPLATE.md` | The pack contract as a skeleton, so a new style is authored against the same headings rather than improvised |
157
159
 
@@ -222,7 +224,7 @@ cd ./ds-workbench && npm install && npm run build
222
224
  then `/design-sync` in that directory, from Claude Code. Three layers cross: the
223
225
  pack's **bans** as the design system's own README, `styles.css` built from
224
226
  `tokens/<pack>.css` verbatim, and the components — a six-name spine that is
225
- identical in all thirty-one kits, so switching packs swaps identity rather than API,
227
+ identical in all thirty-three kits, so switching packs swaps identity rather than API,
226
228
  plus each pack's signature parts. **Motion does not cross**, exactly as it does
227
229
  not cross into Figma: a kit is the static half of a pack, and saying so is what
228
230
  stops an agent inventing motion to fill the silence.
@@ -268,7 +270,7 @@ a pack's four widened sections used to make two gates *quieter* and still green.
268
270
  One honest limit: the npx installer is checked by asserting its runtime bundle
269
271
  walker exists, not by reading a file list — it has none by design. What proves
270
272
  it ships the right files is CI, which installs the bundle through **both**
271
- installers and `diff -r`s the result against the source, then builds all thirty-one
273
+ installers and `diff -r`s the result against the source, then builds all thirty-three
272
274
  kits.
273
275
 
274
276
  `test/scenarios.md` (T1–T30) is the behavioral harness: fresh subagents given a
package/bin/cli.js CHANGED
@@ -234,10 +234,14 @@ ${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-one style packs — instrument-console (dark console),
237
+ styles/ thirty-three 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
- atrium (warm cream consumer health), babylove (white +
240
+ atrium (warm cream consumer health), patchbay (dark live
241
+ schematic), nameplate (a cool near-white slab under a
242
+ square page, the one round shape reserved for a white
243
+ bordered pill carrying somebody else's publication name
244
+ as type), babylove (white +
241
245
  one orange, seven tokens over Tailwind defaults),
242
246
  outrank (white +
243
247
  one violet, a landing and a dashboard in one brand),
@@ -15,7 +15,9 @@ console; editorial-luxury — warm editorial; workbench — quiet light/dark
15
15
  product UI for dashboards and tools, used standalone without the cinematic
16
16
  motion layer; briefing-room — dark 16:9 presentation deck, standalone, slides
17
17
  never animate; atrium — warm cream consumer-health field, one terracotta
18
- accent, fluted-glass hero; babyloveseven brand tokens over Tailwind
18
+ accent, fluted-glass hero; patchbaya dark live schematic, one mint-cyan, 8% hairlines,
19
+ no shadow, an architecture diagram whose cords carry SMIL particles;
20
+ babylove — seven brand tokens over Tailwind
19
21
  defaults, two nested card levels, first-class disconnected states, for
20
22
  SEO and AI-visibility SaaS; outrank — a marketing page and a working tool in
21
23
  one brand, white with one violet, for SEO and growth SaaS; orchard — friendly
@@ -99,7 +101,13 @@ demonstration;
99
101
  bulletin — warm cream paper cut by flat pastel bands, every card and control a
100
102
  1px ink outline standing on a hard zero-blur ink offset it travels into when
101
103
  pressed, a display face at 800 inside controls and 700 in the headline, and no
102
- tracking at any size, for front doors whose argument is breadth);
104
+ tracking at any size, for front doors whose argument is breadth;
105
+ nameplate — a cool near-white slab under a page square on 87% of its elements,
106
+ the one round shape reserved for a white 1px-bordered pill carrying somebody
107
+ else's publication name as type, one family with the body at weight 500, and two
108
+ uppercase registers tracked 0.06em and 0.175em, for pages whose argument is that
109
+ named third parties will vouch for you: press placement, trust marks,
110
+ certification and review aggregation);
103
111
  otherwise follow the contract below (self-contained on purpose).
104
112
 
105
113
  ## Whether to animate at all — before how
@@ -0,0 +1,14 @@
1
+ {
2
+ "pkg": "@sheleg-design/nameplate",
3
+ "globalName": "ShelegNameplate",
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,74 @@
1
+ # Nameplate — the contract this design system ships under
2
+
3
+ **Register.** Choose Nameplate for **pages whose argument is that named third
4
+ parties will vouch for you**: press and media placement, PR distribution, trust
5
+ marks and badges, certification and accreditation, review aggregation and
6
+ "as featured in" surfaces. A cool near-white slab, a page that is square almost
7
+ everywhere, and one round shape reserved for a plate carrying somebody else's
8
+ name. Build every screen against `var(--…)` and never a literal.
9
+
10
+ **The page is square, and that is the discipline.** 87% of the reference's
11
+ rendered elements sit at zero radius — 1,091 of 1,251 measured. `--r-xs` (4px)
12
+ is an inline tag, `--r-sm` (8px) an inner well, `--r-md` (16px) a card or the
13
+ framed panel. `--r-pill` belongs to the plate and to the two CTAs, and to
14
+ nothing else. Every radius you add spends the plate's meaning.
15
+
16
+ **The plate is drawn by its border.** White fill, 1px `--line`, `--r-pill`,
17
+ `--shadow-pill` at 4% and 2px blur, one borrowed name as 15px/500 type at
18
+ `--pill-h`. It carries **type, never a logo** — that is what lets thirty of them
19
+ sit together without becoming a ransom note of thirty typefaces. One name per
20
+ plate. Give it a fill and it stops being a nameplate.
21
+
22
+ **The lift is one gesture.** On hover a plate translates by `--lift` (2px) *and*
23
+ grows its shadow to `--shadow-pill-hover`, both over `--dur-base` (200ms) on
24
+ `--ease-out`. Animating one without the other is the fastest way to break the
25
+ pack. `:active` returns the lift to zero over `--dur-press` (140ms).
26
+
27
+ **Elevation is a hairline, with one exception per screen.** `--shadow-frame` —
28
+ 25px offset, 70px blur, 7% — belongs to the framed panel holding the
29
+ demonstration, and to nothing else. Two of them in one viewport spends the only
30
+ depth the page has; one on a card or a control makes the pack look like a
31
+ template.
32
+
33
+ **The action is a ramp, and it does not change on hover.** `--action` is a
34
+ two-stop coral gradient; hover spends `--shadow-action-hover` plus the lift,
35
+ `:active` swaps to `--action-pressed`. Darkening a coral on hover reads as a
36
+ disabled state. At most one primary per view. The blue is **not** the action —
37
+ it is `--link` and `--focus-color`, and the reference's declared tokens make it
38
+ look primary while the render disagrees.
39
+
40
+ **Colour corrections you must not undo.** White on the reference's own coral is
41
+ 2.90:1; `--action` holds the hue and moves lightness until white clears AA
42
+ along the whole ramp. The reference's body grey is 4.42:1 on `--field`, so
43
+ `--ink-body` is darker. Its focus ring is `rgba(…, 0.35)` at 1.34:1 against a
44
+ 3:1 floor — the ring here keeps the measured hue and is **opaque**. Reverting any
45
+ of the three reintroduces a measured failure.
46
+
47
+ **Two fields, and white is the resting state.** `--bg` is the page, `--field` the
48
+ cool slab an act stands on. A card on the slab is white with no border; the same
49
+ card on white takes a 1px `--line-soft`. This pack never separates by going
50
+ darker — `--surface-2` is an inset well, not a card.
51
+
52
+ **Status is never by colour alone.** `--good` / `--warn` / `--danger` / `--info`
53
+ each take a glyph or a label beside the colour. The set was searched, not
54
+ picked: with a coral action in the palette a conventional red danger is
55
+ indistinguishable from it under every dichromacy, so `--danger` is deep, and
56
+ `--info` is a cyan because the blue is already `--accent`.
57
+
58
+ **Type: one family, body at medium.** Poppins at four weights. Body is 17px at
59
+ **weight 500** with a 1.6 line-height; display is 700 tracked `--track-display`
60
+ (−0.02em). Two uppercase registers exist and are not interchangeable —
61
+ `--track-caps-control` (0.06em) on a button label, `--track-caps-micro`
62
+ (0.175em) on a micro-link.
63
+
64
+ **Every control clears `--tap-min` (44px).** This is a correction: 87 of the
65
+ reference's 137 visible interactive elements at 390px are shorter than that.
66
+
67
+ **No texture, no dark variant, no scroll clock.** There is no grid, noise or
68
+ pattern — acts are separated by returning to white, by a 1px rule, and by the
69
+ `Wave`. The reference has no dark band anywhere, so a dark theme here would be
70
+ invented. `MOTION_INTENSITY` above 4 has nothing legal to buy: no parallax, no
71
+ scrub, no `animation-timeline`.
72
+
73
+ **Reduced motion collapses every duration and the lift to zero**, and content is
74
+ never gated behind a reveal that did not run.
@@ -0,0 +1,44 @@
1
+ # @sheleg-design/nameplate
2
+
3
+ The React reference kit for the SHELEG **Nameplate** style pack — a cool
4
+ near-white slab under a page that is square on 87% of its elements, where the one
5
+ round shape is reserved for a white 1px-bordered pill carrying somebody else's
6
+ publication name as type.
7
+
8
+ It is generated from the pack, not authored beside it: `src/styles.css` opens
9
+ with `styles/tokens/nameplate.css` byte for byte, and the rules the design agent
10
+ must obey are in [`.design-sync/conventions.md`](./.design-sync/conventions.md).
11
+
12
+ ```bash
13
+ npm install && npm run build
14
+ ```
15
+
16
+ ## The spine
17
+
18
+ `Button`, `Card`, `Chip`, `Stat`, `Heading`, `Rule` — identical names, props and
19
+ types in every SHELEG kit, so switching packs swaps identity rather than API.
20
+
21
+ ## This pack's own
22
+
23
+ `Plate` (the borrowed name, and the reason the pack exists), `PlateBand` (the
24
+ wrapping row of them — it wraps, it never scrolls), `Wave` (the 150px arc that
25
+ closes an act, overspilling to 150% width and gone under 768px), `Frame` (the one
26
+ object per screen allowed to wear the 70px-blur shadow) and `Eyebrow` (two
27
+ uppercase registers, tracked 0.06em and 0.175em, and they are not
28
+ interchangeable).
29
+
30
+ ## The three rules that carry the kit
31
+
32
+ 1. **The border is the plate.** A fill takes the meaning away, and a logo takes
33
+ the readability with it. Thirty names set as type read as a list; thirty
34
+ logotypes read as thirty typefaces.
35
+ 2. **The lift is one gesture.** Travel and shadow-growth share `--dur-base`, so a
36
+ plate rises *and* separates in one motion. Animate one without the other and
37
+ the pack is gone.
38
+ 3. **The round shape is rationed.** `Chip` is square at `--r-xs` and `Plate` is a
39
+ pill, and reaching for the pill where the content is not a borrowed name spends
40
+ the only shape on the page that means something.
41
+
42
+ **Motion does not cross into a design tool.** A kit is the static half of a pack:
43
+ the lift and the press are described here and in the pack, and implemented in
44
+ `styles.css`, but nothing in this package animates on its own.
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@sheleg-design/nameplate",
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,23 @@
1
+ ---
2
+ category: Actions
3
+ ---
4
+
5
+ `primary` is the coral ramp — `--action` — and there is at most one per view. Its
6
+ label is 14px/700 uppercase at `--track-caps-control`, and white on it clears AA at
7
+ every point along the ramp because the ramp was corrected for exactly that: the
8
+ reference's own gradient is 2.90:1 under white.
9
+
10
+ Hover does **not** change the fill. It spends `--shadow-action-hover` and lifts by
11
+ `--lift`, both at `--dur-base` — the reference's own hover here is a glow, and
12
+ darkening a coral on hover reads as a disabled state. `active` takes
13
+ `--action-pressed` and returns the lift to 0.
14
+
15
+ `secondary` is a plate that happens to be a button: `--surface` fill, 1px
16
+ `--line-soft`, no fill change on hover. `ghost` carries no border.
17
+
18
+ Every size honours `--tap-min` (44px) as a height floor.
19
+
20
+ ```tsx
21
+ <Button>See how it works</Button>
22
+ <Button variant="secondary">View real results</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={['np-btn', `np-btn--${variant}`, `np-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-md`, and it is drawn two ways on purpose: **no border
6
+ and no shadow** when it stands on `--field`, a 1px `--line-soft` when it stands on
7
+ white. Its separation comes from the field it is on, which is why the same component
8
+ changes clothes.
9
+
10
+ It never wears `--shadow-frame`. That shadow belongs to `Frame`, once per screen.
11
+
12
+ ```tsx
13
+ <Card title="Guaranteed placement" meta="48h">…</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={['np-card', className].filter(Boolean).join(' ')}>
15
+ {head && (
16
+ <div className="np-card__head">
17
+ {title !== undefined && <h3 className="np-card__title">{title}</h3>}
18
+ {meta !== undefined && <span className="np-card__meta">{meta}</span>}
19
+ </div>
20
+ )}
21
+ <div className="np-card__body">{children}</div>
22
+ </section>
23
+ );
24
+ }
@@ -0,0 +1,15 @@
1
+ ---
2
+ category: Foundations
3
+ ---
4
+
5
+ A chip is the small square-ish object at `--r-xs`: an inline tag, a filter, a count.
6
+ It is **not** a plate — a plate is `--r-pill` and carries a borrowed name, and the
7
+ distinction is the pack's whole discipline. Reaching for `Chip` where the content is
8
+ somebody else's name spends the round shape without buying the meaning.
9
+
10
+ `accent` tints with `--accent-wash` rather than filling with `--accent`.
11
+
12
+ ```tsx
13
+ <Chip>Distribution</Chip>
14
+ <Chip tone="accent" selected>Authority</Chip>
15
+ ```
@@ -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
+ 'np-chip',
15
+ `np-chip--${tone}`,
16
+ selected ? 'np-chip--selected' : undefined,
17
+ className,
18
+ ]
19
+ .filter(Boolean)
20
+ .join(' ')}
21
+ >
22
+ {children}
23
+ </span>
24
+ );
25
+ }
@@ -0,0 +1,18 @@
1
+ ---
2
+ category: Foundations
3
+ ---
4
+
5
+ The tracked uppercase micro-label. Two registers, and they are **not**
6
+ interchangeable: `micro` tracks `--track-caps-micro` (+0.175em) at 12px, `control`
7
+ tracks `--track-caps-control` (+0.06em) at 14px.
8
+
9
+ The threefold difference is measured, and the reference's own `--tracking-caps` token
10
+ describes only the second. Collapsing them to one value is how the eyebrows stop
11
+ reading as eyebrows.
12
+
13
+ Above a section head. Never inside a control — that is what `control` register is
14
+ for, and `Button` already applies it.
15
+
16
+ ```tsx
17
+ <Eyebrow>See sample report</Eyebrow>
18
+ ```
@@ -0,0 +1,20 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface EyebrowProps {
4
+ children: ReactNode;
5
+ /** `micro` tracks +0.175em; `control` tracks +0.06em. They are not interchangeable. */
6
+ register?: 'micro' | 'control';
7
+ className?: string;
8
+ }
9
+
10
+ export function Eyebrow({ children, register = 'micro', className }: EyebrowProps) {
11
+ return (
12
+ <span
13
+ className={['np-eyebrow', `np-eyebrow--${register}`, className]
14
+ .filter(Boolean)
15
+ .join(' ')}
16
+ >
17
+ {children}
18
+ </span>
19
+ );
20
+ }
@@ -0,0 +1,14 @@
1
+ ---
2
+ category: Surfaces
3
+ ---
4
+
5
+ The one object per screen allowed to wear `--shadow-frame` — 25px offset, 70px blur,
6
+ 7%. `--r-md`, no border.
7
+
8
+ It holds the demonstration: the mocked article, the report, the product shot. Putting
9
+ this shadow on a card or a control is what makes the pack look like a template, and
10
+ putting two Frames in one viewport spends the only depth the page has.
11
+
12
+ ```tsx
13
+ <Frame><img src="…" alt="A placement on a named outlet" /></Frame>
14
+ ```