sheleg-design-skill 1.18.0 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/README.md +6 -4
  3. package/bin/cli.js +7 -3
  4. package/cursor/rules/sheleg-design.mdc +7 -1
  5. package/kits/datasheet/.design-sync/config.json +14 -0
  6. package/kits/datasheet/.design-sync/conventions.md +72 -0
  7. package/kits/datasheet/README.md +16 -0
  8. package/kits/datasheet/package.json +29 -0
  9. package/kits/datasheet/src/Button.md +17 -0
  10. package/kits/datasheet/src/Button.tsx +33 -0
  11. package/kits/datasheet/src/Card.md +15 -0
  12. package/kits/datasheet/src/Card.tsx +24 -0
  13. package/kits/datasheet/src/Cell.md +16 -0
  14. package/kits/datasheet/src/Cell.tsx +23 -0
  15. package/kits/datasheet/src/Chip.md +16 -0
  16. package/kits/datasheet/src/Chip.tsx +25 -0
  17. package/kits/datasheet/src/Heading.md +14 -0
  18. package/kits/datasheet/src/Heading.tsx +19 -0
  19. package/kits/datasheet/src/Instrument.md +21 -0
  20. package/kits/datasheet/src/Instrument.tsx +33 -0
  21. package/kits/datasheet/src/MonoBadge.md +16 -0
  22. package/kits/datasheet/src/MonoBadge.tsx +12 -0
  23. package/kits/datasheet/src/Rule.md +14 -0
  24. package/kits/datasheet/src/Rule.tsx +18 -0
  25. package/kits/datasheet/src/Stat.md +13 -0
  26. package/kits/datasheet/src/Stat.tsx +17 -0
  27. package/kits/datasheet/src/StatusCell.md +17 -0
  28. package/kits/datasheet/src/StatusCell.tsx +24 -0
  29. package/kits/datasheet/src/ValueChip.md +14 -0
  30. package/kits/datasheet/src/ValueChip.tsx +20 -0
  31. package/kits/datasheet/src/index.ts +25 -0
  32. package/kits/datasheet/src/styles.css +577 -0
  33. package/kits/datasheet/tsconfig.json +15 -0
  34. package/kits/manpage/.design-sync/config.json +14 -0
  35. package/kits/manpage/.design-sync/conventions.md +75 -0
  36. package/kits/manpage/README.md +39 -0
  37. package/kits/manpage/package.json +29 -0
  38. package/kits/manpage/src/Button.md +23 -0
  39. package/kits/manpage/src/Button.tsx +33 -0
  40. package/kits/manpage/src/Card.md +16 -0
  41. package/kits/manpage/src/Card.tsx +24 -0
  42. package/kits/manpage/src/Chip.md +15 -0
  43. package/kits/manpage/src/Chip.tsx +25 -0
  44. package/kits/manpage/src/CodeFrame.md +22 -0
  45. package/kits/manpage/src/CodeFrame.tsx +26 -0
  46. package/kits/manpage/src/EndpointRow.md +20 -0
  47. package/kits/manpage/src/EndpointRow.tsx +33 -0
  48. package/kits/manpage/src/FaqList.md +24 -0
  49. package/kits/manpage/src/FaqList.tsx +31 -0
  50. package/kits/manpage/src/Heading.md +19 -0
  51. package/kits/manpage/src/Heading.tsx +19 -0
  52. package/kits/manpage/src/LabelChip.md +23 -0
  53. package/kits/manpage/src/LabelChip.tsx +26 -0
  54. package/kits/manpage/src/Rule.md +15 -0
  55. package/kits/manpage/src/Rule.tsx +18 -0
  56. package/kits/manpage/src/Stat.md +16 -0
  57. package/kits/manpage/src/Stat.tsx +17 -0
  58. package/kits/manpage/src/TreeItem.md +17 -0
  59. package/kits/manpage/src/TreeItem.tsx +19 -0
  60. package/kits/manpage/src/index.ts +25 -0
  61. package/kits/manpage/src/styles.css +529 -0
  62. package/kits/manpage/tsconfig.json +15 -0
  63. package/package.json +2 -2
  64. package/plugins/sheleg-design/.claude-plugin/plugin.json +2 -2
  65. package/plugins/sheleg-design/commands/sheleg-design.md +2 -2
  66. package/plugins/sheleg-design/skills/sheleg-design/DESIGN_SYNC_BRIDGE.md +1 -1
  67. package/plugins/sheleg-design/skills/sheleg-design/MOBILE_SURFACES.md +2 -2
  68. package/plugins/sheleg-design/skills/sheleg-design/SKILL.md +6 -4
  69. package/plugins/sheleg-design/skills/sheleg-design/SURFACE_COMPOSITION.md +3 -3
  70. package/plugins/sheleg-design/skills/sheleg-design/styles/blueprint.md +11 -0
  71. package/plugins/sheleg-design/skills/sheleg-design/styles/datasheet.md +534 -0
  72. package/plugins/sheleg-design/skills/sheleg-design/styles/field-notes.md +18 -0
  73. package/plugins/sheleg-design/skills/sheleg-design/styles/instrument-console.md +15 -0
  74. package/plugins/sheleg-design/skills/sheleg-design/styles/manpage.md +456 -0
  75. package/plugins/sheleg-design/skills/sheleg-design/styles/scoreboard.md +13 -1
  76. package/plugins/sheleg-design/skills/sheleg-design/styles/showroom.md +13 -1
  77. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/datasheet.css +268 -0
  78. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/manpage.css +272 -0
  79. package/plugins/sheleg-design/skills/sheleg-design/styles/workbench.md +4 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,160 @@ All notable changes to this project are documented in this file. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions
