Skip to content

Commit ace247a

Browse files
committed
Refresh the Impeccable artifact layer for v2.14.0
Re-derived DESIGN.md and .impeccable/design.json from the shipped tree, so the frontmatter rule in check:tokens goes green again. - --warning, --type-document and --type-entity-organisations move to #d66800 in the frontmatter, the sidecar's colorMeta and its typeMap. The prose records why the hue is calibrated as a non-text mark: the two type tokens are dots, and 63.5% lightness clears 3:1 on all four light surfaces where 66% cleared on two. - New "Accent-Not-Text Rule": --primary as running prose measures 4.67 / 4.51 / 4.40 / 4.25:1 across the four light surfaces, so it is scoped to links, controls, state, large display, marks and non-text. Darkening the token is not the fix -- the seed is admin-configurable. - "The Redeclare-In-Dark Rule" becomes "The Mirror-Both-Blocks Rule", now stated in both directions, and the depth vocabulary is recorded as a declared mirror rather than a :root composition, matching the light/dark mixin pair. - New "One-Spelling Breakpoint Rule"; the No-14px rule now says clamp() is not an escape hatch. - The provenance comment stops calling the guard a roadmap item: it exists, and the file is generated -- refresh it with the documenter, never by hand-editing a value to satisfy the check. .impeccable/config.json carries two sanctioned detector ignore-values, the dark --warning-bg/--error-bg mix bases. They are long-shipped and already recorded in the sidecar's purpose strings; the hook's matcher cannot read prose.
1 parent 8064ef9 commit ace247a

3 files changed

Lines changed: 112 additions & 42 deletions

File tree

.impeccable/config.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"detector": {
3+
"ignoreRules": [],
4+
"ignoreFiles": [],
5+
"ignoreValues": [
6+
{
7+
"rule": "design-system-color",
8+
"value": "oklch(55% 0.16 60)",
9+
"createdAt": "2026-08-25T04:56:31.081Z"
10+
},
11+
{
12+
"rule": "design-system-color",
13+
"value": "oklch(50% 0.18 25)",
14+
"createdAt": "2026-08-25T04:56:31.229Z"
15+
}
16+
]
17+
}
18+
}

