@adia-ai/adia-ui-forge 0.8.62 → 0.8.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +2 -2
- package/.codex-plugin/plugin.json +3 -3
- package/CHANGELOG.md +317 -275
- package/README.md +54 -18
- package/__init__.py +8 -8
- package/agents/a2ui-maintenance-agent.corpus.json +7 -7
- package/agents/a2ui-maintenance-agent.md +6 -6
- package/agents/a2ui-planner-agent.md +11 -11
- package/agents/demo-audit-agent.corpus.json +4 -4
- package/agents/demo-audit-agent.md +8 -9
- package/agents/framework-checker.md +10 -11
- package/agents/framework-planner-agent.md +9 -10
- package/agents/gen-ui-review-agent.corpus.json +4 -4
- package/agents/gen-ui-review-agent.md +8 -8
- package/agents/package-release-agent.corpus.json +4 -4
- package/agents/package-release-agent.md +9 -10
- package/agents/primitive-authoring-agent.corpus.json +1 -1
- package/agents/primitive-authoring-agent.md +5 -5
- package/agents/routing-corpus.json +46 -46
- package/codex.interface.json +1 -1
- package/commands/gen-ui-review.md +1 -1
- package/commands/package-release.md +2 -2
- package/package.json +1 -1
- package/plugin.yaml +2 -2
- package/prompts/gen-ui-review.md +1 -1
- package/prompts/package-release.md +2 -2
- package/references/contracts/a2ui-mcp-surface.md +7 -7
- package/references/contracts/migration-guide-format.md +6 -7
- package/references/shared/content-trust.md +3 -5
- package/skills/a2ui-maintenance/SKILL.md +13 -16
- package/skills/a2ui-maintenance/evals/evals.json +2 -2
- package/skills/a2ui-maintenance/evals/routing-corpus.json +1 -1
- package/skills/a2ui-maintenance/references/anti-patterns.md +10 -10
- package/skills/a2ui-maintenance/references/chunk-authoring.md +11 -12
- package/skills/a2ui-maintenance/references/corpus-discipline.md +13 -14
- package/skills/a2ui-maintenance/references/data-model-reactivity.md +22 -25
- package/skills/a2ui-maintenance/references/eval-diagnostics.md +21 -21
- package/skills/a2ui-maintenance/references/format-extension-decisions.md +12 -13
- package/skills/a2ui-maintenance/references/leverage-rules.md +6 -6
- package/skills/a2ui-maintenance/references/mcp-pipeline-ops.md +9 -10
- package/skills/a2ui-maintenance/references/mcp-tool-reference.md +11 -12
- package/skills/a2ui-maintenance/references/pipeline-overview.md +33 -38
- package/skills/a2ui-maintenance/references/semantic-fail-lifting.md +13 -14
- package/skills/a2ui-maintenance/references/strategy-engines.md +14 -16
- package/skills/a2ui-maintenance/references/surface-lifecycle.md +10 -11
- package/skills/a2ui-maintenance/references/zettel-calibration.md +14 -16
- package/skills/component-md-authoring/SKILL.md +22 -22
- package/skills/component-md-authoring/agents/openai.yaml +1 -1
- package/skills/cross-harness-authoring-standards/SKILL.md +19 -21
- package/skills/cross-harness-authoring-standards/agents/openai.yaml +1 -1
- package/skills/cross-harness-compatibility-standards/SKILL.md +35 -34
- package/skills/cross-harness-compatibility-standards/agents/openai.yaml +1 -1
- package/skills/demo-audit/SKILL.md +38 -38
- package/skills/demo-audit/evals/routing-corpus.json +1 -1
- package/skills/demo-audit/references/admin-shell-anatomy.md +21 -25
- package/skills/demo-audit/references/app-shell-pitfalls.md +11 -13
- package/skills/demo-audit/references/auto-fix-allowlist.md +3 -3
- package/skills/demo-audit/references/card-anatomy-sweep.md +10 -11
- package/skills/demo-audit/references/chat-shell-anatomy.md +25 -26
- package/skills/demo-audit/references/editor-shell-anatomy.md +25 -28
- package/skills/demo-audit/references/html-attr-sweep.md +12 -13
- package/skills/demo-audit/references/mode7-status-battery.md +4 -4
- package/skills/demo-audit/references/native-leak-annotations.md +8 -8
- package/skills/demo-audit/references/output-contract-worked-example.md +8 -8
- package/skills/demo-audit/references/probe-discipline-and-escalation.md +2 -2
- package/skills/demo-audit/references/visual-probe-triage.md +21 -23
- package/skills/gen-ui-review/SKILL.md +28 -28
- package/skills/gen-ui-review/agents/openai.yaml +1 -1
- package/skills/gen-ui-review/evals/evals.json +1 -1
- package/skills/gen-ui-review/evals/routing-corpus.json +1 -1
- package/skills/gen-ui-review/references/corpus-html-patterns.md +28 -29
- package/skills/gen-ui-review/references/exit-gate-mechanics.md +5 -5
- package/skills/gen-ui-review/references/lookup-maintenance.md +2 -2
- package/skills/gen-ui-review/references/loop-protocol.md +25 -25
- package/skills/gen-ui-review/references/rubric-cosmetic.md +10 -10
- package/skills/gen-ui-review/references/rubric-decompose.md +15 -16
- package/skills/gen-ui-review/references/rubric-score.md +23 -24
- package/skills/gen-ui-review/references/scorecard-worked-examples.md +12 -12
- package/skills/gen-ui-review/references/scores.schema.json +2 -2
- package/skills/llm-client-maintenance/SKILL.md +12 -12
- package/skills/llm-client-maintenance/evals/routing-corpus.json +1 -1
- package/skills/llm-client-maintenance/references/adapter-contract.md +27 -27
- package/skills/llm-client-maintenance/references/add-a-provider.md +25 -25
- package/skills/llm-client-maintenance/references/bridge-facade.md +31 -31
- package/skills/llm-client-maintenance/references/browser-proxy-boundary.md +17 -17
- package/skills/llm-client-maintenance/references/model-registry.md +14 -14
- package/skills/llm-client-maintenance/references/streaming-sse.md +21 -21
- package/skills/package-release/SKILL.md +21 -23
- package/skills/package-release/evals/evals.json +2 -2
- package/skills/package-release/evals/routing-corpus.json +2 -2
- package/skills/package-release/references/authorization-model.md +6 -8
- package/skills/package-release/references/changelog-discipline.md +25 -27
- package/skills/package-release/references/cut-procedure.md +129 -136
- package/skills/package-release/references/gates-catalog.md +42 -43
- package/skills/package-release/references/independent-package-release.md +14 -15
- package/skills/package-release/references/invariants-detail.md +9 -9
- package/skills/package-release/references/mechanization.md +11 -12
- package/skills/package-release/references/migration-guide-authoring.md +20 -20
- package/skills/package-release/references/notes-authoring.md +20 -20
- package/skills/package-release/references/recovery-paths.md +28 -28
- package/skills/primitive-authoring/SKILL.md +19 -19
- package/skills/primitive-authoring/agents/openai.yaml +1 -1
- package/skills/primitive-authoring/evals/evals.json +2 -2
- package/skills/primitive-authoring/evals/routing-corpus.json +2 -2
- package/skills/primitive-authoring/references/INDEX.md +22 -22
- package/skills/primitive-authoring/references/anti-patterns.md +76 -77
- package/skills/primitive-authoring/references/api-contract.md +63 -65
- package/skills/primitive-authoring/references/authoring-cycle.md +47 -50
- package/skills/primitive-authoring/references/canonical-pattern-index.md +118 -118
- package/skills/primitive-authoring/references/code-style.md +50 -52
- package/skills/primitive-authoring/references/common-gotchas.md +23 -23
- package/skills/primitive-authoring/references/composite-demo-protocol.md +42 -45
- package/skills/primitive-authoring/references/css-patterns.md +97 -97
- package/skills/primitive-authoring/references/for-attribute-event-contract.md +12 -12
- package/skills/primitive-authoring/references/form-control-sizing.md +16 -18
- package/skills/primitive-authoring/references/lifecycle-patterns.md +42 -42
- package/skills/primitive-authoring/references/llm-bridge.md +21 -21
- package/skills/primitive-authoring/references/module-promotion.md +51 -51
- package/skills/primitive-authoring/references/primitive-audit.md +16 -16
- package/skills/primitive-authoring/references/shell-patterns.md +81 -81
- package/skills/primitive-authoring/references/svg-authoring.md +62 -66
- package/skills/primitive-authoring/references/token-contract.md +45 -49
- package/skills/primitive-authoring/references/trait-pages.md +8 -8
- package/skills/primitive-authoring/references/worked-example.md +39 -39
- package/skills/primitive-authoring/references/yaml-contract.md +139 -143
- package/skills/site-deployment/SKILL.md +25 -26
- package/skills/site-deployment/agents/openai.yaml +1 -1
- package/skills/site-deployment/evals/routing-corpus.json +1 -1
- package/skills/site-deployment/references/deploy-playbooks.md +51 -53
- package/skills/site-docs-authoring/SKILL.md +14 -14
- package/skills/site-docs-authoring/agents/openai.yaml +1 -1
- package/skills/site-docs-authoring/evals/evals.json +1 -1
- package/skills/site-docs-authoring/evals/routing-corpus.json +1 -1
- package/skills/site-docs-authoring/intent.md +18 -18
- package/skills/ssr-compatibility/SKILL.md +24 -25
- package/skills/ssr-compatibility/agents/openai.yaml +1 -1
- package/skills/ssr-compatibility/evals/evals.json +1 -1
- package/skills/ssr-compatibility/evals/routing-corpus.json +1 -1
- package/skills/ssr-compatibility/references/consumer-workarounds.md +22 -23
- package/skills/ssr-compatibility/references/failure-shapes.md +84 -91
- package/skills/ssr-compatibility/references/guard-patterns.md +54 -58
- package/skills/ssr-compatibility/references/status-ledger.md +17 -17
- package/skills/ssr-compatibility/references/test-without-linkedom.md +26 -27
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# SVG authoring
|
|
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
|
|
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
|
|
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
|
|
17
|
-
- **NOT SVG** despite living in the chart family: `chart-legend-ui` (`packages/web-components/components/chart-legend/chart-legend.class.js
|
|
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
|
|
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
|
|
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
|
|
33
|
-
- **`qr-code-ui
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
66
|
-
- **X-axis / value labels
|
|
67
|
-
- **Donut center total/label, gauge value, funnel stage/value/drop, radar labels, sankey node labels
|
|
68
|
-
- **Treemap tile labels
|
|
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)
|
|
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
|
|
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
|
|
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)
|
|
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
|
|
88
|
-
card's inset
|
|
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
|
|
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
|
|
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
|
|
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)
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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`)
|
|
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
|
|
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
|
|
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"
|
|
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
|
|
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
|
|
209
|
-
radial-bar arcs
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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]
|
|
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
|
|
246
|
+
(deliberately never theme-derived, light-on-dark won't scan), plus its
|
|
251
247
|
contract pairing with `[background]`.
|
|
252
|
-
- **`icon-ui[weight]
|
|
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
|
|
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
|
|
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)
|
|
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)
|
|
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
|
|
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
|
|
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)
|
|
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
|
|
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
|
-
|
|
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
|
|
62
|
-
3. **Variants override tokens
|
|
63
|
-
4. **`:where(:scope)`** for token defaults
|
|
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
|
|
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
|
|
112
|
+
Added in v0.5.0, use these for UI chrome:
|
|
115
113
|
|
|
116
|
-
- `--a-chrome-light
|
|
117
|
-
- `--a-chrome-dark
|
|
118
|
-
- `--a-chrome-border
|
|
119
|
-
- `--a-chrome-ring-subtle
|
|
120
|
-
- `--a-chrome-shadow-soft
|
|
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
|
|
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
|
|
125
|
+
## Where foundation styles live, dimension × layer
|
|
128
126
|
|
|
129
|
-
Every file under `packages/web-components/styles/` answers two questions
|
|
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
|
|
135
|
-
- `tokens.css`, `typography.css`, `colors/semantics.css`, etc. remain **compat barrels** at their public paths
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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)
|
|
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
|
|
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
|
|
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
|
|
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]
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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`)
|
|
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
|
|
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)
|
|
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
|
|
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
|
|
248
|
-
`[selected][disabled]
|
|
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
|
|
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)
|
|
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
|
|
278
|
-
- [css-patterns.md](css-patterns.md)
|
|
279
|
-
- [anti-patterns.md](anti-patterns.md)
|
|
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
|