sheleg-design-skill 1.24.0 → 1.26.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 (34) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/kits/atrium/src/styles.css +5 -0
  3. package/kits/blueprint/src/styles.css +5 -0
  4. package/kits/cyclorama/src/styles.css +3 -0
  5. package/kits/datasheet/src/styles.css +2 -0
  6. package/kits/editorial-luxury/src/styles.css +20 -0
  7. package/kits/field-notes/src/styles.css +1 -0
  8. package/kits/manpage/src/styles.css +3 -0
  9. package/kits/maquette/src/styles.css +1 -0
  10. package/kits/orchard/src/styles.css +4 -0
  11. package/kits/prism/src/styles.css +5 -0
  12. package/kits/roster/src/styles.css +3 -0
  13. package/kits/scoreboard/src/styles.css +16 -0
  14. package/kits/showroom/src/styles.css +21 -4
  15. package/kits/workbench/src/styles.css +4 -0
  16. package/package.json +1 -1
  17. package/plugins/sheleg-design/.claude-plugin/plugin.json +1 -1
  18. package/plugins/sheleg-design/skills/sheleg-design/SKILL.md +1 -1
  19. package/plugins/sheleg-design/skills/sheleg-design/SURFACE_COMPOSITION.md +6 -1
  20. package/plugins/sheleg-design/skills/sheleg-design/styles/blueprint.md +9 -3
  21. package/plugins/sheleg-design/skills/sheleg-design/styles/editorial-luxury.md +24 -6
  22. package/plugins/sheleg-design/skills/sheleg-design/styles/showroom.md +10 -1
  23. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/atrium.css +4 -0
  24. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/blueprint.css +5 -0
  25. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/cyclorama.css +3 -0
  26. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/datasheet.css +2 -0
  27. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/editorial-luxury.css +19 -0
  28. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/manpage.css +3 -0
  29. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/orchard.css +3 -0
  30. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/prism.css +5 -0
  31. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/roster.css +3 -0
  32. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/scoreboard.css +16 -0
  33. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/showroom.css +6 -0
  34. package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/workbench.css +4 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,104 @@ 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.26.0] - 2026-08-13
8
+
9
+ The palette gate computed one contrast per theme — ink against field — and never once
10
+ asked whether a status colour could be read on the surface it sits on. Twenty-eight
11
+ findings in eleven of seventeen packs were waiting behind that.
12
+
13
+ ### Added
14
+
15
+ - **`validate_status_on_field()`** — every semantic colour is checked against the field
16
+ of **its own theme**, in three tiers, which is what the packs already argued and
17
+ nothing enforced:
18
+ - **≥ 4.5:1** — text, no declaration needed.
19
+ - **≥ 3.0:1** — declared `@role non-text:`, a mark that can be understood on its own.
20
+ - **< 3.0:1** — declared **and** the pack states status is never by colour alone,
21
+ because below the non-text floor the colour is reinforcement and the word is the
22
+ message.
23
+ - **`@role non-text:`, a second canonical role marker**, beside the `@role accent:` this
24
+ library already had. It is the whole point of the release: eleven packs *did* say their
25
+ status colours were not text — in five different phrasings ("never text on the field",
26
+ "a FILL and large-text colour", "a fill, not a text colour", "No coral word under
27
+ 24px", "category marks") — and a class nobody can grep for is a class nobody checks.
28
+ Documented in `SURFACE_COMPOSITION.md` beside the accent marker.
29
+ - Two plants, one per tier, and the check's core was split out so a plant can be a token
30
+ layer written as a string rather than an edit to a real file.
31
+
32
+ ### Fixed
33
+
34
+ - **`scoreboard`'s dark band inherited the paper status set** (board B-034). Its
35
+ `[data-surface="panel"]` block remapped eleven tokens — `--ink-soft` among them, for
36
+ exactly this reason — and left the four statuses pointing at the paper values, so
37
+ `var(--good)` painted **3.69:1** on that band while `--good-on-dark` had **10.21:1**
38
+ waiting three lines away in the same file. The pack has always said to use the on-dark
39
+ set there; now the token layer does it.
40
+ - **`showroom`'s status chip painted its label in its own status colour** (board B-021).
41
+ Measured against the fills the kit actually uses: `--good` **2.03:1**, `--warning`
42
+ **1.54:1**, `--danger` **2.65:1**, at the 12px the same row specifies. The label is
43
+ `--ink` now (14.65–14.73:1) and the status colour is the chip's tint plus a 6px dot —
44
+ which is the pack's own "never by colour alone" rule applied to the component that
45
+ exists to serve it. Corrected in the pack, the kit and with the numbers.
46
+ - **`validate.py` counted its own worktree.** Both of its ROOT markdown walks read every
47
+ `.md` under the repository, and this project's standing practice for a concurrent run is
48
+ an isolated worktree at `.claude/worktrees/<name>` — a full second copy of the tree. So
49
+ the gate reported **2361 checks** where the same commit measures **2067** clean, and the
50
+ ratchet in `floors.json` was one commit away from enshrining the inflated number. A floor
51
+ measured with a worktree present fails the next clean run for a regression that never
52
+ happened, and the failure names a count, not a cause. Nested checkouts are now excluded
53
+ by what they **are** — a directory carrying its own `.git` — not by name, because the name
54
+ is a convention and the next one will differ. The plant is the only one in this suite
55
+ whose pass condition is silence: a nested checkout must change neither the verdict nor the
56
+ count, and with the guard removed it reports `MISSED`.
57
+ - **`blueprint` answered separation and never legibility.** Its four category marks clear
58
+ every separation floor under all three dichromacies, which the pack said — and
59
+ `--good` **2.40:1**, `--warning` **1.72:1** and `--info` **1.21:1** on the stock, which
60
+ it did not. Its own advice, *"label them anyway, because a legend of four coloured
61
+ squares is a legend nobody reads"*, is now the requirement it always was.
62
+
63
+ ### Notes
64
+
65
+ - **Nothing else changed colour.** The other twenty-four findings are declarations: the
66
+ packs were right about their own colours and unreadable about it to a machine.
67
+ - Gates on the merged tree: `validate.py` 2067, palette **1001**, sloplint 450 — palette
68
+ 958 base + 8 from 1.25.0's status set + 35 from this check, additive with nothing hidden
69
+ in it. Floors raised with the reason.
70
+ - **This release is 1.26.0 because 1.25.0 was taken while it was being built.** Two runs
71
+ worked the repository at once and both bumped to the same number; the other reached
72
+ `origin/main`, its tag and npm first, so it keeps 1.25.0 and its entry above. Nothing was
73
+ discarded and nothing was rewritten — the collision cost this release a version number and
74
+ twenty-two version references in prose, each of which would otherwise have named a release
75
+ it is not in.
76
+ - The two remaining `scoreboard` rows (B-033, B-035, B-036) are untouched — a retracted
77
+ permission still live in its token layer, eight numbers that disagree with the values
78
+ beside them, and a pixel-numeral rule with no working mechanism outside WebKit.
79
+
80
+ ## [1.25.0] - 2026-08-13
81
+
82
+ **`editorial-luxury` gets the status set it told surfaces to ask for.**
83
+
84
+ The pack shipped one semantic colour, `--red`, and instructed any surface needing
85
+ a full set to close the gap *in the pack, deliberately, not at the keyboard*. The
86
+ instruction was right and nobody followed it: a production admin console built on
87
+ this pack had invented amber-as-warning and cyan-as-info on top of an amber that
88
+ was also its entire chrome — so the hue meaning "a provider is backing up" was
89
+ the hue of every button on the page. Seven of that surface's colour pairs sat
90
+ below AA and three under 1.1:1.
91
+
92
+ `--status-{ok,warn,info,danger}` and their `-weak` tints close it. Three of the
93
+ four are values the pack already owned: `--status-ok` is `--accent-deep`,
94
+ `--status-danger` is `--red`. Only `warn` and `info` are new, each the existing
95
+ amber and cyan family deepened until it clears the AA floor for normal text on
96
+ **all three** cream grounds — `--paper`, `--paper-2` and `--paper-3` — because
97
+ two grounds would have been the same enumerated-list hole one level down. Status
98
+ is still never carried by colour alone: the tint carries the colour, the word
99
+ carries the meaning in `--ink`.
100
+
101
+ Also: `--dur-hover` and `--dur-state`. The pack said the product register stays
102
+ in the fast range and never named the values, so every product surface built on
103
+ it typed `0.15s` literals — there was nothing to reference.
104
+
7
105
  ## [1.24.0] - 2026-08-13
8
106
 
9
107
  A seventeenth style pack, whose whole argument is other people's names — and a reference
@@ -24,6 +24,10 @@
24
24
  --accent-gradient: linear-gradient(#b05a36, rgba(216, 161, 131, 0.96));
25
25
 
26
26
  /* ---- Semantics (fills and icons — NOT text; see Gotchas) ------------- */
27
+ /* @role non-text: --good, --info — the pack has always said so in words ("fills and
28
+ icons only", "never carries a word"); this is the same statement in the one form a
29
+ gate can read. --good 2.11:1 and --info 3.41:1 on the field, and --danger 5.0:1 is
30
+ the one semantic here that may be text. */
27
31
  --good: #79bd8b; /* 2.1:1 on --bg — never carries a word */
28
32
  --good-tint: #f1f3e5;
29
33
  --info: #488ad5; /* 3.4:1 — large text / UI marks only */
@@ -175,6 +179,7 @@
175
179
  --marquee-cycle: 0s;
176
180
  }
177
181
  }
