sheleg-design-skill 1.50.3 → 1.52.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 (55) hide show
  1. package/CHANGELOG.md +149 -0
  2. package/README.md +7 -5
  3. package/bin/cli.js +5 -2
  4. package/cursor/rules/sheleg-design.mdc +8 -1
  5. package/kits/deskmate/.design-sync/config.json +14 -0
  6. package/kits/deskmate/.design-sync/conventions.md +52 -0
  7. package/kits/deskmate/README.md +36 -0
  8. package/kits/deskmate/package.json +29 -0
  9. package/kits/deskmate/src/Button.md +19 -0
  10. package/kits/deskmate/src/Button.tsx +33 -0
  11. package/kits/deskmate/src/Card.md +16 -0
  12. package/kits/deskmate/src/Card.tsx +24 -0
  13. package/kits/deskmate/src/Chip.md +16 -0
  14. package/kits/deskmate/src/Chip.tsx +25 -0
  15. package/kits/deskmate/src/Empty.md +18 -0
  16. package/kits/deskmate/src/Empty.tsx +19 -0
  17. package/kits/deskmate/src/Eyebrow.md +16 -0
  18. package/kits/deskmate/src/Eyebrow.tsx +20 -0
  19. package/kits/deskmate/src/Field.md +17 -0
  20. package/kits/deskmate/src/Field.tsx +34 -0
  21. package/kits/deskmate/src/Heading.md +16 -0
  22. package/kits/deskmate/src/Heading.tsx +19 -0
  23. package/kits/deskmate/src/Message.md +17 -0
  24. package/kits/deskmate/src/Message.tsx +23 -0
  25. package/kits/deskmate/src/NavSlab.md +22 -0
  26. package/kits/deskmate/src/NavSlab.tsx +20 -0
  27. package/kits/deskmate/src/QuotedCard.md +22 -0
  28. package/kits/deskmate/src/QuotedCard.tsx +30 -0
  29. package/kits/deskmate/src/Rule.md +15 -0
  30. package/kits/deskmate/src/Rule.tsx +18 -0
  31. package/kits/deskmate/src/Skeleton.md +16 -0
  32. package/kits/deskmate/src/Skeleton.tsx +20 -0
  33. package/kits/deskmate/src/Stat.md +13 -0
  34. package/kits/deskmate/src/Stat.tsx +17 -0
  35. package/kits/deskmate/src/Transcript.md +24 -0
  36. package/kits/deskmate/src/Transcript.tsx +24 -0
  37. package/kits/deskmate/src/index.ts +31 -0
  38. package/kits/deskmate/src/styles.css +903 -0
  39. package/kits/deskmate/tsconfig.json +15 -0
  40. package/kits/onionskin/src/styles.css +19 -2
  41. package/package.json +3 -3
  42. package/plugins/sheleg-design/.claude-plugin/plugin.json +2 -2
  43. package/plugins/sheleg-design/commands/sheleg-design.md +2 -2
  44. package/plugins/sheleg-design/skills/sheleg-design/DESIGN_SYNC_BRIDGE.md +1 -1
  45. package/plugins/sheleg-design/skills/sheleg-design/FIGMA_BRIDGE.md +1 -1
  46. package/plugins/sheleg-design/skills/sheleg-design/MOBILE_SURFACES.md +1 -1
  47. package/plugins/sheleg-design/skills/sheleg-design/SKILL.md +3 -3
  48. package/plugins/sheleg-design/skills/sheleg-design/STYLE_PACK_INDEX.md +3 -2
  49. package/plugins/sheleg-design/skills/sheleg-design/SURFACE_COMPOSITION.md +8 -5
  50. package/plugins/sheleg-design/skills/sheleg-design/styles/STYLE_PACK_TEMPLATE.md +1 -1
  51. package/plugins/sheleg-design/skills/sheleg-design/styles/cyclorama.md +7 -0
  52. package/plugins/sheleg-design/skills/sheleg-design/styles/deskmate.md +564 -0
  53. package/plugins/sheleg-design/skills/sheleg-design/styles/tenor.md +7 -1
  54. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/deskmate.css +411 -0
  55. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/onionskin.css +19 -2
