sheleg-design-skill 1.19.0 → 1.21.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 (80) hide show
  1. package/CHANGELOG.md +158 -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/manpage/.design-sync/config.json +14 -0
  6. package/kits/manpage/.design-sync/conventions.md +75 -0
  7. package/kits/manpage/README.md +39 -0
  8. package/kits/manpage/package.json +29 -0
  9. package/kits/manpage/src/Button.md +23 -0
  10. package/kits/manpage/src/Button.tsx +33 -0
  11. package/kits/manpage/src/Card.md +16 -0
  12. package/kits/manpage/src/Card.tsx +24 -0
  13. package/kits/manpage/src/Chip.md +15 -0
  14. package/kits/manpage/src/Chip.tsx +25 -0
  15. package/kits/manpage/src/CodeFrame.md +22 -0
  16. package/kits/manpage/src/CodeFrame.tsx +26 -0
  17. package/kits/manpage/src/EndpointRow.md +20 -0
  18. package/kits/manpage/src/EndpointRow.tsx +33 -0
  19. package/kits/manpage/src/FaqList.md +24 -0
  20. package/kits/manpage/src/FaqList.tsx +31 -0
  21. package/kits/manpage/src/Heading.md +19 -0
  22. package/kits/manpage/src/Heading.tsx +19 -0
  23. package/kits/manpage/src/LabelChip.md +23 -0
  24. package/kits/manpage/src/LabelChip.tsx +26 -0
  25. package/kits/manpage/src/Rule.md +15 -0
  26. package/kits/manpage/src/Rule.tsx +18 -0
  27. package/kits/manpage/src/Stat.md +16 -0
  28. package/kits/manpage/src/Stat.tsx +17 -0
  29. package/kits/manpage/src/TreeItem.md +17 -0
  30. package/kits/manpage/src/TreeItem.tsx +19 -0
  31. package/kits/manpage/src/index.ts +25 -0
  32. package/kits/manpage/src/styles.css +529 -0
  33. package/kits/manpage/tsconfig.json +15 -0
  34. package/kits/pigeonhole/.design-sync/config.json +14 -0
  35. package/kits/pigeonhole/.design-sync/conventions.md +59 -0
  36. package/kits/pigeonhole/README.md +53 -0
  37. package/kits/pigeonhole/package.json +29 -0
  38. package/kits/pigeonhole/src/Button.md +20 -0
  39. package/kits/pigeonhole/src/Button.tsx +33 -0
  40. package/kits/pigeonhole/src/Card.md +16 -0
  41. package/kits/pigeonhole/src/Card.tsx +24 -0
  42. package/kits/pigeonhole/src/CategoryChip.md +44 -0
  43. package/kits/pigeonhole/src/CategoryChip.tsx +42 -0
  44. package/kits/pigeonhole/src/Chip.md +16 -0
  45. package/kits/pigeonhole/src/Chip.tsx +25 -0
  46. package/kits/pigeonhole/src/FaqList.md +11 -0
  47. package/kits/pigeonhole/src/FaqList.tsx +30 -0
  48. package/kits/pigeonhole/src/Heading.md +17 -0
  49. package/kits/pigeonhole/src/Heading.tsx +19 -0
  50. package/kits/pigeonhole/src/LabelledRow.md +27 -0
  51. package/kits/pigeonhole/src/LabelledRow.tsx +54 -0
  52. package/kits/pigeonhole/src/Rule.md +12 -0
  53. package/kits/pigeonhole/src/Rule.tsx +18 -0
  54. package/kits/pigeonhole/src/Stat.md +11 -0
  55. package/kits/pigeonhole/src/Stat.tsx +17 -0
  56. package/kits/pigeonhole/src/WashCard.md +17 -0
  57. package/kits/pigeonhole/src/WashCard.tsx +27 -0
  58. package/kits/pigeonhole/src/index.ts +23 -0
  59. package/kits/pigeonhole/src/styles.css +860 -0
  60. package/kits/pigeonhole/tsconfig.json +15 -0
  61. package/package.json +2 -2
  62. package/plugins/sheleg-design/.claude-plugin/plugin.json +2 -2
  63. package/plugins/sheleg-design/commands/sheleg-design.md +1 -1
  64. package/plugins/sheleg-design/skills/sheleg-design/DESIGN_SYNC_BRIDGE.md +1 -1
  65. package/plugins/sheleg-design/skills/sheleg-design/MOBILE_SURFACES.md +1 -1
  66. package/plugins/sheleg-design/skills/sheleg-design/SKILL.md +9 -5
  67. package/plugins/sheleg-design/skills/sheleg-design/SURFACE_COMPOSITION.md +3 -3
  68. package/plugins/sheleg-design/skills/sheleg-design/styles/blueprint.md +1 -0
  69. package/plugins/sheleg-design/skills/sheleg-design/styles/cyclorama.md +7 -0
  70. package/plugins/sheleg-design/skills/sheleg-design/styles/datasheet.md +2 -1
  71. package/plugins/sheleg-design/skills/sheleg-design/styles/field-notes.md +4 -0
  72. package/plugins/sheleg-design/skills/sheleg-design/styles/instrument-console.md +4 -0
  73. package/plugins/sheleg-design/skills/sheleg-design/styles/manpage.md +459 -0
  74. package/plugins/sheleg-design/skills/sheleg-design/styles/orchard.md +7 -0
  75. package/plugins/sheleg-design/skills/sheleg-design/styles/pigeonhole.md +503 -0
  76. package/plugins/sheleg-design/skills/sheleg-design/styles/scoreboard.md +2 -1
  77. package/plugins/sheleg-design/skills/sheleg-design/styles/showroom.md +11 -1
  78. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/manpage.css +272 -0
  79. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/pigeonhole.css +327 -0
  80. package/plugins/sheleg-design/skills/sheleg-design/styles/workbench.md +7 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,164 @@ 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.21.0] - 2026-08-12