182
+
178
183
  /* ── components ── */
179
184
 
180
185
  /* Everything below consumes var(--…) only. Colour never appears as a literal:
@@ -55,6 +55,11 @@
55
55
  /* Every pair clears both separation floors under all three dichromacies, so
56
56
  no secondary-encoding declaration is required here. It is still good
57
57
  practice to label them; the pack asks for it without needing to. */
58
+ /* @role non-text: --good, --info, --warning — on a drawing sheet these four are
59
+ CATEGORY MARKS, not labels: a swatch, a rule, a chip fill. Measured on the field:
60
+ --good 2.40:1, --warning 1.72:1, --info 1.21:1. All three are under the 3:1 non-text
61
+ floor as well, so the word beside the mark is the message and the pack says status is
62
+ never by colour alone. */
58
63
  --good: #86af80;
59
64
  --info: #8bf6ff;
60
65
  --danger: #a440ce;
@@ -66,6 +66,9 @@
66
66
  #C56200 3.01:1 (large text only), 1.4 from --warning under protanopia
67
67
  There is no text-safe orange in this palette. That is a property of the
68
68
  reference, not a shortcut taken here. */
69
+ /* @role non-text: --accent, --info, --warning — the accent's own declaration has
70
+ always read "NEVER text on the field", and the two statuses render as a mark plus
71
+ their word. Measured: --accent 1.86:1, --warning 3.78:1, --info 4.13:1. */
69
72
  --accent: #ff8c00; /* fill / dot / chart series. NEVER text on the field */
70
73
  --accent-hover: #e69900; /* the accent's hover fill */
71
74
  --on-accent: #1a1a1a; /* 7.46:1 on --accent, 7.39:1 on --accent-hover */
@@ -53,6 +53,8 @@
53
53
  floor for a non-text mark and for large text (>=24px). Its measured jobs on
54
54
  the reference are exactly those: one word of a 48px headline, the mono
55
55
  visitor id, a 2px focus ring, a tick. It is never body copy. */
56
+ /* @role non-text: --accent — 3.17:1 on the field. This pack's accent is a fill, a
57
+ rule and an alarm tint; --accent-ink is what carries a word. */
56
58
  --accent: #f35b22; /* MEASURED — its --orange-7 */
57
59
  --accent-deep: #be400f; /* 5.11:1 on --bg — MEASURED (--orange-8): the one orange that may carry a word */
58
60
  --accent-wash: #ffeadc; /* MEASURED — selected cell, hovered row (--orange-2) */
@@ -19,6 +19,25 @@
19
19
  --accent-on-dark: #9fd9bc;
