@adia-ai/adia-ui-forge 0.8.61 → 0.8.63

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 (142) hide show
  1. package/.claude-plugin/plugin.json +2 -2
  2. package/.codex-plugin/plugin.json +3 -3
  3. package/CHANGELOG.md +309 -274
  4. package/README.md +54 -18
  5. package/__init__.py +8 -8
  6. package/agents/a2ui-maintenance-agent.corpus.json +7 -7
  7. package/agents/a2ui-maintenance-agent.md +6 -6
  8. package/agents/a2ui-planner-agent.md +11 -11
  9. package/agents/demo-audit-agent.corpus.json +4 -4
  10. package/agents/demo-audit-agent.md +8 -9
  11. package/agents/framework-checker.md +10 -11
  12. package/agents/framework-planner-agent.md +9 -10
  13. package/agents/gen-ui-review-agent.corpus.json +4 -4
  14. package/agents/gen-ui-review-agent.md +8 -8
  15. package/agents/package-release-agent.corpus.json +4 -4
  16. package/agents/package-release-agent.md +9 -10
  17. package/agents/primitive-authoring-agent.corpus.json +1 -1
  18. package/agents/primitive-authoring-agent.md +5 -5
  19. package/agents/routing-corpus.json +46 -46
  20. package/codex.interface.json +1 -1
  21. package/commands/gen-ui-review.md +1 -1
  22. package/commands/package-release.md +2 -2
  23. package/package.json +1 -1
  24. package/plugin.yaml +2 -2
  25. package/prompts/gen-ui-review.md +1 -1
  26. package/prompts/package-release.md +2 -2
  27. package/references/contracts/a2ui-mcp-surface.md +7 -7
  28. package/references/contracts/migration-guide-format.md +6 -7
  29. package/references/shared/content-trust.md +3 -5
  30. package/skills/a2ui-maintenance/SKILL.md +13 -16
  31. package/skills/a2ui-maintenance/evals/evals.json +2 -2
  32. package/skills/a2ui-maintenance/evals/routing-corpus.json +1 -1
  33. package/skills/a2ui-maintenance/references/anti-patterns.md +10 -10
  34. package/skills/a2ui-maintenance/references/chunk-authoring.md +11 -12
  35. package/skills/a2ui-maintenance/references/corpus-discipline.md +13 -14
  36. package/skills/a2ui-maintenance/references/data-model-reactivity.md +22 -25
  37. package/skills/a2ui-maintenance/references/eval-diagnostics.md +21 -21
  38. package/skills/a2ui-maintenance/references/format-extension-decisions.md +12 -13
  39. package/skills/a2ui-maintenance/references/leverage-rules.md +6 -6
  40. package/skills/a2ui-maintenance/references/mcp-pipeline-ops.md +9 -10
  41. package/skills/a2ui-maintenance/references/mcp-tool-reference.md +11 -12
  42. package/skills/a2ui-maintenance/references/pipeline-overview.md +33 -38
  43. package/skills/a2ui-maintenance/references/semantic-fail-lifting.md +13 -14
  44. package/skills/a2ui-maintenance/references/strategy-engines.md +14 -16
  45. package/skills/a2ui-maintenance/references/surface-lifecycle.md +10 -11
  46. package/skills/a2ui-maintenance/references/zettel-calibration.md +14 -16
  47. package/skills/component-md-authoring/SKILL.md +20 -21
  48. package/skills/component-md-authoring/agents/openai.yaml +1 -1
  49. package/skills/cross-harness-authoring-standards/SKILL.md +19 -21
  50. package/skills/cross-harness-authoring-standards/agents/openai.yaml +1 -1
  51. package/skills/cross-harness-compatibility-standards/SKILL.md +34 -35
  52. package/skills/cross-harness-compatibility-standards/agents/openai.yaml +1 -1
  53. package/skills/demo-audit/SKILL.md +38 -38
  54. package/skills/demo-audit/evals/routing-corpus.json +1 -1
  55. package/skills/demo-audit/references/admin-shell-anatomy.md +21 -25
  56. package/skills/demo-audit/references/app-shell-pitfalls.md +11 -13
  57. package/skills/demo-audit/references/auto-fix-allowlist.md +3 -3
  58. package/skills/demo-audit/references/card-anatomy-sweep.md +10 -11
  59. package/skills/demo-audit/references/chat-shell-anatomy.md +25 -26
  60. package/skills/demo-audit/references/editor-shell-anatomy.md +25 -28
  61. package/skills/demo-audit/references/html-attr-sweep.md +12 -13
  62. package/skills/demo-audit/references/mode7-status-battery.md +4 -4
  63. package/skills/demo-audit/references/native-leak-annotations.md +8 -8
  64. package/skills/demo-audit/references/output-contract-worked-example.md +8 -8
  65. package/skills/demo-audit/references/probe-discipline-and-escalation.md +2 -2
  66. package/skills/demo-audit/references/visual-probe-triage.md +21 -23
  67. package/skills/gen-ui-review/SKILL.md +27 -27
  68. package/skills/gen-ui-review/agents/openai.yaml +1 -1
  69. package/skills/gen-ui-review/evals/routing-corpus.json +1 -1
  70. package/skills/gen-ui-review/references/corpus-html-patterns.md +28 -29
  71. package/skills/gen-ui-review/references/exit-gate-mechanics.md +5 -5
  72. package/skills/gen-ui-review/references/lookup-maintenance.md +2 -2
  73. package/skills/gen-ui-review/references/loop-protocol.md +25 -25
  74. package/skills/gen-ui-review/references/rubric-cosmetic.md +10 -10
  75. package/skills/gen-ui-review/references/rubric-decompose.md +15 -16
  76. package/skills/gen-ui-review/references/rubric-score.md +23 -24
  77. package/skills/gen-ui-review/references/scorecard-worked-examples.md +12 -12
  78. package/skills/gen-ui-review/references/scores.schema.json +2 -2
  79. package/skills/llm-client-maintenance/SKILL.md +12 -12
  80. package/skills/llm-client-maintenance/evals/routing-corpus.json +1 -1
  81. package/skills/llm-client-maintenance/references/adapter-contract.md +27 -27
  82. package/skills/llm-client-maintenance/references/add-a-provider.md +25 -25
  83. package/skills/llm-client-maintenance/references/bridge-facade.md +31 -31
  84. package/skills/llm-client-maintenance/references/browser-proxy-boundary.md +17 -17
  85. package/skills/llm-client-maintenance/references/model-registry.md +14 -14
  86. package/skills/llm-client-maintenance/references/streaming-sse.md +21 -21
  87. package/skills/package-release/SKILL.md +21 -23
  88. package/skills/package-release/evals/evals.json +2 -2
  89. package/skills/package-release/evals/routing-corpus.json +2 -2
  90. package/skills/package-release/references/authorization-model.md +6 -8
  91. package/skills/package-release/references/changelog-discipline.md +25 -27
  92. package/skills/package-release/references/cut-procedure.md +129 -136
  93. package/skills/package-release/references/gates-catalog.md +42 -43
  94. package/skills/package-release/references/independent-package-release.md +14 -15
  95. package/skills/package-release/references/invariants-detail.md +9 -9
  96. package/skills/package-release/references/mechanization.md +11 -12
  97. package/skills/package-release/references/migration-guide-authoring.md +20 -20
  98. package/skills/package-release/references/notes-authoring.md +20 -20
  99. package/skills/package-release/references/recovery-paths.md +28 -28
  100. package/skills/primitive-authoring/SKILL.md +19 -19
  101. package/skills/primitive-authoring/agents/openai.yaml +1 -1
  102. package/skills/primitive-authoring/evals/evals.json +2 -2
  103. package/skills/primitive-authoring/evals/routing-corpus.json +2 -2
  104. package/skills/primitive-authoring/references/INDEX.md +22 -22
  105. package/skills/primitive-authoring/references/anti-patterns.md +76 -77
  106. package/skills/primitive-authoring/references/api-contract.md +63 -65
  107. package/skills/primitive-authoring/references/authoring-cycle.md +47 -50
  108. package/skills/primitive-authoring/references/canonical-pattern-index.md +118 -118
  109. package/skills/primitive-authoring/references/code-style.md +50 -52
  110. package/skills/primitive-authoring/references/common-gotchas.md +23 -23
  111. package/skills/primitive-authoring/references/composite-demo-protocol.md +42 -45
  112. package/skills/primitive-authoring/references/css-patterns.md +97 -97
  113. package/skills/primitive-authoring/references/for-attribute-event-contract.md +12 -12
  114. package/skills/primitive-authoring/references/form-control-sizing.md +16 -18
  115. package/skills/primitive-authoring/references/lifecycle-patterns.md +42 -42
  116. package/skills/primitive-authoring/references/llm-bridge.md +21 -21
  117. package/skills/primitive-authoring/references/module-promotion.md +51 -51
  118. package/skills/primitive-authoring/references/primitive-audit.md +16 -16
  119. package/skills/primitive-authoring/references/shell-patterns.md +81 -81
  120. package/skills/primitive-authoring/references/svg-authoring.md +62 -66
  121. package/skills/primitive-authoring/references/token-contract.md +45 -49
  122. package/skills/primitive-authoring/references/trait-pages.md +8 -8
  123. package/skills/primitive-authoring/references/worked-example.md +39 -39
  124. package/skills/primitive-authoring/references/yaml-contract.md +139 -143
  125. package/skills/site-deployment/SKILL.md +25 -26
  126. package/skills/site-deployment/agents/openai.yaml +1 -1
  127. package/skills/site-deployment/evals/routing-corpus.json +1 -1
  128. package/skills/site-deployment/references/deploy-playbooks.md +51 -53
  129. package/skills/site-docs-authoring/SKILL.md +14 -14
  130. package/skills/site-docs-authoring/agents/openai.yaml +1 -1
  131. package/skills/site-docs-authoring/evals/evals.json +1 -1
  132. package/skills/site-docs-authoring/evals/routing-corpus.json +1 -1
  133. package/skills/site-docs-authoring/intent.md +18 -18
  134. package/skills/ssr-compatibility/SKILL.md +24 -25
  135. package/skills/ssr-compatibility/agents/openai.yaml +1 -1
  136. package/skills/ssr-compatibility/evals/evals.json +1 -1
  137. package/skills/ssr-compatibility/evals/routing-corpus.json +1 -1
  138. package/skills/ssr-compatibility/references/consumer-workarounds.md +22 -23
  139. package/skills/ssr-compatibility/references/failure-shapes.md +84 -91
  140. package/skills/ssr-compatibility/references/guard-patterns.md +54 -58
  141. package/skills/ssr-compatibility/references/status-ledger.md +17 -17
  142. package/skills/ssr-compatibility/references/test-without-linkedom.md +26 -27