8
+
9
+ A sixteenth style pack, whose eleven pastel hues are a filing scheme rather than a
10
+ mood — and a taxonomy that fails its own contrast floor eight times out of nine.
11
+
12
+ ### Added
13
+
14
+ - **`pigeonhole`** — the sixteenth pack, extracted from `getinboxzero.com` off the
15
+ server-rendered HTML of `/` (399,558 bytes), its two shipped stylesheets
16
+ (599,990 bytes, 152 custom properties) and then off **computed styles on the
17
+ live page** at 1440×900, 768×1024 and 390×844 — 912 rendered elements. A white
18
+ field ruled by hairlines, one blue that only ever appears as a two-stop
19
+ gradient, a display face that never passes weight 400, one italic word in the
20
+ headline, and nine categories in which a hue *is* the category, drawn from an
21
+ eleven-ramp pastel system. For products
22
+ whose job is to sort the reader's incoming mess into named categories — email
23
+ triage, ticket routing, digests, organisers, CRM inboxes. Widened contract, a
24
+ light-only token layer, a full reference kit, and reciprocal forks written into
25
+ `cyclorama`, `showroom`, `orchard`, `workbench` and `manpage`.
26
+ - **The signature element is a chip with two layers.** The outer carries the
27
+ deeper tint pair at radius 8px, the inner the paler pair at 7px with one pixel
28
+ between them — the one place in the reference where radius-by-subtraction
29
+ happens to hold exactly. `CategoryChip`'s label word is a **required** prop, not
30
+ an optional one, and the reason is measured: see below.
31
+ - **Nine category inks, eight of them derived.** The reference paints its chip
32
+ inks on tints of their own hue and eight of nine fail WCAG AA against those very
33
+ tints — `#49d1fa` at 1.53:1, `#d8a40c` at 1.65:1, `#e65707` at 2.71:1, `#17a34a`
34
+ at 2.72:1, `#c942b2` at 2.79:1, `#c94244` at 3.09:1, `#124dff` at 3.89:1,
35
+ `#6410ff` at 4.28:1. Only the neutral `#525252` clears it. Each is re-derived
36
+ along OKLab lightness with hue and chroma held, and each derivation is marked at
37
+ its declaration.
38
+ - **The label word is mandatory, and the number says why.** Darkening those inks
39
+ to clear 4.5:1 compresses them: the worst deuteranopic pair (Marketing against
40
+ Notification) falls from ΔE 4.42 to **1.24**, far under the palette gate's hard
41
+ floor of 10. Eleven hues cannot be simultaneously AA-compliant and mutually
42
+ distinguishable to a dichromatic reader, so the hue is declared a *redundant*
43
+ channel and the category tokens sit deliberately outside the gate's semantic
44
+ peer set — with the reason written at the declaration, so a future widening
45
+ reads it rather than assuming an oversight.
46
+ - **`LabelledRow`, `WashCard` and `FaqList`** join the six-component spine. A
47
+ wash card's shadow is mixed toward its own hue rather than toward black, which
48
+ is why the page reads coloured while the field stays white.
49
+
50
+ ### Fixed
51
+
52
+ - **The primary button's ramp is reversed against the reference.** White on its
53
+ upper gradient stop measures 5.04:1 and on its lower stop 3.29:1 — the label
54
+ passes at the top of the button and fails at the bottom of the same button. The
55
+ pack and the kit run the gradient light-to-dark so the worst case is the passing
56
+ colour.
57
+ - **The focused CTA gets a ring.** The reference computes `outline-style: none` on
58
+ its focused primary button with no compensating shadow. The kit ships 2px of
59
+ `--accent-strong` at 2px offset.
60
+ - **The lede ink.** `#848484` at 3.74:1, painted at 18px regular, replaced by the
61
+ reference's own `#6b7280` at 4.83:1.
62
+ - **Two counts that reached no check** are corrected in passing:
63
+ `SURFACE_COMPOSITION.md`'s accent-role tally (thirteen → fourteen, recounted
64
+ from the token layers rather than incremented) and the kit-count claims in
65
+ `README.md`. The class stays open on the board as B-016.
66
+ - **B-015 is closed.** `.tmp-fp-hero.png` leaves the tree and `.gitignore` gains
67
+ the `.tmp*` rule that would have stopped it entering.
68
+
69
+ ### Notes
70
+
71
+ - **Two claims from the screenshots were refuted by the DOM and are recorded
72
+ rather than dropped.** There is no rotation anywhere on the page — zero
73
+ elements carry a rotation term in `transform` or the individual `rotate`
74
+ property, at three viewports — so a scattered, tilted pile is explicitly not
75
+ this pack. And the before/after diptych is not built from DOM rows: the words
76
+ `Before` and `After` appear zero times in the served HTML and zero times in the
77
+ live DOM after a full scroll pass. The section is raster art, and the pack
78
+ specifies it as art direction with an aspect ratio rather than as a component.
79
+ - **No dark theme.** The reference's stylesheet carries a `.dark` block, but it
80
+ belongs to the application's stock shadcn slate theme and nothing on the
81
+ marketing page consumes it. Shipping an undocumented dark twin is a defect this
82
+ library already carries once (board B-018), so the pack is light only and says
83
+ so.
84
+
85
+ ## [1.20.0] - 2026-08-12
86
+
87
+ A fifteenth style pack, whose display typeface costs zero bytes — and three
88
+ WCAG failures in the reference, one of them on the very element the design is
89
+ remembered by.
90
+
91
+ ### Added
92
+
93
+ - **`manpage`** — the fifteenth pack, extracted from `zernio.com` off the
94
+ server-rendered HTML of three pages and its two shipped stylesheets, which
95
+ declare 398 custom properties: the Tailwind v4 default ramps plus twelve
96
+ bespoke brand names (coral, cream, ink, charcoal, burgundy, each with a
97
+ `-muted` partner). Cream paper, a 48px display that never grows louder, a
98
+ 576px argument column narrower than most prose, coral label chips that are
99
+ real `<h2>`s, `└` tree glyphs in their own grid column, and one dark code
100
+ frame as the focal point. For developer products whose buyer reads code —
101
+ APIs, SDKs, CLIs, MCP servers. Widened contract, a two-theme token layer, a
102
+ full reference kit, and reciprocal forks written into `blueprint`,
103
+ `datasheet`, `field-notes`, `instrument-console`, `scoreboard`, `showroom`
104
+ and `workbench`.
105
+ - **The display face is the system monospace, and that is the whole identity.**
106
+ The reference loads exactly one webfont — a single variable Geist Sans — and
107
+ sets its headline, body, chips, code frames and FAQ in
108
+ `Menlo, Consolas, Monaco, "Liberation Mono", "Courier New", monospace`, which
109
+ is already on the reader's machine. No render-blocking request for the face
110
+ that carries the page, and no swap window on the headline. Substituting a
111
+ webfont mono is banned in the pack: it costs a request to look less native.
112
+ - **The section heading is a chip and the chip is a real heading.** `LabelChip`
113
+ wraps its span in an `<h2>`, which is why the reference keeps a clean outline
114
+ — one `h1`, one `h2` per section — while reading as a printed specification.
115
+ - **`FaqList` is a `<dl>` that never collapses.** The reference ships zero
116
+ `<details>` and zero `<summary>` on its FAQ: every answer is flat text in the
117
+ DOM, paired with its question, extractable without running JavaScript. The
118
+ component has no `collapsed` prop and will not get one.
119
+
120
+ ### Fixed
121
+
122
+ Four corrections to the reference, every replacement a colour it already ships:
123
+
124
+ - **The white button label fails AA.** `Start for Free` is white on coral at
125
+ **4.16:1**, on both the hero and the closing CTA. The fill is kept — the coral
126
+ button *is* the identity — and the label darkens to `--on-action` (ink) at
127
+ **4.55:1**. `--action-strong` is the reference's burgundy, carrying white at
128
+ 13.34:1.
129
+ - **The signature element is the least readable thing on the page.** The section
130
+ chip paints 12px coral on a coral/8 wash: **3.24:1**, worse than coral on bare
131
+ cream because the wash lifts the field. The wash and edge are kept so the chip
132
+ looks identical; the label becomes `--accent-ink` at **10.40:1**.
133
+ - **The live-status green fails AA at 2.82:1.** `green-600` carries the credit
134
+ balance, the `online` badge and both weekly counters. Its own ramp cannot be
135
+ stepped into a legal set — `green-700` still misses at 4.35:1 and `green-800`
136
+ clears AA but separates by only 3.9 under dichromacy — so success takes
137
+ emerald-800, a ramp the reference also ships in full.
138
+ - **One reduced-motion gate out of eight animations.** The reference gates its
139
+ 40s logo marquee behind `motion-safe:` and leaves the hero blur-in, every
140
+ section rise, `fadeInScale`, `slideInRight`, `pulse`, `ping` and a **1.1s
141
+ infinite `waveform`** running for a reader who asked for stillness. The pack
142
+ collapses the whole surface, and infinite motion **stops** rather than
143
+ shortens.
144
+
145
+ ### Changed
146
+
147
+ - `test/floors.json` raised: `validate.py` 1647 → 1788, `validate_palette.py`
148
+ 716 → 791, `sloplint.py` 366 → 422.
149
+ - **The stated-ratio checker earned its keep twice on this pack**, catching
150
+ `--ink-strong` claimed at 18.98:1 against a computed 19.44 and
151
+ `--on-action-strong` at 6.71:1 against 7.76 — both authored by hand, both
152
+ wrong, neither visible on inspection.
153
+ - `SURFACE_COMPOSITION.md`: three counts corrected by measurement — the accent
154
+ resolves as `--accent` in **thirteen** packs, `--brand` in `field-notes` and
155
+ `--cta` in `orchard`. **B-016 stays open**: none of the three reaches a check,
156
+ because `in thirteen,` is not followed by a counted noun. This is the third
157
+ release in which they were fixed by hand.
158
+ - `plugins/sheleg-design/.claude-plugin/plugin.json` said **thirteen** style
159
+ packs while fourteen shipped. `validate_counted_claims()` did not catch it:
160
+ its pattern wants `<number> [pluggable|locked] style packs` and the manifest
161
+ wrote `pluggable visual style packs`, so the intervening adjective hid a stale
162
+ count from the gate that exists to find them. Corrected to fifteen; the
163
+ pattern gap is the same class as B-016.
164
+
7
165
  ## [1.19.0] - 2026-08-12