20
20
  --terra: #b5623f;
21
21
  --red: #a83a2b;
22
+
23
+ /* Status set — added 2026-08-13. Three of the four are values this pack
24
+ already owned; only warn and info are new, each its own family deepened
25
+ until it clears the AA floor for normal text on every cream ground this
26
+ pack renders on, not only the two obvious ones. The three ratios beside
27
+ each value are those grounds, lightest first. The status word renders in
28
+ --ink on the -weak tint, so status is never carried by colour alone. */
29
+ --status-ok: #2f5e47; /* 6.93 / 6.25 / 5.72 — = --accent-deep */
30
+ --status-warn: #7d5416; /* 6.19 / 5.58 / 5.11 */
31
+ --status-info: #2f5c7d; /* 6.61 / 5.96 / 5.46 */
32
+ --status-danger: #a83a2b; /* 5.90 / 5.32 / 4.87 — = --red */
33
+ --status-ok-weak: rgba(47,94,71,0.12);
34
+ --status-warn-weak: rgba(125,84,22,0.12);
35
+ --status-info-weak: rgba(47,92,125,0.12);
36
+ --status-danger-weak: rgba(168,58,43,0.12);
37
+
38
+ /* Product register durations — the range the pack described in prose. */
39
+ --dur-hover: 0.12s;
40
+ --dur-state: 0.18s;
22
41
  --hair: rgba(36, 28, 20, 0.13);
23
42
  --hair-strong: rgba(36, 28, 20, 0.22);
24
43
 
@@ -56,6 +75,7 @@
56
75
  cross-pack document can name them truthfully. Alias, never a new colour. */
57
76
  --bg: var(--paper); /* role alias -> --paper, this pack's field */
58
77
  }
78
+
59
79
  /* ── components ── */
60
80
 
61
81
  /* Everything below consumes var(--…) only. The token block above is the whole
@@ -299,6 +299,7 @@
299
299
  --dur-marquee: 0s;
300
300
  }
301
301
  }
302
+
302
303
  /* ── components ── */
303
304
 
304
305
  /* Everything below consumes var(--…) only. Colour never appears as a literal:
@@ -53,6 +53,9 @@
53
53
  spends it on the wordmark, the button fill, a 12px label chip and a link.
54
54
  The chip and the link are the two that do not clear the floor; see
55
55
  --accent-ink, which is what this pack paints a word in. */
56
+ /* @role non-text: --accent, --action — 3.61:1 on cream, which is why the pack's own
57
+ Bans read "No coral word under 24px" and point at --accent-ink. The coral fills, it
58
+ rules, and it sets large type; it does not set a sentence. */
56
59
  --accent: #eb3514; /* MEASURED — its --color-coral, and its <meta name=theme-color> */
57
60
  --accent-ink: #660202; /* 11.59:1 on --bg — MEASURED (--color-burgundy): the accent that may carry a word */
58
61
  --accent-wash: #f0e0da; /* COMPOSITED — bg-coral/8 over --bg */
@@ -152,6 +152,7 @@
152
152
  --dur-slow: 0s;
153
153
  }
154
154
  }
155
+
155
156
  /* ── components ── */
156
157
 
157
158
  /* Everything below consumes var(--…) only. Colour never appears as a literal:
@@ -18,6 +18,9 @@
18
18
  --on-primary: #fffef4; /* only on --primary-deep, never on --primary */
19
19
 
20
20
  /* ---- Brand green ----------------------------------------------------- */
21
+ /* @role non-text: --primary, --cta — sage at 3.42:1 and the candy orange at 2.75:1
22
+ are fills and large-text colours, which both declarations already say; --primary-deep
23
+ is the text-safe green at 4.9:1. */
21
24
  --primary: #78934a; /* sage — 3.4:1 on --bg: a FILL and large-text colour */
22
25
  --primary-deep: #61783b; /* the text-safe green: 4.9:1 under --on-primary */
23
26
  --primary-light: #aaca73; /* the mark / illustration green */
@@ -126,6 +129,7 @@
126
129
  --reveal-floor: 1;
127
130
  }
128
131
  }
132
+
129
133
  /* ── components ── */
130
134
 
131
135
  /* Everything below consumes var(--…) only. Colour never appears as a literal:
@@ -38,6 +38,11 @@
38
38
  This pack does not propagate it: the cyan is a fill, a rule, a chart series
39
39
  and a mark. Ink on it is 8.02:1, which is what --on-accent is for. Where a
40
40
  heading needs the accent's family in text, it takes --accent-ink. */
41
+ /* @role non-text: --accent, --good, --info, --warning — an iridescent pack whose
42
+ status hues are mint, cyan and orange: --good 1.33:1, --accent 2.36:1, --info 2.36:1,
43
+ --warning 3.33:1 on the field. The pack already calls --good "a fill, not a text
44
+ colour"; the other three are the same kind. Three of the four are under the 3:1
45
+ non-text floor, so the word carries the meaning and the pack says so. */
41
46
  --accent: #00b3ff; /* FILL ONLY — 2.36:1 as text on --bg */
42
47
  --accent-hover: #0077ad;
43
48
  --accent-ink: #1493cc; /* 3.4:1 — large display text only, never body */
@@ -74,6 +74,9 @@
74
74
  /* Both of the reference's oranges fail AA as a text or label colour: white on #f25533
75
75
  is 3.43:1 at the nav pill's 16px/600, and #fa5c12 is 3.18:1 on white. The accent is
76
76
  therefore a FILL and a large-text colour; --accent-ink is what carries a word. */
77
+ /* @role non-text: --accent — 3.18:1 on the field, which is legal for the 68px
78
+ headline phrase as large text and for a fill, and illegal for anything smaller.
79
+ --accent-ink is what carries a word. */
77
80
  --accent: #fa5c12; /* MEASURED — the headline phrase and the gradient's head */
