@epa-wg/custom-element-dist 0.0.33 → 0.0.35
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/settings.local.json +18 -0
- package/.gitattributes +4 -0
- package/.github/workflows/deploy.yml +59 -0
- package/.idea/copilot.data.migration.agent.xml +6 -0
- package/.idea/copilot.data.migration.ask.xml +6 -0
- package/.idea/copilot.data.migration.ask2agent.xml +6 -0
- package/.idea/copilot.data.migration.edit.xml +6 -0
- package/.idea/custom-element-dist.iml +2 -0
- package/.storybook/main.ts +20 -17
- package/.storybook/preview.ts +23 -23
- package/.yarn/install-state.gz +0 -0
- package/.yarnrc.yml +1 -0
- package/README.md +6 -4
- package/coverage/block-navigation.js +1 -1
- package/coverage/coverage-final.json +4 -3
- package/coverage/index.html +34 -19
- package/coverage/sorter.js +21 -7
- package/coverage/src/custom-element/coverage.svg +1 -1
- package/coverage/src/custom-element/custom-element.js/coverage.svg +1 -1
- package/coverage/src/custom-element/custom-element.js.html +448 -391
- package/coverage/src/custom-element/http-request.js/coverage.svg +1 -1
- package/coverage/src/custom-element/http-request.js.html +38 -17
- package/coverage/src/custom-element/index.html +26 -26
- package/coverage/src/custom-element/local-storage.js.html +1 -1
- package/coverage/src/custom-element/location-element.js.html +1 -1
- package/coverage/src/custom-element/module-url.js.html +1 -1
- package/coverage/src/index.html +1 -1
- package/coverage/src/material/theme/colors.js/coverage.svg +10 -0
- package/coverage/src/material/theme/colors.js.html +217 -0
- package/coverage/src/material/theme/coverage.svg +10 -0
- package/coverage/src/material/theme/index.html +116 -0
- package/coverage/src/mocks/handlers.ts.html +1 -1
- package/coverage/src/mocks/index.html +1 -1
- package/coverage/src/stories/frame.canvas.ts.html +1 -1
- package/coverage/src/stories/http-request.stories.ts.html +1 -1
- package/coverage/src/stories/index.html +1 -1
- package/coverage/src/stories/testStoryBook.ts.html +12 -12
- package/coverage/src/sum.ts.html +1 -1
- package/dist/custom-element-Bssk9jRy.cjs +97 -0
- package/dist/{custom-element-WnOqmEOe.js → custom-element-BzDjIYMe.js} +193 -183
- package/dist/custom-element-bundle.cjs +1 -1
- package/dist/custom-element-bundle.js +3 -3
- package/dist/demo/a.html +10 -3
- package/dist/demo/a.svg +26 -26
- package/dist/demo/html-template.html +4 -3
- package/dist/demo/s.xml +1 -75
- package/dist/demo/s.xslt +351 -72
- package/dist/demo/s1.xml +3706 -0
- package/dist/http-request-DSaowcG1.cjs +1 -0
- package/dist/{http-request-BOvP4KTl.js → http-request-DTCzZ1gc.js} +15 -9
- package/dist/mockServiceWorker.js +105 -63
- package/package.json +5 -4
- package/public/demo/a.html +10 -3
- package/public/demo/a.svg +26 -26
- package/public/demo/html-template.html +4 -3
- package/public/demo/s.xml +1 -75
- package/public/demo/s.xslt +351 -72
- package/public/demo/s1.xml +3706 -0
- package/public/mockServiceWorker.js +105 -63
- package/src/custom-element/custom-element.js +28 -9
- package/src/custom-element/demo/a.html +10 -3
- package/src/custom-element/demo/a.svg +26 -26
- package/src/custom-element/demo/html-template.html +4 -3
- package/src/custom-element/demo/s.xml +1 -75
- package/src/custom-element/demo/s.xslt +351 -72
- package/src/custom-element/demo/s1.xml +3706 -0
- package/src/custom-element/http-request.js +7 -0
- package/src/custom-element/ide/customData-dce.json +123 -0
- package/src/custom-element/ide/web-types-dce.json +128 -1
- package/src/custom-element/ide/web-types-xsl.json +1 -1
- package/src/material/angular.css +987 -987
- package/src/material/components/action.html +262 -0
- package/src/material/components/autocomplete.html +167 -239
- package/src/material/components/badge.html +238 -239
- package/src/material/components/dropdown.html +0 -1
- package/src/material/components/icon-link.html +160 -161
- package/src/material/components/icon.html +251 -252
- package/src/material/components/input.html +569 -570
- package/src/material/components/menu.html +235 -236
- package/src/material/components.html +157 -158
- package/src/material/demo.css +36 -36
- package/src/material/index.html +109 -110
- package/src/material/material.css +356 -356
- package/src/material/theme/Base-Principles.md +339 -0
- package/src/material/theme/README.md +298 -18
- package/src/material/theme/UI Domain Model in web applications.svg +1 -0
- package/src/material/theme/User Semantic Theme tokens.svg +1 -0
- package/src/material/theme/action-pending-poc.html +62 -0
- package/src/material/theme/actions-color.html +141 -0
- package/src/material/theme/colors-light.html +631 -0
- package/src/material/theme/colors-native.html +51 -0
- package/src/material/theme/colors-poc.html +66 -0
- package/src/material/theme/colors.html +297 -0
- package/src/material/theme/colors.js +44 -0
- package/src/material/theme/consumer-theme.css +745 -0
- package/src/material/theme/semantic.css +132 -132
- package/src/material/theme/style-bug.html +123 -0
- package/src/material/theme/theme-data.css +43 -0
- package/src/material/theme/theme-data.xhtml +2926 -0
- package/src/material/theme/todo.md +274 -0
- package/src/material/theme/tokens/action-colors.png +0 -0
- package/src/material/theme/tokens/cem-article-illustration-4x1-letterbox-2000x500.png +0 -0
- package/src/material/theme/tokens/cem-breakpoints.md +519 -0
- package/src/material/theme/tokens/cem-colors.md +715 -0
- package/src/material/theme/tokens/cem-consumerflow-typography-matrix.svg +198 -0
- package/src/material/theme/tokens/cem-coupling.md +372 -0
- package/src/material/theme/tokens/cem-data-vs-reading-numerals.svg +164 -0
- package/src/material/theme/tokens/cem-dimension.md +625 -0
- package/src/material/theme/tokens/cem-layering.md +562 -0
- package/src/material/theme/tokens/cem-m3-parity.md +343 -0
- package/src/material/theme/tokens/cem-responsive.md +238 -0
- package/src/material/theme/tokens/cem-shape.md +691 -0
- package/src/material/theme/tokens/cem-stroke-density-illustration-4to1-v3.svg +102 -0
- package/src/material/theme/tokens/cem-stroke.md +480 -0
- package/src/material/theme/tokens/cem-timing.md +198 -0
- package/src/material/theme/tokens/cem-typography-model-stack.svg +64 -0
- package/src/material/theme/tokens/cem-voice-fonts-typography.md +718 -0
- package/src/material/theme/tokens/cem-voice-ladder.svg +91 -0
- package/src/material/theme/tokens/chips.png +0 -0
- package/src/material/theme/tokens/columns-page.png +0 -0
- package/src/material/theme/tokens/initials.png +0 -0
- package/src/material/theme/tokens/nav-buttons.png +0 -0
- package/src/material/theme/tokens/script.png +0 -0
- package/src/material/theme/tokens/sufler.png +0 -0
- package/src/material/theme/tokens/typography-icons.png +0 -0
- package/src/mocks/versions.mock.ts +1 -1
- package/src/stories/__screenshots__/dom-merge.test.stories.ts/dom-merge-dom-merge-OrderPreservingOn2ndTransform-1.png +0 -0
- package/src/stories/__screenshots__/xslt-conditionals.test.stories.ts/xslt-conditionals-xslt-conditionals-MultipleIfOrderingWorkaround-1.png +0 -0
- package/src/stories/dom-merge.test.stories.ts +25 -1
- package/src/stories/xslt-conditionals.test.stories.ts +492 -0
- package/src/stories/xslt-for-each.test.stories.ts +336 -0
- package/src/stories/xslt-if.test.stories.ts +89 -0
- package/storybook-static/assets/{Color-F6OSRLHC-Cbp293x2.js → Color-F6OSRLHC-DeDlDLjU.js} +1 -1
- package/storybook-static/assets/{Configure-BrFr4SLE.js → Configure-CH_tIP5N.js} +1 -1
- package/storybook-static/assets/{DocsRenderer-CFRXHY34-DhHzJiIO.js → DocsRenderer-CFRXHY34-Bc9EPsUI.js} +2 -2
- package/storybook-static/assets/{attributes.test.stories-Gg9LQTEK.js → attributes.test.stories-BtamFQkF.js} +1 -1
- package/storybook-static/assets/{css.test.stories-B_3ltOrx.js → css.test.stories-BfNxLgwr.js} +1 -1
- package/storybook-static/assets/{custom-element-CPnvJnn8.js → custom-element-CnmjNo0g.js} +6 -6
- package/storybook-static/assets/{dom-merge.test.stories-nQxcgLoM.js → dom-merge.test.stories-DxnitrLK.js} +47 -6
- package/storybook-static/assets/entry-preview-BNCt9WBs.js +26 -0
- package/storybook-static/assets/{entry-preview-docs-Dwczwtsc.js → entry-preview-docs-CbF8-81D.js} +2 -2
- package/storybook-static/assets/{external-template.test.stories-DZ-rjnfd.js → external-template.test.stories-BTsww7B0.js} +1 -1
- package/storybook-static/assets/{form.test.stories-DQhPYtMj.js → form.test.stories-DNJFtPJb.js} +1 -1
- package/storybook-static/assets/{frame.canvas-ClTqYyMN.js → frame.canvas-E5n9h6j1.js} +1 -1
- package/storybook-static/assets/{handlers-CLkps6Nz.js → handlers-Dvg8CAeR.js} +1 -1
- package/storybook-static/assets/http-request-BWeEEBkP.js +1 -0
- package/storybook-static/assets/{http-request.stories-jo0f73nw.js → http-request.stories-DgrBNle8.js} +1 -1
- package/storybook-static/assets/{iframe-CZwRpnn9.js → iframe-DiVWehoI.js} +11 -11
- package/storybook-static/assets/{index-Dr4PwNfd.js → index-CGuyH0k-.js} +87 -87
- package/storybook-static/assets/{index-CJQtnF9V.js → index-CdEbhcV9.js} +1 -1
- package/storybook-static/assets/index-DO1nmyvI.js +11 -0
- package/storybook-static/assets/{index-B68YUdzy.js → index-w6iX3YlR.js} +3 -3
- package/storybook-static/assets/{local-storage.test.stories-uA5EKRPf.js → local-storage.test.stories-Hwq80yUr.js} +1 -1
- package/storybook-static/assets/{location-element.test.stories-Cu-6Elcg.js → location-element.test.stories-mEhZzm7x.js} +1 -1
- package/storybook-static/assets/{module-url.test.stories-CD_wusXQ.js → module-url.test.stories-Bj46iT0V.js} +1 -1
- package/storybook-static/assets/{preview-CuCH40jj.js → preview-BjbXcJci.js} +2 -2
- package/storybook-static/assets/{preview-BFlNN3Wj.js → preview-Bn8igYMp.js} +1 -1
- package/storybook-static/assets/{preview-Cm4PPhHS.js → preview-CfuT8gak.js} +1 -1
- package/storybook-static/assets/{set-url.test.stories-CY7B9BVZ.js → set-url.test.stories-hzxLcqmm.js} +1 -1
- package/storybook-static/assets/{slice-events.test.stories-BVnPXm6e.js → slice-events.test.stories-DVyXFRU1.js} +1 -1
- package/storybook-static/assets/{slots.test.stories-Dxsa9KdA.js → slots.test.stories-CS544nS4.js} +1 -1
- package/storybook-static/assets/{version-select.test.stories-Buga1PAa.js → version-select.test.stories-D36nfYBq.js} +1 -1
- package/storybook-static/assets/xslt-conditionals.test.stories-BS1PTIHe.js +633 -0
- package/storybook-static/assets/xslt-for-each.test.stories-CtPS20RK.js +329 -0
- package/storybook-static/assets/xslt-if.test.stories-DcHrAMSY.js +71 -0
- package/storybook-static/demo/a.html +10 -3
- package/storybook-static/demo/a.svg +26 -26
- package/storybook-static/demo/html-template.html +4 -3
- package/storybook-static/demo/s.xml +1 -75
- package/storybook-static/demo/s.xslt +351 -72
- package/storybook-static/demo/s1.xml +3706 -0
- package/storybook-static/iframe.html +2 -2
- package/storybook-static/index.json +1 -1
- package/storybook-static/mockServiceWorker.js +105 -63
- package/storybook-static/project.json +1 -1
- package/storybook-static/sb-addons/essentials-controls-1/manager-bundle.js +69 -66
- package/storybook-static/sb-addons/essentials-docs-3/manager-bundle.js +62 -59
- package/dist/custom-element-6slVaFEs.cjs +0 -97
- package/dist/http-request-DPrY7mGh.cjs +0 -1
- package/storybook-static/assets/attributes.test.stories-CzWkKw0e.js +0 -1
- package/storybook-static/assets/entry-preview-DHVXbf3x.js +0 -26
- package/storybook-static/assets/external-template.test.stories-BivZqBTp.js +0 -1
- package/storybook-static/assets/http-request-DNq59pnj.js +0 -1
- package/storybook-static/assets/index-BwkS7JH_.js +0 -8
- package/storybook-static/assets/module-url.test.stories-CTjUAk3J.js +0 -1
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1400" height="860" viewBox="0 0 1400 860" role="img" aria-label="Consumer-flow mapping matrix: purpose by semantic typography roles with default voice levels">
|
|
3
|
+
<defs>
|
|
4
|
+
<style>
|
|
5
|
+
:root {
|
|
6
|
+
--cem-ink: #111;
|
|
7
|
+
--cem-surface: #fff;
|
|
8
|
+
--cem-outline: #c7c7c7;
|
|
9
|
+
--cem-muted: #666;
|
|
10
|
+
--cem-accent: #005a9c;
|
|
11
|
+
--cem-soft: #f6f7f9;
|
|
12
|
+
}
|
|
13
|
+
.frame { fill: var(--cem-surface); stroke: var(--cem-outline); stroke-width: 2; rx: 18; }
|
|
14
|
+
.cell { fill: var(--cem-surface); stroke: var(--cem-outline); stroke-width: 1.5; rx: 12; }
|
|
15
|
+
.cell-soft { fill: var(--cem-soft); stroke: var(--cem-outline); stroke-width: 1.5; rx: 12; }
|
|
16
|
+
.cell-accent { fill: var(--cem-surface); stroke: var(--cem-accent); stroke-width: 2.2; rx: 12; }
|
|
17
|
+
.title { font: 700 32px system-ui, -apple-system, "Segoe UI", Arial, sans-serif; fill: var(--cem-ink); }
|
|
18
|
+
.muted { font: 400 16px system-ui, -apple-system, "Segoe UI", Arial, sans-serif; fill: var(--cem-muted); }
|
|
19
|
+
.h { font: 650 18px system-ui, -apple-system, "Segoe UI", Arial, sans-serif; fill: var(--cem-ink); }
|
|
20
|
+
.t { font: 400 16px system-ui, -apple-system, "Segoe UI", Arial, sans-serif; fill: var(--cem-ink); }
|
|
21
|
+
.tag { font: 600 14px ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; fill: var(--cem-accent); }
|
|
22
|
+
</style>
|
|
23
|
+
</defs>
|
|
24
|
+
|
|
25
|
+
<rect x="30" y="30" width="1340" height="800" class="frame"/>
|
|
26
|
+
|
|
27
|
+
<text x="70" y="92" class="title">Consumer flow → semantic typography roles → default voice</text>
|
|
28
|
+
<text x="70" y="125" class="muted">Roles are “what the content is”; flow is “what the user is doing”.</text>
|
|
29
|
+
|
|
30
|
+
<rect x="70" y="170" width="260" height="60" class="cell-soft"/>
|
|
31
|
+
<text x="90" y="208" class="h">Consumer flow purpose</text>
|
|
32
|
+
|
|
33
|
+
<g>
|
|
34
|
+
<rect x="340" y="170" width="170" height="60" class="cell-soft"/>
|
|
35
|
+
<text x="360" y="198" class="h">Reading</text>
|
|
36
|
+
<text x="360" y="220" class="tag">voice: regular</text>
|
|
37
|
+
|
|
38
|
+
<rect x="520" y="170" width="170" height="60" class="cell-soft"/>
|
|
39
|
+
<text x="540" y="198" class="h">UI</text>
|
|
40
|
+
<text x="540" y="220" class="tag">voice: gentle</text>
|
|
41
|
+
|
|
42
|
+
<rect x="700" y="170" width="170" height="60" class="cell-soft"/>
|
|
43
|
+
<text x="720" y="198" class="h">Data</text>
|
|
44
|
+
<text x="720" y="220" class="tag">voice: regular</text>
|
|
45
|
+
|
|
46
|
+
<rect x="880" y="170" width="170" height="60" class="cell-soft"/>
|
|
47
|
+
<text x="900" y="198" class="h">Tag</text>
|
|
48
|
+
<text x="900" y="220" class="tag">voice: firm</text>
|
|
49
|
+
|
|
50
|
+
<rect x="1060" y="170" width="170" height="60" class="cell-soft"/>
|
|
51
|
+
<text x="1080" y="198" class="h">Script</text>
|
|
52
|
+
<text x="1080" y="220" class="tag">voice: gentle</text>
|
|
53
|
+
|
|
54
|
+
<rect x="1240" y="170" width="120" height="60" class="cell-soft"/>
|
|
55
|
+
<text x="1255" y="198" class="h">Init.</text>
|
|
56
|
+
<text x="1255" y="220" class="tag">voice: firm</text>
|
|
57
|
+
</g>
|
|
58
|
+
|
|
59
|
+
<!-- Row 1 -->
|
|
60
|
+
<g>
|
|
61
|
+
<rect x="70" y="245" width="260" height="80" class="cell-soft"/>
|
|
62
|
+
<text x="90" y="285" class="h">Orient / Navigate</text>
|
|
63
|
+
<text x="90" y="310" class="muted">Where am I? Where do I go?</text>
|
|
64
|
+
|
|
65
|
+
<rect x="340" y="245" width="170" height="80" class="cell"/>
|
|
66
|
+
<text x="360" y="292" class="t">Sometimes</text>
|
|
67
|
+
|
|
68
|
+
<rect x="520" y="245" width="170" height="80" class="cell-accent"/>
|
|
69
|
+
<text x="540" y="280" class="t">Primary</text>
|
|
70
|
+
<text x="540" y="305" class="tag">voice: gentle</text>
|
|
71
|
+
|
|
72
|
+
<rect x="700" y="245" width="170" height="80" class="cell"/>
|
|
73
|
+
<text x="720" y="292" class="t">Rare</text>
|
|
74
|
+
|
|
75
|
+
<rect x="880" y="245" width="170" height="80" class="cell-accent"/>
|
|
76
|
+
<text x="900" y="280" class="t">Often</text>
|
|
77
|
+
<text x="900" y="305" class="tag">voice: firm</text>
|
|
78
|
+
|
|
79
|
+
<rect x="1060" y="245" width="170" height="80" class="cell"/>
|
|
80
|
+
<text x="1080" y="292" class="t">Sometimes</text>
|
|
81
|
+
|
|
82
|
+
<rect x="1240" y="245" width="120" height="80" class="cell-accent"/>
|
|
83
|
+
<text x="1255" y="280" class="t">Often</text>
|
|
84
|
+
<text x="1255" y="305" class="tag">voice: firm</text>
|
|
85
|
+
</g>
|
|
86
|
+
|
|
87
|
+
<!-- Row 2 -->
|
|
88
|
+
<g>
|
|
89
|
+
<rect x="70" y="345" width="260" height="80" class="cell-soft"/>
|
|
90
|
+
<text x="90" y="385" class="h">Scan / Compare</text>
|
|
91
|
+
<text x="90" y="410" class="muted">What differs? Which option wins?</text>
|
|
92
|
+
|
|
93
|
+
<rect x="340" y="345" width="170" height="80" class="cell"/>
|
|
94
|
+
<text x="360" y="392" class="t">Sometimes</text>
|
|
95
|
+
|
|
96
|
+
<rect x="520" y="345" width="170" height="80" class="cell-accent"/>
|
|
97
|
+
<text x="540" y="380" class="t">Often</text>
|
|
98
|
+
<text x="540" y="405" class="tag">voice: gentle</text>
|
|
99
|
+
|
|
100
|
+
<rect x="700" y="345" width="170" height="80" class="cell-accent"/>
|
|
101
|
+
<text x="720" y="380" class="t">Primary</text>
|
|
102
|
+
<text x="720" y="405" class="tag">voice: regular</text>
|
|
103
|
+
|
|
104
|
+
<rect x="880" y="345" width="170" height="80" class="cell-accent"/>
|
|
105
|
+
<text x="900" y="380" class="t">Often</text>
|
|
106
|
+
<text x="900" y="405" class="tag">voice: firm</text>
|
|
107
|
+
|
|
108
|
+
<rect x="1060" y="345" width="170" height="80" class="cell"/>
|
|
109
|
+
<text x="1080" y="392" class="t">Rare</text>
|
|
110
|
+
|
|
111
|
+
<rect x="1240" y="345" width="120" height="80" class="cell"/>
|
|
112
|
+
<text x="1255" y="392" class="t">Sometimes</text>
|
|
113
|
+
</g>
|
|
114
|
+
|
|
115
|
+
<!-- Row 3 -->
|
|
116
|
+
<g>
|
|
117
|
+
<rect x="70" y="445" width="260" height="80" class="cell-soft"/>
|
|
118
|
+
<text x="90" y="485" class="h">Read / Learn</text>
|
|
119
|
+
<text x="90" y="510" class="muted">Understand and retain</text>
|
|
120
|
+
|
|
121
|
+
<rect x="340" y="445" width="170" height="80" class="cell-accent"/>
|
|
122
|
+
<text x="360" y="480" class="t">Primary</text>
|
|
123
|
+
<text x="360" y="505" class="tag">voice: regular</text>
|
|
124
|
+
|
|
125
|
+
<rect x="520" y="445" width="170" height="80" class="cell"/>
|
|
126
|
+
<text x="540" y="492" class="t">Sometimes</text>
|
|
127
|
+
|
|
128
|
+
<rect x="700" y="445" width="170" height="80" class="cell"/>
|
|
129
|
+
<text x="720" y="492" class="t">Sometimes</text>
|
|
130
|
+
|
|
131
|
+
<rect x="880" y="445" width="170" height="80" class="cell"/>
|
|
132
|
+
<text x="900" y="492" class="t">Sometimes</text>
|
|
133
|
+
|
|
134
|
+
<rect x="1060" y="445" width="170" height="80" class="cell"/>
|
|
135
|
+
<text x="1080" y="492" class="t">Sometimes</text>
|
|
136
|
+
|
|
137
|
+
<rect x="1240" y="445" width="120" height="80" class="cell"/>
|
|
138
|
+
<text x="1255" y="492" class="t">Rare</text>
|
|
139
|
+
</g>
|
|
140
|
+
|
|
141
|
+
<!-- Row 4 -->
|
|
142
|
+
<g>
|
|
143
|
+
<rect x="70" y="545" width="260" height="80" class="cell-soft"/>
|
|
144
|
+
<text x="90" y="585" class="h">Act</text>
|
|
145
|
+
<text x="90" y="610" class="muted">Commit: click, submit, approve</text>
|
|
146
|
+
|
|
147
|
+
<rect x="340" y="545" width="170" height="80" class="cell"/>
|
|
148
|
+
<text x="360" y="592" class="t">Rare</text>
|
|
149
|
+
|
|
150
|
+
<rect x="520" y="545" width="170" height="80" class="cell-accent"/>
|
|
151
|
+
<text x="540" y="580" class="t">Primary</text>
|
|
152
|
+
<text x="540" y="605" class="tag">voice: firm</text>
|
|
153
|
+
|
|
154
|
+
<rect x="700" y="545" width="170" height="80" class="cell"/>
|
|
155
|
+
<text x="720" y="592" class="t">Sometimes</text>
|
|
156
|
+
|
|
157
|
+
<rect x="880" y="545" width="170" height="80" class="cell-accent"/>
|
|
158
|
+
<text x="900" y="580" class="t">Often</text>
|
|
159
|
+
<text x="900" y="605" class="tag">voice: firm</text>
|
|
160
|
+
|
|
161
|
+
<rect x="1060" y="545" width="170" height="80" class="cell"/>
|
|
162
|
+
<text x="1080" y="592" class="t">Rare</text>
|
|
163
|
+
|
|
164
|
+
<rect x="1240" y="545" width="120" height="80" class="cell"/>
|
|
165
|
+
<text x="1255" y="592" class="t">Rare</text>
|
|
166
|
+
</g>
|
|
167
|
+
|
|
168
|
+
<!-- Row 5 -->
|
|
169
|
+
<g>
|
|
170
|
+
<rect x="70" y="645" width="260" height="80" class="cell-soft"/>
|
|
171
|
+
<text x="90" y="685" class="h">Confirm / Reflect</text>
|
|
172
|
+
<text x="90" y="710" class="muted">Understand outcomes and status</text>
|
|
173
|
+
|
|
174
|
+
<rect x="340" y="645" width="170" height="80" class="cell-accent"/>
|
|
175
|
+
<text x="360" y="680" class="t">Often</text>
|
|
176
|
+
<text x="360" y="705" class="tag">voice: regular</text>
|
|
177
|
+
|
|
178
|
+
<rect x="520" y="645" width="170" height="80" class="cell-accent"/>
|
|
179
|
+
<text x="540" y="680" class="t">Often</text>
|
|
180
|
+
<text x="540" y="705" class="tag">voice: gentle</text>
|
|
181
|
+
|
|
182
|
+
<rect x="700" y="645" width="170" height="80" class="cell-accent"/>
|
|
183
|
+
<text x="720" y="680" class="t">Often</text>
|
|
184
|
+
<text x="720" y="705" class="tag">voice: regular</text>
|
|
185
|
+
|
|
186
|
+
<rect x="880" y="645" width="170" height="80" class="cell-accent"/>
|
|
187
|
+
<text x="900" y="680" class="t">Often</text>
|
|
188
|
+
<text x="900" y="705" class="tag">voice: firm</text>
|
|
189
|
+
|
|
190
|
+
<rect x="1060" y="645" width="170" height="80" class="cell"/>
|
|
191
|
+
<text x="1080" y="692" class="t">Rare</text>
|
|
192
|
+
|
|
193
|
+
<rect x="1240" y="645" width="120" height="80" class="cell"/>
|
|
194
|
+
<text x="1255" y="692" class="t">Sometimes</text>
|
|
195
|
+
</g>
|
|
196
|
+
|
|
197
|
+
<text x="70" y="792" class="muted">Tip: Treat alerts and safety-critical announcements as separate semantics (not just typography). Use ARIA live regions appropriately.</text>
|
|
198
|
+
</svg>
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
# Semantic Density and Coupling Tokens (CEM) — Canonical Spec
|
|
2
|
+
|
|
3
|
+
**Status:** Canonical (v1.0)
|
|
4
|
+
|
|
5
|
+
**Last updated:** December 17, 2025
|
|
6
|
+
|
|
7
|
+
**Audience:** Design Systems, Product Design, Front-End Engineering
|
|
8
|
+
|
|
9
|
+
**Applies to:** Modality-neutral interactive operability (pointer, touch, stylus, gaze, switch, remote)
|
|
10
|
+
|
|
11
|
+
**Companion specs:**
|
|
12
|
+
- **D0. Color (Emotional Palette)** ([`cem-colors.md`](./cem-colors.md)) — color intensity perception affected by density
|
|
13
|
+
- **D1. Space & Rhythm** ([`cem-dimension.md`](./cem-dimension.md)) — gaps/insets/rhythm; must never violate D2 safety minimums
|
|
14
|
+
- **D3. Shape — Bend** ([`cem-shape.md`](./cem-shape.md)) — bend interacts with control height for round-ends
|
|
15
|
+
- **D4. Layering** ([`cem-layering.md`](./cem-layering.md)) — overlay/modal control sizing
|
|
16
|
+
- **D5. Stroke & Separation** ([`cem-stroke.md`](./cem-stroke.md)) — focus ring offset; D2 constrains adjacent zones so indicators have room
|
|
17
|
+
- **D7. Time & Motion** ([`cem-timing.md`](./cem-timing.md)) — timing for density transitions
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. Why "Coupling"
|
|
22
|
+
|
|
23
|
+
In CEM terms, **coupling** names the moment when **user intent successfully binds to an interactive affordance**—independent of input modality:
|
|
24
|
+
|
|
25
|
+
- pointer (mouse/trackpad/stylus)
|
|
26
|
+
- direct touch (touchscreen)
|
|
27
|
+
- gaze (eye tracking + dwell)
|
|
28
|
+
- switch scanning (highlight + select)
|
|
29
|
+
- remote / gamepad focus navigation
|
|
30
|
+
|
|
31
|
+
This supports the UI-space meaning you called out: proximity implies **relationship/coupling between controls**, and insufficient distancing causes **interference**.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 2. Taxonomy placement
|
|
36
|
+
|
|
37
|
+
- **D2. Coupling & Compactness** (this canvas)
|
|
38
|
+
- operable zone minimums
|
|
39
|
+
- interference/isolation minimums
|
|
40
|
+
- halo/expansion policy (visual size vs operable size)
|
|
41
|
+
- control geometry endpoints that affect operability (heights/paddings/row sizes)
|
|
42
|
+
|
|
43
|
+
- **D1. Space & Rhythm** (from [`cem-dimension.md`](./cem-dimension.md))
|
|
44
|
+
- gaps/insets/rhythm
|
|
45
|
+
- may *suggest* coupling semantics, but must never violate D2 safety minimums
|
|
46
|
+
|
|
47
|
+
- **D5. Stroke & Separation** (from [`cem-stroke.md`](./cem-stroke.md))
|
|
48
|
+
- focus/selection/target indicator thickness and indicator placement (e.g., `--cem-stroke-focus`, `--cem-stroke-indicator-offset`, zebra ring tokens)
|
|
49
|
+
- D2 does not define focus-ring offset; D2 constrains adjacent operable zones so D5 indicators have room and do not create interference
|
|
50
|
+
|
|
51
|
+
Boundary heuristic:
|
|
52
|
+
|
|
53
|
+
- If it changes **distance between items** → D1
|
|
54
|
+
- If it changes **operable geometry of an interactive element** → D2
|
|
55
|
+
- If it changes **indicator thickness or indicator offset** → D5 (e.g., `--cem-stroke-focus`, `--cem-stroke-indicator-offset`)
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 3. Coupling vocabulary (semantic + dimensional)
|
|
60
|
+
|
|
61
|
+
To keep the naming dimensional and unambiguous, use three core nouns:
|
|
62
|
+
|
|
63
|
+
- **Zone**: the operable area where intent can couple
|
|
64
|
+
- **Halo**: invisible expansion beyond visuals (to meet zone minimums without enlarging chrome)
|
|
65
|
+
- **Guard**: the minimum distancing preventing interference between adjacent zones
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 4. Canonical D2 coupling tokens
|
|
70
|
+
|
|
71
|
+
### 4.1 Hard safety minimums (policy anchors)
|
|
72
|
+
|
|
73
|
+
```css
|
|
74
|
+
:root {
|
|
75
|
+
/* Minimum operable zone (layout-level). Keep invariant across density modes. */
|
|
76
|
+
--cem-coupling-zone-min: 3rem; /* nominally 48px @ 16px root */
|
|
77
|
+
|
|
78
|
+
/* Minimum distancing between adjacent operable zones (prevents interference). */
|
|
79
|
+
--cem-coupling-guard-min: 0.5rem; /* nominally 8px */
|
|
80
|
+
|
|
81
|
+
/* Invisible expansion beyond visuals (halo). */
|
|
82
|
+
--cem-coupling-halo: 0.25rem; /* nominally 4px */
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Interpretation:
|
|
87
|
+
|
|
88
|
+
- **Zone** answers: “Can the user reliably couple intent to this control?”
|
|
89
|
+
- **Guard** answers: “Will adjacent controls accidentally steal intent?”
|
|
90
|
+
- **Halo** answers: “Can we keep visuals compact while preserving operability?”
|
|
91
|
+
|
|
92
|
+
### 4.1.1 D2/D5 compatibility: guard must cover indicator outset (normative)
|
|
93
|
+
|
|
94
|
+
When D5 indicators render **outside** the control edge (e.g., `outline-offset`, zebra rings),
|
|
95
|
+
`--cem-coupling-guard-min` SHOULD be large enough to prevent indicator collisions between adjacent controls.
|
|
96
|
+
|
|
97
|
+
Define the worst-case outward indicator extent as:
|
|
98
|
+
|
|
99
|
+
- Zebra ring (contrast): `4 * --cem-zebra-strip-size`
|
|
100
|
+
- Outline ring: `--cem-stroke-indicator-offset + --cem-stroke-focus`
|
|
101
|
+
|
|
102
|
+
Compatibility guideline:
|
|
103
|
+
|
|
104
|
+
`--cem-coupling-guard-min` SHOULD be ≥
|
|
105
|
+
`max(4 * --cem-zebra-strip-size, --cem-stroke-indicator-offset + --cem-stroke-focus)`
|
|
106
|
+
|
|
107
|
+
Default CEM values satisfy this (8px guard vs 8px zebra extent when `--cem-zebra-strip-size = 2px`).
|
|
108
|
+
|
|
109
|
+
### 4.2 Control geometry endpoints (typical mapping layer)
|
|
110
|
+
|
|
111
|
+
```css
|
|
112
|
+
:root {
|
|
113
|
+
/* Generic control geometry */
|
|
114
|
+
--cem-control-height: 2.5rem;
|
|
115
|
+
--cem-control-padding-x: 0.75rem;
|
|
116
|
+
--cem-control-padding-y: 0.5rem;
|
|
117
|
+
|
|
118
|
+
/* Icon buttons: typically make the visible container meet the operable zone */
|
|
119
|
+
--cem-icon-button-size: var(--cem-coupling-zone-min);
|
|
120
|
+
--cem-icon-button-icon-size: 1.25rem;
|
|
121
|
+
|
|
122
|
+
/* Lists / menus */
|
|
123
|
+
--cem-list-row-height: 3rem;
|
|
124
|
+
--cem-menu-row-height: 3rem;
|
|
125
|
+
|
|
126
|
+
/* Data tables */
|
|
127
|
+
--cem-table-row-height: 2.5rem;
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Note: control geometry can vary by mode; coupling minimums should remain invariant.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 5. Coupling modes (consumer semantics)
|
|
136
|
+
|
|
137
|
+
### 5.1 Mode meanings
|
|
138
|
+
|
|
139
|
+
- **forgiving**
|
|
140
|
+
- optimized for imprecise input: thumbs, gloves, kiosks, gaze/dwell
|
|
141
|
+
- larger visual controls; halo may be smaller because visuals already provide the zone
|
|
142
|
+
|
|
143
|
+
- **balanced**
|
|
144
|
+
- default for mixed mouse/touch usage
|
|
145
|
+
|
|
146
|
+
- **compact**
|
|
147
|
+
- optimized for scan-heavy/data-rich screens
|
|
148
|
+
- visuals may get tighter, but **coupling remains safe via halo + guard**
|
|
149
|
+
|
|
150
|
+
### 5.2 Mode switch
|
|
151
|
+
|
|
152
|
+
```css
|
|
153
|
+
:root { --cem-coupling: balanced; }
|
|
154
|
+
|
|
155
|
+
:root[data-cem-coupling="balanced"] { /* baseline */ }
|
|
156
|
+
|
|
157
|
+
:root[data-cem-coupling="forgiving"] {
|
|
158
|
+
--cem-control-height: 2.75rem;
|
|
159
|
+
--cem-control-padding-x: 1rem;
|
|
160
|
+
--cem-control-padding-y: 0.625rem;
|
|
161
|
+
|
|
162
|
+
--cem-list-row-height: 3.25rem;
|
|
163
|
+
--cem-menu-row-height: 3.25rem;
|
|
164
|
+
|
|
165
|
+
/* Coupling remains safe; halo can be smaller because visuals are larger */
|
|
166
|
+
--cem-coupling-halo: 0.125rem;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
:root[data-cem-coupling="compact"] {
|
|
170
|
+
--cem-control-height: 2.25rem;
|
|
171
|
+
--cem-control-padding-x: 0.625rem;
|
|
172
|
+
--cem-control-padding-y: 0.375rem;
|
|
173
|
+
|
|
174
|
+
--cem-list-row-height: 2.75rem;
|
|
175
|
+
--cem-menu-row-height: 2.75rem;
|
|
176
|
+
|
|
177
|
+
/* Keep coupling safe when visuals shrink */
|
|
178
|
+
--cem-coupling-halo: 0.375rem;
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Governance rule: **do not reduce** `--cem-coupling-zone-min` or `--cem-coupling-guard-min` per mode.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 6. Implementation patterns
|
|
187
|
+
|
|
188
|
+
### 6.1 Halo-based operability (compact visuals, safe coupling)
|
|
189
|
+
|
|
190
|
+
```css
|
|
191
|
+
.cem-control {
|
|
192
|
+
min-block-size: var(--cem-coupling-zone-min);
|
|
193
|
+
padding-inline: var(--cem-control-padding-x);
|
|
194
|
+
padding-block: var(--cem-control-padding-y);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* Optional: halo expansion when visible chrome is smaller than the operable contract */
|
|
198
|
+
.cem-coupling-halo {
|
|
199
|
+
position: relative;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.cem-coupling-halo::after {
|
|
203
|
+
content: "";
|
|
204
|
+
position: absolute;
|
|
205
|
+
inset: calc(-1 * var(--cem-coupling-halo));
|
|
206
|
+
background: transparent;
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### 6.2 D1/D2 enforcement: spacing must not violate coupling guard
|
|
211
|
+
|
|
212
|
+
```css
|
|
213
|
+
.cem-actions {
|
|
214
|
+
display: flex;
|
|
215
|
+
gap: max(var(--cem-gap-related, 0px), var(--cem-coupling-guard-min));
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## 7. Optional: coupling semantics between controls (proximity implies relatedness)
|
|
222
|
+
|
|
223
|
+
If you want proximity to intentionally communicate relationship, add an optional D1/D2 bridge token set:
|
|
224
|
+
|
|
225
|
+
```css
|
|
226
|
+
:root {
|
|
227
|
+
/* Within this distance, controls are perceived as a group (segmented, paired actions). */
|
|
228
|
+
--cem-coupling-affinity-max: 0.375rem;
|
|
229
|
+
|
|
230
|
+
/* Beyond this distance, controls read as independent. */
|
|
231
|
+
--cem-coupling-decouple-min: 0.75rem;
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Guidance:
|
|
236
|
+
|
|
237
|
+
- Keep **affinity/decouple** separate from **guard**.
|
|
238
|
+
- `guard` is safety; `affinity/decouple` is meaning.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## 8. Material / Angular Material mapping (implementation layer)
|
|
245
|
+
|
|
246
|
+
- Map `data-cem-coupling="forgiving|balanced|compact"` to the library’s density knob.
|
|
247
|
+
- Keep **CEM public semantics** stable; keep library numeric scales in the adapter.
|
|
248
|
+
|
|
249
|
+
Example mapping guidance:
|
|
250
|
+
|
|
251
|
+
- forgiving → comfortable / default density
|
|
252
|
+
- balanced → slightly denser
|
|
253
|
+
- compact → compact / denser
|
|
254
|
+
|
|
255
|
+
(Exact numeric mapping remains an implementation choice per Angular Material version.)
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## 9. Finalization checklist (what “done” means)
|
|
260
|
+
|
|
261
|
+
### 9.1 Canonical decisions to lock
|
|
262
|
+
|
|
263
|
+
- **Category name:** D2. Coupling & Compactness (or D2. Coupling Safety)
|
|
264
|
+
- **Public axis name:** `coupling` (semantic), with exactly **three modes**: `forgiving | balanced | compact`
|
|
265
|
+
- **Core nouns:** zone / halo / guard
|
|
266
|
+
- **Units:** `rem` for all coupling and geometry values
|
|
267
|
+
|
|
268
|
+
### 9.2 Token set acceptance criteria
|
|
269
|
+
|
|
270
|
+
A draft becomes “canonical” when all of the following are true:
|
|
271
|
+
|
|
272
|
+
1) **Minimal core:** only these coupling policy anchors are required:
|
|
273
|
+
- `--cem-coupling-zone-min`
|
|
274
|
+
- `--cem-coupling-guard-min`
|
|
275
|
+
- `--cem-coupling-halo`
|
|
276
|
+
|
|
277
|
+
2) **Invariants are explicit:**
|
|
278
|
+
- `zone-min` and `guard-min` are **mode-invariant**
|
|
279
|
+
- mode changes may adjust **visual geometry** and **halo** only
|
|
280
|
+
|
|
281
|
+
3) **Orthogonality is enforceable:**
|
|
282
|
+
- a normative rule exists for layout code: `gap = max(D1 gap, D2 guard)`
|
|
283
|
+
|
|
284
|
+
4) **Component mapping is possible without special cases:**
|
|
285
|
+
- for each major control family (buttons, icon buttons, chips, list rows, menu rows, table rows), you can map either:
|
|
286
|
+
- visible size ≥ zone-min, or
|
|
287
|
+
- visible size < zone-min with halo expansion
|
|
288
|
+
|
|
289
|
+
5) **Accessibility posture is documented:**
|
|
290
|
+
- how zone-min relates to your a11y baseline
|
|
291
|
+
- how halo avoids overlap and preserves guard
|
|
292
|
+
|
|
293
|
+
### 9.3 Mode matrix (canonical)
|
|
294
|
+
|
|
295
|
+
| Mode | Product intent | Visual geometry | Halo (`--cem-coupling-halo`) | Typical surfaces |
|
|
296
|
+
|---|---|---|---|---|
|
|
297
|
+
| `forgiving` | Minimize mis-coupling for imprecise input | Larger controls/rows; more internal padding | Smaller (visuals already meet zone) | mobile-first, kiosks, accessibility-first, gaze/dwell |
|
|
298
|
+
| `balanced` | General default across modalities | Baseline control heights/rows | Baseline | mixed pointer + touch, general app UI |
|
|
299
|
+
| `compact` | Increase information density without breaking operability | Smaller visual heights/rows; reduced chrome | Larger (use halo to preserve zone) | data grids, admin tools, scan-heavy panels |
|
|
300
|
+
|
|
301
|
+
Normative rule:
|
|
302
|
+
|
|
303
|
+
- `--cem-coupling-zone-min` and `--cem-coupling-guard-min` are **mode-invariant**.
|
|
304
|
+
- Modes may adjust **visual geometry** and **halo** only.
|
|
305
|
+
|
|
306
|
+
### 9.4 Component mapping checklist (how to apply)
|
|
307
|
+
|
|
308
|
+
Use this checklist to implement Coupling consistently. The same rules apply whether input is pointer, touch, stylus, gaze, switch scanning, or remote focus.
|
|
309
|
+
|
|
310
|
+
#### Common rules (apply to all components)
|
|
311
|
+
|
|
312
|
+
- **Zone rule:** each interactive affordance must provide an operable zone of at least `--cem-coupling-zone-min` in both dimensions *or* be wrapped/expanded so the operable zone meets the minimum.
|
|
313
|
+
- **Guard rule:** adjacent interactive affordances must not violate `--cem-coupling-guard-min` *between their operable zones*.
|
|
314
|
+
- **Halo rule:** if halo expansion is used, ensure halos do not overlap adjacent operable zones. For dense clusters, compute layout spacing with halo in mind.
|
|
315
|
+
- **State legibility:** focus/hover/pressed affordances must remain legible across modes (especially in `compact`).
|
|
316
|
+
|
|
317
|
+
#### Buttons
|
|
318
|
+
|
|
319
|
+
- Map `min-block-size` to `--cem-coupling-zone-min` (or set visible height ≥ zone-min).
|
|
320
|
+
- Use `--cem-control-height` and `--cem-control-padding-*` for visual sizing; do not shrink below operability.
|
|
321
|
+
- For grouped buttons, enforce **guard** using layout `gap` (or dividers) that respects `--cem-coupling-guard-min`.
|
|
322
|
+
|
|
323
|
+
#### Icon buttons
|
|
324
|
+
|
|
325
|
+
- Prefer visible container size = `--cem-coupling-zone-min`.
|
|
326
|
+
- If the visible icon container is smaller, implement halo expansion on a wrapper.
|
|
327
|
+
- In clusters (toolbars), validate guard and halo non-overlap explicitly.
|
|
328
|
+
|
|
329
|
+
#### Chips / tags
|
|
330
|
+
|
|
331
|
+
- Visual chip height may be below `zone-min` in `compact`, but operability must still meet `zone-min` via:
|
|
332
|
+
- a halo wrapper, or
|
|
333
|
+
- additional padding on the interactive container.
|
|
334
|
+
- Ensure dismiss icons (×) meet the zone rule independently if separately interactive.
|
|
335
|
+
|
|
336
|
+
#### List rows / menu rows
|
|
337
|
+
|
|
338
|
+
- Ensure row **height** maps to at least `zone-min` where the row itself is the interactive affordance.
|
|
339
|
+
- If only a sub-region is interactive (e.g., trailing actions), each action must meet zone/guard rules.
|
|
340
|
+
- For trailing action clusters, apply guard via `gap` or reserved spacing to avoid interference.
|
|
341
|
+
|
|
342
|
+
#### Table rows / data grids
|
|
343
|
+
|
|
344
|
+
- It is acceptable for `--cem-table-row-height` to be below `zone-min` *only if* row actions / selection / checkboxes still meet the zone rule via:
|
|
345
|
+
- larger interactive containers within cells, or
|
|
346
|
+
- halo expansion that does not overlap neighbors.
|
|
347
|
+
- Validate dense rows with:
|
|
348
|
+
- selection controls (checkbox/radio)
|
|
349
|
+
- row-level actions (kebab, inline icons)
|
|
350
|
+
- row click targets (if the row is clickable)
|
|
351
|
+
|
|
352
|
+
### 9.5 Implementation proof points (required) (required)
|
|
353
|
+
|
|
354
|
+
Validate each mode on at least one representative surface:
|
|
355
|
+
|
|
356
|
+
- **Forms:** text field + primary button + icon button cluster
|
|
357
|
+
- **Navigation list:** list rows + trailing actions
|
|
358
|
+
- **Data-heavy:** table with row actions and selection
|
|
359
|
+
|
|
360
|
+
Pass criteria:
|
|
361
|
+
|
|
362
|
+
- adjacent operable zones never violate `guard-min`
|
|
363
|
+
- no halo overlap between neighbors
|
|
364
|
+
- focus ring / hover affordances remain legible when visuals are compact (see [`cem-stroke.md`](./cem-stroke.md) §5 for focus indicator thickness)
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## 10. Change management (canonical governance)
|
|
369
|
+
- Treat coupling tokens as **contract-level**: changes should be versioned (SemVer or equivalent).
|
|
370
|
+
- Avoid renaming tokens once canonical; prefer adding new tokens or revising mode values.
|
|
371
|
+
- Keep the public axis semantic; keep library-specific numeric scales in adapters.
|
|
372
|
+
|