8
166
 
9
167
  A fourteenth style pack, and the Refero style card it started from was wrong in
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 **fourteen locked style
14
+ decides whether to animate before it decides how, and **sixteen 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
 
@@ -59,6 +59,8 @@ into the cinematic layer, and says so in its own *Motion flavor* section.
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
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 |
63
+ | `pigeonhole` | a white field ruled by hairlines, a display face that never passes weight 400 with exactly one italic word, and **nine categories in which a hue is the category**, drawn from an eleven-ramp pastel system — each rendered as a two-layer chip, 8px outside and 7px inside, whose label word is mandatory because the hue cannot carry the meaning alone | products whose job is to sort the reader's incoming mess into named categories — email triage, ticket routing, notification digests, file organisers, CRM inboxes |
62
64
 
63
65
  Each pack locks palette, type, texture, motion tokens, signature motifs and
64
66
  bans — and ships a `tokens/<pack>.css` to copy verbatim, so the agent never
@@ -133,7 +135,7 @@ skills.
133
135
  | `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 |
134
136
  | `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 |
135
137
  | `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 |
136
- | `styles/*.md` | The fourteen style packs — palette, type, texture, motion tokens, motifs, bans, and the traps each one carries |
138
+ | `styles/*.md` | The sixteen style packs — palette, type, texture, motion tokens, motifs, bans, and the traps each one carries |
137
139
  | `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) |
138
140
  | `styles/STYLE_PACK_TEMPLATE.md` | The pack contract as a skeleton, so a new style is authored against the same headings rather than improvised |
139
141
 
@@ -204,7 +206,7 @@ cd ./ds-workbench && npm install && npm run build
204
206
  then `/design-sync` in that directory, from Claude Code. Three layers cross: the
205
207
  pack's **bans** as the design system's own README, `styles.css` built from
206
208
  `tokens/<pack>.css` verbatim, and the components — a six-name spine that is
207
- identical in all fourteen kits, so switching packs swaps identity rather than API,
209
+ identical in all sixteen kits, so switching packs swaps identity rather than API,
208
210
  plus each pack's signature parts. **Motion does not cross**, exactly as it does
209
211
  not cross into Figma: a kit is the static half of a pack, and saying so is what
210
212
  stops an agent inventing motion to fill the silence.
@@ -250,7 +252,7 @@ a pack's four widened sections used to make two gates *quieter* and still green.
250
252
  One honest limit: the npx installer is checked by asserting its runtime bundle
251
253
  walker exists, not by reading a file list — it has none by design. What proves
252
254
  it ships the right files is CI, which installs the bundle through **both**
253
- installers and `diff -r`s the result against the source, then builds all fourteen
255
+ installers and `diff -r`s the result against the source, then builds all sixteen
254
256
  kits.
255
257
 
256
258
  `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/ fourteen style packs — instrument-console (dark console),
237
+ styles/ sixteen 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
@@ -245,7 +245,11 @@ ${c("bold", "What it installs")}
245
245
  maquette (cream axonometric models on a dark table),
246
246
  scoreboard (warm paper, pixel numerals, a dark ledger),
247
247
  datasheet (an off-white spec sheet whose live instrument
248
- goes dark when it catches the reader hiding) —
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),
251
+ pigeonhole (a white sorting wall whose nine pastel
252
+ categories are a filing scheme, one hue each) —
249
253
  plus a ready-made token CSS per pack and
250
254
  STYLE_PACK_TEMPLATE.md for authoring more
251
255
  `);
@@ -327,7 +331,7 @@ function main() {
327
331
  ` ${c("dim", "SKILL.md")} the agent skill\n` +
328
332
  ` ${c("dim", "SHELEG_DESIGN.md")} the full reference\n` +
329
333
  ` ${c("dim", "MOTION_DOCTRINE.md")} whether to animate at all — read before any animation\n` +
330
- ` ${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)\n\n` +
334
+ ` ${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 / pigeonhole)\n\n` +
331
335
  `Your Cursor / Claude agent can now discover the skill and build\n` +
332
336
  `cinematic, scroll-driven pages — or style product UI (dashboards, admin,\n` +
333
337
  `internal tools) from a standalone pack — on its principles.\n\n` +
@@ -33,7 +33,13 @@ numbers are set in an aliased pixel face, for a product whose argument is
33
33
  an accumulating number; datasheet — an off-white spec
34
34
  sheet whose focal element is a live instrument ruled at radius zero and
35
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);
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; pigeonhole — a
40
+ white sorting wall whose nine pastel categories are a taxonomy rather than
41
+ a mood, each a two-layer chip whose label word is mandatory, for products
42
+ that file the reader's incoming mess into named categories);
37
43
  otherwise follow the contract below (self-contained on purpose).
38
44
 
39
45
  ## Whether to animate at all — before how
@@ -0,0 +1,14 @@
1
+ {
2
+ "pkg": "@sheleg-design/manpage",
3
+ "globalName": "ShelegManpage",
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,75 @@
1
+ # Manpage — the contract this design system ships under
2
+
3
+ **Register.** Choose Manpage for **a developer product whose buyer reads code for
4
+ a living**: an API, an SDK, a CLI, a protocol, an MCP server, developer
5
+ infrastructure. The landing page *is* the documentation, set in the typeface of
6
+ the documentation — the honest hero is not a screenshot but six lines of a request.
7
+ The fork people get wrong is against `datasheet`, which shares the off-white
8
+ paper, the hairlines and the one warm orange-red: there the page is about **the
9
+ reader** and its mono carries a reading the product produced (*what did you get?*);
10
+ here the page is about **the product** and its mono is the whole body face, showing
11
+ a call the reader will write (*what will you type?*). Build every screen against
12
+ `var(--…)` and never a literal.
13
+
14
+ **The display face is free, and that is the identity.** One webfont ships — a
15
+ single variable Geist Sans — and everything visible is set in `--font-mono`, which
16
+ is the **system** monospace stack: Menlo, Consolas, Monaco. There is nothing to
17
+ download for the face that carries the whole page, no swap window on the headline,
18
+ and the slight variation between machines is the point — it reads as the terminal
19
+ rather than as art direction. Substituting a webfont mono costs a render-blocking
20
+ request to look less native. Do not.
21
+
22
+ **The accent is a mark, a fill and a wash — almost never a word.** `--accent`
23
+ measures 3.61:1 on the field: enough for a non-text mark and for large text at 24px
24
+ and above, not enough at body size. Where a word must be coral, use `--accent-ink`
25
+ at 11.59:1. The primary button keeps the coral fill — the coral button *is* the
26
+ identity — but its label is `--on-action`, which is ink at 4.55:1, because white on
27
+ coral measures 4.16:1 and fails AA. `--action-strong` is the burgundy that carries
28
+ white at 13.34:1.
29
+
30
+ **The section heading is a chip, and the chip is a real heading.** `LabelChip`
31
+ wraps its span in an `<h2>`. That single decision is why the page keeps a clean
32
+ outline — one `h1`, one `h2` per section — while reading as a printed specification.
33
+ An eyebrow that merely sits above a heading is not this component and does not earn
34
+ the identity.
35
+
36
+ **The width ladder is the layout.** The argument runs in `--measure-text` (576px),
37
+ narrower than most prose; the hero and its code frame take `--measure-hero`
38
+ (768px); only evidence widens — `--measure-proof` for the testimonial grid,
39
+ `--measure-foot` for the footer, `--measure-wall` for the logo wall. Below 896px
40
+ every step collapses to `--measure-text` and the rhythm carries the structure
41
+ instead.
42
+
43
+ **Radii stay small and nothing is a pill.** 2px on the label chip, 6px on a
44
+ control, 8px on a button, 12px on a card, 16px on a panel. A fully round control
45
+ breaks the printed-tag reading immediately.
46
+
47
+ **One hairline, no shadow.** `--lift-card` is a single 1px bottom line. Nothing
48
+ casts downward, nothing lifts on hover, nothing scales. The one glow that exists,
49
+ `--glow-accent`, is a glow and not an elevation.
50
+
51
+ **The 4px frame is load-bearing.** `body { padding: var(--frame) }` insets the
52
+ whole document from the window edge so every panel closes against a visible margin
53
+ of paper. It is the cheapest identity move in the system and the easiest to delete
54
+ by accident in a layout refactor — it has a token name so it has something worth
55
+ preserving.
56
+
57
+ **Status is never by colour alone.** Every status carries its word, exactly as the
58
+ reference does — `online`, `Done`, `GET`, `POST`. The light set clears both gate
59
+ floors (17.5 at full colour, 8.1 under dichromacy); the dark set clears the hard
60
+ floor at 10.1 and runs tight at 6.8 under dichromacy, which the word covers. Note
61
+ that `--warning` is a near-black brown by arithmetic rather than by taste: the
62
+ coral accent occupies the warning hue, and every amber that reads as a warning
63
+ collides either with the accent under dichromacy or with danger at full colour.
64
+
65
+ **Motion is entry only, and then the page holds still.** One `fadeInBlur` on the
66
+ headline (0.6s, `--ease-out`, after `--stagger`), one entrance per section on the
67
+ way down, and nothing moves again — no hover lift, no drifting gradient, no
68
+ counter that keeps counting. **No scroll clock, no scrubbing, no parallax:** this
69
+ is the calmest pack in the family. At `prefers-reduced-motion: reduce` every
70
+ duration and the stagger go to zero, the blur never applies, and infinite motion
71
+ **stops** rather than shortens.
72
+
73
+ **The FAQ does not open.** `FaqList` is a `<dl>` whose answers are always visible,
74
+ because a collapsed answer is an answer no machine can extract. There is no
75
+ `collapsed` prop and there will not be one.
@@ -0,0 +1,39 @@
1
+ # @sheleg-design/manpage
2
+
3
+ The React reference kit for the SHELEG **Manpage** style pack — cream paper under
4
+ the reader's own system monospace, a 48px display that never grows louder, coral
5
+ label chips that are real headings, and one dark code frame as the argument.
6
+
7
+ It is generated from the pack, not authored beside it: `src/styles.css` opens with
8
+ `styles/tokens/manpage.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
+ Then run `/design-sync` in Claude Code from this directory to push it to
16
+ claude.ai/design.
17
+
18
+ ## The spine, and this pack's five
19
+
20
+ `Button`, `Card`, `Chip`, `Stat`, `Heading` and `Rule` are identical in name, props
21
+ and types across every SHELEG kit — switching packs swaps identity, not API.
22
+
23
+ The five that are this pack's own:
24
+
25
+ | Component | What it is |
26
+ |---|---|
27
+ | `LabelChip` | the coral section tag that **is** an `<h2>` — the signature element |
28
+ | `CodeFrame` | the dark panel holding the call; scrolls, never reflows |
29
+ | `TreeItem` | a `└` glyph in its own grid column, never a text prefix |
30
+ | `FaqList` | a `<dl>` whose answers are always visible, so a machine can quote them |
31
+ | `EndpointRow` | a method badge, a path in mono, one line of prose |
32
+
33
+ ## The two things most likely to be broken
34
+
35
+ **The 4px body frame.** `body { padding: var(--frame) }` is what makes the page
36
+ read as a sheet laid on a desk. Deleting it produces no error and no failing test.
37
+
38
+ **The label chip's ink.** It is `--accent-ink`, not `--accent`. Coral on the chip's
39
+ own wash measures 3.24:1; burgundy measures 10.40:1, and the chip looks the same.
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@sheleg-design/manpage",
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
+ One accent fill per view. In this pack the fill is `--action` — the reference's
6
+ own coral, kept because the coral button *is* the identity — but the label is
7
+ `--on-action`, which is ink rather than white. White on coral measures 4.16:1 and
8
+ does not clear AA at the 16px semibold the reference ships; ink clears at 4.55:1.
9
+ Where a white label is non-negotiable, hover onto `--action-strong` (burgundy,
10
+ 13.34:1).
11
+
12
+ `secondary` is the white 1px-bordered control the reference pairs with it — on a
13
+ developer page that is usually an OAuth continue. `ghost` changes ink and nothing
14
+ else.
15
+
16
+ Transitions `background-color` over `--dur-instant` and nothing more: no lift, no
17
+ scale, no shadow bloom.
18
+
19
+ ```tsx
20
+ <Button onClick={start}>Start for Free</Button>
21
+ <Button variant="secondary" onClick={google}>Continue with Google</Button>
22
+ <Button variant="ghost" size="sm" onClick={skip}>I'll do this later</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={['mp-btn', `mp-btn--${variant}`, `mp-btn--${size}`, className]
25
+ .filter(Boolean)
26
+ .join(' ')}
27
+ disabled={disabled}
28
+ onClick={onClick}
29
+ >
30
+ {children}
31
+ </button>
32
+ );
33
+ }
@@ -0,0 +1,16 @@
1
+ ---
2
+ category: Surfaces
3
+ ---
4
+
5
+ `--surface` on `--rule`, radius `--r-card`, padding `--pad-card`, and a single 1px
6
+ bottom line for lift. The reference uses it for testimonials at
7
+ `--measure-proof`, three across.
8
+
9
+ **No hover state.** The card does not lift, tint or shift on this pack — it is a
10
+ quotation on paper, and paper does not respond to a cursor.
11
+
12
+ ```tsx
13
+ <Card title="Dev Singh" meta="Founder, ad-attribution SaaS">
14
+ I love the speed and quality here.
15
+ </Card>
16
+ ```
@@ -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={['mp-card', className].filter(Boolean).join(' ')}>
15
+ {head && (
16
+ <div className="mp-card__head">
17
+ {title !== undefined && <h3 className="mp-card__title">{title}</h3>}
18
+ {meta !== undefined && <span className="mp-card__meta">{meta}</span>}
19
+ </div>
20
+ )}
21
+ <div className="mp-card__body">{children}</div>
22
+ </section>
23
+ );
24
+ }
@@ -0,0 +1,15 @@
1
+ ---
2
+ category: Data
3
+ ---
4
+
5
+ The generic tag. For the section-heading chip that carries this pack's identity,
6
+ use `LabelChip` instead — it wraps a real `<h2>` and this one does not.
7
+
8
+ `neutral` is `--surface-2` under `--ink-soft`. `accent` is `--accent-wash` under
9
+ `--accent-ink`, never under `--accent`: coral on its own wash is 3.24:1.
10
+ `selected` adds a `--accent-edge` border.
11
+
12
+ ```tsx
13
+ <Chip>CASE STUDY</Chip>
14
+ <Chip tone="accent" selected>Free credits</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
+ 'mp-chip',
15
+ `mp-chip--${tone}`,
16
+ selected ? 'mp-chip--selected' : undefined,
17
+ className,
18
+ ]
19
+ .filter(Boolean)
20
+ .join(' ')}
21
+ >
22
+ {children}
23
+ </span>
24
+ );
25
+ }
@@ -0,0 +1,22 @@
1
+ ---
2
+ category: Surfaces
3
+ ---
4
+
5
+ The focal element of the hero, and the only dark surface on the light theme. A
6
+ `--r-panel` frame with three window dots, a filename, a language label and a copy
7
+ affordance — because the page's argument is the call, not a screenshot of a
8
+ dashboard.
9
+
10
+ Two rules it does not bend. It **scrolls horizontally and never reflows**: a
11
+ wrapped code sample is a wrong code sample. And it **never shrinks its type** below
12
+ `--t-mono` (12px) on small screens — the sample stops being evidence the moment it
13
+ becomes unreadable.
14
+
15
+ Place it at `--measure-hero` and let the fold crop it. The crop is what signals
16
+ there is more.
17
+
18
+ ```tsx
19
+ <CodeFrame filename="zernio.ts" language="TypeScript">
20
+ {snippet}
21
+ </CodeFrame>
22
+ ```
@@ -0,0 +1,26 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface CodeFrameProps {
4
+ /** The filename in the header row — `zernio.ts`, `main.py`. */
5
+ filename: string;
6
+ /** The language label on the right of the header row. */
7
+ language?: string;
8
+ /** Rendered as-is; highlight upstream. Never reflowed, never resized. */
9
+ children: ReactNode;
10
+ className?: string;
11
+ }
12
+
13
+ export function CodeFrame({ filename, language, children, className }: CodeFrameProps) {
14
+ return (
15
+ <figure className={['mp-code', className].filter(Boolean).join(' ')}>
16
+ <figcaption className="mp-code__head">
17
+ <span className="mp-code__dots" aria-hidden="true">
18
+ <i /><i /><i />
19
+ </span>
20
+ <span className="mp-code__name">{filename}</span>
21
+ {language !== undefined && <span className="mp-code__lang">{language}</span>}
22
+ </figcaption>
23
+ <pre className="mp-code__body"><code>{children}</code></pre>
24
+ </figure>
25
+ );
26
+ }
@@ -0,0 +1,20 @@
1
+ ---
2
+ category: Data
3
+ ---
4
+
5
+ The "what you can do" row: a method badge, a path in mono, and one line of prose.
6
+ It is how this pack lists capability without a feature grid — each row is a thing
7
+ you can call, so the list reads as an index rather than as marketing.
8
+
9
+ The badge is a status token on its own tint at `--r-chip`: `--info` for writes,
10
+ `--success` for reads. The method word is always spelled out, never colour alone —
11
+ `GET` and `POST` separate by 29.2 at full colour but only 16.3 under dichromacy,
12
+ and a reader who cannot tell them apart still has to be able to read them.
13
+
14
+ `selected` marks the row the surrounding copy is talking about, using
15
+ `--accent-wash`. At most one per list.
16
+
17
+ ```tsx
18
+ <EndpointRow method="GET" path="/connect/{platform}">One OAuth flow for every platform.</EndpointRow>
19
+ <EndpointRow method="POST" path="/posts" selected>One call, 16 platforms.</EndpointRow>
20
+ ```