package/CHANGELOG.md CHANGED
@@ -6,6 +6,155 @@ follow [SemVer](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.52.0] - 2026-08-27
10
+
11
+ ### The front door is the designs
12
+
13
+ Opening the published catalogue used to mean reading three tiles and clicking one before
14
+ seeing a single pack. It now opens **on the packs**, behind a tab strip of three real
15
+ screens: **Designs** (the front door, and it is the gallery), **Audit** and **Method** —
16
+ the prose that used to occupy the index. `packs.html` stays as a canonical alias for the
17
+ URL the gallery lived at, because two URLs serving one gallery is duplicate content.
18
+
19
+ The tabs are links to pages rather than panels toggled in one document, and that is the
20
+ SEO decision, not a shortcut: one URL per screen, every screen fully in the HTML, nothing
21
+ behind a click a crawler has to execute. `rel="prefetch"` buys the tab feel back.
22
+
23
+ ### What the SEO pass changed
24
+
25
+ - **Structured data, with one publisher everybody points at.** A `CollectionPage` whose
26
+ `mainEntity` is an `ItemList` of all thirty-six packs, each with its own anchor
27
+ (`/#pack-<name>`), so an answer engine can lift the list without running the filter JS;
28
+ a `SoftwareApplication` on Method; `BreadcrumbList` on both sub-pages; and an
29
+ `Organization` node referenced by `@id` from every page. The family site shipped the
30
+ opposite of that for two releases — a publisher node nothing pointed at.
31
+ - **A `404.html`**, a three-URL sitemap that lists only indexable screens, `llms.txt`
32
+ rewritten for the new shape, and `max-image-preview:large`, `color-scheme`,
33
+ `theme-color`, `og:site_name` and `og:locale` on every page.
34
+ - **Two defects the pass found in the old pages:** `method.html` was emitting its
35
+ canonical and description **twice** (the shared `HEAD` and `page_meta` both fired), and
36
+ the published dark-field count was **wrong** — `gallery.py` could not parse
37
+ `oklch()`, so `briefing-room`'s near-black field read as light and the site, its
38
+ `llms.txt` and its gallery all said five dark packs where six ship.
39
+
40
+ ### `deskmate`, corrected by its own audit
41
+
42
+ A blind read of the pack (T34a, which also routed correctly and rejected `tenor` on the
43
+ quoted clause) found nine defects. The important one is a colour pairing no gate can see:
44
+ `--surface` stays white on the dusk surface while `--ink` inverts to white, so **a card
45
+ that painted its heading in `--ink` painted white on white** inside an inverted section.
46
+ The pack now ships `--on-surface` and `--on-surface-soft` — words on a card take those,
47
+ words on the field take `--ink` — and the kit consumes them.
48
+
49
+ Also fixed: the focus ring shipped derived-only, so a browser without `color-mix()` got
50
+ **no ring at all** (literal first now, derived inside `@supports`, the mechanism
51
+ `showroom` established); the slab's inset was 40px in the pack and 80px in the token
52
+ layer; the gutter ramp stated four steps and shipped two tokens; the measure stated in
53
+ prose (62ch/68ch) had no token; the entrance duration's two measurements (0.64s declared,
54
+ 0.7s computed) were never reconciled; "the smallest radius that ships" and "every control
55
+ is 56px" were both contradicted by the pack's own body; and the provenance promise was
56
+ broader than the layer delivers.
57
+
58
+ One finding was rejected: `deskmate` is absent from `MOTION_DOCTRINE.md`'s ceiling
59
+ passage, and that passage is a named selection whose home the gate declares to be the
60
+ pack's own Register.
61
+
62
+ ### `onionskin` gets the same focus-ring guard
63
+
64
+ The audit that read `deskmate` measured the whole library for the same shape: three
65
+ packs derive a focus ring, and only `showroom` guarded it. `onionskin`'s ring was
66
+ `color-mix()`-only, so a browser without `color-mix()` painted **no ring at all** — the
67
+ one degradation both packs say they may not ship. The literal now sits in `:root` and the
68
+ derived form in an `@supports` block; no computed value changed, and its kit's token
69
+ block was resynced with it.
70
+
71
+ ### A count nothing could check, and now can
72
+
73
+ `SURFACE_COMPOSITION.md` claimed `--accent` in **twenty-nine** of the packs at a true
74
+ **thirty-three**, and named two of the three packs that call the accent something else —
75
+ omitting `babylove` entirely, so a reader following that sentence would look for a token
76
+ that pack does not have. `validate_counted_claims` cannot read a count whose noun is
77
+ "packs that declare a token", so `validate_token_population_counts` now derives both that
78
+ figure and the `@role non-text:` population from the token layers, and refuses a pack
79
+ that declares neither an `--accent` nor an `@role accent:` marker. The same sweep found
80
+ `SKILL.md` still saying "the twenty-nine".
81
+
82
+ ### The scenario that produced all of this, and the half of it that did not run
83
+
84
+ `T34` is the behavioural scenario shipped with `deskmate`, and 1.52.0 is the first
85
+ release where one of these branches was actually run rather than filed. **`T34a`
86
+ passed**: it routed to the pack, quoted the deciding clause, rejected `tenor` on the
87
+ clause that rules it out, and stayed inside the bundle directory. It then did the half
88
+ these scenarios have always asked for and never exercised — read the chosen pack in full
89
+ and report defects with quotes — and returned nine, eight of which are fixed above.
90
+
91
+ **`T34b`, the negative branch, terminated on an account spend limit before reporting a
92
+ verdict, and is not counted as a result.** A positive-only pass cannot detect the failure
93
+ that branch exists to detect. `test/scenarios.md` records both outcomes.
94
+
95
+ ### Ratchets
96
+
97
+ `validate.py` 5047 → 5060, `validate_palette.py` 3013 → 3023, `computed_at_least`
98
+ 686 → 691. Thirteen of the first delta are the new gate.
99
+
100
+ ## [1.51.1] - 2026-08-27
101
+
102
+ ### `deskmate`, the thirty-sixth pack — a colleague, quoted
103
+
104
+ **`v1.51.0` is a ghost tag and nothing shipped under it.** It was pushed while `main`
105
+ had moved on, so the tag named a commit no clone could reach; the release workflow's
106
+ own guard refused before cutting a release or publishing, which is what that guard is
107
+ for. The tag ruleset forbids moving or deleting a `v*` tag and the branch ruleset
108
+ forbids the merge commit that would make it reachable, so the release ships one patch
109
+ number up rather than as a tag pointing off the default branch. The content is this
110
+ entry's; there is no 1.51.0 release to look for.
111
+
112
+ Extracted from a front page for an AI employee that works inside a chat client, read
113
+ off computed styles at 1440×900 (1,191 visible of 1,888 elements on a 10,211px page)
114
+ plus four more pages for the components the front page does not paint: the form field,
115
+ the search shell, the dusk surface, and the reference's own published brand page.
116
+
117
+ **What the pack contributes.** One light source, and it is above the top edge: every
118
+ gradient origin in the reference's own library sits at or above `y=0`, and every one of
119
+ them runs the same four stops — peach, lilac, violet, deep navy. One ramp does three
120
+ jobs (a field bleed, a panel fill, and one word of a heading through
121
+ `background-clip: text`), everything a hand touches is a 56px pill against 32px slabs,
122
+ and elevation is a field step: 68 white fills on a beige field, 51 hairlines, and two
123
+ shadows on the whole page.
124
+
125
+ **The signature element is a framed transcript**, and the mechanism worth having is its
126
+ quarantine: the quoted chat client keeps its own face, its own ink and its own two
127
+ status colours under a `--quoted-*` namespace, so a status dot cannot reach a third
128
+ party's palette by accident. The frame's radius is concentric and the reference computes
129
+ it at runtime — 47.319px outside, a 15px inset, 32.254px inside — which the pack ships
130
+ as `--r-frame: 48px` and `calc(48px - 15px)`.
131
+
132
+ **Four corrections travel with it.** The `aria-invalid` border misses the 3:1 boundary
133
+ floor by 0.03 (`#b5856c` is 2.97:1 on the field `#faf5f1`); the icon grey is 2.77:1 on
134
+ that field and is declared non-text; 47 interactive elements render under 44px at the
135
+ narrow width; and the light status set measures 2.6–4.1:1 on the dusk field, so that
136
+ surface carries its own four. The status set is lifted out of the quoted transcript
137
+ rather than invented — the brand layer paints no state at all — and `--info` is an alias
138
+ of `--accent` on purpose.
139
+
140
+ ### The render caught four things no gate could
141
+
142
+ The `deskmate` kit was mounted and rendered at 1440, 768 and 390, then read back
143
+ through `getComputedStyle`, per CONTRIBUTING step 8. It found: a navigation slab that kept its link row to 390px and
144
+ pushed its trailing control to **515px against a 390px page**; a secondary control on
145
+ the dusk surface painting **white on white**, because a card there stays white by
146
+ measurement and the control had inherited the card's fill; an accent chip label at
147
+ **3.36:1** on the composited dusk wash, where the brighter step is 6.18:1; and every
148
+ anchor-shaped control carrying the UA's underline. All four are fixed, and the slab's
149
+ collapse is now stated in the pack.
150
+
151
+ ### Ratchets
152
+
153
+ `validate.py` 4882 → 5047, `validate_palette.py` 2875 → 3013, `sloplint.py` 720 → 734,
154
+ and `computed_at_least` 628 → 686. Both ratio ceilings held for the fourth consecutive
155
+ release; two claims that landed unpairable on the first run were rewritten rather than
156
+ pinned.
157
+
9
158
  ## [1.50.3] - 2026-08-26
