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.
- package/CHANGELOG.md +177 -0
- package/README.md +6 -4
- package/bin/cli.js +6 -2
- package/cursor/rules/sheleg-design.mdc +10 -2
- 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/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 +25 -0
- package/kits/nameplate/src/styles.css +762 -0
- package/kits/nameplate/tsconfig.json +15 -0
- package/kits/patchbay/.design-sync/config.json +14 -0
- package/kits/patchbay/.design-sync/conventions.md +38 -0
- package/kits/patchbay/package.json +14 -0
- package/kits/patchbay/src/Button.md +17 -0
- package/kits/patchbay/src/Button.tsx +33 -0
- package/kits/patchbay/src/Card.md +18 -0
- package/kits/patchbay/src/Card.tsx +23 -0
- package/kits/patchbay/src/Chip.md +15 -0
- package/kits/patchbay/src/Chip.tsx +21 -0
- package/kits/patchbay/src/Diagram.md +39 -0
- package/kits/patchbay/src/Diagram.tsx +91 -0
- package/kits/patchbay/src/Heading.md +15 -0
- package/kits/patchbay/src/Heading.tsx +13 -0
- package/kits/patchbay/src/Rule.md +12 -0
- package/kits/patchbay/src/Rule.tsx +8 -0
- package/kits/patchbay/src/Stat.md +17 -0
- package/kits/patchbay/src/Stat.tsx +26 -0
- package/kits/patchbay/src/StatusDot.md +19 -0
- package/kits/patchbay/src/StatusDot.tsx +15 -0
- package/kits/patchbay/src/index.ts +8 -0
- package/kits/patchbay/src/styles.css +272 -0
- package/kits/patchbay/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 +2 -2
- package/plugins/sheleg-design/skills/sheleg-design/MOBILE_SURFACES.md +1 -1
- package/plugins/sheleg-design/skills/sheleg-design/MOTION_DOCTRINE.md +31 -0
- package/plugins/sheleg-design/skills/sheleg-design/SKILL.md +5 -3
- package/plugins/sheleg-design/skills/sheleg-design/SURFACE_COMPOSITION.md +5 -5
- package/plugins/sheleg-design/skills/sheleg-design/styles/STYLE_PACK_TEMPLATE.md +3 -2
- package/plugins/sheleg-design/skills/sheleg-design/styles/nameplate.md +421 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/patchbay.md +418 -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 +309 -0
- 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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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),
|
|
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;
|
|
18
|
+
accent, fluted-glass hero; patchbay — a 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
|
+
```
|