sheleg-design-skill 1.49.0 → 1.50.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 +78 -0
- package/README.md +18 -4
- package/bin/cli.js +5 -2
- package/cursor/rules/sheleg-design.mdc +6 -1
- package/kits/almanac/src/styles.css +3 -3
- package/kits/daylight/src/styles.css +3 -3
- package/kits/notation/src/styles.css +3 -3
- package/kits/onionskin/.design-sync/config.json +14 -0
- package/kits/onionskin/.design-sync/conventions.md +63 -0
- package/kits/onionskin/README.md +37 -0
- package/kits/onionskin/package.json +29 -0
- package/kits/onionskin/src/Button.md +16 -0
- package/kits/onionskin/src/Button.tsx +33 -0
- package/kits/onionskin/src/Card.md +13 -0
- package/kits/onionskin/src/Card.tsx +24 -0
- package/kits/onionskin/src/Chip.md +14 -0
- package/kits/onionskin/src/Chip.tsx +25 -0
- package/kits/onionskin/src/Data.md +16 -0
- package/kits/onionskin/src/Data.tsx +16 -0
- package/kits/onionskin/src/Grid.md +14 -0
- package/kits/onionskin/src/Grid.tsx +15 -0
- package/kits/onionskin/src/Heading.md +14 -0
- package/kits/onionskin/src/Heading.tsx +19 -0
- package/kits/onionskin/src/Micro.md +17 -0
- package/kits/onionskin/src/Micro.tsx +17 -0
- package/kits/onionskin/src/Panel.md +21 -0
- package/kits/onionskin/src/Panel.tsx +25 -0
- package/kits/onionskin/src/Rule.md +14 -0
- package/kits/onionskin/src/Rule.tsx +18 -0
- package/kits/onionskin/src/Stat.md +11 -0
- package/kits/onionskin/src/Stat.tsx +17 -0
- package/kits/onionskin/src/index.ts +23 -0
- package/kits/onionskin/src/styles.css +532 -0
- package/kits/onionskin/tsconfig.json +15 -0
- package/kits/vitrine/src/styles.css +3 -3
- 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 +1 -1
- package/plugins/sheleg-design/skills/sheleg-design/styles/blueprint.md +1 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/onionskin.md +329 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/tenor.md +1 -1
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/onionskin.css +271 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,84 @@ follow [SemVer](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.50.0] - 2026-08-25
|
|
10
|
+
|
|
11
|
+
### Onionskin, the thirty-fifth pack — two bases, and everything quiet is an alpha
|
|
12
|
+
|
|
13
|
+
Extracted from `supermemory.ai`, the front page of a memory and context engine for AI
|
|
14
|
+
applications. The site is Astro: two stylesheets totalling 154,143 bytes declaring 151
|
|
15
|
+
custom properties. Every value was read off **computed styles on the live page** through
|
|
16
|
+
CDP at 1440×900 — 1,469 visible of 1,653 elements on a 14,437px page — and at a
|
|
17
|
+
device-emulated 390×844.
|
|
18
|
+
|
|
19
|
+
**The principle is measurable and it is the pack.** 199 of the 631 colour-carrying
|
|
20
|
+
values on the page carry an alpha, drawn from exactly **two** bases: 97 from a navy used
|
|
21
|
+
for every rule and panel edge and never for a word, and 83 from the ink, used only for
|
|
22
|
+
words. Text dims through the ink; structure dims through the navy. **There is no grey
|
|
23
|
+
ramp in this pack**, because the reference has none — which is what separates it from
|
|
24
|
+
every other near-white technical sheet in the library.
|
|
25
|
+
|
|
26
|
+
At **96.5% zero radius** — 1,418 of 1,469 rendered elements — it is the squarest page
|
|
27
|
+
here by a margin. Three faces with one job each (a grotesque displays, a sans carries
|
|
28
|
+
sentences, a mono carries every number and key), a dot grid at 0.8px, dashed hairlines
|
|
29
|
+
for a provisional boundary, and a **working size of 11px**, which is the most frequent
|
|
30
|
+
size on the page at every width.
|
|
31
|
+
|
|
32
|
+
**An alpha composites in sRGB, not in linear light, and the first pass of this pack got
|
|
33
|
+
that wrong.** Mixed in linear space, the ink at 60% computes 2.32:1 and reads as a
|
|
34
|
+
failure that would have forced every tier to be redrawn; composited the way a browser
|
|
35
|
+
does it, the same value is **4.98:1** and passes. The correction is recorded at the top
|
|
36
|
+
of the token layer, because a pack whose central mechanism is alpha cannot afford the
|
|
37
|
+
next reader to repeat it.
|
|
38
|
+
|
|
39
|
+
**Four corrections travel with the pack.** A grey that fails on every field it is used
|
|
40
|
+
on — `#888e94` on 48 text nodes, 3.31:1 on white, 3.17:1 on the panel, 2.97:1 on the
|
|
41
|
+
tint. The lowest ink tier, which composites to 3.08:1 and carries six live text nodes,
|
|
42
|
+
demoted to non-text. The rules, at 1.21:1 and 1.10:1, declared as rules rather than
|
|
43
|
+
marks — with a ban requiring a second separator rather than a darker hairline. And 88 of
|
|
44
|
+
123 interactive elements at 1440 under 44px, 73 of 106 at 390.
|
|
45
|
+
|
|
46
|
+
**The accent needed none, which is rare here:** `#0562ef` is 5.25:1 on white *and*
|
|
47
|
+
5.25:1 under white, so one token carries the fill and the word with no derived twin.
|
|
48
|
+
|
|
49
|
+
**The fork against `blueprint` is the closest call in the library** and is written from
|
|
50
|
+
both ends. Both are near-white technical sheets, both spend one blue that writes and
|
|
51
|
+
fills, both are zero-radius, and `blueprint`'s Register describes this reference too.
|
|
52
|
+
The separation is mechanical: `blueprint` builds its quiet layer from solid greys and
|
|
53
|
+
rules with a solid line; this one has no greys at all. Body 16px against 11px, two
|
|
54
|
+
families against three, a ruled field with ticks against a dot field.
|
|
55
|
+
|
|
56
|
+
**The gate caught five of my own numbers.** Every stated ratio in the dark block was
|
|
57
|
+
computed by hand and five were wrong; the gate supplied the right ones and they are what
|
|
58
|
+
shipped. It also refused a `color-mix` with `transparent` inside it as uncomputable, and
|
|
59
|
+
refused three tight status pairs in a dark set I had picked rather than searched — the
|
|
60
|
+
searched set clears all ten pairs.
|
|
61
|
+
|
|
62
|
+
**Rendering the kit found nothing**, for the first time in four releases. Worth saying
|
|
63
|
+
because the step is not decoration: both classes it caught before — a control following
|
|
64
|
+
its field into the dark act, and a box that grew by its own padding — were pre-empted
|
|
65
|
+
here, `box-sizing` from the first line and a dark block with no light twin to invert.
|
|
66
|
+
|
|
67
|
+
`MOTION_INTENSITY` is pinned at **2**: 1,290 of 1,469 elements compute
|
|
68
|
+
`transition-duration: 0s` and `animation-timeline` appears zero times in 154,143 bytes.
|
|
69
|
+
|
|
70
|
+
`ADR-0001` gains a further application, with `quadrille`, `tracing` and `vellum` weighed
|
|
71
|
+
and rejected on its own criteria.
|
|
72
|
+
|
|
73
|
+
Gates: 4754 → **4882**, 2784 → **2875**, 706 → **720**. Both palette ceilings held for
|
|
74
|
+
the third consecutive release; `computed_at_least` rises 600 → 628.
|
|
75
|
+
|
|
76
|
+
## [1.49.1] - 2026-08-25
|
|
77
|
+
|
|
78
|
+
### Fixed
|
|
79
|
+
|
|
80
|
+
The README told a reader to run commands the published package cannot run: it ships no
|
|
81
|
+
`test/` directory, so `python3 test/validate.py` resolves in a clone and nowhere else. Measured against the
|
|
82
|
+
published tarball on 2026-08-25. Shipping the suite does not fix it — the plants live in
|
|
83
|
+
`.github/workflows/`, which no packaging npm can express puts in a tarball — so the document
|
|
84
|
+
now names where the command runs instead of claiming it, beside a marker the umbrella's
|
|
85
|
+
validator reads. Naming a dead command is this family's own rule; claiming one is the defect.
|
|
86
|
+
|
|
9
87
|
## [1.49.0] - 2026-08-24
|
|
10
88
|
|
|
11
89
|
### Rimlight, the thirty-fourth pack — elevation made of light
|
package/README.md
CHANGED
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/sheleg-design-skill)
|
|
4
4
|
[](https://github.com/ssheleg/sheleg-design-skill/actions/workflows/validate.yml)
|
|
5
5
|
[](./LICENSE)
|
|
6
|
+
[](https://skills.sshlg.me/skills/sheleg-design/)
|
|
7
|
+
|
|
8
|
+
**[Docs, and every skill →](https://skills.sshlg.me/)** · [this skill's page](https://skills.sshlg.me/skills/sheleg-design/) · [follow @sshlg93 on X](https://x.com/intent/follow?screen_name=sshlg93)
|
|
9
|
+
|
|
10
|
+
Loads in **DeepSeek Harness** (`dsh`) with **no plugin to write**: it reads the
|
|
11
|
+
Agent Skills standard directly, scanning `~/.agents/skills` — where `npx skills
|
|
12
|
+
add` puts this pack — at rank 500.
|
|
13
|
+
|
|
14
|
+
**[Browse every style pack →](https://ssheleg.github.io/sheleg-design-skill/)** — every card rendered in its own token layer, plus the collection audit. The site each pack was measured from stays in the pack; the published pages name none of them.
|
|
6
15
|
|
|
7
16
|
An agent can generate a landing page in under a minute, and it will look like
|
|
8
17
|
every other generated landing page: three cards, a gradient, a hero that does
|
|
@@ -11,7 +20,7 @@ problem — invented colors, six accent hues, dark mode retrofitted later.
|
|
|
11
20
|
|
|
12
21
|
This skill is the taste layer. It gives a coding agent **one motion
|
|
13
22
|
methodology** for cinematic, scroll-driven pages, **a motion doctrine** that
|
|
14
|
-
decides whether to animate before it decides how, and **thirty-
|
|
23
|
+
decides whether to animate before it decides how, and **thirty-five locked style
|
|
15
24
|
packs** with ready-made design tokens, so what it builds reads as one system
|
|
16
25
|
instead of a pile of effects.
|
|
17
26
|
|
|
@@ -54,6 +63,7 @@ into the cinematic layer, and says so in its own *Motion flavor* section.
|
|
|
54
63
|
| `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
64
|
| `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) |
|
|
56
65
|
| `rimlight` | a white field with a cool grey act separator and one near-black act, a grotesque for every sentence and a **monospace for every piece of chrome**, square on 84% of its elements, tracked negative at every size and with **no bold anywhere** — and an elevation made of **coloured light**: a sixteen-layer rig, six layers lit and ten held at alpha 0, thrown from below-left onto one control per viewport | **a studio's own front door and the pages that sell what it makes** — design and engineering agencies, product studios, service and case-study pages (standalone) |
|
|
66
|
+
| `onionskin` | a white technical sheet at **96.5% zero radius** — the squarest page in the library — where **two bases do all the work and everything quiet is one of them at an alpha**: text dims through the ink, structure through a navy that is never a word, so there is no grey ramp at all. A dot grid, dashed hairlines, three faces with one job each, and an **11px working size** | **developer and AI infrastructure whose front page is a working document** — memory and context engines, retrieval services, agent runtimes, evaluation and observability surfaces (standalone) |
|
|
57
67
|
| `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 |
|
|
58
68
|
| `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 |
|
|
59
69
|
| `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 |
|
|
@@ -154,7 +164,7 @@ skills.
|
|
|
154
164
|
| `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 |
|
|
155
165
|
| `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 |
|
|
156
166
|
| `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 |
|
|
157
|
-
| `styles/*.md` | The thirty-
|
|
167
|
+
| `styles/*.md` | The thirty-five style packs — palette, type, texture, motion tokens, motifs, bans, and the traps each one carries |
|
|
158
168
|
| `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) |
|
|
159
169
|
| `styles/STYLE_PACK_TEMPLATE.md` | The pack contract as a skeleton, so a new style is authored against the same headings rather than improvised |
|
|
160
170
|
|
|
@@ -225,7 +235,7 @@ cd ./ds-workbench && npm install && npm run build
|
|
|
225
235
|
then `/design-sync` in that directory, from Claude Code. Three layers cross: the
|
|
226
236
|
pack's **bans** as the design system's own README, `styles.css` built from
|
|
227
237
|
`tokens/<pack>.css` verbatim, and the components — a six-name spine that is
|
|
228
|
-
identical in all thirty-
|
|
238
|
+
identical in all thirty-five kits, so switching packs swaps identity rather than API,
|
|
229
239
|
plus each pack's signature parts. **Motion does not cross**, exactly as it does
|
|
230
240
|
not cross into Figma: a kit is the static half of a pack, and saying so is what
|
|
231
241
|
stops an agent inventing motion to fill the silence.
|
|
@@ -248,6 +258,10 @@ repo. Entirely optional; without it the skill works from the pack alone.
|
|
|
248
258
|
|
|
249
259
|
## Development
|
|
250
260
|
|
|
261
|
+
<!-- commands-run-in: a clone -->
|
|
262
|
+
These run **in a clone of this repository**. The published npm package ships no
|
|
263
|
+
`test/` directory, so from an install they are names, not commands.
|
|
264
|
+
|
|
251
265
|
```bash
|
|
252
266
|
python3 test/validate.py # or: npm test
|
|
253
267
|
```
|
|
@@ -271,7 +285,7 @@ a pack's four widened sections used to make two gates *quieter* and still green.
|
|
|
271
285
|
One honest limit: the npx installer is checked by asserting its runtime bundle
|
|
272
286
|
walker exists, not by reading a file list — it has none by design. What proves
|
|
273
287
|
it ships the right files is CI, which installs the bundle through **both**
|
|
274
|
-
installers and `diff -r`s the result against the source, then builds all thirty-
|
|
288
|
+
installers and `diff -r`s the result against the source, then builds all thirty-five
|
|
275
289
|
kits.
|
|
276
290
|
|
|
277
291
|
`test/scenarios.md` (T1–T30) is the behavioral harness: fresh subagents given a
|
package/bin/cli.js
CHANGED
|
@@ -234,7 +234,7 @@ ${c("bold", "What it installs")}
|
|
|
234
234
|
DESIGN_SYNC_BRIDGE.md the Claude Design contract (what a pack sends, and
|
|
235
235
|
what does not cross)
|
|
236
236
|
AI_PRODUCT_PATTERNS.md chat / agent / streaming surfaces (honest state)
|
|
237
|
-
styles/ thirty-
|
|
237
|
+
styles/ thirty-five 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
|
|
@@ -244,7 +244,10 @@ ${c("bold", "What it installs")}
|
|
|
244
244
|
as type), rimlight (a white field and one near-black
|
|
245
245
|
act, a monospace carrying every label and button, and
|
|
246
246
|
an elevation made of coloured light rather than a
|
|
247
|
-
shadow),
|
|
247
|
+
shadow), onionskin (a white technical sheet at 96.5%
|
|
248
|
+
zero radius where every quiet value is the ink or a
|
|
249
|
+
navy at an alpha, over a dot grid, at an 11px working
|
|
250
|
+
size), babylove (white +
|
|
248
251
|
one orange, seven tokens over Tailwind defaults),
|
|
249
252
|
outrank (white +
|
|
250
253
|
one violet, a landing and a dashboard in one brand),
|
|
@@ -112,7 +112,12 @@ rimlight — a white field with a cool grey act separator and one near-black act
|
|
|
112
112
|
grotesque for every sentence and a monospace for every piece of chrome, square on 84%
|
|
113
113
|
of its elements, tracked negative at every size and with no bold anywhere, whose only
|
|
114
114
|
elevation is a sixteen-layer coloured light rig thrown from below-left onto one
|
|
115
|
-
control per viewport, for a studio's front door and its service and case-study pages
|
|
115
|
+
control per viewport, for a studio's front door and its service and case-study pages;
|
|
116
|
+
onionskin — a white technical sheet at 96.5% zero radius where two bases do all the
|
|
117
|
+
work and everything quiet is one of them at an alpha: text dims through the ink,
|
|
118
|
+
structure through a navy that is never a word, over a dot grid with dashed hairlines,
|
|
119
|
+
three faces with one job each and an 11px working size, for developer and AI
|
|
120
|
+
infrastructure whose front page is a working document);
|
|
116
121
|
otherwise follow the contract below (self-contained on purpose).
|
|
117
122
|
|
|
118
123
|
## Whether to animate at all — before how
|
|
@@ -404,7 +404,7 @@
|
|
|
404
404
|
}
|
|
405
405
|
|
|
406
406
|
.al-status--ok .al-status__dot {
|
|
407
|
-
background: var(--ok
|
|
407
|
+
background: var(--ok);
|
|
408
408
|
}
|
|
409
409
|
|
|
410
410
|
.al-status--warn {
|
|
@@ -413,7 +413,7 @@
|
|
|
413
413
|
}
|
|
414
414
|
|
|
415
415
|
.al-status--warn .al-status__dot {
|
|
416
|
-
background: var(--warn
|
|
416
|
+
background: var(--warn);
|
|
417
417
|
}
|
|
418
418
|
|
|
419
419
|
.al-status--danger {
|
|
@@ -422,7 +422,7 @@
|
|
|
422
422
|
}
|
|
423
423
|
|
|
424
424
|
.al-status--danger .al-status__dot {
|
|
425
|
-
background: var(--danger
|
|
425
|
+
background: var(--danger);
|
|
426
426
|
}
|
|
427
427
|
|
|
428
428
|
.al-status--neutral {
|
|
@@ -398,7 +398,7 @@
|
|
|
398
398
|
}
|
|
399
399
|
|
|
400
400
|
.dy-status--ok .dy-status__dot {
|
|
401
|
-
background: var(--ok
|
|
401
|
+
background: var(--ok);
|
|
402
402
|
}
|
|
403
403
|
|
|
404
404
|
.dy-status--warn {
|
|
@@ -407,7 +407,7 @@
|
|
|
407
407
|
}
|
|
408
408
|
|
|
409
409
|
.dy-status--warn .dy-status__dot {
|
|
410
|
-
background: var(--warn
|
|
410
|
+
background: var(--warn);
|
|
411
411
|
}
|
|
412
412
|
|
|
413
413
|
.dy-status--danger {
|
|
@@ -416,7 +416,7 @@
|
|
|
416
416
|
}
|
|
417
417
|
|
|
418
418
|
.dy-status--danger .dy-status__dot {
|
|
419
|
-
background: var(--danger
|
|
419
|
+
background: var(--danger);
|
|
420
420
|
}
|
|
421
421
|
|
|
422
422
|
.dy-status--neutral {
|
|
@@ -396,7 +396,7 @@ strong, b { font-weight: var(--w-bold); }
|
|
|
396
396
|
}
|
|
397
397
|
|
|
398
398
|
.nt-status--ok .nt-status__dot {
|
|
399
|
-
background: var(--ok
|
|
399
|
+
background: var(--ok);
|
|
400
400
|
}
|
|
401
401
|
|
|
402
402
|
.nt-status--warn {
|
|
@@ -405,7 +405,7 @@ strong, b { font-weight: var(--w-bold); }
|
|
|
405
405
|
}
|
|
406
406
|
|
|
407
407
|
.nt-status--warn .nt-status__dot {
|
|
408
|
-
background: var(--warn
|
|
408
|
+
background: var(--warn);
|
|
409
409
|
}
|
|
410
410
|
|
|
411
411
|
.nt-status--danger {
|
|
@@ -414,7 +414,7 @@ strong, b { font-weight: var(--w-bold); }
|
|
|
414
414
|
}
|
|
415
415
|
|
|
416
416
|
.nt-status--danger .nt-status__dot {
|
|
417
|
-
background: var(--danger
|
|
417
|
+
background: var(--danger);
|
|
418
418
|
}
|
|
419
419
|
|
|
420
420
|
.nt-status--neutral {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pkg": "@sheleg-design/onionskin",
|
|
3
|
+
"globalName": "ShelegOnionskin",
|
|
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,63 @@
|
|
|
1
|
+
# Onionskin — the contract this design system ships under
|
|
2
|
+
|
|
3
|
+
**Register.** Choose Onionskin for **developer and AI infrastructure whose front page is
|
|
4
|
+
a working document**: memory and context engines, retrieval and embedding services,
|
|
5
|
+
agent runtimes, evaluation and observability surfaces. A white technical sheet at 96.5%
|
|
6
|
+
zero radius, ruled by hairlines, over a dot grid. Build every screen against `var(--…)`
|
|
7
|
+
and never a literal.
|
|
8
|
+
|
|
9
|
+
**Two bases, and everything quiet is one of them at an alpha.** Text dims through
|
|
10
|
+
`--ink`; structure dims through a navy that is never a word. There is **no grey ramp in
|
|
11
|
+
this pack** — introducing `#6b7280` here introduces a third base and dissolves the whole
|
|
12
|
+
construction. 199 of the reference's 631 colour-carrying values carry an alpha, from
|
|
13
|
+
exactly two bases.
|
|
14
|
+
|
|
15
|
+
**An alpha composites in sRGB, not in linear light.** `--ink` at 60% is 4.98:1 and
|
|
16
|
+
passes; computed in linear space the same value reads 2.32:1 and looks like a failure.
|
|
17
|
+
Every ratio in the token layer was computed the way a browser composites.
|
|
18
|
+
|
|
19
|
+
**The navy is never a word.** At its working alpha it is 1.21:1. It rules and it edges.
|
|
20
|
+
|
|
21
|
+
**A rule is never the only separator.** At 1.21:1 a hairline is below every floor there
|
|
22
|
+
is, so the region it bounds must ALSO change field, gain a label, or gain space. This is
|
|
23
|
+
a ban, not a preference.
|
|
24
|
+
|
|
25
|
+
**Elevation is an edge.** `--edge-lit` — the accent at 28%, inset on the left and right
|
|
26
|
+
— marks the one panel per section that is the subject. `--shadow-lift` appears once on
|
|
27
|
+
the reference and should appear once on your page. A panel never floats.
|
|
28
|
+
|
|
29
|
+
**The working size is 11px.** It is the most frequent size on the reference at every
|
|
30
|
+
width, and it carries labels, keys, annotations and mono data. Setting it at 13px "for
|
|
31
|
+
readability" turns this into a different pack; the density is the argument.
|
|
32
|
+
|
|
33
|
+
**Tracking is two-sided.** `--track-micro` (+0.18em) on the uppercase label,
|
|
34
|
+
`--track-display` (−0.05em) on the display. Collapsing them loses the page's texture.
|
|
35
|
+
|
|
36
|
+
**Three faces, one job each.** Space Grotesk displays, DM Sans carries every sentence,
|
|
37
|
+
DM Mono carries every number, key and identifier. Nothing crosses: a number in the sans
|
|
38
|
+
or a sentence in the mono and the page stops reading as a document.
|
|
39
|
+
|
|
40
|
+
**No bold.** 500 is the ceiling at scale — 205 nodes against 400's 175, with 600 on
|
|
41
|
+
seven elements and 700 on four.
|
|
42
|
+
|
|
43
|
+
**No radius above 5px.** 1,418 of 1,469 rendered elements are square.
|
|
44
|
+
|
|
45
|
+
**The dot grid is a field.** Under a section, never inside a panel, never a border, and
|
|
46
|
+
its 24px step does not rescale at narrow widths.
|
|
47
|
+
|
|
48
|
+
**The accent needed no correction.** `#0562ef` is 5.25:1 on white and 5.25:1 under
|
|
49
|
+
white — one token for the fill and the word.
|
|
50
|
+
|
|
51
|
+
**Status is never by colour alone**, on either field, and the dark band carries its own
|
|
52
|
+
four because the light set measures 1.4–2.6:1 there.
|
|
53
|
+
|
|
54
|
+
**`[data-surface="dark"]` is one section, never the document.** Its rules invert base
|
|
55
|
+
rather than alpha — white at 14% instead of navy at 10% — because a navy at 10% over
|
|
56
|
+
near-black is nothing at all.
|
|
57
|
+
|
|
58
|
+
**Every control clears `--tap-min` (44px).** A correction: 88 of the reference's 123
|
|
59
|
+
interactive elements at 1440 are shorter, and 73 of 106 at 390.
|
|
60
|
+
|
|
61
|
+
**Nothing travels in space.** No parallax, no scrub, no `animation-timeline`, no press
|
|
62
|
+
translation. `MOTION_INTENSITY` above 2 has nothing legal to buy, and reduced motion
|
|
63
|
+
collapses every duration to zero.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @sheleg-design/onionskin
|
|
2
|
+
|
|
3
|
+
The React reference kit for the SHELEG **Onionskin** style pack — a white technical
|
|
4
|
+
sheet at 96.5% zero radius where two bases do all the work and everything quiet is one
|
|
5
|
+
of them at an alpha.
|
|
6
|
+
|
|
7
|
+
It is generated from the pack, not authored beside it: `src/styles.css` opens with
|
|
8
|
+
`styles/tokens/onionskin.css` byte for byte, and the rules the design agent must obey
|
|
9
|
+
are in [`.design-sync/conventions.md`](./.design-sync/conventions.md).
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install && npm run build
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## The spine
|
|
16
|
+
|
|
17
|
+
`Button`, `Card`, `Chip`, `Stat`, `Heading`, `Rule` — identical names, props and types
|
|
18
|
+
in every SHELEG kit, so switching packs swaps identity rather than API. On this pack
|
|
19
|
+
`Card` exists for parity only: reach for `Panel`.
|
|
20
|
+
|
|
21
|
+
## This pack's own
|
|
22
|
+
|
|
23
|
+
`Panel` (the signature — a ruled region of the sheet, with a provisional dashed variant
|
|
24
|
+
and a lit-edge subject variant), `Micro` (11px uppercase tracked open, the most repeated
|
|
25
|
+
object on the page), `Data` (the monospace, which owns every number) and `Grid` (the dot
|
|
26
|
+
field, which goes under a section and nowhere else).
|
|
27
|
+
|
|
28
|
+
## The three rules that carry the kit
|
|
29
|
+
|
|
30
|
+
1. **There is no grey.** Every quiet value is `--ink` or the navy at an alpha. The
|
|
31
|
+
moment a third base appears, the system reads as an ordinary light UI.
|
|
32
|
+
2. **Nothing floats.** Elevation is a 1px rule and, once per section, an inset lit edge.
|
|
33
|
+
3. **The three faces do not cross.** Display, sentences, numbers — one family each.
|
|
34
|
+
|
|
35
|
+
**Motion does not cross into a design tool.** A kit is the static half of a pack, and
|
|
36
|
+
this one is nearly all static by measurement: 1,290 of the reference's 1,469 visible
|
|
37
|
+
elements do not transition at all.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sheleg-design/onionskin",
|
|
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,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: Actions
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
`primary` fills with `--accent` and takes `--on-accent` — 5.25:1, and the same number
|
|
6
|
+
in both directions, which is why this pack needs no derived accent twin. `secondary` is
|
|
7
|
+
`--surface` with a 1px `--rule` and an `--ink` label; hover moves the border to
|
|
8
|
+
`--accent-rule` and never adds a fill.
|
|
9
|
+
|
|
10
|
+
`--r-sm` (4px), and every size clears `--tap-min` (44px). The reference's own controls
|
|
11
|
+
are 36px — that is the one geometry this pack corrects rather than copies.
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<Button>Get an API key</Button>
|
|
15
|
+
<Button variant="secondary">Read the docs</Button>
|
|
16
|
+
```
|
|
@@ -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={['ok-btn', `ok-btn--${variant}`, `ok-btn--${size}`, className]
|
|
25
|
+
.filter(Boolean)
|
|
26
|
+
.join(' ')}
|
|
27
|
+
disabled={disabled}
|
|
28
|
+
onClick={onClick}
|
|
29
|
+
>
|
|
30
|
+
{children}
|
|
31
|
+
</button>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: Surfaces
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Present for spine parity. **On this pack, reach for `Panel` instead** — the reference
|
|
6
|
+
has no rounded card, and `Card` here is a `Panel` with a title row: zero radius, a 1px
|
|
7
|
+
`--rule`, no shadow.
|
|
8
|
+
|
|
9
|
+
If a design calls for something that floats, it is not this pack.
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
<Card title="Throughput" meta="p95">…</Card>
|
|
13
|
+
```
|
|
@@ -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={['ok-card', className].filter(Boolean).join(' ')}>
|
|
15
|
+
{head && (
|
|
16
|
+
<div className="ok-card__head">
|
|
17
|
+
{title !== undefined && <h3 className="ok-card__title">{title}</h3>}
|
|
18
|
+
{meta !== undefined && <span className="ok-card__meta">{meta}</span>}
|
|
19
|
+
</div>
|
|
20
|
+
)}
|
|
21
|
+
<div className="ok-card__body">{children}</div>
|
|
22
|
+
</section>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: Foundations
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
A tag at `--r-xs` (2px) with a 1px `--rule` and a `--t-micro` label. `accent` tints with
|
|
6
|
+
`--accent-wash`, which is `--tint` — the reference's called-out block colour doing double
|
|
7
|
+
duty.
|
|
8
|
+
|
|
9
|
+
`selected` fills `--accent` with `--on-accent`.
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
<Chip>embeddings</Chip>
|
|
13
|
+
<Chip tone="accent" selected>vector</Chip>
|
|
14
|
+
```
|
|
@@ -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
|
+
'ok-chip',
|
|
15
|
+
`ok-chip--${tone}`,
|
|
16
|
+
selected ? 'ok-chip--selected' : undefined,
|
|
17
|
+
className,
|
|
18
|
+
]
|
|
19
|
+
.filter(Boolean)
|
|
20
|
+
.join(' ')}
|
|
21
|
+
>
|
|
22
|
+
{children}
|
|
23
|
+
</span>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: Data
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Every number, key and identifier on the page, in `--font-mono` at `--t-micro` or
|
|
6
|
+
`--t-xs`. The figure takes `--ink`; its unit takes `--ink-soft`, which is the ink at
|
|
7
|
+
60% rather than a grey.
|
|
8
|
+
|
|
9
|
+
**Nothing crosses the three families.** A number in the sans, or a sentence in the mono,
|
|
10
|
+
and the page stops reading as a document — that is the whole reason the pack carries
|
|
11
|
+
three faces instead of two.
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<Data value="42" unit="ms" />
|
|
15
|
+
<Data value="sk_live_…" />
|
|
16
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface DataProps {
|
|
2
|
+
/** The figure. Always the monospace — a number in the sans breaks the document. */
|
|
3
|
+
value: string;
|
|
4
|
+
/** Its unit or suffix, one tier quieter. */
|
|
5
|
+
unit?: string;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function Data({ value, unit, className }: DataProps) {
|
|
10
|
+
return (
|
|
11
|
+
<span className={['ok-data', className].filter(Boolean).join(' ')}>
|
|
12
|
+
<span className="ok-data__value">{value}</span>
|
|
13
|
+
{unit !== undefined && <span className="ok-data__unit">{unit}</span>}
|
|
14
|
+
</span>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: Surfaces
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
The page's only texture: a `radial-gradient` dot of `--grid-color` at `--grid-dot`
|
|
6
|
+
(0.8px) repeated every `--grid-step` (24px).
|
|
7
|
+
|
|
8
|
+
It is a **field**. It belongs under a section, and it is never a border and never
|
|
9
|
+
inside a panel. The step does not rescale at narrow widths — a field that rescales
|
|
10
|
+
stops reading as paper.
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
<Grid><section>…</section></Grid>
|
|
14
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface GridProps {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** The dot field. It goes UNDER a section — never inside a panel, never as a border. */
|
|
9
|
+
export function Grid({ children, className }: GridProps) {
|
|
10
|
+
return (
|
|
11
|
+
<div className={['ok-grid', className].filter(Boolean).join(' ')} data-grid="">
|
|
12
|
+
{children}
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: Foundations
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Three levels in `--font-display` (Space Grotesk) at `--weight-medium` (500) with
|
|
6
|
+
`--track-display` (−0.05em): 1 is `--t-display` (56px, 34px at ≤768), 2 is `--t-h2`
|
|
7
|
+
(40px), 3 is `--t-h3` (28px).
|
|
8
|
+
|
|
9
|
+
**No bold.** 500 is the ceiling at scale on this pack; a headline is loud because it is
|
|
10
|
+
56px and tracked in hard.
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
<Heading level={1}>Memory for your agents</Heading>
|
|
14
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface HeadingProps {
|
|
4
|
+
/** 1 = page title (28px), 2 = section (20px), 3 = card title (15px). */
|
|
5
|
+
level?: 1 | 2 | 3;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function Heading({ level = 2, children, className }: HeadingProps) {
|
|
11
|
+
const Tag = `h${level}` as 'h1' | 'h2' | 'h3';
|
|
12
|
+
return (
|
|
13
|
+
<Tag
|
|
14
|
+
className={['ok-heading', `ok-heading--${level}`, className].filter(Boolean).join(' ')}
|
|
15
|
+
>
|
|
16
|
+
{children}
|
|
17
|
+
</Tag>
|
|
18
|
+
);
|
|
19
|
+
}
|