78
81
  /* The reference spends this on its nav fill AND on its eyebrows. The pack keeps it for
79
82
  the fill and overrides the eyebrow to --accent-ink, because at the eyebrow's 16px this
@@ -46,6 +46,9 @@
46
46
  non-text mark. The reference obeys this without saying so: its orange is a
47
47
  3x18 tick, a ::marker, a focus ring, a link underline and one oversized
48
48
  chevron. Its primary button is ink. See the pack's Palette. */
49
+ /* @role non-text: --accent — 3.23:1 on the paper. In this pack the accent "only
50
+ marks": a 3x18px tick, a chip edge, a rule. --accent-ink is the one that may carry a
51
+ word, and the 1.13.1 correction says no orange here may carry a link. */
49
52
  --accent: #ff4801;
50
53
  --accent-hover: #e03d00; /* 4.12:1 on --bg — the one orange that may carry a link */
51
54
  --accent-wash: #fff7ed; /* MEASURED — selected row, hovered cell */
@@ -228,6 +231,19 @@
228
231
  --action: #ffffff;
229
232
  --action-hover: #e4e4e7;
230
233
  --on-action: #0a0a0a;
234
+
235
+ /* The status set has to be remapped here, and until 1.26.0 it was not. The block
236
+ already remapped eleven tokens for exactly this reason -- --ink-soft among them --
237
+ and left --good, --warn, --danger and --info pointing at the paper values, so
238
+ `var(--good)` painted 3.69:1 on this band while --good-on-dark had 10.21 waiting
239
+ three lines away in the same file. The pack has always said to use the on-dark set
240
+ here; nothing made the token layer do it, because the palette gate checked ink
241
+ against field and never a status against field. validate_status_on_field() checks it
242
+ now, and this is the defect it was written for (board B-034). */
243
+ --good: #00d492; /* 10.21:1 on this band — was inheriting the paper value at 3.69 */
244
+ --warn: #ffb900; /* 11.49:1 — was 3.94 */
245
+ --danger: #ff637e; /* 6.92:1 — was 2.37 */
246
+ --info: #51a2ff; /* 7.51:1 — was 2.90 */
231
247
  }
232
248
 
233
249
  /* The reference ships no reduced-motion branch. This pack requires one: every
@@ -55,6 +55,12 @@
55
55
  deuteranopia — the classic pair, under the floor of 8. Legal only with the
56
56
  secondary encoding the pack declares: a status is a chip with a word in it,
57
57
  never a bare dot. */
58
+ /* @role non-text: --good, --warning, --danger — 2.33:1, 1.78:1 and 3.05:1 on the
59
+ field. Since 1.26.0 the status chip's LABEL is --ink and these three are the chip's
60
+ tint and its 6px dot; before that the label was painted in them, at 2.03, 1.54 and
61
+ 2.65 against the fills the kit actually uses. All three are at or under the 3:1
62
+ non-text floor, so the word is the message — which is what the pack's own "never by
63
+ colour alone" rule has always said. */
58
64
  --good: #0fc27b;
59
65
  --warning: #f5b900;
60
66
  --danger: #ff5b59;
@@ -243,10 +249,21 @@
243
249
 
244
250
  .sw-status { display: inline-flex; align-items: center; border-radius: var(--radius-sm);
245
251
  padding: 2px var(--space-2); font-family: var(--font-mono); font-size: var(--t-xs); }
246
- .sw-status--good { background-color: var(--accent-wash); color: var(--good); }
247
- .sw-status--warning { background-color: var(--surface-2); color: var(--warning); }
248
- .sw-status--danger { background-color: var(--surface-2); color: var(--danger); }
249
- .sw-status--neutral { background-color: var(--surface-2); color: var(--ink-soft); }
252
+ /* The label is --ink and the status colour is the dot. Until 1.26.0 the label WAS the
253
+ status colour, which measured 2.03:1 for good, 1.54 for warning and 2.65 for danger at
254
+ the 12px this chip is set in; --ink on the same fills is 14.65-14.73. The pack's own
255
+ "never by colour alone" rule is what the dot implements. */
256
+ .sw-status { color: var(--ink); gap: var(--space-1); }
257
+ .sw-status::before { content: ""; width: 6px; height: 6px; border-radius: 999px;
258
+ background-color: currentColor; }
259
+ .sw-status--good { background-color: var(--accent-wash); }
260
+ .sw-status--good::before { background-color: var(--good); }
261
+ .sw-status--warning { background-color: var(--surface-2); }
262
+ .sw-status--warning::before { background-color: var(--warning); }
263
+ .sw-status--danger { background-color: var(--surface-2); }
264
+ .sw-status--danger::before { background-color: var(--danger); }
265
+ .sw-status--neutral { background-color: var(--surface-2); }
266
+ .sw-status--neutral::before { background-color: var(--ink-soft); }
250
267
 