5
5
  follow [SemVer](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [1.20.0] - 2026-08-12
8
+
9
+ A fifteenth style pack, whose display typeface costs zero bytes — and three
10
+ WCAG failures in the reference, one of them on the very element the design is
11
+ remembered by.
12
+
13
+ ### Added
14
+
15
+ - **`manpage`** — the fifteenth pack, extracted from `zernio.com` off the
16
+ server-rendered HTML of three pages and its two shipped stylesheets, which
17
+ declare 398 custom properties: the Tailwind v4 default ramps plus twelve
18
+ bespoke brand names (coral, cream, ink, charcoal, burgundy, each with a
19
+ `-muted` partner). Cream paper, a 48px display that never grows louder, a
20
+ 576px argument column narrower than most prose, coral label chips that are
21
+ real `<h2>`s, `└` tree glyphs in their own grid column, and one dark code
22
+ frame as the focal point. For developer products whose buyer reads code —
23
+ APIs, SDKs, CLIs, MCP servers. Widened contract, a two-theme token layer, a
24
+ full reference kit, and reciprocal forks written into `blueprint`,
25
+ `datasheet`, `field-notes`, `instrument-console`, `scoreboard`, `showroom`
26
+ and `workbench`.
27
+ - **The display face is the system monospace, and that is the whole identity.**
28
+ The reference loads exactly one webfont — a single variable Geist Sans — and
29
+ sets its headline, body, chips, code frames and FAQ in
30
+ `Menlo, Consolas, Monaco, "Liberation Mono", "Courier New", monospace`, which
31
+ is already on the reader's machine. No render-blocking request for the face
32
+ that carries the page, and no swap window on the headline. Substituting a
33
+ webfont mono is banned in the pack: it costs a request to look less native.
34
+ - **The section heading is a chip and the chip is a real heading.** `LabelChip`
35
+ wraps its span in an `<h2>`, which is why the reference keeps a clean outline
36
+ — one `h1`, one `h2` per section — while reading as a printed specification.
37
+ - **`FaqList` is a `<dl>` that never collapses.** The reference ships zero
38
+ `<details>` and zero `<summary>` on its FAQ: every answer is flat text in the
39
+ DOM, paired with its question, extractable without running JavaScript. The
40
+ component has no `collapsed` prop and will not get one.
41
+
42
+ ### Fixed
43
+
44
+ Four corrections to the reference, every replacement a colour it already ships:
45
+
46
+ - **The white button label fails AA.** `Start for Free` is white on coral at
47
+ **4.16:1**, on both the hero and the closing CTA. The fill is kept — the coral
48
+ button *is* the identity — and the label darkens to `--on-action` (ink) at
49
+ **4.55:1**. `--action-strong` is the reference's burgundy, carrying white at
50
+ 13.34:1.
51
+ - **The signature element is the least readable thing on the page.** The section
52
+ chip paints 12px coral on a coral/8 wash: **3.24:1**, worse than coral on bare
53
+ cream because the wash lifts the field. The wash and edge are kept so the chip
54
+ looks identical; the label becomes `--accent-ink` at **10.40:1**.
55
+ - **The live-status green fails AA at 2.82:1.** `green-600` carries the credit
56
+ balance, the `online` badge and both weekly counters. Its own ramp cannot be
57
+ stepped into a legal set — `green-700` still misses at 4.35:1 and `green-800`
58
+ clears AA but separates by only 3.9 under dichromacy — so success takes
59
+ emerald-800, a ramp the reference also ships in full.
60
+ - **One reduced-motion gate out of eight animations.** The reference gates its
61
+ 40s logo marquee behind `motion-safe:` and leaves the hero blur-in, every
62
+ section rise, `fadeInScale`, `slideInRight`, `pulse`, `ping` and a **1.1s
63
+ infinite `waveform`** running for a reader who asked for stillness. The pack
64
+ collapses the whole surface, and infinite motion **stops** rather than
65
+ shortens.
66
+
67
+ ### Changed
68
+
69
+ - `test/floors.json` raised: `validate.py` 1647 → 1788, `validate_palette.py`
70
+ 716 → 791, `sloplint.py` 366 → 422.
71
+ - **The stated-ratio checker earned its keep twice on this pack**, catching
72
+ `--ink-strong` claimed at 18.98:1 against a computed 19.44 and
73
+ `--on-action-strong` at 6.71:1 against 7.76 — both authored by hand, both
74
+ wrong, neither visible on inspection.
75
+ - `SURFACE_COMPOSITION.md`: three counts corrected by measurement — the accent
76
+ resolves as `--accent` in **thirteen** packs, `--brand` in `field-notes` and
77
+ `--cta` in `orchard`. **B-016 stays open**: none of the three reaches a check,
78
+ because `in thirteen,` is not followed by a counted noun. This is the third
79
+ release in which they were fixed by hand.
80
+ - `plugins/sheleg-design/.claude-plugin/plugin.json` said **thirteen** style
81
+ packs while fourteen shipped. `validate_counted_claims()` did not catch it:
82
+ its pattern wants `<number> [pluggable|locked] style packs` and the manifest
83
+ wrote `pluggable visual style packs`, so the intervening adjective hid a stale
84
+ count from the gate that exists to find them. Corrected to fifteen; the
85
+ pattern gap is the same class as B-016.
86
+
87
+ ## [1.19.0] - 2026-08-12
88
+
89
+ A fourteenth style pack, and the Refero style card it started from was wrong in
90
+ four measurable places.
91
+
92
+ ### Added
93
+
94
+ - **`datasheet`** — the fourteenth pack, extracted from `fingerprint.com` off its
95
+ live computed styles and its shipped stylesheet, which declares 140 custom
96
+ properties including ten-step ramps for nine hues. An off-white spec sheet, one
97
+ vivid orange, Inter over JetBrains Mono, a concentric radius family from 16 down
98
+ to 2 — and a **live instrument ruled out of hairlines at radius 0 which re-skins
99
+ itself dark when it detects the reader is hiding**. For B2B SaaS whose product is
100
+ a verdict about the visitor, the request or the device: fraud and bot detection,
101
+ device intelligence, identity, API products sold on their payload. Widened
102
+ contract, a two-theme token layer, a full reference kit, and reciprocal forks
103
+ written into `field-notes`, `instrument-console`, `showroom`, `blueprint` and
104
+ `scoreboard`.
105
+ - **The dark half is a state, not a theme.** `[data-state="alarm"]` — the token
106
+ `--dash-dark` appears in 97 rules on the reference and every one of them is an
107
+ incognito selector; 134 rules in total re-skin the instrument when it detects
108
+ evasion. Wiring that surface to a user preference is banned in the pack, because
109
+ it destroys the only idea the pack has.
110
+
111
+ ### Fixed
112
+
113
+ - **`validate_counted_claims()` did not read the three manifests, and both carried
114
+ a stale count.** `.claude-plugin/marketplace.json` said *"twelve pluggable style
115
+ packs"* above a list of thirteen for two releases, and `package.json` said
116
+ *"thirteen"* on the day the fourteenth landed. Names in those files were already
117
+ checked; the number beside the names was not, because the source list was
118
+ all-markdown plus two scripts. The list now includes both plugin manifests and
119
+ `package.json`, watched saying no against a planted `eleven` in the real file and
120
+ again as a permanent self-test plant that derives its wrong number from whatever
121
+ the manifest currently claims.
122
+ - **Four corrections to the reference, recorded rather than applied silently.** Its
123
+ primary button sets white on `--orange-7` at **3.32:1**, so the pack's resting
124
+ fill moves one ramp step to `--orange-8` (5.34:1) and hovers to `--orange-9`
125
+ (9.02:1) — no colour invented, and the darkening direction kept. Its 8px mono
126
+ badge is set in `--gray-6` at **2.51:1** and the pack refuses that ink. Its `h1`
127
+ is pure black while its `body` is `#141415`, and the pack ships one ink. Its
128
+ `prefers-reduced-motion` block covers one group of hero animations out of roughly
129
+ twenty keyframe sets, and the token layer collapses the whole surface.
130
+ - **Eleven defects in the new pack, found by its own routing scenario and fixed
131
+ before the tag.** T24 ran both branches in fresh contexts — the positive branch
132
+ chose `datasheet`, the negative stayed on `field-notes` — and each was asked to
133
+ read its chosen pack and report defects. The sharpest: in the alarm state
134
+ `--danger` on its own tint measured **4.44:1**, in the one cell that state exists
135
+ to render (the tint moves to `--pink-10`, 6.24:1); and the focus ring at
136
+ `--accent` measured **2.85:1** on `--accent-wash`, the surface the pack itself
137
+ mandates for a selected cell (a new `--focus-color` is `--accent-deep` on paper).
138
+ Also fixed: an accent job list that contradicted the pack's own ban, a button
139
+ border rule with no token behind it, 54 of 118 token declarations carrying
140
+ neither MEASURED nor SELECTED, a "hard floor" argument that sat exactly on the
141
+ floor, a duration measured against the wrong ceiling, an empty state using an ink
142
+ the palette table forbids for content, and two type values outside the ramp. Full
143
+ table in `test/scenarios.md` under T24.
144
+ - **`SKILL.md` said "Six of the fourteen … The other seven answer all four."** Six
145
+ plus seven is thirteen against a fourteen-row table, and the pack left out of the
146
+ sentence was the one this release adds. It was this release's own count edit that
147
+ did it — **and the identical defect was found by a scenario agent in the previous
148
+ pack release**, fixed then as an instance. `validate_contract_split()` now derives
149
+ all three numbers from the table, watched saying no against a planted remainder
150
+ and shipped with a permanent self-test plant that reads whatever the paragraph
151
+ currently claims.
152
+ - **`SURFACE_COMPOSITION.md` said the accent role resolves to `--accent` in ten
153
+ packs.** True at twelve, silently wrong at thirteen, twelve at fourteen. Fixed by
154
+ hand and filed as **B-016**, because the phrase reaches no check: `in ten,` is
155
+ not followed by a counted noun.
156
+
157
+ ### Changed
158
+
159
+ - Ratchet floors raised to **1647 / 716 / 366** from 1507 / 603 / 352.
160
+
7
161
  ## [1.18.0] - 2026-08-12
8
162
 
9
163
  ### Changed
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 **thirteen locked style
14
+ decides whether to animate before it decides how, and **fifteen 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
 
@@ -58,6 +58,8 @@ into the cinematic layer, and says so in its own *Motion flavor* section.
58
58
  | `maquette` | near-black table, cream ink and cream axonometric models, mono block labels, pale aqua that works as text, one offset shadow | enterprise data infrastructure sold to an architecture buyer |
59
59
  | `scoreboard` | warm paper and warm near-black ink, 2–3px radii, an ink primary button, one hot orange that only ever marks, and a dark ledger of dotted-leader rows whose numbers are set in an aliased pixel face | products whose argument is an accumulating number — ads and SEO operators, growth tools, revenue dashboards sold on results |
60
60
  | `cyclorama` | a pale field cycling through six pastel stops on a 32s loop under near-black ink that never moves with it, a monospaced typewriter serif over mono, one orange used only as a fill, a particle organ that holds then redeploys, no shadows anywhere | enterprise AI transformation, applied-AI services, technical consultancies — where what is sold is a change of state and there is no screenshot worth showing |
61
+ | `datasheet` | an off-white spec sheet ruled with dashed page guides, a live instrument built from hairline cells at radius zero, one vivid orange, Inter over JetBrains Mono, concentric radii from 16 to 2, and a dark alarm state the instrument enters when it detects the reader is hiding | B2B SaaS whose product is a verdict about the visitor, the request or the device — fraud and bot detection, device intelligence, identity and verification, API products sold on their payload |
62
+ | `manpage` | cream paper under the reader's own system monospace — zero webfont bytes for the display face — a 48px display that never grows louder, a 576px argument column, coral label chips that are real `<h2>`s, `└` tree glyphs in their own grid column, and one dark code frame as the focal point | developer products whose buyer reads code — APIs, SDKs, CLIs, MCP servers, developer infrastructure, where the honest hero is the call itself |
61
63
 
62
64
  Each pack locks palette, type, texture, motion tokens, signature motifs and
63
65
  bans — and ships a `tokens/<pack>.css` to copy verbatim, so the agent never
@@ -132,7 +134,7 @@ skills.
132
134
  | `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 |
133
135
  | `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 |
134
136
  | `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 |
135
- | `styles/*.md` | The thirteen style packs — palette, type, texture, motion tokens, motifs, bans, and the traps each one carries |
137
+ | `styles/*.md` | The fifteen style packs — palette, type, texture, motion tokens, motifs, bans, and the traps each one carries |
136
138
  | `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) |
137
139
  | `styles/STYLE_PACK_TEMPLATE.md` | The pack contract as a skeleton, so a new style is authored against the same headings rather than improvised |
138
140
 
@@ -203,7 +205,7 @@ cd ./ds-workbench && npm install && npm run build
203
205
  then `/design-sync` in that directory, from Claude Code. Three layers cross: the
204
206
  pack's **bans** as the design system's own README, `styles.css` built from
205
207
  `tokens/<pack>.css` verbatim, and the components — a six-name spine that is
206
- identical in all thirteen kits, so switching packs swaps identity rather than API,
208
+ identical in all fifteen kits, so switching packs swaps identity rather than API,
207
209
  plus each pack's signature parts. **Motion does not cross**, exactly as it does
208
210
  not cross into Figma: a kit is the static half of a pack, and saying so is what
209
211
  stops an agent inventing motion to fill the silence.
@@ -249,7 +251,7 @@ a pack's four widened sections used to make two gates *quieter* and still green.
249
251
  One honest limit: the npx installer is checked by asserting its runtime bundle
250
252
  walker exists, not by reading a file list — it has none by design. What proves
251
253
  it ships the right files is CI, which installs the bundle through **both**
252
- installers and `diff -r`s the result against the source, then builds all thirteen
254
+ installers and `diff -r`s the result against the source, then builds all fifteen
253
255
  kits.
254
256
 
255
257
  `test/scenarios.md` (T1–T19) is the behavioral harness: fresh subagents given a
package/bin/cli.js CHANGED
@@ -234,7 +234,7 @@ ${c("bold", "What it installs")}
234
234
  DESIGN_SYNC_BRIDGE.md the Claude Design contract (what a pack sends, and
235
235
  what does not cross)
236
236
  AI_PRODUCT_PATTERNS.md chat / agent / streaming surfaces (honest state)
237
- styles/ thirteen style packs — instrument-console (dark console),
237
+ styles/ fifteen style packs — instrument-console (dark console),
238
238
  editorial-luxury (warm editorial), workbench (light/dark
239
239
  product UI), briefing-room (dark 16:9 presentation deck),
240
240
  atrium (warm cream consumer health), orchard (friendly
@@ -243,7 +243,11 @@ ${c("bold", "What it installs")}
243
243
  (the product as the exhibit), blueprint (a drawing sheet,
244
244
  zero radius), prism (one iridescent wash over mono body),
245
245
  maquette (cream axonometric models on a dark table),
246
- scoreboard (warm paper, pixel numerals, a dark ledger) —
246
+ scoreboard (warm paper, pixel numerals, a dark ledger),
247
+ datasheet (an off-white spec sheet whose live instrument
248
+ goes dark when it catches the reader hiding),
249
+ manpage (a developer landing page set in the reader's
250
+ own system monospace, cream paper, coral label chips) —
247
251
  plus a ready-made token CSS per pack and
248
252
  STYLE_PACK_TEMPLATE.md for authoring more
249
253
  `);
@@ -325,7 +329,7 @@ function main() {
325
329
  ` ${c("dim", "SKILL.md")} the agent skill\n` +
326
330
  ` ${c("dim", "SHELEG_DESIGN.md")} the full reference\n` +
327
331
  ` ${c("dim", "MOTION_DOCTRINE.md")} whether to animate at all — read before any animation\n` +
328
- ` ${c("dim", "styles/")} style packs + token CSS (instrument-console / editorial-luxury / workbench / briefing-room / atrium / orchard / field-notes / cyclorama / showroom / blueprint / prism / maquette / scoreboard)\n\n` +
332
+ ` ${c("dim", "styles/")} style packs + token CSS (instrument-console / editorial-luxury / workbench / briefing-room / atrium / orchard / field-notes / cyclorama / showroom / blueprint / prism / maquette / scoreboard / datasheet / manpage)\n\n` +
329
333
  `Your Cursor / Claude agent can now discover the skill and build\n` +
330
334
  `cinematic, scroll-driven pages — or style product UI (dashboards, admin,\n` +
331
335
  `internal tools) from a standalone pack — on its principles.\n\n` +
@@ -30,7 +30,13 @@ static iridescent wash over mono body copy; maquette — enterprise data
30
30
  infrastructure as a cream axonometric model on a near-black table;
31
31
  scoreboard — warm paper and a dark ledger of dotted-leader rows whose
32
32
  numbers are set in an aliased pixel face, for a product whose argument is
33
- an accumulating number);
33
+ an accumulating number; datasheet — an off-white spec
34
+ sheet whose focal element is a live instrument ruled at radius zero and
35
+ which re-skins itself dark when it detects the reader is hiding, for B2B
36
+ SaaS whose product is a verdict about the visitor or the device;
37
+ manpage — a developer landing page set in the reader's own system
38
+ monospace on cream paper, coral label chips that are real headings and a
39
+ dark code frame as the argument, for APIs, SDKs and CLIs);
34
40
  otherwise follow the contract below (self-contained on purpose).
35
41
 
36
42
  ## Whether to animate at all — before how
@@ -0,0 +1,14 @@
1
+ {
2
+ "pkg": "@sheleg-design/datasheet",
3
+ "globalName": "ShelegDatasheet",
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,72 @@
1
+ # Datasheet — the contract this design system ships under
2
+
3
+ **Register.** Choose Datasheet for **B2B SaaS whose product is a verdict about
4
+ the visitor, the request or the device**: fraud and bot detection, device
5
+ intelligence, identity and verification, anti-abuse, payment risk, API products
6
+ sold on the payload they return. The page is a spec sheet, and the specimen is the
7
+ reader — the instrument shows *their* visitor id, *their* city, *their* IP, and a
8
+ verdict reached seconds ago. The fork people get wrong is against `field-notes`,
9
+ which shares the off-white paper, the hairlines and the one warm accent: there the
10
+ small mono type annotates a **source** so a claim can be checked (*how do you
11
+ know?*); here it carries a **reading** about the reader (*what did you get?*).
12
+ Build every screen against `var(--…)` and never a literal.
13
+
14
+ **The accent is a mark and a fill, not a voice.** `--accent` measures 3.17:1 on
15
+ the field: enough for a non-text mark and for large text at 24px and above, not
16
+ enough for anything at body size. Its jobs are one word of a display headline, the
17
+ mono visitor id, a 2px focus ring, a tick, and a wash on a selected cell. Where an
18
+ orange must carry a word at body size, use `--accent-deep` at 5.11:1. The primary
19
+ button fills with `--action` — one ramp step deeper than the brand orange, because
20
+ white on the brand step measures 3.32:1 and fails AA — and darkens to
21
+ `--action-hover` on press.
22
+
23
+ **The instrument is not a card, and its cells never round.** A card is
24
+ `--surface` at `--r-card` with the inset elevation pair. The instrument is a grid
25
+ whose walls are 1px `--rule` at `--r-cell`, which is **0**. Rounding those cells is
26
+ the single edit that turns this system into a generic SaaS page. Padding is
27
+ `--pad-cell`; every cell is a 9px uppercase label over an 11px value, and machine
28
+ output — ids, IPs, hashes, timestamps — takes the mono face.
29
+
30
+ **Radii are concentric and the arithmetic is real.** 16 outside, 8 inside, 12 on
31
+ cards, 6 on buttons, 4 on controls, 2 on a chip, 0 on the instrument. An inner
32
+ radius is the outer radius minus the padding between them: the frame carries
33
+ `--grid` of padding, so its shell resolves to `--r-inner`. Never the same radius
34
+ twice in a nest.
35
+
36
+ **Elevation is inset, never dropped.** Cards and the frame carry the measured
37
+ two-line inset — a pale top edge, a darker bottom one. Nothing casts a downward
38
+ shadow, nothing lifts on hover, nothing scales.
39
+
40
+ **Status is never by colour alone.** Every status cell tints its whole background
41
+ with the matching `--*-weak` and **writes the verdict out**. The four statuses do
42
+ not separate under dichromacy — success and warning are 3.7 apart under
43
+ deuteranopia against a floor of 8 — so the tint is emphasis and the word is the
44
+ meaning. No bare dots, no traffic lights, and never a hue swap to signal disabled:
45
+ a disabled cell that changes colour reads as a different verdict. Disabled is 0.7
46
+ opacity with pointer events off.
47
+
48
+ **The dark surface is a state, not a theme.** `[data-state="alarm"]` exists
49
+ because the instrument **detected** something — on the reference, that the reader
50
+ is in incognito. Never wire it to a user preference, a toggle or
51
+ `prefers-color-scheme`; that destroys the one idea this system has. In the alarm
52
+ state the accent steps lighter, text on the accent flips to ink, and the status set
53
+ is selected rather than measured, danger from the pink ramp because a red cannot
54
+ sit beside an orange accent on a dark field.
55
+
56
+ **Type.** Two families only: Inter and JetBrains Mono. No serif anywhere. The
57
+ display weight is **500, not 600** — at 48px with -0.0625em tracking, 500 reads as
58
+ engineered and 600 reads as an advertisement. Tracking runs both ways: negative
59
+ and steep above 36px, positive and wide below 12px. A 9px label at neutral tracking
60
+ is the fastest way to make this system look like a generic admin theme.
61
+
62
+ **Motion.** One curve, one measured base duration (`--dur-base`, the reference's
63
+ own single duration token), and one measured stagger of `--stagger` between the
64
+ words of a display headline. Nothing exceeds 300ms. Colour and opacity only —
65
+ never a layout property, and never `ease-in`. Under
66
+ `prefers-reduced-motion: reduce` every duration and the stagger go to zero and the
67
+ headline arrives already in place.
68
+
69
+ **Never fabricate a reading.** If the instrument cannot show live data, it shows a
70
+ labelled last-known one — the reference's own badge reads *this is a demo,
71
+ production accuracy will be higher*. A screen in this system that invents an IP
72
+ address has told its only lie in its loudest element.
@@ -0,0 +1,16 @@
1
+ # @sheleg-design/datasheet
2
+
3
+ The React reference kit for the SHELEG **Datasheet** style pack — an off-white spec
4
+ sheet, a live instrument built from hairline cells at radius zero, and a dark alarm
5
+ state the instrument enters when it detects the reader is hiding.
6
+
7
+ It is generated from the pack, not authored beside it: `src/styles.css` opens with
8
+ `styles/tokens/datasheet.css` byte for byte, and the rules the design agent must
9
+ obey are in [`.design-sync/conventions.md`](./.design-sync/conventions.md).
10
+
11
+ ```bash
12
+ npm install && npm run build
13
+ ```
14
+
15
+ Then run `/design-sync` in Claude Code from this directory to push it to
16
+ claude.ai/design.
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@sheleg-design/datasheet",
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,17 @@
1
+ ---
2
+ category: Actions
3
+ ---
4
+
5
+ One accent fill per view, and in this pack that fill is `--action` — the
6
+ reference's own orange one ramp step deeper than its brand orange, because white
7
+ on the brand step measures 3.32:1 and does not clear AA. `secondary` is the
8
+ 1px-bordered outline in `--accent-deep`; `ghost` is the bare navigation control
9
+ that changes ink and nothing else. Hover moves fill, border and colour over
10
+ `--dur-base`; nothing lifts, scales or gains a shadow. Disabled drops to 0.7
11
+ opacity with pointer events off, measured off the reference.
12
+
13
+ ```tsx
14
+ <Button onClick={start}>Get started</Button>
15
+ <Button variant="secondary" onClick={contact}>Contact sales</Button>
16
+ <Button variant="ghost" size="sm" onClick={close}>Dismiss</Button>
17
+ ```
@@ -0,0 +1,33 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface ButtonProps {
4
+ /** `primary` is the pack's default fill — at most one accent action 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={['ds-btn', `ds-btn--${variant}`, `ds-btn--${size}`, className]
25
+ .filter(Boolean)
26
+ .join(' ')}
27
+ disabled={disabled}
28
+ onClick={onClick}
29
+ >
30
+ {children}
31
+ </button>
32
+ );
33
+ }
@@ -0,0 +1,15 @@
1
+ ---
2
+ category: Surfaces
3
+ ---
4
+
5
+ The bounded surface for prose and features: `--surface` at `--r-card` with the
6
+ measured inset pair for elevation and no border. It is deliberately **not** the
7
+ instrument — the instrument is a hairline grid at radius 0 and never becomes a
8
+ card, because rounding a reading is the edit that turns this pack into a generic
9
+ SaaS page. `meta` sits on the right of the title row in the label style.
10
+
11
+ ```tsx
12
+ <Card title="Account takeover" meta="12ms p95">
13
+ <Cell label="Signals" value="device, network, behaviour" />
14
+ </Card>
15
+ ```
@@ -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={['ds-card', className].filter(Boolean).join(' ')}>
15
+ {head && (
16
+ <div className="ds-card__head">
17
+ {title !== undefined && <h3 className="ds-card__title">{title}</h3>}
18
+ {meta !== undefined && <span className="ds-card__meta">{meta}</span>}
19
+ </div>
20
+ )}
21
+ <div className="ds-card__body">{children}</div>
22
+ </section>
23
+ );
24
+ }
@@ -0,0 +1,16 @@
1
+ ---
2
+ category: Data
3
+ ---
4
+
5
+ The pack's atom: a 9px uppercase label in `--ink-soft` above an 11px value in
6
+ `--ink`, inside a cell walled with 1px `--rule` at radius **0**. Padding is the
7
+ measured `10px 12px`. Set `mono` for anything a machine produced — an id, an IP,
8
+ a hash — because a proportional face makes two similar hashes look identical.
9
+
10
+ The value is never justified and never hyphenated: a broken IP address is a wrong
11
+ IP address.
12
+
13
+ ```tsx
14
+ <Cell label="Geolocation">1 location</Cell>
15
+ <Cell label="Visitor ID" mono>JzzA01Muat9b30Sh60KJ</Cell>
16
+ ```
@@ -0,0 +1,23 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface CellProps {
4
+ /** The 9px uppercase label. Always present: a value with no label is a number nobody can use. */
5
+ label: string;
6
+ children: ReactNode;
7
+ /** Set for machine output — ids, IPs, hashes, timestamps. */
8
+ mono?: boolean;
9
+ className?: string;
10
+ }
11
+
12
+ export function Cell({ label, children, mono = false, className }: CellProps) {
13
+ return (
14
+ <div className={['ds-cell', className].filter(Boolean).join(' ')}>
15
+ <span className="ds-cell__label">{label}</span>
16
+ <span className={['ds-cell__value', mono ? 'ds-cell__value--mono' : undefined]
17
+ .filter(Boolean)
18
+ .join(' ')}>
19
+ {children}
20
+ </span>
21
+ </div>
22
+ );
23
+ }
@@ -0,0 +1,16 @@
1
+ ---
2
+ category: Data
3
+ ---
4
+
5
+ The small static label: 11px in the data face at `--r-chip`, 1px `--rule`,
6
+ padding of 2 by 8. Chips name things that are already true — a region, a plan, a
7
+ version — and never stand in for a button. `tone="accent"` is for the one value
8
+ that is the screen's subject; `selected` fills with `--accent-wash` behind an
9
+ accent edge, which is how the reference marks its active summary cell. A chip
10
+ carrying a verdict is a `ValueChip`; a chip carrying an action is a `Button`.
11
+
12
+ ```tsx
13
+ <Chip>eu-central-1</Chip>
14
+ <Chip tone="accent">v3</Chip>
15
+ <Chip selected>Suspect only</Chip>
16
+ ```
@@ -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
+ 'ds-chip',
15
+ `ds-chip--${tone}`,
16
+ selected ? 'ds-chip--selected' : undefined,
17
+ className,
18
+ ]
19
+ .filter(Boolean)
20
+ .join(' ')}
21
+ >
22
+ {children}
23
+ </span>
24
+ );
25
+ }
@@ -0,0 +1,14 @@
1
+ ---
2
+ category: Foundations
3
+ ---
4
+
5
+ Three levels, all Inter at `--w-medium` — 500, not 600, which is the single
6
+ value a second-hand description of this reference gets wrong most often. Tracking
7
+ is negative and steep at the display size (-0.0625em at 48px) and relaxes with
8
+ the size. One word inside a heading may take `--accent` at `--w-semi`; weight and
9
+ colour arrive together, once, and never as a whole phrase.
10
+
11
+ ```tsx
12
+ <Heading level={1}>Identify every visitor</Heading>
13
+ <Heading level={2}>What the API returns</Heading>
14
+ ```
@@ -0,0 +1,19 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface HeadingProps {
4
+ /** 1 = page title, 2 = section, 3 = sub-head. */
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={['ds-heading', `ds-heading--${level}`, className].filter(Boolean).join(' ')}
15
+ >
16
+ {children}
17
+ </Tag>
18
+ );
19
+ }
@@ -0,0 +1,21 @@
1
+ ---
2
+ category: Signature
3
+ ---
4
+
5
+ The pack's signature element: the frame that reads the visitor's own data back to
6
+ them. `--r-frame` on `--surface`, 8px of padding so an inner shell resolves to
7
+ `--r-inner` (16 − 8 = 8, which the reference proves on itself), and a grid of
8
+ cells whose walls are hairlines at radius 0.
9
+
10
+ `alarm` is the whole idea. On the reference, 134 rules re-skin this frame dark
11
+ when it detects the reader is in incognito — the argument completes itself with no
12
+ copy. It is a **state, not a theme**: drive it from what was detected, never from
13
+ a preference or a media query. Build the light state first and completely.
14
+
15
+ ```tsx
16
+ <Instrument title="Hello, visitor JzzA01Muat9b30Sh60KJ" badge="This is real data">
17
+ <Cell label="Weekly visit summary">You visited 1 time</Cell>
18
+ <Cell label="IP address" mono>83.175.182.157</Cell>
19
+ <StatusCell label="Incognito" reading="success">Not detected</StatusCell>
20
+ </Instrument>
21
+ ```