@@ -1,8 +1,8 @@
1
- # SVG authoring coordinate, color, and hit-testing quirks
1
+ # SVG authoring, coordinate, color, and hit-testing quirks
2
2
 
3
3
  SVG content behaves differently from HTML in ways that don't show up until a
4
4
  primitive is placed inside a themed, resizable, or bled container. This file
5
- collects the SVG-specific rules everything else about authoring a
5
+ collects the SVG-specific rules, everything else about authoring a
6
6
  primitive (yaml, tokens, lifecycle) is the rest of this skill's charter, not
7
7
  repeated here. Load this file when modifying `chart-ui`, `qr-code-ui`,
8
8
  `icon-ui`, or authoring any NEW primitive whose `class.js` builds `<svg>`
@@ -10,98 +10,98 @@ markup (via `document.createElementNS`/`innerHTML`) rather than plain HTML.
10
10
 
11
11
  ## 0. Which primitives actually render SVG (scope check first)
12
12
 
13
- Not every chart-family or chart-adjacent primitive renders SVG check
13
+ Not every chart-family or chart-adjacent primitive renders SVG, check
14
14
  before assuming this file applies:
15
15
 
16
- - **Genuinely SVG-rendered**: `chart-ui` (`packages/web-components/components/chart/chart.class.js` builds a `<svg>` string per chart type, §§#renderBar/#renderLine/etc.), `qr-code-ui` (`packages/web-components/components/qr-code/qr-code.class.js:107-118` + `qr-encoder.js:609-631`'s `matrixToSVG`), `icon-ui` (`packages/web-components/components/icon/icon.class.js:97-98` stamps a Phosphor `<svg>` string via `getIcon()`).
17
- - **NOT SVG** despite living in the chart family: `chart-legend-ui` (`packages/web-components/components/chart-legend/chart-legend.class.js` composes `<badge-ui>` + `<swatch-ui>`, no `<svg>` anywhere) and `swatch-ui` (`packages/web-components/components/swatch/swatch.class.js` plain `<span data-tile>` divs styled via CSS `background`/`border`, confirmed by `grep -rn svg` returning nothing in either file). gh#1344's own body assumed `chart-legend-ui` was SVG-adjacent; it isn't its swatch shapes (dot/square/line/dashed) are CSS box-model tricks, not paths.
16
+ - **Genuinely SVG-rendered**: `chart-ui` (`packages/web-components/components/chart/chart.class.js`, builds a `<svg>` string per chart type, §§#renderBar/#renderLine/etc.), `qr-code-ui` (`packages/web-components/components/qr-code/qr-code.class.js:107-118` + `qr-encoder.js:609-631`'s `matrixToSVG`), `icon-ui` (`packages/web-components/components/icon/icon.class.js:97-98`, stamps a Phosphor `<svg>` string via `getIcon()`).
17
+ - **NOT SVG** despite living in the chart family: `chart-legend-ui` (`packages/web-components/components/chart-legend/chart-legend.class.js`, composes `<badge-ui>` + `<swatch-ui>`, no `<svg>` anywhere) and `swatch-ui` (`packages/web-components/components/swatch/swatch.class.js`, plain `<span data-tile>` divs styled via CSS `background`/`border`, confirmed by `grep -rn svg` returning nothing in either file). gh#1344's own body assumed `chart-legend-ui` was SVG-adjacent; it isn't, its swatch shapes (dot/square/line/dashed) are CSS box-model tricks, not paths.
18
18
 
19
19
  A future primitive whose `class.js` calls `createElementNS('http://www.w3.org/2000/svg', ...)` or sets `innerHTML` to a string containing `<svg>` is in scope for every rule below; one that only composes other `*-ui` elements (however chart-shaped visually) is not.
20
20
 
21
- ## 1. viewBox is a coordinate system, not a size two sizing strategies coexist
21
+ ## 1. viewBox is a coordinate system, not a size, two sizing strategies coexist
22
22
 
23
23
  `viewBox="minX minY width height"` defines the SVG's INTERNAL coordinate
24
24
  system; the element's rendered box size is separate (CSS `width`/`height` or
25
25
  SVG `width`/`height` attributes). Every number emitted into the SVG markup
26
26
  (`x`, `y`, `r`, `stroke-width`, `font-size`) is in viewBox units, not CSS
27
- pixels the browser scales the whole coordinate system to fit the rendered
27
+ pixels, the browser scales the whole coordinate system to fit the rendered
28
28
  box (`preserveAspectRatio`, default `xMidYMid meet`).
29
29
 
30
30
  Two different sizing strategies are in use, deliberately:
31
31
 
32
- - **`chart-ui` responsive viewBox, CSS owns the box.** `chart.css:143-149` sets `svg { width: 100%; height: auto; max-height: 100%; overflow: visible }`; `#dims()` (`chart.class.js:405-457`) computes `width`/`height` FROM `this.clientWidth`/`clientHeight` every render, and `#renderChart()` sets `viewBox="0 0 ${width} ${height}"` (e.g. `chart.class.js:523`) to match. Because the viewBox is recomputed from the actual container size on every render, viewBox units and CSS px are numerically equal in the steady state a `stroke-width: 2` in `chart.css:199` reads as 2 real px. A `ResizeObserver` (`chart.class.js:363-380`, debounced via `requestAnimationFrame`) keeps this in sync across container resizes; there's a brief window between a resize and the debounced re-render where the OLD viewBox is still active against the NEW box size, during which strokes/dots/fonts visually scale up or down with the mismatch this is inherent to the responsive-viewBox strategy, not a bug to fix per-primitive.
33
- - **`qr-code-ui` fixed pixel viewBox, explicit width/height attributes.** `matrixToSVG` (`qr-encoder.js:609-631`) sets `viewBox="0 0 ${total} ${total}"` AND `width="${total}" height="${total}"` (equal, so no scaling happens at generation time); `qr-code.class.js:123-127` then overwrites the `width`/`height` ATTRIBUTES (not CSS) to the `[size]` prop after `innerHTML` is set. `qr-code.css:23-28` documents why it does NOT use `width: 100%`: the host is `display: block` sized-to-content (the SVG itself), so a CSS-percentage width on the SVG would create a circular sizing dependency "trust the SVG attributes" is the comment's own words.
32
+ - **`chart-ui`, responsive viewBox, CSS owns the box.** `chart.css:143-149` sets `svg { width: 100%; height: auto; max-height: 100%; overflow: visible }`; `#dims()` (`chart.class.js:405-457`) computes `width`/`height` FROM `this.clientWidth`/`clientHeight` every render, and `#renderChart()` sets `viewBox="0 0 ${width} ${height}"` (e.g. `chart.class.js:523`) to match. Because the viewBox is recomputed from the actual container size on every render, viewBox units and CSS px are numerically equal in the steady state, a `stroke-width: 2` in `chart.css:199` reads as 2 real px. A `ResizeObserver` (`chart.class.js:363-380`, debounced via `requestAnimationFrame`) keeps this in sync across container resizes; there's a brief window between a resize and the debounced re-render where the OLD viewBox is still active against the NEW box size, during which strokes/dots/fonts visually scale up or down with the mismatch: this is inherent to the responsive-viewBox strategy, not a bug to fix per-primitive.
33
+ - **`qr-code-ui`, fixed pixel viewBox, explicit width/height attributes.** `matrixToSVG` (`qr-encoder.js:609-631`) sets `viewBox="0 0 ${total} ${total}"` AND `width="${total}" height="${total}"` (equal, so no scaling happens at generation time); `qr-code.class.js:123-127` then overwrites the `width`/`height` ATTRIBUTES (not CSS) to the `[size]` prop after `innerHTML` is set. `qr-code.css:23-28` documents why it does NOT use `width: 100%`: the host is `display: block` sized-to-content (the SVG itself), so a CSS-percentage width on the SVG would create a circular sizing dependency, "trust the SVG attributes" is the comment's own words.
34
34
 
35
- **When authoring a new SVG primitive**, pick one of these two strategies deliberately and document which: responsive-viewBox (chart-ui's approach needed when the primitive must fill an arbitrary, resizable container) or fixed-attribute (qr-code-ui's approach needed when the primitive has a scannable/pixel-exact payload where uncontrolled scaling would break fidelity, and the `[size]` prop is the only sizing lever a consumer needs).
35
+ **When authoring a new SVG primitive**, pick one of these two strategies deliberately and document which: responsive-viewBox (chart-ui's approach, needed when the primitive must fill an arbitrary, resizable container) or fixed-attribute (qr-code-ui's approach, needed when the primitive has a scannable/pixel-exact payload where uncontrolled scaling would break fidelity, and the `[size]` prop is the only sizing lever a consumer needs).
36
36
 
37
37
  ## 2. `stroke-width` and other bare numbers scale with the coordinate system
38
38
 
39
39
  Because `stroke-width`, circle `r`, and `font-size` values written into the
40
40
  SVG markup are viewBox-unit numbers (see §1), they are NOT the same kind of
41
- value as a CSS `border-width` or `font-size` on an HTML element an HTML
41
+ value as a CSS `border-width` or `font-size` on an HTML element, an HTML
42
42
  border stays a fixed px regardless of ancestor `transform: scale()` (the
43
43
  border itself doesn't get bigger, only the box does); an SVG stroke drawn in
44
44
  viewBox units scales proportionally with ANY transform that changes the
45
45
  effective viewBox-to-rendered-size ratio, including a CSS `transform: scale()`
46
46
  on the `<svg>` or an ancestor, and including the responsive-viewBox mismatch
47
47
  window described in §1. `chart.css:198-203`'s `[data-line] { stroke-width:
48
- var(--chart-line-width) }` (unitless SVG interprets an unadorned number as
48
+ var(--chart-line-width) }` (unitless, SVG interprets an unadorned number as
49
49
  user units) is a concrete example: at steady state this renders at the CSS
50
50
  `--chart-line-width` value in real px, but during a `transform: scale(1.5)`
51
51
  hover-zoom on a chart card it renders at 1.5× that, same as every other
52
- number in the shape's geometry there is no way to pin stroke-width to a
52
+ number in the shape's geometry, there is no way to pin stroke-width to a
53
53
  fixed screen px independent of the coordinate system short of
54
54
  `vector-effect: non-scaling-stroke` (not used anywhere in this codebase
55
- today flag it if a future primitive needs scale-independent strokes).
55
+ today, flag it if a future primitive needs scale-independent strokes).
56
56
 
