sheleg-design-skill 1.47.0 → 1.48.1
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.
- package/CHANGELOG.md +169 -0
- package/README.md +5 -4
- package/bin/cli.js +5 -2
- package/cursor/rules/sheleg-design.mdc +7 -1
- package/kits/nameplate/.design-sync/config.json +14 -0
- package/kits/nameplate/.design-sync/conventions.md +74 -0
- package/kits/nameplate/README.md +44 -0
- package/kits/nameplate/package.json +29 -0
- package/kits/nameplate/src/Button.md +23 -0
- package/kits/nameplate/src/Button.tsx +33 -0
- package/kits/nameplate/src/Card.md +14 -0
- package/kits/nameplate/src/Card.tsx +24 -0
- package/kits/nameplate/src/Chip.md +15 -0
- package/kits/nameplate/src/Chip.tsx +25 -0
- package/kits/nameplate/src/Eyebrow.md +18 -0
- package/kits/nameplate/src/Eyebrow.tsx +20 -0
- package/kits/nameplate/src/Frame.md +14 -0
- package/kits/nameplate/src/Frame.tsx +15 -0
- package/kits/nameplate/src/Heading.md +14 -0
- package/kits/nameplate/src/Heading.tsx +19 -0
- package/kits/nameplate/src/Mark.md +24 -0
- package/kits/nameplate/src/Mark.tsx +26 -0
- package/kits/nameplate/src/Plate.md +24 -0
- package/kits/nameplate/src/Plate.tsx +23 -0
- package/kits/nameplate/src/PlateBand.md +20 -0
- package/kits/nameplate/src/PlateBand.tsx +19 -0
- package/kits/nameplate/src/Rule.md +13 -0
- package/kits/nameplate/src/Rule.tsx +18 -0
- package/kits/nameplate/src/Stat.md +14 -0
- package/kits/nameplate/src/Stat.tsx +17 -0
- package/kits/nameplate/src/Wave.md +17 -0
- package/kits/nameplate/src/Wave.tsx +19 -0
- package/kits/nameplate/src/index.ts +27 -0
- package/kits/nameplate/src/styles.css +832 -0
- package/kits/nameplate/tsconfig.json +15 -0
- package/package.json +2 -2
- package/plugins/sheleg-design/.claude-plugin/plugin.json +2 -2
- package/plugins/sheleg-design/commands/sheleg-design.md +2 -2
- package/plugins/sheleg-design/skills/sheleg-design/DESIGN_SYNC_BRIDGE.md +1 -1
- package/plugins/sheleg-design/skills/sheleg-design/FIGMA_BRIDGE.md +1 -1
- package/plugins/sheleg-design/skills/sheleg-design/MOBILE_SURFACES.md +1 -1
- package/plugins/sheleg-design/skills/sheleg-design/SKILL.md +4 -3
- package/plugins/sheleg-design/skills/sheleg-design/SURFACE_COMPOSITION.md +3 -3
- package/plugins/sheleg-design/skills/sheleg-design/styles/STYLE_PACK_TEMPLATE.md +50 -1
- package/plugins/sheleg-design/skills/sheleg-design/styles/nameplate.md +455 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/pigeonhole.md +1 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/roster.md +9 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/tenor.md +1 -1
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/nameplate.css +356 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,175 @@ follow [SemVer](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.48.1] - 2026-08-24
|
|
10
|
+
|
|
11
|
+
### The assembly rule is written down, and the render step earned its place within the hour
|
|
12
|
+
|
|
13
|
+
The procedure for adding a pack lived in three homes — `CONTRIBUTING.md`'s authoring
|
|
14
|
+
steps, `docs/DOCMAP.md`'s propagation row, and the release section — and three things
|
|
15
|
+
the `nameplate` run actually did were in none of them. They are now in the home that
|
|
16
|
+
already owns each, rather than in a fourth document, which `DOCMAP`'s single-homes
|
|
17
|
+
discipline forbids.
|
|
18
|
+
|
|
19
|
+
**`STYLE_PACK_TEMPLATE.md` gains rules 7 and 8**, and both ship inside the bundle so an
|
|
20
|
+
author holding only the installed skill sees them. Rule 7 is *read the render, not the
|
|
21
|
+
stylesheet*: a dead token reads as the brand, a gradient hides from a
|
|
22
|
+
`background-color` census because its element's is transparent, and vendor CSS
|
|
23
|
+
outnumbers authored CSS — three traps met in one run, each with the shipped pack that
|
|
24
|
+
met it. Rule 8 is *write a ratio so the gate can compute it*, with all four shapes that
|
|
25
|
+
fail: a line break between the number and its partner, a token whose name contains
|
|
26
|
+
another's (`--on-action` resolves as `--action`, a gradient that pairs with nothing), a
|
|
27
|
+
table row stating a bare ratio, and a line whose **only** token is the partner, so it
|
|
28
|
+
fills both slots and pairs with itself.
|
|
29
|
+
|
|
30
|
+
**`CONTRIBUTING.md` gains step 8: render the kit and check its computed values against
|
|
31
|
+
what the pack claims.** Not a screenshot for the eye — mount it on real content at
|
|
32
|
+
1440, 768 and an emulated 390, then read `getComputedStyle` back. The step existed
|
|
33
|
+
only as a sentence in `bulletin`'s CHANGELOG entry, so whether it happened depended on
|
|
34
|
+
the next author reading that paragraph. **It caught a defect within the hour of being
|
|
35
|
+
written:** the `Mark` tile added in this same release rendered 60×60 against a declared
|
|
36
|
+
50, because `.np-mark` was not in the kit's `border-box` list — the same defect as the
|
|
37
|
+
plate one release earlier, in the component added to close a different gap.
|
|
38
|
+
|
|
39
|
+
### The mark set, and what measuring it changed
|
|
40
|
+
|
|
41
|
+
`nameplate`'s reference repeats one object per feature on the report surface it embeds:
|
|
42
|
+
a 50×50 tile at 16px radius wearing a **5px solid ring**, a wash at 9–12% alpha and a
|
|
43
|
+
glyph in the matching solid. Six hue pairs drive it and the pack carried none of them,
|
|
44
|
+
so an agent building the reference's own feature row would have invented six colours.
|
|
45
|
+
|
|
46
|
+
**Measuring them changed the answer from "carry six hues" to "carry a mechanism and a
|
|
47
|
+
ban".** The set does not separate from itself: seven of its fifteen pairs are tight,
|
|
48
|
+
and the reference's own indigo `#5b4bd4` and violet `#6c5ce7` are 5.58 OKLab units
|
|
49
|
+
apart at full colour — one colour wearing two names. So the hue is variety, never
|
|
50
|
+
meaning; it does not encode which feature this is; and the tokens are **numbered**
|
|
51
|
+
rather than named so nothing can misread them as semantic. That is the measured
|
|
52
|
+
difference from `pigeonhole`, whose hues *are* a taxonomy and whose label word is
|
|
53
|
+
therefore mandatory, and the fork is now written from both ends.
|
|
54
|
+
|
|
55
|
+
The sixth hue was dropped for a second measured reason: the reference's red `#d94558`
|
|
56
|
+
sits **2.92** units from `--action-to` and 1.12 under the worst dichromacy — the same
|
|
57
|
+
colour to every reader, not only to a colourblind one. A mark in the action's colour
|
|
58
|
+
reads as a control, so the coral stays with `--action` and the sixth slot takes
|
|
59
|
+
`--mark-neutral`. All six clear the 3:1 non-text floor on their own composited tile,
|
|
60
|
+
and none may carry a word: `--mark-4` is 3.89:1 on the slab, `--mark-2` is 4.27:1, and
|
|
61
|
+
the dropped red was 3.96:1.
|
|
62
|
+
|
|
63
|
+
Ships `Mark` as the kit's twelfth component, with `tone` as a number precisely because
|
|
64
|
+
it means nothing.
|
|
65
|
+
|
|
66
|
+
### A claim that was wider than its evidence
|
|
67
|
+
|
|
68
|
+
The pack said *the reference has no dark band anywhere*, and what had been checked was
|
|
69
|
+
the home page at 1440. The authored stylesheets declare **eight** dark fills, and both
|
|
70
|
+
kinds were then checked: `.bg-header-blue` (`#231b66`) is dead CSS no served page uses,
|
|
71
|
+
and the one that renders (`#1f2937`) sits inside a **mock of another product's
|
|
72
|
+
interface** in the hero image. The claim holds in substance and now states the width it
|
|
73
|
+
was measured at, in the Bans and in the token layer's own comment. A claim wider than
|
|
74
|
+
its evidence is this repository's own defect class, and it does not get an exemption
|
|
75
|
+
for being ours.
|
|
76
|
+
|
|
77
|
+
### Still open, and named rather than quietly dropped
|
|
78
|
+
|
|
79
|
+
`T31` is unrun — it needs fresh contexts reading blind. And 29% of the reference's
|
|
80
|
+
authored CSS is still unmeasured into the pack: the checkout (`order-form-cro.css`,
|
|
81
|
+
66,546 B) and the report surface (71,302 B) of 500,051 B total. The pack describes a
|
|
82
|
+
front door and says nothing about the payment step. That is an extension to a second
|
|
83
|
+
surface, not a loose end.
|
|
84
|
+
|
|
85
|
+
Gates: 4617 → **4626**, 2659 → **2681**, 692 unchanged. Both palette ceilings held.
|
|
86
|
+
|
|
87
|
+
## [1.48.0] - 2026-08-24
|
|
88
|
+
|
|
89
|
+
### Nameplate, the thirty-third pack — the proof is a borrowed name
|
|
90
|
+
|
|
91
|
+
Extracted from `brandpush.co` by enumerating its sitemap (20 URLs, 12 distinct pages),
|
|
92
|
+
fetching every page `robots.txt` permits, reading the **11 stylesheets the site itself
|
|
93
|
+
authors** — 500,051 bytes, 20,240 lines, 136 custom properties — and then reading
|
|
94
|
+
**computed styles on the live page** through CDP at 1440×900 (1,251 visible of 5,694
|
|
95
|
+
elements), 768×1168 and a device-emulated 390×844. Vendor layers were excluded from every
|
|
96
|
+
count: the site also loads Bootstrap 4.6.2, a purchased *ave* theme, a *frest* admin theme
|
|
97
|
+
and three Font Awesome versions, and counting those reads Bootstrap's defaults as the
|
|
98
|
+
brand's decisions.
|
|
99
|
+
|
|
100
|
+
What the pack takes is a **rationed round shape**. 1,091 of the 1,251 rendered elements sit
|
|
101
|
+
at zero radius — 87% of the page — and the pill is 52 of them, spent on one thing: a white
|
|
102
|
+
1px-bordered plate carrying somebody else's publication name as 15px/500 type rather than
|
|
103
|
+
as a logo. Thirty names set as type read as a list a visitor can check; thirty logotypes
|
|
104
|
+
read as thirty typefaces. Around it, one family at four weights with the **body at weight
|
|
105
|
+
500** (172 nodes at 1440, 459 at 768, 182 at 390 — the dominant weight at every width), a
|
|
106
|
+
display face tracked −0.02em, and two uppercase registers tracked 0.06em and 0.175em.
|
|
107
|
+
|
|
108
|
+
**The declared token layer disagreed with the render, and this is the sharpest case in the
|
|
109
|
+
library.** The site declares `--primary: #84B761` three times — a green nothing paints — and
|
|
110
|
+
its real primary action is a gradient, whose element carries a *transparent*
|
|
111
|
+
`background-color`. A colour census reading `background-color` alone finds the blue cookie
|
|
112
|
+
banner and reports it as the brand. Both facts were caught by rendering rather than by
|
|
113
|
+
reading, which is why the pack's Origin records the method and not just the address.
|
|
114
|
+
|
|
115
|
+
**Five corrections travel with the pack, each with its number at the declaration.** The
|
|
116
|
+
primary action is white on a coral ramp at 2.90:1 and 3.19:1, 3.04:1 at the midpoint — under
|
|
117
|
+
AA and under the 3:1 large-text floor, with a 12px uppercase label on it; `--action` holds
|
|
118
|
+
both hues in OKLab and moves lightness until white clears 4.5:1 along the whole ramp. The
|
|
119
|
+
body grey is 4.74:1 on white and **4.42:1 on the cool slab the hero stands on** — the
|
|
120
|
+
correction a white-background-only check never finds. The focus ring is
|
|
121
|
+
`rgba(5,135,237,0.35)`, which composites to `#d3dff9` at 1.34:1, and all three of its alpha
|
|
122
|
+
variants miss the 3:1 non-text floor on both fields; the hue was never the problem, so the
|
|
123
|
+
ring keeps the measured colour and drops the alpha. Two greys carry live text at 2.46:1 and
|
|
124
|
+
2.54:1 across 46 nodes. And 87 of 137 visible interactive elements at 390 are under 44px
|
|
125
|
+
tall, so `--tap-min` is a floor.
|
|
126
|
+
|
|
127
|
+
**The status set was searched, not chosen, and the palette gate is why.** With a coral action
|
|
128
|
+
in play a conventional red `--danger` lands 2.93 OKLab units from it and is
|
|
129
|
+
indistinguishable under all three dichromacies, so danger is held at the reference's red hue
|
|
130
|
+
and taken deep to `#900004`. And `--info` may not be the reference's blue, because that blue
|
|
131
|
+
is already `--accent`: the two came out 7.5 units apart, which the gate refuses outright —
|
|
132
|
+
one colour cannot be both a link and a state. Info is the reference's cyan instead. All
|
|
133
|
+
fifteen pairs across the four states, the accent and the action clear 15 units at full
|
|
134
|
+
colour and 8 under protanopia, deuteranopia and tritanopia.
|
|
135
|
+
|
|
136
|
+
**The first pack in four not to move a ratio pin.** `outrank`, `babylove` and `patchbay` each
|
|
137
|
+
raised `unresolved_at_most` or `unguarded_at_most`, because a pack that documents what it
|
|
138
|
+
*refused* carries ratios whose subject it deliberately does not ship. This pack carries the
|
|
139
|
+
same kind of evidence and states each claim with its subject and its partner on one line, so
|
|
140
|
+
all 34 of its ratios are **computed** and both ceilings held; `computed_at_least` rises
|
|
141
|
+
536 → 570 to lock that in. Three claims did land unpairable on the first run and every one
|
|
142
|
+
was a line break — `--on-action`'s own name resolves as `--action`, which is a gradient and
|
|
143
|
+
pairs with nothing — and a fourth was a table row stating a ratio with no partner named. All
|
|
144
|
+
four were rewritten rather than pinned.
|
|
145
|
+
|
|
146
|
+
A fifth standalone pack pins its own motion ceiling: **nameplate at 4**, the same as `roster`
|
|
147
|
+
and for the same reason. 1,057 of the 1,251 visible elements compute
|
|
148
|
+
`transition-duration: 0s`, `animation-timeline` appears zero times in 20,240 lines, and the
|
|
149
|
+
whole budget is an entrance reveal, a 2px hover lift whose shadow grows in the same 200ms,
|
|
150
|
+
and a 140ms press. The reference also ships a real reduced-motion contract — 23 blocks across
|
|
151
|
+
7 of its 11 authored stylesheets, with the reveal layer restoring `visibility` and `opacity`
|
|
152
|
+
so content is never locked behind an animation that did not run. That shape is kept without
|
|
153
|
+
correction; it is the one thing here worth copying as measured.
|
|
154
|
+
|
|
155
|
+
**Rendering the kit found two defects three green gates could not**, which is the second
|
|
156
|
+
consecutive release where the browser beat the ratchet. The plate came out **78px against a
|
|
157
|
+
stated 50** — the kit declared no `box-sizing`, and Chrome's UA stylesheet gives `button`
|
|
158
|
+
`border-box` while giving an anchor `content-box`, so the button beside it kept its promise
|
|
159
|
+
and the plate silently grew by its own padding. And body copy rendered at **weight 400
|
|
160
|
+
against the pack's central claim of 500**, because `--weight-body` existed and no component
|
|
161
|
+
consumed it — a token that is decorative rather than binding. Both fixed and re-measured at
|
|
162
|
+
50px and 500. A gate reads structure, not layout.
|
|
163
|
+
|
|
164
|
+
**The fork against `roster` is recorded in both directions**, because the two share one
|
|
165
|
+
sentence — the proof is a name — and nothing else. `roster` proves by *who already uses us*
|
|
166
|
+
and makes the pill its most frequent shape (102 elements) with a body at weight 300 on a
|
|
167
|
+
grid-textured white field; nameplate proves by *who will carry you*, is square on 87% of its
|
|
168
|
+
elements, and sets its body at 500 on an untextured slab. `ADR-0001` gains a further
|
|
169
|
+
application: the pack is named for the register, with `masthead`, `newsstand`, `clipping` and
|
|
170
|
+
`hallmark` weighed and rejected on its own criteria — the last of those for trademark
|
|
171
|
+
surface, which is the clause `datasheet` was chosen under.
|
|
172
|
+
|
|
173
|
+
Ships `styles/nameplate.md` on the widened thirteen-heading contract with `Themes:` and
|
|
174
|
+
`Rank:` declared, `styles/tokens/nameplate.css`, the `kits/nameplate` reference kit (the
|
|
175
|
+
six-name spine plus `Plate`, `PlateBand`, `Wave`, `Frame` and `Eyebrow`), routing in every
|
|
176
|
+
enumeration surface, and the `.cursor` mirror.
|
|
177
|
+
|
|
9
178
|
## [1.47.0] - 2026-08-22
|
|
10
179
|
|
|
11
180
|
### `patchbay` — the thirty-second pack, and the first read off a page with no tokens at all
|
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-
|
|
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
|
|
|
@@ -52,6 +52,7 @@ into the cinematic layer, and says so in its own *Motion flavor* section.
|
|
|
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
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) |
|
|
55
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 |
|
|
56
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 |
|
|
57
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 |
|
|
@@ -152,7 +153,7 @@ skills.
|
|
|
152
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 |
|
|
153
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 |
|
|
154
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 |
|
|
155
|
-
| `styles/*.md` | The thirty-
|
|
156
|
+
| `styles/*.md` | The thirty-three style packs — palette, type, texture, motion tokens, motifs, bans, and the traps each one carries |
|
|
156
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) |
|
|
157
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 |
|
|
158
159
|
|
|
@@ -223,7 +224,7 @@ cd ./ds-workbench && npm install && npm run build
|
|
|
223
224
|
then `/design-sync` in that directory, from Claude Code. Three layers cross: the
|
|
224
225
|
pack's **bans** as the design system's own README, `styles.css` built from
|
|
225
226
|
`tokens/<pack>.css` verbatim, and the components — a six-name spine that is
|
|
226
|
-
identical in all thirty-
|
|
227
|
+
identical in all thirty-three kits, so switching packs swaps identity rather than API,
|
|
227
228
|
plus each pack's signature parts. **Motion does not cross**, exactly as it does
|
|
228
229
|
not cross into Figma: a kit is the static half of a pack, and saying so is what
|
|
229
230
|
stops an agent inventing motion to fill the silence.
|
|
@@ -269,7 +270,7 @@ a pack's four widened sections used to make two gates *quieter* and still green.
|
|
|
269
270
|
One honest limit: the npx installer is checked by asserting its runtime bundle
|
|
270
271
|
walker exists, not by reading a file list — it has none by design. What proves
|
|
271
272
|
it ships the right files is CI, which installs the bundle through **both**
|
|
272
|
-
installers and `diff -r`s the result against the source, then builds all thirty-
|
|
273
|
+
installers and `diff -r`s the result against the source, then builds all thirty-three
|
|
273
274
|
kits.
|
|
274
275
|
|
|
275
276
|
`test/scenarios.md` (T1–T30) is the behavioral harness: fresh subagents given a
|
package/bin/cli.js
CHANGED
|
@@ -234,11 +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-
|
|
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
240
|
atrium (warm cream consumer health), patchbay (dark live
|
|
241
|
-
schematic),
|
|
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 +
|
|
242
245
|
one orange, seven tokens over Tailwind defaults),
|
|
243
246
|
outrank (white +
|
|
244
247
|
one violet, a landing and a dashboard in one brand),
|
|
@@ -101,7 +101,13 @@ demonstration;
|
|
|
101
101
|
bulletin — warm cream paper cut by flat pastel bands, every card and control a
|
|
102
102
|
1px ink outline standing on a hard zero-blur ink offset it travels into when
|
|
103
103
|
pressed, a display face at 800 inside controls and 700 in the headline, and no
|
|
104
|
-
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);
|
|
105
111
|
otherwise follow the contract below (self-contained on purpose).
|
|
106
112
|
|
|
107
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
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface FrameProps {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** The one object per screen allowed to wear --shadow-frame. */
|
|
9
|
+
export function Frame({ children, className }: FrameProps) {
|
|
10
|
+
return (
|
|
11
|
+
<figure className={['np-frame', className].filter(Boolean).join(' ')}>
|
|
12
|
+
{children}
|
|
13
|
+
</figure>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: Foundations
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Three levels, all in the one family at `--weight-display` (700) with
|
|
6
|
+
`--track-display` (−0.02em): 1 is the hero at `--t-hero` (45px, and 30px at ≤768), 2
|
|
7
|
+
is the section at `--t-section`, 3 is a card title.
|
|
8
|
+
|
|
9
|
+
The tracking is negative at every level and the body is untracked — that pair is the
|
|
10
|
+
pack's type signature, and setting a heading at 0 tracking loses it.
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
<Heading level={1}>Build instant authority</Heading>
|
|
14
|
+
```
|