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.
- package/CHANGELOG.md +149 -0
- package/README.md +7 -5
- package/bin/cli.js +5 -2
- package/cursor/rules/sheleg-design.mdc +8 -1
- package/kits/deskmate/.design-sync/config.json +14 -0
- package/kits/deskmate/.design-sync/conventions.md +52 -0
- package/kits/deskmate/README.md +36 -0
- package/kits/deskmate/package.json +29 -0
- package/kits/deskmate/src/Button.md +19 -0
- package/kits/deskmate/src/Button.tsx +33 -0
- package/kits/deskmate/src/Card.md +16 -0
- package/kits/deskmate/src/Card.tsx +24 -0
- package/kits/deskmate/src/Chip.md +16 -0
- package/kits/deskmate/src/Chip.tsx +25 -0
- package/kits/deskmate/src/Empty.md +18 -0
- package/kits/deskmate/src/Empty.tsx +19 -0
- package/kits/deskmate/src/Eyebrow.md +16 -0
- package/kits/deskmate/src/Eyebrow.tsx +20 -0
- package/kits/deskmate/src/Field.md +17 -0
- package/kits/deskmate/src/Field.tsx +34 -0
- package/kits/deskmate/src/Heading.md +16 -0
- package/kits/deskmate/src/Heading.tsx +19 -0
- package/kits/deskmate/src/Message.md +17 -0
- package/kits/deskmate/src/Message.tsx +23 -0
- package/kits/deskmate/src/NavSlab.md +22 -0
- package/kits/deskmate/src/NavSlab.tsx +20 -0
- package/kits/deskmate/src/QuotedCard.md +22 -0
- package/kits/deskmate/src/QuotedCard.tsx +30 -0
- package/kits/deskmate/src/Rule.md +15 -0
- package/kits/deskmate/src/Rule.tsx +18 -0
- package/kits/deskmate/src/Skeleton.md +16 -0
- package/kits/deskmate/src/Skeleton.tsx +20 -0
- package/kits/deskmate/src/Stat.md +13 -0
- package/kits/deskmate/src/Stat.tsx +17 -0
- package/kits/deskmate/src/Transcript.md +24 -0
- package/kits/deskmate/src/Transcript.tsx +24 -0
- package/kits/deskmate/src/index.ts +31 -0
- package/kits/deskmate/src/styles.css +903 -0
- package/kits/deskmate/tsconfig.json +15 -0
- package/kits/onionskin/src/styles.css +19 -2
- package/package.json +3 -3
- package/plugins/sheleg-design/.claude-plugin/plugin.json +2 -2
- package/plugins/sheleg-design/commands/sheleg-design.md +2 -2
- package/plugins/sheleg-design/skills/sheleg-design/DESIGN_SYNC_BRIDGE.md +1 -1
- package/plugins/sheleg-design/skills/sheleg-design/FIGMA_BRIDGE.md +1 -1
- package/plugins/sheleg-design/skills/sheleg-design/MOBILE_SURFACES.md +1 -1
- package/plugins/sheleg-design/skills/sheleg-design/SKILL.md +3 -3
- package/plugins/sheleg-design/skills/sheleg-design/STYLE_PACK_INDEX.md +3 -2
- package/plugins/sheleg-design/skills/sheleg-design/SURFACE_COMPOSITION.md +8 -5
- package/plugins/sheleg-design/skills/sheleg-design/styles/STYLE_PACK_TEMPLATE.md +1 -1
- package/plugins/sheleg-design/skills/sheleg-design/styles/cyclorama.md +7 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/deskmate.md +564 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/tenor.md +7 -1
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/deskmate.css +411 -0
- 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
|
[](https://github.com/ssheleg/sheleg-design-skill/actions/workflows/validate.yml)
|
|
5
5
|
[](./LICENSE)
|
|
6
6
|
[](https://skills.sshlg.me/skills/sheleg-design/)
|
|
7
|
+
[](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-
|
|
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-
|
|
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-
|
|
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-
|
|
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–
|
|
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-
|
|
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),
|
|
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
|
+
```
|