10
159
 
11
160
  ### The shared seam is explicit
package/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
  [![CI](https://github.com/ssheleg/sheleg-design-skill/actions/workflows/validate.yml/badge.svg)](https://github.com/ssheleg/sheleg-design-skill/actions/workflows/validate.yml)
5
5
  [![license: MIT](https://img.shields.io/badge/license-MIT-blue)](./LICENSE)
6
6
  [![site](https://img.shields.io/badge/docs-skills.sshlg.me-8ab0ff)](https://skills.sshlg.me/skills/sheleg-design/)
7
+ [![skills.sh](https://skills.sh/b/ssheleg/sheleg-design-skill)](https://skills.sh/ssheleg/sheleg-design-skill)
7
8
 
8
9
  **Give coding agents a deliberate visual system: style packs, tokens, motion doctrine and a clean Figma seam.**
9
10
 
@@ -30,7 +31,7 @@ problem — invented colors, six accent hues, dark mode retrofitted later.
30
31
 
31
32
  This skill is the taste layer. It gives a coding agent **one motion
32
33
  methodology** for cinematic, scroll-driven pages, **a motion doctrine** that
33
- decides whether to animate before it decides how, and **thirty-five locked style
34
+ decides whether to animate before it decides how, and **thirty-six locked style
34
35
  packs** with ready-made design tokens, so what it builds reads as one system
35
36
  instead of a pile of effects.
36
37
 
@@ -74,6 +75,7 @@ into the cinematic layer, and says so in its own *Motion flavor* section.
74
75
  | `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) |
75
76
  | `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) |
76
77
  | `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) |
78
+ | `deskmate` | a warm beige working day lit from **one source above the top edge** — every gradient origin in the reference sits at or above `y=0` — where a single four-stop ramp (peach, lilac, violet, deep navy) washes the field, fills a panel and fills one word of a heading, and everything a hand touches is a 56px pill against 32px slabs | **products sold as a colleague rather than a tool** — AI employees and chat-native agents, assistants living in Slack or Teams, delegation and approval surfaces (standalone) |
77
79
  | `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 |
78
80
  | `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 |
79
81
  | `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 |
@@ -174,7 +176,7 @@ skills.
174
176
  | `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 |
175
177
  | `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 |
176
178
  | `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 |
177
- | `styles/*.md` | The thirty-five style packs — palette, type, texture, motion tokens, motifs, bans, and the traps each one carries |
179
+ | `styles/*.md` | The thirty-six style packs — palette, type, texture, motion tokens, motifs, bans, and the traps each one carries |
178
180
  | `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) |
179
181
  | `styles/STYLE_PACK_TEMPLATE.md` | The pack contract as a skeleton, so a new style is authored against the same headings rather than improvised |
180
182
 
@@ -245,7 +247,7 @@ cd ./ds-workbench && npm install && npm run build
245
247
  then `/design-sync` in that directory, from Claude Code. Three layers cross: the
246
248
  pack's **bans** as the design system's own README, `styles.css` built from
247
249
  `tokens/<pack>.css` verbatim, and the components — a six-name spine that is
248
- identical in all thirty-five kits, so switching packs swaps identity rather than API,
250
+ identical in all thirty-six kits, so switching packs swaps identity rather than API,
249
251
  plus each pack's signature parts. **Motion does not cross**, exactly as it does
250
252
  not cross into Figma: a kit is the static half of a pack, and saying so is what
251
253
  stops an agent inventing motion to fill the silence.
@@ -295,10 +297,10 @@ a pack's four widened sections used to make two gates *quieter* and still green.
295
297
  One honest limit: the npx installer is checked by asserting its runtime bundle
296
298
  walker exists, not by reading a file list — it has none by design. What proves
297
299
  it ships the right files is CI, which installs the bundle through **both**
298
- installers and `diff -r`s the result against the source, then builds all thirty-five
300
+ installers and `diff -r`s the result against the source, then builds all thirty-six
299
301
  kits.
300
302
 
301
- `test/scenarios.md` (T1–T30) is the behavioral harness: fresh subagents given a
303
+ `test/scenarios.md` (T1–T34) is the behavioral harness: fresh subagents given a
302
304
  task, checking that the skill is discovered, applied and quoted correctly.
303
305
  Re-run the affected scenarios after any edit to `SKILL.md`, a pack or the
304
306
  reference.
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-five style packs — instrument-console (dark console),
237
+ styles/ thirty-six 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
@@ -247,7 +247,10 @@ ${c("bold", "What it installs")}
247
247
  shadow), onionskin (a white technical sheet at 96.5%
248
248
  zero radius where every quiet value is the ink or a
249
249
  navy at an alpha, over a dot grid, at an 11px working
250
- size), babylove (white +
250
+ size), deskmate (warm beige lit from above the top
251
+ edge, one four-stop ramp, 56px pills against 32px
252
+ slabs, and a framed transcript as the set piece),
253
+ babylove (white +
251
254
  one orange, seven tokens over Tailwind defaults),
252
255
  outrank (white +
253
256
  one violet, a landing and a dashboard in one brand),
@@ -117,7 +117,14 @@ onionskin — a white technical sheet at 96.5% zero radius where two bases do al
117
117
  work and everything quiet is one of them at an alpha: text dims through the ink,
118
118
  structure through a navy that is never a word, over a dot grid with dashed hairlines,
119
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);
120
+ infrastructure whose front page is a working document;
121
+ deskmate — a warm beige working day lit from one source above the top edge, where a
122
+ single four-stop ramp of peach, lilac, violet and deep navy washes the field, fills a
123
+ panel and fills one word of a heading, every control is a 56px pill against 32px
124
+ slabs, elevation is a field step with two shadows on the whole page, and the set
125
+ piece is a framed transcript whose quoted chat client keeps its own face and colours
126
+ under a --quoted-* namespace, for products sold as a colleague rather than a tool:
127
+ AI employees and chat-native agents);
121
128
  otherwise follow the contract below (self-contained on purpose).
122
129
 
123
130
  ## Whether to animate at all — before how
@@ -0,0 +1,14 @@
1
+ {
2
+ "pkg": "@sheleg-design/deskmate",
3
+ "globalName": "ShelegDeskmate",
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,52 @@
1
+ # Deskmate — conventions for a design agent
2
+
3
+ This kit is the built form of the Deskmate style pack. Read these before generating
4
+ anything with it; they are the pack's bans, stated where a design tool will see them.
5
+
6
+ ## Geometry
7
+
8
+ - **A control is a pill.** `--r-pill` on anything a hand touches, at `--control-h`
9
+ (56px) or `--control-h-sm` (40px), never below `--tap-min` (44px).
10
+ - **A container is a slab** at `--r-card` (32px). Sections and the navigation slab take
11
+ `--r-section` and round their **bottom** corners only — nothing here rounds a top
12
+ corner against the page edge.
13
+ - **The frame is concentric**: `--r-frame` outside, `--frame-inset` of padding, and
14
+ `--r-frame-inner` = `calc(--r-frame - --frame-inset)` inside. Never the same radius on
15
+ both boxes.
16
+
17
+ ## Colour
18
+
19
+ - One accent, `--accent`, which both writes and fills. `--peach` is a gradient stop and
20
+ a mark; it is never a word.
21
+ - **One ramp**, `--gradient-dusk`, and its origin is always above the top edge of the
22
+ box it fills. A gradient lit from below is not this pack.
23
+ - **One gradient word per heading** and one gradient control per page. The word carries
24
+ `--gradient-word-fallback` as a solid `color` first, or it disappears where
25
+ `background-clip: text` is unsupported.
26
+ - **Status is never by colour alone.** Every state takes a word or a glyph beside its
27
+ colour, on the light field and on the dusk surface.
28
+ - `[data-surface="dusk"]` is a **surface variant, not a dark theme**. Put it on a
29
+ section, never on `:root`, and leave the frame alone — it re-declares its own tokens
30
+ on purpose.
31
+
32
+ ## Type
33
+
34
+ - `--font-display` for headings only, at `--weight-regular` for the display and
35
+ `--weight-strong` for a section heading, always at `--track-display`.
36
+ - `--font-body` at **`--weight-body` (500)** for every sentence. An unset weight gives
37
+ 400 and renders the whole page one step light.
38
+ - `--font-quoted` only inside the frame. It is the chat client's face, not the brand's.
39
+
40
+ ## Motion
41
+
42
+ - Two clocks: `--dur-fast` for anything a pointer caused, `--dur-enter` on `--ease-out`
43
+ for anything the scroll revealed. No scroll clock, no parallax, no `animation-timeline`.
44
+ - No shimmer on a skeleton.
45
+ - Reduced motion collapses every duration and `--enter-y`: colour still changes, nothing
46
+ travels.
47
+
48
+ ## Breakpoints
49
+
50
+ A component sizes against its own box. `container-type: inline-size` on the root and
51
+ `@container` on the descendant; a viewport query is only for the page's gutter and the
52
+ display's own size, and both are marked in the stylesheet with the reason.
@@ -0,0 +1,36 @@
1
+ # @sheleg-design/deskmate
2
+
3
+ The React reference kit for the **Deskmate** style pack — a warm beige working day lit
4
+ from one source above the top edge, where the product's own messages are the page's
5
+ illustrations.
6
+
7
+ The pack document is the source of truth for every value here:
8
+ `plugins/sheleg-design/skills/sheleg-design/styles/deskmate.md`. This kit is what those
9
+ values look like when they are built, and `src/styles.css` opens with the pack's token
10
+ layer copied byte for byte — never transcribed.
11
+
12
+ ```bash
13
+ npm install && npm run build # tsc only; there is no bundler in this kit
14
+ ```
15
+
16
+ ## What ships
17
+
18
+ **The spine**, identical in name, props and types across every SHELEG kit, so switching
19
+ packs swaps identity rather than API: `Button`, `Card`, `Chip`, `Stat`, `Heading`,
20
+ `Rule`.
21
+
22
+ **The signature**, which is this pack's own: `Transcript` (the framed quote, and the
23
+ element a page here is remembered by), `Message`, `QuotedCard`, `Eyebrow`, `Field`,
24
+ `NavSlab`, `Empty` and `Skeleton`.
25
+
26
+ ## Three rules this kit exists to keep
27
+
28
+ 1. **A control is a pill at 56px; a container is a slab at 32px.** Swap them and the
29
+ pack inverts. Every control also declares `box-sizing: border-box` and a
30
+ `min-height` of `--tap-min`, because an anchor and a button disagree about padding
31
+ otherwise and the taller one silently wins.
32
+ 2. **Elevation is a field step.** There are two shadow tokens and both are spent — one
33
+ under the frame, one bloom. A third shadow is a defect.
34
+ 3. **Nothing inside the frame is the brand's.** The quoted client keeps `--quoted-*`:
35
+ its own face, its own ink, its own two status colours. A status dot that reaches for
36
+ them has adopted somebody else's design system.
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@sheleg-design/deskmate",
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,19 @@
1
+ ---
2
+ category: Actions
3
+ ---
4
+
5
+ Every control is a pill at `--control-h` (56px) with `--control-px` of horizontal
6
+ padding and a `--rule-w` border, 16px at `--weight-body` and `--track-control`. `sm` is
7
+ `--control-h-sm` (40px) at 24px and 14px — the navigation's size.
8
+
9
+ `primary` fills with `--ink` and labels in `--on-ink`, 17.36:1. `secondary` is
10
+ `--surface` with the ink at 12% and hover mixes 6% of the ink into the fill.
11
+ `ghost` is transparent until hover, which fills with the ink at 5%.
12
+
13
+ Press travels 1px down and nothing scales. Focus moves the border to `--focus-color`
14
+ and adds `--focus-ring` — both, always.
15
+
16
+ ```tsx
17
+ <Button>Get started for free</Button>
18
+ <Button variant="secondary">Book a demo</Button>
19
+ ```
@@ -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={['dm-btn', `dm-btn--${variant}`, `dm-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 `--bg` at `--r-card`, no border and no shadow: the field step **is** the
6
+ elevation, and the page has two shadows in total, both spent elsewhere.
7
+
8
+ Padding is 24px above the title and 32px around the body, which is the reference's own
9
+ asymmetry — the title row sits closer to the top edge than the body does to the sides.
10
+ For the two-half card the page is actually made of, see `QuotedCard`.
11
+
12
+ ```tsx
13
+ <Card title="Payroll prep" meta="10:20 AM">
14
+ <p>Attendance and collections reconciled.</p>
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={['dm-card', className].filter(Boolean).join(' ')}>
15
+ {head && (
16
+ <div className="dm-card__head">
17
+ {title !== undefined && <h3 className="dm-card__title">{title}</h3>}
18
+ {meta !== undefined && <span className="dm-card__meta">{meta}</span>}
19
+ </div>
20
+ )}
21
+ <div className="dm-card__body">{children}</div>
22
+ </section>
23
+ );
24
+ }
@@ -0,0 +1,16 @@
1
+ ---
2
+ category: Data
3
+ ---
4
+
5
+ A small tile rather than a pill label: `--r-chip` (19.6px), `--surface`, a `--rule-w`
6
+ hairline at the ink's 5%, 10px of padding. It carries a filename, a tool mark, a count
7
+ — something the page is quoting.
8
+
9
+ `accent` swaps the fill for `--accent-wash` and the label for `--accent-deep`, which is
10
+ the eyebrow's pair. `selected` moves the hairline to `--accent` and nothing else moves;
11
+ for a status, use the word and the glyph, because status here is never by colour alone.
12
+
13
+ ```tsx
14
+ <Chip>Weekly-Performance.pdf</Chip>
15
+ <Chip tone="accent" selected>3 discrepancies</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
+ 'dm-chip',
15
+ `dm-chip--${tone}`,
16
+ selected ? 'dm-chip--selected' : undefined,
17
+ className,
18
+ ]
19
+ .filter(Boolean)
20
+ .join(' ')}
21
+ >
22
+ {children}
23
+ </span>
24
+ );
25
+ }
@@ -0,0 +1,18 @@
1
+ ---
2
+ category: Signature
3
+ ---
4
+
5
+ What a screen with no data says. It exists because the reference does not have one: a
6
+ search matching nothing collapses its grid to zero height and prints no message, which
7
+ is indistinguishable from a broken query.
8
+
9
+ A `--surface` card at `--r-card`, a line in `--ink`, one sentence in `--ink-soft`, and
10
+ one control that changes the condition.
11
+
12
+ ```tsx
13
+ <Empty
14
+ title="No integrations match “zzq”."
15
+ detail="Check the spelling, or clear the filter to see all of them."
16
+ action={<Button variant="secondary" size="sm">Clear filter</Button>}
17
+ />
18
+ ```
@@ -0,0 +1,19 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface EmptyProps {
4
+ title: string;
5
+ /** One sentence saying what would fill this, or what to change. */
6
+ detail?: string;
7
+ action?: ReactNode;
8
+ className?: string;
9
+ }
10
+
11
+ export function Empty({ title, detail, action, className }: EmptyProps) {
12
+ return (
13
+ <div className={['dm-empty', className].filter(Boolean).join(' ')}>
14
+ <p className="dm-empty__title">{title}</p>
15
+ {detail !== undefined && <p className="dm-empty__detail">{detail}</p>}
16
+ {action !== undefined && <div className="dm-empty__action">{action}</div>}
17
+ </div>
18
+ );
19
+ }
@@ -0,0 +1,16 @@
1
+ ---
2
+ category: Foundations
3
+ ---
4
+
5
+ The 20px pill above a heading: `--wash` fill, `--accent-deep` label, 12px at
6
+ `--track-caps`. It names the section before the heading argues it, and it is the only
7
+ place in the pack where a tracked-open label appears.
8
+
9
+ On a dusk section pass `surface="dusk"` — the fill becomes the on-dusk wash and the
10
+ label `--accent`. The shape is identical, which is the point: a surface variant changes
11
+ values, never geometry.
12
+
13
+ ```tsx
14
+ <Eyebrow>Why it feels like a hire</Eyebrow>
15
+ <Eyebrow surface="dusk">One hire, the output of a team</Eyebrow>
16
+ ```
@@ -0,0 +1,20 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface EyebrowProps {
4
+ children: ReactNode;
5
+ /** `dusk` swaps the wash for the on-dusk pair; the shape never changes. */
6
+ surface?: 'light' | 'dusk';
7
+ className?: string;
8
+ }
9
+
10
+ export function Eyebrow({ children, surface = 'light', className }: EyebrowProps) {
11
+ return (
12
+ <span
13
+ className={['dm-eyebrow', `dm-eyebrow--${surface}`, className]
14
+ .filter(Boolean)
15
+ .join(' ')}
16
+ >
17
+ {children}
18
+ </span>
19
+ );
20
+ }
@@ -0,0 +1,17 @@
1
+ ---
2
+ category: Actions
3
+ ---
4
+
5
+ The pill field: `--control-h` tall, `--surface` fill, a `--rule-w` hairline at the
6
+ ink's 12%, and the same geometry as `Button` so a form row reads as one object. Focus
7
+ moves the border to `--focus-color` **and** adds `--focus-ring-field` — both, because
8
+ the ring alone composites too faint to carry the state.
9
+
10
+ `error` is what makes the invalid state legal: the border takes `--danger` and the
11
+ message appears with it. A red border on its own is a colour carrying meaning alone,
12
+ which this pack bans.
13
+
14
+ ```tsx
15
+ <Field label="Work email" name="email" type="email" placeholder="you@company.com" />
16
+ <Field label="Work email" name="email" type="email" error="Use your work address." />
17
+ ```