57
57
  ## 3. `text-anchor`/`dominant-baseline` position an anchor POINT, not a box corner
58
58
 
59
- SVG `<text>` has no intrinsic box model `x`/`y` mark a single anchor
59
+ SVG `<text>` has no intrinsic box model, `x`/`y` mark a single anchor
60
60
  point, and `text-anchor`/`dominant-baseline` say which part of the glyph run
61
61
  sits at that point. Getting this wrong is the single most common SVG label
62
62
  bug (text drifts off its intended mark as content length changes). Every
63
63
  label renderer in `chart.class.js` picks the anchor deliberately:
64
64
 
65
- - **Y-axis labels** `text-anchor="end"` (`chart.class.js:1053`): the anchor point sits at the RIGHT edge of the label so labels of different digit-widths ("5", "5,000") stay right-aligned against the axis rather than growing rightward from a fixed left point.
66
- - **X-axis / value labels** `text-anchor="middle"` (`chart.class.js:1068`, `1109`): centers over each bar/point regardless of label width.
67
- - **Donut center total/label, gauge value, funnel stage/value/drop, radar labels, sankey node labels** `dominant-baseline="central"` (e.g. `chart.class.js:1270-1271`, `1585,1587`, `1642-1648`, `1357`, `1846,1852`): vertically centers the glyph on its `y` coordinate needed anywhere a label sits beside or inside a shape whose center, not its top, is the meaningful reference point (a donut's numeric center, a radial label at a computed angle).
68
- - **Treemap tile labels `dominant-baseline` switches per available space** (`chart.class.js:1766-1771`): `hanging` (top-aligned, the default vertical-metrics baseline) for a "tall" tile where label + value stack top-down, `central` for a "short" tile where only the label fits and it should sit mid-height rather than clipped against the top edge. Pick the baseline that matches the layout decision, not a single default for the whole primitive.
65
+ - **Y-axis labels**, `text-anchor="end"` (`chart.class.js:1053`): the anchor point sits at the RIGHT edge of the label so labels of different digit-widths ("5", "5,000") stay right-aligned against the axis rather than growing rightward from a fixed left point.
66
+ - **X-axis / value labels**, `text-anchor="middle"` (`chart.class.js:1068`, `1109`): centers over each bar/point regardless of label width.
67
+ - **Donut center total/label, gauge value, funnel stage/value/drop, radar labels, sankey node labels**, `dominant-baseline="central"` (e.g. `chart.class.js:1270-1271`, `1585,1587`, `1642-1648`, `1357`, `1846,1852`): vertically centers the glyph on its `y` coordinate, needed anywhere a label sits beside or inside a shape whose center, not its top, is the meaningful reference point (a donut's numeric center, a radial label at a computed angle).
68
+ - **Treemap tile labels, `dominant-baseline` switches per available space** (`chart.class.js:1766-1771`): `hanging` (top-aligned, the default vertical-metrics baseline) for a "tall" tile where label + value stack top-down, `central` for a "short" tile where only the label fits and it should sit mid-height rather than clipped against the top edge. Pick the baseline that matches the layout decision, not a single default for the whole primitive.
69
69
 
70
- Rule of thumb: `text-anchor` picks the horizontal anchor (`start`/`middle`/`end`), `dominant-baseline` picks the vertical one (`hanging`/`central`/`middle`/the default alphabetic baseline) set both explicitly whenever a label's position depends on computed geometry rather than a fixed corner.
70
+ Rule of thumb: `text-anchor` picks the horizontal anchor (`start`/`middle`/`end`), `dominant-baseline` picks the vertical one (`hanging`/`central`/`middle`/the default alphabetic baseline), set both explicitly whenever a label's position depends on computed geometry rather than a fixed corner.
71
71
 
72
- ## 4. Card-boundary clipping `overflow: visible` is the default; a bleed section changes the contract
72
+ ## 4. Card-boundary clipping, `overflow: visible` is the default; a bleed section changes the contract
73
73
 
74
74
  `chart.css:143-149`'s `svg { overflow: visible }` is intentional: chart
75
75
  labels routinely extend slightly past the nominal plot rectangle (Y-axis
76
76
  labels sit at `pad.left - 4`, per §3), and `overflow: visible` lets that
77
77
  render instead of clipping at the SVG's own box edge. That default is safe
78
78
  inside a normally-inset `card-ui` section. It stops being safe the moment
79
- the SAME chart sits inside a `<section bleed>` `card-ui`'s `:scope` itself
79
+ the SAME chart sits inside a `<section bleed>`, `card-ui`'s `:scope` itself
80
80
  clips at `overflow: hidden` with a rounded `border-radius` (`card.css`, top
81
81
  of file), and `[bleed]` zeroes the section's own margin/padding
82
- (`card.css:342` onward) so a chart's axis-label overhang, or gridlines
82
+ (`card.css:342` onward), so a chart's axis-label overhang, or gridlines
83
83
  extending to the plot edge, lands flush against that rounded corner and
84
84
  clips silently. This was gh#1095's original incident (PR #1105): a
85
85
  bar/line chart's Y-axis labels clipped under a bled card's corner.
86
86
 
87
- **gh#1095's own mechanical fix a `:has()`-based auto-restore of the
88
- card's inset was itself unratified and removed (gh#1801, operator ruling
87
+ **gh#1095's own mechanical fix, a `:has()`-based auto-restore of the
88
+ card's inset, was itself unratified and removed (gh#1801, operator ruling
89
89
  2026-08-20)**: it silently defeated an author's own `[bleed]` the moment a
90
90
  chart drew any guide/value text or paired a `<chart-legend-ui>`, which
91
91
  collided with the card-chart design language's overlay-chip labels
92
92
  (rendered INSET within the plot box on purpose, but still enough to trip
93
93
  the old guard's `:not([no-grid])`/`:not([no-values])` test). `[bleed]` is
94
- now unconditionally author-controlled card.css never re-inserts an inset
94
+ now unconditionally author-controlled, card.css never re-inserts an inset
95
95
  the author explicitly zeroed.
96
96
 
97
- **The clipping hazard itself is real and unchanged** only the mitigation
97
+ **The clipping hazard itself is real and unchanged**, only the mitigation
98
98
  moved from mechanical CSS to documented author responsibility. card.yaml's
99
99
  `bleed` prop docs and `chart-in-card.examples.html` both carry the
100
100
  resulting rule: a full-bleed chart-ui with visible guide/value text, or one
101
101
  paired with a `<chart-legend-ui>`, must keep that text clear of the card's
102
- rounded-corner clip e.g. by rendering it as an overlay chip INSET within
102
+ rounded-corner clip, e.g. by rendering it as an overlay chip INSET within
103
103
  the plot area (never hanging outside it, and never relying on card margin
104
- for clearance) or by putting the legend in its own non-bled section.
104
+ for clearance), or by putting the legend in its own non-bled section.
105
105
  `#renderSparkline()` is still the only renderer that never emits axis
106
106
  ticks, gridlines, value text, or a legend (`chart.class.js`'s sparkline
107
107
  branch), so it's still the one type where a bare `[bleed]` needs no such
@@ -113,88 +113,84 @@ section**: an SVG whose content can extend past its own nominal box
113
113
  either a "bare marks" mode (no overhanging content) that's safe to bleed,
114
114
  or a documented author-responsibility note at the point of use (the
115
115
  `bleed`-prop docs, the pattern's own examples) naming the clipping hazard
116
- and its mitigation never a mechanical CSS guard that silently overrides
116
+ and its mitigation, never a mechanical CSS guard that silently overrides
117
117
  an author's own explicit attribute (gh#1801's own lesson). Don't assume
118
- `overflow: hidden` on the ancestor container will clip cleanly SVG content
118
+ `overflow: hidden` on the ancestor container will clip cleanly, SVG content
119
119
  drawn PAST an ancestor's padding box (not its own) clips at whatever
120
120
  ancestor in the chain actually sets `overflow: hidden`, which for
121
121
  `card-ui` is the rounded-corner boundary itself, producing the specific
122
122
  silently-clipped-under-a-curve look #1095 originally reported.
123
123
 
124
- ## 5. CSS custom properties don't resolve inside raw SVG attribute strings only inside actual CSS declarations
124
+ ## 5. CSS custom properties don't resolve inside raw SVG attribute strings, only inside actual CSS declarations
125
125
 
126
126
  A CSS custom property (`var(--foo)`) only resolves where the CSS cascade
127
127
  parses it: inside a stylesheet rule, or inside an inline `style="..."`
128
128
  attribute. It does NOT resolve inside an arbitrary SVG presentation
129
- attribute value written as a plain string (`fill="var(--foo)"` is invalid
130
- the literal text `var(--foo)` is not a recognized SVG color, and the shape
129
+ attribute value written as a plain string (`fill="var(--foo)"` is invalid: the literal text `var(--foo)` is not a recognized SVG color, and the shape
131
130
  renders with the initial/inherited fill instead, silently). `currentColor`
132
131
  is different: it's a CSS-wide keyword the SVG spec itself recognizes inside
133
132
  presentation attributes, and it resolves against the computed `color`
134
- property the normal way so `fill="currentColor"` written directly into
133
+ property the normal way, so `fill="currentColor"` written directly into
135
134
  markup DOES cascade correctly. `icon-ui` relies on exactly this: the
136
135
  installed Phosphor SVGs ship `fill="currentColor"` on their root `<svg>`
137
- (confirmed: `node_modules/@phosphor-icons/core/assets/regular/caret-right.svg`
138
- — `<svg ... fill="currentColor">`), and `icon.css`'s `:scope { color:
136
+ (confirmed: `node_modules/@phosphor-icons/core/assets/regular/caret-right.svg`, `<svg ... fill="currentColor">`), and `icon.css`'s `:scope { color:
139
137
  var(--icon-color) }` (`icon.css:11`) drives it through the ordinary
140
- `color` inheritance chain no `var()` inside the SVG markup is needed
138
+ `color` inheritance chain, no `var()` inside the SVG markup is needed
141
139
  because `currentColor` isn't a custom property.
142
140
 
143
141
  `chart-ui` hit this distinction directly and got it wrong once (gh#561,
144
142
  documented in `chart.class.js:548-560`): an earlier version wrote
145
143
  `--color-{key}: var(--chart-N)` as an inline STYLE on the chart HOST, then
146
- tried to reference `--color-{key}` from series-colored shapes but because
144
+ tried to reference `--color-{key}` from series-colored shapes, but because
147
145
  inline styles win the cascade over everything except `!important`, a
148
146
  consumer's own `--color-MAU` set on an ancestor lost to the chart's own
149
147
  inline default, making the documented "override `--color-{key}` to recolor
150
148
  a series" hook unusable. **The fix, and the pattern to follow**: never set
151
149
  the color custom property on the host; instead emit it as an inline `style`
152
150
  attribute ON THE SHAPE ITSELF, with the fallback chain built into the same
153
- declaration `#seriesFill()`/`#seriesStroke()` (`chart.class.js:568-575`)
151
+ declaration, `#seriesFill()`/`#seriesStroke()` (`chart.class.js:568-575`)
154
152
  emit ` style="fill: var(--color-${seriesKey}, var(--chart-${slotIdx}))"` per
155
153
  `<path>`/`<circle>`. Because this IS a real CSS declaration (inside
156
154
  `style=""`), `var()` resolves normally, an ancestor-set `--color-{key}`
157
155
  flows through the cascade and wins, and an unset one falls through to the
158
- palette slot exactly the semantics a bare attribute string can't provide.
156
+ palette slot, exactly the semantics a bare attribute string can't provide.
159
157
 
160
158
  **`qr-code-ui` shows the failure mode `chart-ui` avoided**: `qr-code.css:7-8`
161
159
  declares `--qr-code-fg: currentColor` / `--qr-code-bg: transparent` and sets
162
- them as `color`/`background` on the HOST (`qr-code.css:17-18`) but the
160
+ them as `color`/`background` on the HOST (`qr-code.css:17-18`), but the
163
161
  actual QR modules are painted via `matrixToSVG` (`qr-encoder.js:609-631`),
164
162
  which bakes `fill="${fg}"`/`fill="${bg}"` as literal hex strings
165
163
  (`options.color || '#000'`, `qr-code.class.js:115-116` passes
166
164
  `this.color || '#000000'`) directly into the generated markup at render
167
165
  time. The `--qr-code-fg`/`--qr-code-bg` tokens are real and declared, but
168
- nothing in the render path ever reads them setting `color` on an ancestor
166
+ nothing in the render path ever reads them, setting `color` on an ancestor
169
167
  of a default `<qr-code-ui>` does nothing to its rendered fill; only the
170
- explicit `[color]`/`[background]` HTML attributes do (and deliberately so
171
- the code comment at `qr-code.class.js:110-115` explains theme-aware
168
+ explicit `[color]`/`[background]` HTML attributes do (and deliberately so, the code comment at `qr-code.class.js:110-115` explains theme-aware
172
169
  `currentColor` would produce light-on-dark QR codes that most phone cameras
173
170
  refuse to scan). **When authoring a new SVG primitive with a
174
171
  "theming token" in its CSS, verify the render path actually consumes it as
175
172
  a live CSS value (inline `style=` per shape, or a bare `currentColor`
176
173
  keyword) rather than baking a computed color into the generated markup as a
177
- one-time string a declared-but-dead token is a real trap for the next
174
+ one-time string, a declared-but-dead token is a real trap for the next
178
175
  author who tries to theme the primitive from outside.**
179
176
 
180
177
  ## 6. Hit-testing: `fill: transparent` is clickable, `fill: none` is not
181
178
 
182
179
  SVG's default `pointer-events: visiblePainted` treats a shape as
183
- hit-testable only if it's "painted" `fill: transparent` counts as painted
180
+ hit-testable only if it's "painted", `fill: transparent` counts as painted
184
181
  (alpha-zero, but still a fill), `fill: none` does not. `chart.css:381-386`
185
182
  states this explicitly as the reason its hit-target circles are always
186
183
  `fill: transparent !important` rather than `fill: none`:
187
184
 
188
185
  ```css
189
- /* Hit-target overlays must never be filled by the slice palette
190
- they're meant to be invisible pointer-event surfaces. */
186
+ /* Hit-target overlays must never be filled by the slice palette, they're meant to be invisible pointer-event surfaces. */
191
187
  circle[data-hit] {
192
188
  fill: transparent !important;
193
189
  stroke: none;
194
190
  }
195
191
  ```
196
192
 
197
- This matters most for THIN shapes a `<path data-line>` stroke has almost
193
+ This matters most for THIN shapes, a `<path data-line>` stroke has almost
198
194
  zero hit area along its own geometry, so `chart.class.js` renders a SEPARATE,
199
195
  generously-radiused invisible circle per point (`data-hit`, `hitR =
200
196
  Math.max(dotR, 10)` at `chart.class.js:1157/1165`, similarly `1450` for
@@ -205,8 +201,8 @@ a LINE instead of a point: the visible dashed average line is `stroke-width:
205
201
  line with `stroke: transparent stroke-width="12"` rides on top purely for
206
202
  hit area (`chart.class.js:1119,1177`, labeled "Wider invisible hit target so
207
203
  the thin dashed line is hoverable" in the source comment). By contrast,
208
- FILLED shapes with real area bars (`<path data-bar>`), pie/donut slices,
209
- radial-bar arcs carry their `tip()` data attributes directly on the
204
+ FILLED shapes with real area, bars (`<path data-bar>`), pie/donut slices,
205
+ radial-bar arcs, carry their `tip()` data attributes directly on the
210
206
  visible shape (`chart.class.js:1106`, `1209`, `1514`) with no separate hit
211
207
  overlay needed, because the visible fill already satisfies
212
208
  `visiblePainted`.
@@ -214,24 +210,24 @@ overlay needed, because the visible fill already satisfies
214
210
  **Rule for a new SVG primitive**: any interactive target whose visible
215
211
  stroke/fill area is too thin or too small to reliably hit with a pointer
216
212
  needs an invisible, generously-sized `fill: transparent` (never `fill:
217
- none`) overlay shape carrying the actual event data don't rely on the
213
+ none`) overlay shape carrying the actual event data, don't rely on the
218
214
  visible geometry's own hit area once its rendered stroke-width or radius
219
215
  drops below a comfortable pointer target size (chart-ui's overlays use
220
216
  `r ≥ 10`, `stroke-width ≥ 12` as the floor).
221
217
 
222
- ## 7. `shape-rendering` pixel-grid content vs. smooth curves
218
+ ## 7. `shape-rendering`, pixel-grid content vs. smooth curves
223
219
 
224
220
  `qr-code.css:29`/`qr-encoder.js:629` set `shape-rendering: crispEdges` on
225
- the generated QR `<svg>` this disables anti-aliasing so each QR module
221
+ the generated QR `<svg>`, this disables anti-aliasing so each QR module
226
222
  renders as a hard-edged square rather than a slightly blurred one, which
227
223
  matters for scanner reliability (soft edges reduce contrast at module
228
224
  boundaries a camera decoder relies on). `chart-ui` sets no `shape-rendering`
229
- override anywhere its curves (`smoothPath`'s Catmull-Rom bezier
225
+ override anywhere, its curves (`smoothPath`'s Catmull-Rom bezier
230
226
  conversion, `chart.class.js:158-180`) are meant to anti-alias normally.
231
227
  **When authoring a new SVG primitive rendering a hard pixel/module grid
232
228
  (a matrix code, a pixel-art preview, anything where edge crispness affects
233
229
  correctness rather than just aesthetics), set `shape-rendering: crispEdges`
234
- explicitly** the browser default (`auto`, effectively anti-aliased) is
230
+ explicitly**, the browser default (`auto`, effectively anti-aliased) is
235
231
  correct for everything else and should stay the default.
236
232
 
237
233
  ## 8. Attribute-shadowing on SVG-adjacent primitives (ADR-0053/0054/0070)
@@ -239,17 +235,17 @@ correct for everything else and should stay the default.
239
235
  Two of the global-attribute-grammar collisions gh#1335 surfaced are
240
236
  specifically SVG-rendered primitives; both exemptions are **GRANTED**
241
237
  (ADR-0070, ratified 2026-08-17), closing the last 2 of gh#1335's 17 and
242
- emptying `check-attribute-shadowing.mjs`'s `KNOWN_FINDINGS` the gate now
238
+ emptying `check-attribute-shadowing.mjs`'s `KNOWN_FINDINGS`, the gate now
243
239
  enforces via `attribute-api-system.md`'s ratified §11 table alone:
244
240
 
245
- - **`qr-code-ui[color]`** a free-form CSS color string (drives the raw
241
+ - **`qr-code-ui[color]`**, a free-form CSS color string (drives the raw
246
242
  `fill` baked into the generated matrix SVG, §5 above), structurally
247
243
  identical to the ratified `swatch-ui`/`noodles-ui[color]` §11 exemptions
248
244
  ("the component's entire subject is a color"). The exemption covers the
249
245
  free-form value space AND the hardcoded `#000000` scanability fallback
250
- (deliberately never theme-derived light-on-dark won't scan), plus its
246
+ (deliberately never theme-derived, light-on-dark won't scan), plus its
251
247
  contract pairing with `[background]`.
252
- - **`icon-ui[weight]`** Phosphor's own glyph-variant vocabulary
248
+ - **`icon-ui[weight]`**, Phosphor's own glyph-variant vocabulary
253
249
  (`thin/light/regular/bold/fill/duotone`, selecting which pre-rendered SVG
254
250
  set `getIcon()` loads) is a DIFFERENT CONCEPT from CSS `font-weight`
255
251
  despite the shared name; the global utility's `font-weight` is inert on
@@ -259,24 +255,24 @@ Known residual (documented in the §11 justification, accepted not fixed):
259
255
  global-only `weight` values (`normal|medium|semibold`) select no Phosphor
260
256
  set and fall back to `regular`; a semantic-enum value on `qr-code-ui[color]`
261
257
  (`color="danger"`) passes to the SVG fill verbatim, unresolved through
262
- tokens. Cite this section ADR-0070 not a fresh investigation, if either
258
+ tokens. Cite this section, ADR-0070, not a fresh investigation, if either
263
259
  attribute surfaces again in a yaml audit; the next colliding
264
260
  `color`/`weight` on any OTHER component still needs its own ADR.
265
261
 
266
262
  ## When to load this file
267
263
 
268
264
  Any authoring task touching `chart-ui`, `qr-code-ui`, `icon-ui`, or a new
269
- primitive whose `class.js` emits `<svg>` markup a new chart type, a
265
+ primitive whose `class.js` emits `<svg>` markup, a new chart type, a
270
266
  label-positioning fix, a card-bleed interaction, a hit-target bug, or a
271
267
  color/theming prop on an SVG-rendered primitive. NOT for `chart-legend-ui`
272
- or `swatch-ui` (§0) those are HTML/CSS primitives despite the chart-family
268
+ or `swatch-ui` (§0), those are HTML/CSS primitives despite the chart-family
273
269
  name; their authoring questions route through the general
274
270
  [css-patterns.md](css-patterns.md) / [api-contract.md](api-contract.md)
275
271
  same as any other component. The `--chart-*` and `--qr-code-*` TOKEN
276
272
  declarations themselves (naming, `:where(:scope)` placement) still follow
277
- [token-contract.md](token-contract.md) this file covers only what's
273
+ [token-contract.md](token-contract.md), this file covers only what's
278
274
  SVG-specific once those tokens reach the render path. The ≤2px raw
279
275
  `stroke-width` carve-out in [css-patterns.md](css-patterns.md)'s "Raw
280
276
  values" section is the general rule this file's §2 explains the SVG-specific
281
- mechanism behind cite both together when a stroke-width literal comes up
277
+ mechanism behind, cite both together when a stroke-width literal comes up
282
278
  in review.
@@ -6,12 +6,12 @@ Use when adding a new component, modifying an existing component's CSS, or inves
6
6
 
7
7
  ## The Component Token Contract (mandatory)
8
8
 
9
- Spec: `.claude/docs/specs/component-token-contract.md` (live source of truth if this doc contradicts that one, the spec wins; patch this skill).
9
+ Spec: `.claude/docs/specs/component-token-contract.md` (live source of truth, if this doc contradicts that one, the spec wins; patch this skill).
10
10
 
11
11
  Every component's `.css` file must follow the **two-block @scope** pattern:
12
12
 
13
13
  ```css
14
- /* Block 1: token declarations on :where(:scope) zero specificity */
14
+ /* Block 1: token declarations on :where(:scope), zero specificity */
15
15
  @scope (my-component-ui) {
16
16
  :where(:scope) {
17
17
  --my-bg: var(--a-bg);
@@ -40,15 +40,13 @@ Every component's `.css` file must follow the **two-block @scope** pattern:
40
40
 
41
41
  A `@scope (my-component-ui)` donor selector matches by **literal tag
42
42
  name**. That's a live gap for any component whose own contract lets it
43
- render as a different tag than its own custom-element name `text-ui`
43
+ render as a different tag than its own custom-element name, `text-ui`
44
44
  already did this coincidentally (`variant="body"` → `<p>`,
45
45
  `variant="caption"` → `<small>`), relying on ambient/bare-tag styling
46
46
  rather than the component's own `[variant]` rules. ADR-0102 (`text-ui`'s
47
47
  `level` prop, promoting the element to a real `<h1>`-`<h6>`) made this a
48
- first-class case, requiring the donor selector to stay tag-independent
49
- e.g. `@scope (text-ui, [data-text])` with the renderer stamping a stable
50
- marker attribute on every such element regardless of the resolved tag —
51
- so a promoted `<h2 level="2" variant="heading">` keeps its component
48
+ first-class case, requiring the donor selector to stay tag-independent, e.g. `@scope (text-ui, [data-text])` with the renderer stamping a stable
49
+ marker attribute on every such element regardless of the resolved tag, so a promoted `<h2 level="2" variant="heading">` keeps its component
52
50
  styling instead of silently falling back to bare-tag `:where(hN)`
53
51
  defaults elsewhere in the cascade. Any component that can render under a
54
52
  tag other than its own name needs the same tag-independent scope check.
@@ -58,9 +56,9 @@ tag other than its own name needs the same tag-independent scope check.
58
56
  1. **Zero raw color values** anywhere in component CSS
59
57
  - No `#fff`, `rgb(...)`, `oklch(...)` literals
60
58
  - All colors must reference `--a-chrome-*`, `--a-data-0..9`, `--a-fg*`, `--a-bg-*`, or family tokens (`--a-brand-*`, `--a-primary-*`, etc.)
61
- 2. **Two-block pattern** token declarations separate from style rules
62
- 3. **Variants override tokens** never rewrite base styles inside `[variant]` selectors. Change token values; the base styles absorb the change.
63
- 4. **`:where(:scope)`** for token defaults zero specificity lets consumers override tokens from outside
59
+ 2. **Two-block pattern**, token declarations separate from style rules
60
+ 3. **Variants override tokens**, never rewrite base styles inside `[variant]` selectors. Change token values; the base styles absorb the change.
61
+ 4. **`:where(:scope)`** for token defaults, zero specificity lets consumers override tokens from outside
64
62
 
65
63
  ## Audit procedure
66
64
 
@@ -106,44 +104,43 @@ tag other than its own name needs the same tag-independent scope check.
106
104
 
107
105
  If you find a raw value elsewhere, either:
108
106
 
109
- - Fix it by adding a proper token in the owning `styles/` dimension×layer file (see "Where foundation styles live" below `tokens.css` is a compat barrel, not a home)
107
+ - Fix it by adding a proper token in the owning `styles/` dimension×layer file (see "Where foundation styles live" below, `tokens.css` is a compat barrel, not a home)
110
108
  - Or document the exemption in `.claude/docs/specs/component-token-contract.md`
111
109
 
112
110
  ## Chrome token palette
113
111
 
114
- Added in v0.5.0 use these for UI chrome:
112
+ Added in v0.5.0, use these for UI chrome:
115
113
 
116
- - `--a-chrome-light` light scrim / overlay
117
- - `--a-chrome-dark` dark scrim
118
- - `--a-chrome-border` subtle hairline borders
119
- - `--a-chrome-ring-subtle` focus rings, outlines
120
- - `--a-chrome-shadow-soft` elevation shadows
114
+ - `--a-chrome-light`, light scrim / overlay
115
+ - `--a-chrome-dark`, dark scrim
116
+ - `--a-chrome-border`, subtle hairline borders
117
+ - `--a-chrome-ring-subtle`, focus rings, outlines
118
+ - `--a-chrome-shadow-soft`, elevation shadows
121
119
  - `--a-chrome-backdrop` - generic 50% chrome overlay, reserved for non-dialog UI chrome; no current consumers. Modal/drawer backdrops use `--a-scrim-dialog` instead (80% black, gh#373, see `--a-chrome-scrim-dialog` in features.css' CHROME block)
122
120
 
123
121
  ## Data palette
124
122
 
125
- For charts, stat colors, category markers use `--a-data-0` through `--a-data-9`. Do NOT hardcode chart colors.
123
+ For charts, stat colors, category markers, use `--a-data-0` through `--a-data-9`. Do NOT hardcode chart colors.
126
124
 
127
- ## Where foundation styles live dimension × layer
125
+ ## Where foundation styles live, dimension × layer
128
126
 
129
- Every file under `packages/web-components/styles/` answers two questions which *dimension* (color · type · space · size · radius · motion · elevation) and which *layer* (primitive → semantic → role → element → attribute-API → context → reset) and declares its cell with a machine-readable header tag `adia:<layer>/<dimension>`. When adding or moving a declaration:
127
+ Every file under `packages/web-components/styles/` answers two questions, which *dimension* (color · type · space · size · radius · motion · elevation) and which *layer* (primitive → semantic → role → element → attribute-API → context → reset), and declares its cell with a machine-readable header tag `adia:<layer>/<dimension>`. When adding or moving a declaration:
130
128
 
131
129
  - Spatial / size / radius / motion / elevation primitives → `styles/foundation/` (one file per dimension)
132
130
  - Type scale (L1/L2), roles (L3), native-element defaults → `styles/type/`
133
131
  - Color layers → `styles/colors/` (parameters · primitives-* · surfaces · scrims · semantics/*)
134
- - The global attribute API (`[size]` `[density]` `[gap]` `[padding]` `[margin]` …) → `styles/api/{sizing,text,layout}.css` the ONLY home for global `[attr]` selectors (governed by the grammar spec below)
135
- - `tokens.css`, `typography.css`, `colors/semantics.css`, etc. remain **compat barrels** at their public paths they only `@import`; never add declarations to them
132
+ - The global attribute API (`[size]` `[density]` `[gap]` `[padding]` `[margin]` …) → `styles/api/{sizing,text,layout}.css`, the ONLY home for global `[attr]` selectors (governed by the grammar spec below)
133
+ - `tokens.css`, `typography.css`, `colors/semantics.css`, etc. remain **compat barrels** at their public paths, they only `@import`; never add declarations to them
136
134
 
137
135
  Enforced by `scripts/release/check-foundation-layer-placement.mjs` (wired into `npm run check`): header tags present, primitive files free of attribute selectors, the attribute API confined to `api/`, no orphan files. Import order inside `colors/semantics/` is load-bearing (`aliases.css` last) and asserted in file headers.
138
136
 
139
137
  Source: ADR-0035.
140
138
 
141
- ## The global attribute grammar read the spec before naming an attribute
139
+ ## The global attribute grammar, read the spec before naming an attribute
142
140
 
143
141
  The global attribute API is a *designed system* with its own spec:
144
142
  `.claude/docs/specs/attribute-api-system.md` (companion audit:
145
- `.claude/docs/specs/attribute-api-audit.md`). That spec not this checklist
146
- owns the grammar. Two rules an author must know exist (read the spec for the
143
+ `.claude/docs/specs/attribute-api-audit.md`). That spec, not this checklist, owns the grammar. Two rules an author must know exist (read the spec for the
147
144
  mechanics; never work from this summary alone):
148
145
 
149
146
  - **Explicit vs ambient** (spec §5.2): `[gap]`/`[padding]`/`[margin]` are
@@ -151,8 +148,7 @@ mechanics; never work from this summary alone):
151
148
  are *ambient* context-setters (inheriting by design). Which axis an attribute
152
149
  sits on decides its `@property` registration and how components read it.
153
150
  - **No shadowing** (`docs/ops/adr/adr-0053-no-shadowing-global-attributes.md`): a
154
- component-local attribute may not share a name with any global attribute
155
- the global name always means the global thing. Before minting any attribute
151
+ component-local attribute may not share a name with any global attribute: the global name always means the global thing. Before minting any attribute
156
152
  in a component yaml, check it against the spec's attribute inventory; the
157
153
  only sanctioned collisions live in the spec's §11 Global-Attribute Exemption
158
154
  List (first entries: `swatch-ui[color]` / `noodles-ui[color]`, ADR-0054;
@@ -161,19 +157,19 @@ mechanics; never work from this summary alone):
161
157
 
162
158
  Beyond the global grammar, ADR-0063
163
159
  (`docs/ops/adr/adr-0063-attribute-grammar-addendum.md`) ratifies seven
164
- CROSS-SIBLING conventions for component-local attribute naming the axis
160
+ CROSS-SIBLING conventions for component-local attribute naming, the axis
165
161
  ADR-0053/0054 don't cover. Any new attribute follows these:
166
162
 
167
- - **`no-*` is the canonical negation prefix** `hide-*` retires
163
+ - **`no-*` is the canonical negation prefix**, `hide-*` retires
168
164
  (`chart[hideAverage|hideGrid|hideValues]` → `no*`, `stream[hide-cursor]`
169
165
  → `[no-cursor]`).
170
166
  - **Interactivity is opt-in by a positively-named boolean, default `false`**
171
- (the `stepper-item[interactive]` / `swatch[selectable]` shape) never
167
+ (the `stepper-item[interactive]` / `swatch[selectable]` shape), never
172
168
  opt-out. `chart-legend[static]` retires to `[interactive]` (inverted
173
- default). Applies to single-purpose toggles only `noodles`'s
169
+ default). Applies to single-purpose toggles only, `noodles`'s
174
170
  `editable`/`readonly` pair is two orthogonal booleans and stays.
175
171
  - **One spelling survives a documented alias; the alias retires
176
- everywhere it appeared** never a deprecation window. `alert[dismissible]`
172
+ everywhere it appeared**, never a deprecation window. `alert[dismissible]`
177
173
  retired (`[closable]` survives, the implemented prop); `card`'s style
178
174
  enum's duplicate `outline` member retired (`outlined` survives, the
179
175
  ADR-0044 style-axis token).
@@ -186,32 +182,32 @@ ADR-0053/0054 don't cover. Any new attribute follows these:
186
182
  comma-separated value IS accepted as the sanctioned multiple+options
187
183
  encoding, and toggle-group's comma-string resolves under that same ruling
188
184
  rather than being re-ticketed. This is a ruling on those two existing
189
- components, not a loophole a NEW component's multi-value attribute still
185
+ components, not a loophole, a NEW component's multi-value attribute still
190
186
  uses form (a) or (b) above, never a delimited string, unless a separate
191
187
  ruling says otherwise.
192
188
  - **Cross-sibling naming convergence:** `pane[side]` → `pane[edge]`
193
189
  (drawer keeps physical `side`); `menu-item[subtitle]` →
194
190
  `menu-item[description]` (the ~20-component majority word); `placement`
195
191
  ratified as-is for anchored popovers. Documented exception:
196
- `input[maxlength]` a primitive wrapping a genuine native `<input>`
192
+ `input[maxlength]`, a primitive wrapping a genuine native `<input>`
197
193
  keeps the native attribute's exact casing (ADR-0025/0055 conformance);
198
194
  new non-native length constraints use the framework's
199
195
  camelCase-property/kebab-attribute pair.
200
196
  - **`completed` is the stage-terminal word** for a `status` lifecycle enum
201
197
  (`agent-reasoning[status]`'s `done` converges).
202
198
  - **`-picker` is reserved for the outer trigger+popover form-associated
203
- composite** never the inline substrate it composes. `color-picker-ui`
199
+ composite**, never the inline substrate it composes. `color-picker-ui`
204
200
  (the inline substrate) renames to `color-area-ui`.
205
201
  - **[verified 2026-08-19] Preset-boolean-vs-alias-retirement boundary**
206
202
  (ADR-0076): a boolean attribute that is additive sugar over several
207
- existing granular `no-*` opt-outs `table-toolbar-ui[chrome-only]`
203
+ existing granular `no-*` opt-outs, `table-toolbar-ui[chrome-only]`
208
204
  equivalent to setting all four of `no-filter`, `no-sort`, `no-columns`,
209
- `no-search` is NOT an ADR-0063-style alias-retirement case (the
205
+ `no-search`, is NOT an ADR-0063-style alias-retirement case (the
210
206
  granular attributes aren't duplicate spellings of one concept the way
211
207
  `alert[dismissible]`/`[closable]` were; a consumer may legitimately want
212
208
  a subset off, a combination the preset alone can't express), so the
213
209
  granular attributes stay shipped, independently-addressable API with no
214
- deprecation. The preset's precedence is **pure, absolute OR never a
210
+ deprecation. The preset's precedence is **pure, absolute OR, never a
215
211
  tri-state**: while set, all covered controls are off, full stop, with no
216
212
  partial re-enable via clearing an individual `no-*` while the preset
217
213
  remains set (ADR-0076: "`no-*` attributes are presence-based booleans …
@@ -219,13 +215,13 @@ ADR-0053/0054 don't cover. Any new attribute follows these:
219
215
  [the preset]'s own OR' is not without a tri-state amendment"). A second
220
216
  auto-snap-enum precedent alongside ADR-0074's chart `ratio` lands here
221
217
  too: `table-toolbar-ui[stage]` (`full | search-tight | icon-only |
222
- overflow`) unset auto-snaps via `@container` queries against studied
218
+ overflow`), unset auto-snaps via `@container` queries against studied
223
219
  breakpoints, an explicit value pins and overrides the query, no
224
220
  interpolation between stages.
225
221
 
226
222
  The renames above have LANDED: dual-read compat shims shipped via gh#1563,
227
223
  and the breaking cut removed the old names in 0.8.43 (gh#1617). The new
228
- spellings are the sole ones a yaml or demo still showing an old name is
224
+ spellings are the sole ones, a yaml or demo still showing an old name is
229
225
  stale and should be fixed.
230
226
 
231
227
  ## Disabled-state tokens
@@ -235,20 +231,20 @@ control supporting the `disabled` boolean attribute/state:
235
231
 
236
232
  - **Shared bg role, container-low tier.** `--a-ui-bg-disabled`
237
233
  (`styles/colors/semantics/features.css`) resolves to
238
- `var(--md-sys-color-neutral-container-low)` the same 10%-tint role
234
+ `var(--md-sys-color-neutral-container-low)`, the same 10%-tint role
239
235
  `--a-bg-hover` / `--a-bg-muted` ride for REST-state de-emphasis. A
240
236
  component's own `--<component>-bg-disabled` indirection aliases this
241
- shared role never a raw color, never a per-family `*-container-low`
237
+ shared role, never a raw color, never a per-family `*-container-low`
242
238
  variant (none exists in the disabled path).
243
239
  - **`[state][disabled]` specificity override for checked/selected fills.**
244
240
  A `[checked]`/`[selected]` selector outranks a plain `[disabled]` rule on
245
241
  CSS specificity (2 attribute selectors beat 1), so a checked+disabled
246
242
  control silently keeps its active-state fill unless the component adds an
247
- explicit higher-specificity override `[checked][disabled]` /
248
- `[selected][disabled]` routed through its own
243
+ explicit higher-specificity override, `[checked][disabled]` /
244
+ `[selected][disabled]`, routed through its own
249
245
  `--<component>-...-checked-disabled` (or `-selected-disabled`) custom
250
246
  prop, itself aliasing `--a-ui-bg-disabled`.
251
- - **Reduced-contrast disabled border the `--input-border-disabled`
247
+ - **Reduced-contrast disabled border, the `--input-border-disabled`
252
248
  pattern.** Any control that renders a border when disabled adds
253
249
  `--<component>-border-disabled: var(--a-ui-border-disabled)`, applied as
254
250
  `border-color` under `:scope[disabled]`.
@@ -262,7 +258,7 @@ aliasing `--a-ui-bg-disabled` … uses the reduced-contrast
262
258
  specificity rather than relying on `[disabled]` alone."
263
259
 
264
260
  The re-runnable check is `scripts/audit/audit-disabled-fill-tokens.mjs`
265
- (`check:disabled-fill-audit`, advisory) it fails any `[disabled]`-scoped
261
+ (`check:disabled-fill-audit`, advisory), it fails any `[disabled]`-scoped
266
262
  `background`/`background-color` that doesn't resolve through a
267
263
  disabled-aware indirection.
268
264
 
@@ -274,6 +270,6 @@ If you add a new token category (like `--a-chrome-*` was added), update both thi
274
270
 
275
271
  ## Cross-references
276
272
 
277
- - [authoring-cycle.md](authoring-cycle.md) Step 5 verification gates (`npm run verify:palette` is the mechanical check for raw colors)
278
- - [css-patterns.md](css-patterns.md) the two-block `@scope` pattern in depth, with rationale
279
- - [anti-patterns.md](anti-patterns.md) failure catalogue including variant-rewrites-base bugs
273
+ - [authoring-cycle.md](authoring-cycle.md) Step 5, verification gates (`npm run verify:palette` is the mechanical check for raw colors)
274
+ - [css-patterns.md](css-patterns.md), the two-block `@scope` pattern in depth, with rationale
275
+ - [anti-patterns.md](anti-patterns.md), failure catalogue including variant-rewrites-base bugs