.impeccable/design.json

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"schemaVersion": 2,
3-
"generatedAt": "2026-08-24T00:00:00Z",
3+
"generatedAt": "2026-08-25T00:00:00Z",
44
"title": "Design System: IWAC Theme",
55
"extensions": {
66
"colorMeta": {
@@ -231,7 +231,7 @@
231231
"warning": {
232232
"role": "status",
233233
"displayName": "Warning Amber",
234-
"canonical": "#de7000",
234+
"canonical": "#d66800",
235235
"dark": "#f99532",
236236
"tonalRamp": [
237237
"#340000",
@@ -242,7 +242,8 @@
242242
"#f4842b",
243243
"#ffa956",
244244
"#ffcd7c"
245-
]
245+
],
246+
"purpose": "oklch(63.5% 0.18 60), darkened from 66% in 2.14. Also --type-document and --type-entity-organisations, which render as solid dots, so it is calibrated as a non-text mark: 3:1 on all four light surfaces (3.51 / 3.40 / 3.31 / 3.20). Chroma holds at 0.18, keeping the amber identity and ~16 degrees of hue separation from --primary. Not a text colour at any lightness that stays amber. Dark unchanged."
246247
},
247248
"error": {
248249
"role": "status",
@@ -414,61 +415,61 @@
414415
"name": "shadow-xs",
415416
"value": "0 1px 2px 0 rgba(20, 22, 27, 0.06)",
416417
"dark": "0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 2px -1px rgba(0, 0, 0, 0.1)",
417-
"purpose": "Minimal ambient depth."
418+
"purpose": "Minimal ambient depth. Declared in BOTH theme mixins, not composed once on :root."
418419
},
419420
{
420421
"name": "shadow-sm",
421422
"value": "0 1px 3px 0 rgba(9, 11, 15, 0.12), 0 1px 2px -1px rgba(20, 22, 27, 0.06)",
422423
"dark": "0 2px 6px -1px rgba(0, 0, 0, 0.18), 0 2px 4px -2px rgba(0, 0, 0, 0.12)",
423-
"purpose": "Default panel shadow (= --panel-shadow)."
424+
"purpose": "Default panel shadow (= --panel-shadow). Declared in BOTH theme mixins, not composed once on :root."
424425
},
425426
{
426427
"name": "shadow-md",
427428
"value": "0 4px 6px -1px rgba(9, 11, 15, 0.12), 0 2px 4px -2px rgba(20, 22, 27, 0.06)",
428429
"dark": "0 6px 16px -4px rgba(0, 0, 0, 0.25), 0 4px 8px -4px rgba(0, 0, 0, 0.25)",
429-
"purpose": "Dropdowns and floating panels."
430+
"purpose": "Dropdowns and floating panels. Declared in BOTH theme mixins, not composed once on :root."
430431
},
431432
{
432433
"name": "shadow-lg",
433434
"value": "0 10px 15px -3px rgba(9, 11, 15, 0.12), 0 4px 6px -4px rgba(20, 22, 27, 0.06)",
434435
"dark": "0 12px 28px -8px rgba(0, 0, 0, 0.35), 0 8px 16px -8px rgba(0, 0, 0, 0.2)",
435-
"purpose": "Modals; rare. Dark is struck from pure black at higher alpha and greater spread — a neutral-ink shadow is invisible on a lamplit ground."
436+
"purpose": "Modals; rare. Dark is struck from pure black at higher alpha and greater spread — a neutral-ink shadow is invisible on a lamplit ground. Declared in BOTH theme mixins, not composed once on :root."
436437
},
437438
{
438439
"name": "glow-xs",
439440
"value": "0 1px 3px 0 rgba(206, 65, 21, 0.2)",
440441
"dark": "0 1px 3px 0 rgba(236, 101, 63, 0.2)",
441-
"purpose": "Filled-primary button, active/pressed."
442+
"purpose": "Filled-primary button, active/pressed. Declared in BOTH theme mixins, not composed once on :root."
442443
},
443444
{
444445
"name": "glow-sm",
445446
"value": "0 2px 6px -1px rgba(206, 65, 21, 0.25)",
446447
"dark": "0 2px 6px -1px rgba(236, 101, 63, 0.25)",
447-
"purpose": "Filled-primary button at rest."
448+
"purpose": "Filled-primary button at rest. Declared in BOTH theme mixins, not composed once on :root."
448449
},
449450
{
450451
"name": "glow-md",
451452
"value": "0 4px 12px -2px rgba(206, 65, 21, 0.3)",
452453
"dark": "0 4px 12px -2px rgba(236, 101, 63, 0.3)",
453-
"purpose": "Filled-primary button on hover."
454+
"purpose": "Filled-primary button on hover. Declared in BOTH theme mixins, not composed once on :root."
454455
},
455456
{
456457
"name": "focus-outline",
457458
"value": "2px solid #ce4115",
458459
"dark": "2px solid #ec653f",
459-
"purpose": "THE default focus indicator, paired with outline-offset: 2px."
460+
"purpose": "THE default focus indicator, paired with outline-offset: 2px. Declared in BOTH theme mixins, not composed once on :root."
460461
},
461462
{
462463
"name": "ring-focus",
463464
"value": "0 0 0 3px rgba(206, 65, 21, 0.3)",
464465
"dark": "0 0 0 3px rgba(236, 101, 63, 0.35)",
465-
"purpose": "Box-shadow focus ring — the exception, for controls an outline would be clipped on. Also the input focus ring, paired with a primary border."
466+
"purpose": "Box-shadow focus ring — the exception, for controls an outline would be clipped on. Also the input focus ring, paired with a primary border. Declared in BOTH theme mixins, not composed once on :root."
466467
},
467468
{
468469
"name": "ring-focus-sm",
469470
"value": "0 0 0 2px rgba(206, 65, 21, 0.22)",
470471
"dark": "0 0 0 2px rgba(236, 101, 63, 0.22)",
471-
"purpose": "Tighter 2px ring for dense controls."
472+
"purpose": "Tighter 2px ring for dense controls. Declared in BOTH theme mixins, not composed once on :root."
472473
}
473474
],
474475
"motion": [
@@ -539,7 +540,7 @@
539540
},
540541
{
541542
"name": "--type-document",
542-
"light": "#de7000",
543+
"light": "#d66800",
543544
"dark": "#f99532"
544545
},
545546
{
@@ -564,7 +565,7 @@
564565
},
565566
{
566567
"name": "--type-entity-organisations",
567-
"light": "#de7000",
568+
"light": "#d66800",
568569
"dark": "#f99532"
569570
}
570571
]
@@ -782,7 +783,7 @@
782783
"Density over comfort; typography as architecture, rules as joinery",
783784
"Near-white cool-neutral light mode; warm \"lamplit reading room\" dark mode",
784785
"One brand accent used sparingly; category encoded as dots, never fills",
785-
"Two themes declared in full — dark is a redeclaration, never an inherited substitution",
786+
"Two themes declared in full as a mixin pair — dark is a redeclaration, never an inherited substitution, and light is never a gap",
786787
"AI-generated content visibly marked (computational honesty)"
787788
],
788789
"rules": [
@@ -796,14 +797,19 @@
796797
"body": "Category is a dot on an outlined chip, never a filled or pastel badge. Ten filled badges per screen is shouting; ten dots is an index.",
797798
"section": "colors"
798799
},
800+
{
801+
"name": "The Accent-Not-Text Rule",
802+
"body": "`--primary` is an accent, never running body text. As text it measures 4.67 / 4.51 / 4.40 / 4.25:1 on surface / raised / background / sunken — it clears AA on the two lightest surfaces and misses on the other two, so the same declaration passes or fails depending on which panel it lands in. Sanctioned: links, controls and their states, current/active state, large display text (the 404 numeral, KPI figures), pseudo-element marks (interpuncts, `::marker`, chevrons, dots), and every non-text use. Not sanctioned: paragraph, list-item or table-cell copy, ledes, abstracts, anything set from `--font-serif-text`. Darkening the token is not the fix — the brand seed is admin-configurable, so scoping the use is the durable answer.",
803+
"section": "colors"
804+
},
799805
{
800806
"name": "The Data-Only Secondary Rule",
801807
"body": "#394f68 exists for data. If it appears on a button, link, or focus ring, it is a bug.",
802808
"section": "colors"
803809
},
804810
{
805-
"name": "The Redeclare-In-Dark Rule",
806-
"body": "A token whose value contains `var(…)` must be declared in the dark block too. Declared once on `:root`, it is substituted against light referents and inherited frozen by every dark descendant — a whole-theme failure that looks like nothing until a contrast probe finds it.",
811+
"name": "The Mirror-Both-Blocks Rule",
812+
"body": "A custom property is substituted at computed-value time on the element that *declares* it, so a token whose value contains `var(…)` must be declared in BOTH theme blocks, and the rule runs both directions. A light-only composition is substituted against light referents and inherited frozen by every dark descendant. A dark-only declaration is worse-hidden still: with no light counterpart, `body[data-theme=\"light\"]` cannot win back on a dark-OS machine. Since 2.14 the light and dark token blocks are a mixin pair carrying the same names — shadows, panel tokens, glows and the three composed focus tokens are declared twice by construction.",
807813
"section": "colors"
808814
},
809815
{
@@ -823,7 +829,7 @@
823829
},
824830
{
825831
"name": "The No-14px Rule",
826-
"body": "The type scale deliberately skips 14px; 11px (`--text-2xs`) is the floor. Absolute `font-size` literals fail `npm run check:tokens` — use `--text-*` tokens.",
832+
"body": "The type scale deliberately skips 14px; 11px (`--text-2xs`) is the floor. Absolute `font-size` literals fail `npm run check:tokens` — use `--text-*` tokens. The check reads the whole value, not just its first token, so `clamp()`, `min()` and `max()` are not an escape hatch: a fluid size composes `--text-*` steps (or `calc()` on them).",
827833
"section": "typography"
828834
},
829835
{
@@ -850,11 +856,16 @@
850856
"name": "The Outline-First Focus Rule",
851857
"body": "`--focus-outline` at 2px offset is the focus indicator. The `--ring-focus` halo is the exception for clipped or oddly-shaped controls, never the default — it is the weaker indicator, and reaching for it by habit is how a surface ends up with no visible focus at all.",
852858
"section": "elevation-and-depth"
859+
},
860+
{
861+
"name": "The One-Spelling Breakpoint Rule",
862+
"body": "`min-width` sits ON a published breakpoint and `max-width` at breakpoint − 1px — one spelling, not two. Bootstrap's − 0.02px was tolerated until 2.14 and the theme carried both idioms in files that reflow the same header; neither is wrong alone, carrying both is. Guards in this repo and in both consumer modules assert it. `@container` queries are exempt.",
863+
"section": "layout"
853864
}
854865
],
855866
"dos": [
856867
"**Do** open sections with 2px ink rules and divide rows with hairlines — typography is the architecture, rules are the joinery.",
857-
"**Do** declare every composed or aliased token in the dark block as well as the light one, so dark pages compose against dark referents.",
868+
"**Do** declare every composed or aliased token in **both** theme blocks — dark pages must compose against dark referents, and a manual light choice on a dark OS must be able to win back.",
858869
"**Do** publish any color that encodes data as a named light/dark pair in `tokens.json`, and let the guards compare it.",
859870
"**Do** keep transitions 150–300ms on `cubic-bezier(0.25, 1, 0.5, 1)`; real objects decelerate smoothly.",
860871
"**Do** mark AI-generated fields with the EU \"AI GENERATED\" provenance treatment on the label, and open the lede on an ink rule.",
@@ -865,12 +876,13 @@
865876
"donts": [
866877
"**Don't** use warm cream or parchment surfaces — wrong register (\"manuscript museum brochure\").",
867878
"**Don't** add atmospheric gradients, gradient bars, glassmorphism (outside the sticky header), or colored side-stripes.",
868-
"**Don't** fill category badges or color `h2`s in primary — category is a dot, primary is an accent.",
879+
"**Don't** fill category badges, color `h2`s, or set running prose in primary — category is a dot, primary is an accent (4.25:1 on sunken as text).",
869880
"**Don't** use cold blue-gray dark surfaces — dark mode is warm (\"lamplit\"), hue ~70–80.",
870881
"**Don't** box result rows, filter bars, linked-resource lists, or the AI lede into cards.",
871882
"**Don't** replace an outline focus indicator with the `--ring-focus` halo for convenience, and never with `outline: none` alone.",
872883
"**Don't** use bouncing/elastic easing or hover-triggered hero animations (the one-time Ken Burns settle is the sanctioned exception).",
873-
"**Don't** pair Besley with `tabular-nums` (broken tnum zero) or track it tighter than -0.01em."
884+
"**Don't** pair Besley with `tabular-nums` (broken tnum zero) or track it tighter than -0.01em.",
885+
"**Don't** write an absolute `font-size` literal, inside `clamp()` included, or spell the \"below\" half of a breakpoint pair any way but breakpoint − 1px."
874886
]
875887
}
876888
}

0 commit comments

Comments
 (0)