251
268
  .sw-col { display: flex; align-items: center; gap: var(--space-2); height: 36px; padding: 0 var(--space-4);
252
269
  border-bottom: 1px solid var(--line); font-size: var(--t-xs); color: var(--ink-soft);
@@ -9,6 +9,10 @@
9
9
  --muted: #5b6472;
10
10
  --border: #e6e9ef;
11
11
  --border-strong: #d7dce4;
12
+ /* @role non-text: --accent, --info — 4.30:1 in both themes, just under AA. The accent
13
+ fills buttons and marks selection and --accent-ink is the text that sits ON it;
14
+ --info deliberately IS the accent hue, for "running / working", and it is a dot and a
15
+ bar rather than a word. */
12
16
  --accent: #2f6feb;
13
17
  --accent-weak: #eaf0fe;
14
18
  --accent-ink: #ffffff; /* text ON the accent — 4.57:1 on --accent */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sheleg-design-skill",
3
- "version": "1.24.0",
3
+ "version": "1.26.0",
4
4
  "description": "Design taste as an installable agent skill. Cinematic scroll-driven landing pages built on one scroll clock and layered degrade-to-calm motion, a motion doctrine that decides whether to animate before it decides how, three calibration dials, and seventeen locked style packs with ready-made design tokens — instrument-console, editorial-luxury, workbench, briefing-room, atrium, orchard, field-notes, cyclorama, showroom, blueprint, prism, maquette, scoreboard, datasheet, manpage, pigeonhole and roster. Colour, slop and fork-reciprocity gates run as scripts, not opinions. Works with Cursor, Claude Code and any agent that reads a SKILL.md.",
5
5
  "bin": {
6
6
  "sheleg-design-skill": "bin/cli.js"
@@ -2,7 +2,7 @@
2
2
  "name": "sheleg-design",
3
3
  "displayName": "SHELEG Design",
4
4
  "description": "SHELEG Design methodology: cinematic scroll-driven landing pages (single scroll clock, layered degrade-to-calm motion, WebGL particle formations), a motion doctrine that decides whether to animate before it decides how, and seventeen pluggable visual style packs — instrument-console (dark console), editorial-luxury (warm editorial), workbench (light/dark product UI for dashboards and tools), briefing-room (dark 16:9 deck), atrium (warm consumer health), orchard (friendly consumer biotech), field-notes (warm paper for dev tools sold on auditability), cyclorama (a pastel field on a 32s cycle), showroom (the product as the exhibit), blueprint (a drawing sheet, zero radius), prism (one iridescent wash over mono body), maquette (cream axonometric models on a dark table), scoreboard (warm paper, pixel numerals, a dark ledger of results), datasheet (an off-white spec sheet whose live instrument goes dark when it detects the reader is hiding), manpage (a developer landing page set in the reader's own system monospace on cream paper, with coral label chips that are real headings and a dark code frame as the argument), pigeonhole (a white sorting wall whose nine pastel categories are a filing scheme rather than a mood, each a two-layer chip whose label word is mandatory), roster (a white field in a faint grid of squares whose argument is other people's marks — client logotypes in pill-labelled industry columns, an engine's wordmark inside the headline — where the proof is a name rather than a number). Ships the sheleg-design skill, the architecture reference, the motion doctrine, the Figma and Claude Design bridges, AI-surface patterns, style packs with ready-made token CSS, and the /sheleg-design command.",
5
- "version": "1.24.0",
5
+ "version": "1.26.0",
6
6
  "author": {
7
7
  "name": "ssheleg",
8
8
  "url": "https://x.com/sshlg93"
@@ -3,7 +3,7 @@ name: sheleg-design
3
3
  description: Use when building or upgrading a cinematic scroll-driven landing page, marketing site or hero (particle/WebGL background, scroll-linked animation, parallax, scrubbed sections) — when such a page feels busy or janky or its motion layers drift out of sync — or when styling product UI with its style packs - dashboards, admin panels, internal/dev tools, mobile app screens, design tokens, light/dark themes - or when carrying a visual system across the Figma border (publishing tokens as variables, implementing a design without importing raw values). Triggers - "cinematic landing" / "кинематографичный лендинг", "scroll animation" / "скролл-анимация", "dashboard style" / "стиль дашборда", "design tokens" / "дизайн-токены", "light/dark theme" / "светлая/тёмная тема", "figma variables / figma to code" / "переменные фигмы, фигма в код", "chat/agent UI" / "интерфейс чата или агента", "streaming output" / "стриминг ответа", "mobile screen" / "мобильный экран".
4
4
  license: MIT
5
5
  metadata:
6
- version: 1.24.0
6
+ version: 1.26.0
7
7
  ---
8
8
 
9
9
  # SHELEG Design
@@ -61,7 +61,12 @@ the runnable palette validation; a pack is the *parameter set* it consumes.
61
61
  This table is by *role*, not by token, because the names are not uniform across
62
62
  the seventeen: only `--bg` and `--ink` resolve in every pack. The accent is
63
63
  `--accent` in fifteen, `--brand` in `field-notes` and `--cta` in `orchard` (each
64
- declares `@role accent:` in its token layer). Status colours are the least uniform
64
+ declares `@role accent:` in its token layer). **A second marker joins it in 1.26.0:**
65
+ a semantic colour that sits below WCAG AA on its own field declares
66
+ `@role non-text:` with the reason and the ratio, because eleven of the seventeen packs
67
+ had such a colour and each said so in a different phrasing — "never text on the field",
68
+ "a FILL and large-text colour", "a fill, not a text colour", "No coral word under 24px",
69
+ "category marks" — which is exactly why nothing could check it. Status colours are the least uniform
65
70
  thing in the library, so the full map is here rather than summarised: the pair
66
71
  `--ok` / `--warn` in `workbench` and `instrument-console`; the pair `--good` /
67
72
  `--warning` in `blueprint`, `cyclorama`, `maquette`, `prism` and `showroom`;
@@ -116,9 +116,15 @@ Three rules carry this palette.
116
116
  white. `validate_palette.py` now recomputes every stated ratio.)*
117
117
  - **These are category marks, not statuses.** The pack labels *kinds* — an index
118
118
  type, a tier, a language — not health. Every pair clears both separation floors
119
- under all three dichromacies, so no secondary-encoding declaration is required
120
- here; label them anyway, because a legend of four coloured squares is a legend
121
- nobody reads.
119
+ under all three dichromacies, so telling two marks apart is not the problem here.
120
+ - **And a mark is never by colour alone, because on this stock it cannot be.**
121
+ *Separation* and *legibility on the field* are different questions, and the pack
122
+ answered only the first until 1.26.0. Measured against `--bg`: `--good` **2.40:1**,
123
+ `--warning` **1.72:1**, `--info` **1.21:1** — all three under the 3:1 floor a mark
124
+ needs to be understood on its own (WCAG 1.4.11). So each mark carries its name, and
125
+ what used to be advice here — *"label them anyway, because a legend of four coloured
126
+ squares is a legend nobody reads"* — is the requirement. The three are declared
127
+ `@role non-text:` in the token layer, and `validate_status_on_field()` is what asked.
122
128
  - **`--ink-faint` is furniture.** At 2.5:1 it draws a grid line or a tick and
123
129
  never a word.
124
130
 
@@ -45,6 +45,11 @@ Ready-made token layer: [`tokens/editorial-luxury.css`](./tokens/editorial-luxur
45
45
  | `--accent-on-dark` | `#9fd9bc` | the accent brightened for espresso sections |
46
46
  | `--terra` | `#b5623f` | rare editorial highlight only |
47
47
  | `--red` | `#a83a2b` | negatives ONLY (comparison "without") |
48
+ | `--status-ok` | `#2f5e47` | healthy / active / done — **= `--accent-deep`**, not a new hue |
49
+ | `--status-warn` | `#7d5416` | a human is needed, and nothing else |
50
+ | `--status-info` | `#2f5c7d` | running / working |
51
+ | `--status-danger` | `#a83a2b` | failed — **= `--red`**, unchanged |
52
+ | each `-weak` | `rgba(…, 0.12)` of its own hue | chip and banner fill; the status **word** on top is `--ink` |
48
53
  | `--hair` / `-strong` | `rgba(36,28,20,0.13)` / `rgba(36,28,20,0.22)` | hairline rules |
49
54
 
50
55
  Contrast: body on cream must clear 4.5:1 — `--ink` / `--ink-soft`, never
@@ -74,7 +79,10 @@ sage accent switches to `--accent-on-dark`.
74
79
  0.3, 1)`; the pack wins, per SHELEG_DESIGN §10.
75
80
  - Spring `cubic-bezier(0.32, 0.72, 0, 1)` — press/magnetic feedback only.
76
81
  - Base duration `0.7s` for brand-register reveals; product register stays in
77
- the SHELEG fast/base range (≤0.32s).
82
+ the SHELEG fast/base range (≤0.32s), and that range now ships as values rather
83
+ than as a sentence: `--dur-hover` `0.12s` for a paint-only hover, `--dur-state`
84
+ `0.18s` for a state change. Prose-only, every product surface built on this
85
+ pack typed `0.15s` literals — there was nothing to reference.
78
86
 
79
87
  ## Signature motifs
80
88
 
@@ -114,11 +122,21 @@ How this pack rides the SHELEG motion layer (brand register only):
114
122
 
115
123
  ## Bans
116
124
 
117
- - **A status palette this pack barely has.** `editorial-luxury` ships one
118
- semantic colour, `--red`, for negatives only. There is no green, amber or
119
- blue here to confuse with it. State is carried by a word; if a surface needs
120
- a full status set, that is a gap to close in this pack deliberately, not a
121
- set of hues invented at the keyboard status is **never by colour alone**.
125
+ - **The status set is four colours and no more, and three of them already
126
+ existed.** This pack shipped one semantic colour, `--red`, and told any surface
127
+ needing a full set to close the gap *in the pack, deliberately, not at the
128
+ keyboard*. It was closed on 2026-08-13, and what closed it is the evidence for
129
+ why the instruction was worth writing: an admin console had invented
130
+ amber-as-warning and cyan-as-info on top of an amber that was also its entire
131
+ chrome — so the hue meaning "a provider is backing up" was the hue of every
132
+ button on the page, and seven of its pairs sat below AA with three under
133
+ 1.1:1. `--status-ok` is `--accent-deep` unchanged, `--status-danger` is `--red`
134
+ unchanged, and only `--status-warn` and `--status-info` are new values, each
135
+ the existing amber and cyan family deepened until it clears **4.5:1 on all
136
+ three grounds this pack renders on** — `--paper`, `--paper-2` and `--paper-3`.
137
+ Two grounds would have been the same enumerated-list hole one level down.
138
+ Status is still **never by colour alone**: the tint carries the colour, the
139
+ word carries the meaning in `--ink` (11.8:1 on every tint).
122
140
  *(Corrected 2026-08-10: this bullet was copy-pasted into six of the twelve and
123
141
  claimed a measurement across "several pairs" that one colour cannot form.)*
124
142
  - No gradient text, no side-stripe accent borders, no glassmorphism, no
@@ -121,6 +121,15 @@ Three rules carry this palette.
121
121
  The reason there is one token is that 4.64:1 clears AA in the hardest role it
122
122
  takes (text), so the link, the focus ring and the filled button can share it
123
123
  with no "on-dark" variant. Do not add one.
124
+ - **[CORRECTION — 1.26.0] The chip's label is `--ink`, not its status colour.** Until
125
+ 1.26.0 this row read *"that status's ink"*, and the kit did what it said: the label was
126
+ painted in the status colour, which measures **2.03:1** for `--good` on
127
+ `--accent-wash`, **1.54:1** for `--warning` and **2.65:1** for `--danger` on
128
+ `--surface-2` — the pack's own status colours, unreadable at the 12px the same row
129
+ specifies. `--ink` on those fills is 14.65–14.73:1. The status colour keeps the tint
130
+ and gains a 6px dot, which is the *"never by colour alone"* rule below applied to the
131
+ component that exists to serve it. The three status tokens are declared
132
+ `@role non-text:` in the token layer as a result.
124
133
  - **Status is never by colour alone.** `--good` and `--danger` separate by 33.7 at
125
134
  full colour but only **4.9 under deuteranopia**, which is the classic pair and
126
135
  the one nobody catches by looking. Every status renders as a chip containing its
@@ -185,7 +194,7 @@ Measured off the reference unless a row says **pack decision**.
185
194
  | **Nav item** | transparent, `--radius-lg`, `0 12px`, 15px/500, `--ink-2` | fill → `--surface-2`, colour → `--ink` over `0.3s` | — | — |
186
195
  | **Specimen frame** | `--surface` fill, `--radius-3xl`, `--shadow-specimen`, a 3-dot title bar | none — it is an exhibit, not a control | — | — |
187
196
  | **Data row** | transparent on `--surface`, `1px --line-weak` bottom rule, 36px tall | fill → `--accent-wash` | fill → `--accent-wash`, left edge 2px `--accent` | — |
188
- | **Status chip** | tinted fill of its own status at ~12%, that status's ink, `--radius-sm`, `2px 8px`, 12px mono, **always with its word** | none | — | — |
197
+ | **Status chip** | tinted fill of its own status, **`--ink` as the label**, a 6px dot of the status colour before the word, `--radius-sm`, `2px 8px`, 12px mono, **always with its word** | none | — | — |
189
198
  | **Column header** | `--ink-soft`, 12px/500, an icon at 14px, `1px --line` bottom | fill → `--surface-2` | sorted: `--ink` plus a caret | — |
190
199
  | **Input** | `--surface`, `1px --line`, `--radius-lg`, `8px 12px`, **16px** Inter | border → `--line-strong` | focus: `--accent` border plus `--ring-focus` | `--disabled` text |
191
200
  | **Loader** | **pack decision:** a skeleton whose geometry matches the row it replaces — same height, same radius, `--surface-2` fill, no shimmer | — | — | — |
@@ -24,6 +24,10 @@
24
24
  --accent-gradient: linear-gradient(#b05a36, rgba(216, 161, 131, 0.96));
25
25
 
26
26
  /* ---- Semantics (fills and icons — NOT text; see Gotchas) ------------- */
27
+ /* @role non-text: --good, --info — the pack has always said so in words ("fills and
28
+ icons only", "never carries a word"); this is the same statement in the one form a
29
+ gate can read. --good 2.11:1 and --info 3.41:1 on the field, and --danger 5.0:1 is
30
+ the one semantic here that may be text. */
27
31
  --good: #79bd8b; /* 2.1:1 on --bg — never carries a word */
28
32
  --good-tint: #f1f3e5;
29
33
  --info: #488ad5; /* 3.4:1 — large text / UI marks only */
@@ -55,6 +55,11 @@
55
55
  /* Every pair clears both separation floors under all three dichromacies, so
56
56
  no secondary-encoding declaration is required here. It is still good
57
57
  practice to label them; the pack asks for it without needing to. */
58
+ /* @role non-text: --good, --info, --warning — on a drawing sheet these four are
59
+ CATEGORY MARKS, not labels: a swatch, a rule, a chip fill. Measured on the field:
60
+ --good 2.40:1, --warning 1.72:1, --info 1.21:1. All three are under the 3:1 non-text
61
+ floor as well, so the word beside the mark is the message and the pack says status is
62
+ never by colour alone. */
58
63
  --good: #86af80;
59
64
  --info: #8bf6ff;
60
65
  --danger: #a440ce;
@@ -66,6 +66,9 @@
66
66
  #C56200 3.01:1 (large text only), 1.4 from --warning under protanopia
67
67
  There is no text-safe orange in this palette. That is a property of the
68
68
  reference, not a shortcut taken here. */
69
+ /* @role non-text: --accent, --info, --warning — the accent's own declaration has
70
+ always read "NEVER text on the field", and the two statuses render as a mark plus
71
+ their word. Measured: --accent 1.86:1, --warning 3.78:1, --info 4.13:1. */
69
72
  --accent: #ff8c00; /* fill / dot / chart series. NEVER text on the field */
70
73
  --accent-hover: #e69900; /* the accent's hover fill */
71
74
  --on-accent: #1a1a1a; /* 7.46:1 on --accent, 7.39:1 on --accent-hover */
@@ -53,6 +53,8 @@
53
53
  floor for a non-text mark and for large text (>=24px). Its measured jobs on
54
54
  the reference are exactly those: one word of a 48px headline, the mono
55
55
  visitor id, a 2px focus ring, a tick. It is never body copy. */
56
+ /* @role non-text: --accent — 3.17:1 on the field. This pack's accent is a fill, a
57
+ rule and an alarm tint; --accent-ink is what carries a word. */
56
58
  --accent: #f35b22; /* MEASURED — its --orange-7 */
57
59
  --accent-deep: #be400f; /* 5.11:1 on --bg — MEASURED (--orange-8): the one orange that may carry a word */
58
60
  --accent-wash: #ffeadc; /* MEASURED — selected cell, hovered row (--orange-2) */
@@ -19,6 +19,25 @@
19
19
  --accent-on-dark: #9fd9bc;
20
20
  --terra: #b5623f;
21
21
  --red: #a83a2b;
22
+
23
+ /* Status set — added 2026-08-13. Three of the four are values this pack
24
+ already owned; only warn and info are new, each its own family deepened
25
+ until it clears the AA floor for normal text on every cream ground this
26
+ pack renders on, not only the two obvious ones. The three ratios beside
27
+ each value are those grounds, lightest first. The status word renders in
28
+ --ink on the -weak tint, so status is never carried by colour alone. */
29
+ --status-ok: #2f5e47; /* 6.93 / 6.25 / 5.72 — = --accent-deep */
30
+ --status-warn: #7d5416; /* 6.19 / 5.58 / 5.11 */
31
+ --status-info: #2f5c7d; /* 6.61 / 5.96 / 5.46 */
32
+ --status-danger: #a83a2b; /* 5.90 / 5.32 / 4.87 — = --red */
33
+ --status-ok-weak: rgba(47,94,71,0.12);
34
+ --status-warn-weak: rgba(125,84,22,0.12);
35
+ --status-info-weak: rgba(47,92,125,0.12);
36
+ --status-danger-weak: rgba(168,58,43,0.12);
37
+
38
+ /* Product register durations — the range the pack described in prose. */
39
+ --dur-hover: 0.12s;
40
+ --dur-state: 0.18s;
22
41
  --hair: rgba(36, 28, 20, 0.13);
23
42
  --hair-strong: rgba(36, 28, 20, 0.22);
24
43
 
@@ -53,6 +53,9 @@
53
53
  spends it on the wordmark, the button fill, a 12px label chip and a link.
54
54
  The chip and the link are the two that do not clear the floor; see
55
55
  --accent-ink, which is what this pack paints a word in. */
56
+ /* @role non-text: --accent, --action — 3.61:1 on cream, which is why the pack's own
57
+ Bans read "No coral word under 24px" and point at --accent-ink. The coral fills, it
58
+ rules, and it sets large type; it does not set a sentence. */
56
59
  --accent: #eb3514; /* MEASURED — its --color-coral, and its <meta name=theme-color> */
57
60
  --accent-ink: #660202; /* 11.59:1 on --bg — MEASURED (--color-burgundy): the accent that may carry a word */
58
61
  --accent-wash: #f0e0da; /* COMPOSITED — bg-coral/8 over --bg */
@@ -18,6 +18,9 @@
18
18
  --on-primary: #fffef4; /* only on --primary-deep, never on --primary */
19
19
 
20
20
  /* ---- Brand green ----------------------------------------------------- */
21
+ /* @role non-text: --primary, --cta — sage at 3.42:1 and the candy orange at 2.75:1
22
+ are fills and large-text colours, which both declarations already say; --primary-deep
23
+ is the text-safe green at 4.9:1. */
21
24
  --primary: #78934a; /* sage — 3.4:1 on --bg: a FILL and large-text colour */
22
25
  --primary-deep: #61783b; /* the text-safe green: 4.9:1 under --on-primary */
23
26
  --primary-light: #aaca73; /* the mark / illustration green */
@@ -38,6 +38,11 @@
38
38
  This pack does not propagate it: the cyan is a fill, a rule, a chart series
39
39
  and a mark. Ink on it is 8.02:1, which is what --on-accent is for. Where a
40
40
  heading needs the accent's family in text, it takes --accent-ink. */
41
+ /* @role non-text: --accent, --good, --info, --warning — an iridescent pack whose
42
+ status hues are mint, cyan and orange: --good 1.33:1, --accent 2.36:1, --info 2.36:1,
43
+ --warning 3.33:1 on the field. The pack already calls --good "a fill, not a text
44
+ colour"; the other three are the same kind. Three of the four are under the 3:1
45
+ non-text floor, so the word carries the meaning and the pack says so. */
41
46
  --accent: #00b3ff; /* FILL ONLY — 2.36:1 as text on --bg */
42
47
  --accent-hover: #0077ad;
43
48
  --accent-ink: #1493cc; /* 3.4:1 — large display text only, never body */
@@ -74,6 +74,9 @@
74
74
  /* Both of the reference's oranges fail AA as a text or label colour: white on #f25533
75
75
  is 3.43:1 at the nav pill's 16px/600, and #fa5c12 is 3.18:1 on white. The accent is
76
76
  therefore a FILL and a large-text colour; --accent-ink is what carries a word. */
77
+ /* @role non-text: --accent — 3.18:1 on the field, which is legal for the 68px
78
+ headline phrase as large text and for a fill, and illegal for anything smaller.
79
+ --accent-ink is what carries a word. */
77
80
  --accent: #fa5c12; /* MEASURED — the headline phrase and the gradient's head */
78
81
  /* The reference spends this on its nav fill AND on its eyebrows. The pack keeps it for
79
82
  the fill and overrides the eyebrow to --accent-ink, because at the eyebrow's 16px this
@@ -46,6 +46,9 @@
46
46
  non-text mark. The reference obeys this without saying so: its orange is a
47
47
  3x18 tick, a ::marker, a focus ring, a link underline and one oversized
48
48
  chevron. Its primary button is ink. See the pack's Palette. */
49
+ /* @role non-text: --accent — 3.23:1 on the paper. In this pack the accent "only
50
+ marks": a 3x18px tick, a chip edge, a rule. --accent-ink is the one that may carry a
51
+ word, and the 1.13.1 correction says no orange here may carry a link. */
49
52
  --accent: #ff4801;
50
53
  --accent-hover: #e03d00; /* 4.12:1 on --bg — the one orange that may carry a link */
51
54
  --accent-wash: #fff7ed; /* MEASURED — selected row, hovered cell */
@@ -228,6 +231,19 @@
228
231
  --action: #ffffff;
229
232
  --action-hover: #e4e4e7;
230
233
  --on-action: #0a0a0a;
234
+
235
+ /* The status set has to be remapped here, and until 1.26.0 it was not. The block
236
+ already remapped eleven tokens for exactly this reason -- --ink-soft among them --
237
+ and left --good, --warn, --danger and --info pointing at the paper values, so
238
+ `var(--good)` painted 3.69:1 on this band while --good-on-dark had 10.21 waiting
239
+ three lines away in the same file. The pack has always said to use the on-dark set
240
+ here; nothing made the token layer do it, because the palette gate checked ink
241
+ against field and never a status against field. validate_status_on_field() checks it
242
+ now, and this is the defect it was written for (board B-034). */
243
+ --good: #00d492; /* 10.21:1 on this band — was inheriting the paper value at 3.69 */
244
+ --warn: #ffb900; /* 11.49:1 — was 3.94 */
245
+ --danger: #ff637e; /* 6.92:1 — was 2.37 */
246
+ --info: #51a2ff; /* 7.51:1 — was 2.90 */
231
247
  }
232
248
 
233
249
  /* The reference ships no reduced-motion branch. This pack requires one: every
@@ -55,6 +55,12 @@
55
55
  deuteranopia — the classic pair, under the floor of 8. Legal only with the
56
56
  secondary encoding the pack declares: a status is a chip with a word in it,
57
57
  never a bare dot. */
58
+ /* @role non-text: --good, --warning, --danger — 2.33:1, 1.78:1 and 3.05:1 on the
59
+ field. Since 1.26.0 the status chip's LABEL is --ink and these three are the chip's
60
+ tint and its 6px dot; before that the label was painted in them, at 2.03, 1.54 and
61
+ 2.65 against the fills the kit actually uses. All three are at or under the 3:1
62
+ non-text floor, so the word is the message — which is what the pack's own "never by
63
+ colour alone" rule has always said. */
58
64
  --good: #0fc27b;
59
65
  --warning: #f5b900;
60
66
  --danger: #ff5b59;
@@ -9,6 +9,10 @@
9
9
  --muted: #5b6472;
10
10
  --border: #e6e9ef;
11
11
  --border-strong: #d7dce4;
12
+ /* @role non-text: --accent, --info — 4.30:1 in both themes, just under AA. The accent
13
+ fills buttons and marks selection and --accent-ink is the text that sits ON it;
14
+ --info deliberately IS the accent hue, for "running / working", and it is a dot and a
15
+ bar rather than a word. */
12
16
  --accent: #2f6feb;
13
17
  --accent-weak: #eaf0fe;
14
18
  --accent-ink: #ffffff; /* text ON the accent — 4.57:1 on --accent */