@oneli8/core 1.0.0-beta.5 → 1.0.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +19 -7
  2. package/ai-context.json +170 -3
  3. package/package.json +18 -3
  4. package/src/atoms/index.d.ts +1 -0
  5. package/src/atoms/index.js +1 -0
  6. package/src/atoms/navigation-badge/index.d.ts +1 -0
  7. package/src/atoms/navigation-badge/index.js +1 -0
  8. package/src/atoms/navigation-badge/navigation-badge.css +35 -0
  9. package/src/atoms/navigation-badge/navigation-badge.d.ts +5 -0
  10. package/src/atoms/navigation-badge/navigation-badge.js +21 -0
  11. package/src/atoms/selection-indicator/selection-indicator.css +13 -4
  12. package/src/materials/gem/gem.css +52 -5
  13. package/src/molecules/choice-chip/choice-chip.css +214 -0
  14. package/src/molecules/choice-chip/choice-chip.d.ts +27 -0
  15. package/src/molecules/choice-chip/choice-chip.js +170 -0
  16. package/src/molecules/choice-chip/index.d.ts +2 -0
  17. package/src/molecules/choice-chip/index.js +10 -0
  18. package/src/molecules/form-message/form-message.css +46 -0
  19. package/src/molecules/form-message/form-message.d.ts +23 -0
  20. package/src/molecules/form-message/form-message.js +73 -0
  21. package/src/molecules/form-message/index.d.ts +1 -0
  22. package/src/molecules/form-message/index.js +1 -0
  23. package/src/molecules/index.d.ts +6 -0
  24. package/src/molecules/index.js +6 -0
  25. package/src/molecules/link/index.d.ts +1 -0
  26. package/src/molecules/link/index.js +1 -0
  27. package/src/molecules/link/link.css +114 -0
  28. package/src/molecules/link/link.d.ts +27 -0
  29. package/src/molecules/link/link.js +76 -0
  30. package/src/molecules/navigation-item/index.js +1 -0
  31. package/src/molecules/navigation-item/navigation-item.css +153 -0
  32. package/src/molecules/navigation-item/navigation-item.js +95 -0
  33. package/src/molecules/select/index.js +1 -0
  34. package/src/molecules/select/select.css +31 -0
  35. package/src/molecules/select/select.js +127 -0
  36. package/src/molecules/selection-option/selection-option.js +4 -4
  37. package/src/molecules/text-field/index.d.ts +1 -0
  38. package/src/molecules/text-field/index.js +5 -0
  39. package/src/molecules/text-field/text-field.css +266 -0
  40. package/src/molecules/text-field/text-field.d.ts +69 -0
  41. package/src/molecules/text-field/text-field.js +234 -0
  42. package/src/molecules/token/index.d.ts +2 -0
  43. package/src/molecules/token/index.js +1 -0
  44. package/src/molecules/token/token.css +63 -0
  45. package/src/molecules/token/token.d.ts +35 -0
  46. package/src/molecules/token/token.js +83 -0
  47. package/src/organisms/choice-picker/choice-picker.css +98 -0
  48. package/src/organisms/choice-picker/choice-picker.d.ts +62 -0
  49. package/src/organisms/choice-picker/choice-picker.js +201 -0
  50. package/src/organisms/choice-picker/index.d.ts +1 -0
  51. package/src/organisms/choice-picker/index.js +1 -0
  52. package/src/organisms/combobox/combobox.css +40 -0
  53. package/src/organisms/combobox/combobox.js +309 -0
  54. package/src/organisms/combobox/index.js +5 -0
  55. package/src/organisms/index.d.ts +7 -0
  56. package/src/organisms/index.js +7 -0
  57. package/src/organisms/multi-select-field/index.d.ts +1 -0
  58. package/src/organisms/multi-select-field/index.js +1 -0
  59. package/src/organisms/multi-select-field/multi-select-field.css +76 -0
  60. package/src/organisms/multi-select-field/multi-select-field.d.ts +64 -0
  61. package/src/organisms/multi-select-field/multi-select-field.js +317 -0
  62. package/src/organisms/navigation-keys.js +58 -0
  63. package/src/organisms/segmented-control/index.d.ts +1 -0
  64. package/src/organisms/segmented-control/index.js +1 -0
  65. package/src/organisms/segmented-control/segmented-control.css +113 -0
  66. package/src/organisms/segmented-control/segmented-control.d.ts +43 -0
  67. package/src/organisms/segmented-control/segmented-control.js +163 -0
  68. package/src/organisms/selection-popup/index.js +1 -0
  69. package/src/organisms/selection-popup/selection-popup.css +90 -0
  70. package/src/organisms/selection-popup/selection-popup.js +100 -0
  71. package/src/organisms/tab-bar/index.d.ts +1 -0
  72. package/src/organisms/tab-bar/index.js +1 -0
  73. package/src/organisms/tab-bar/tab-bar.css +45 -0
  74. package/src/organisms/tab-bar/tab-bar.d.ts +37 -0
  75. package/src/organisms/tab-bar/tab-bar.js +104 -0
  76. package/src/organisms/tabs/index.d.ts +1 -0
  77. package/src/organisms/tabs/index.js +1 -0
  78. package/src/organisms/tabs/tabs.css +53 -0
  79. package/src/organisms/tabs/tabs.d.ts +48 -0
  80. package/src/organisms/tabs/tabs.js +152 -0
  81. package/styles.css +18 -2
package/README.md CHANGED
@@ -26,13 +26,15 @@ hydrateButtons();
26
26
 
27
27
  | Tier | Component |
28
28
  |---|---|
29
- | Atom | `Icon`, `Selection Indicator` (checkbox, radio, switch) |
30
- | Molecule | `Button`, `Icon Button`, `Choice Item`, `Selection Option` |
31
- | Organism | `Choice Group` (checkbox hierarchy, radio group) |
29
+ | Atom | `Icon`, `Selection Indicator` (checkbox, radio, switch), `Navigation Badge` |
30
+ | Molecule | `Button`, `Icon Button`, `Link`, `Text Field`, `Form Message`, `Select`, `Choice Item`, `Selection Option`, `Choice Chip`, `Token` |
31
+ | Organism | `Tabs`, `Tab Bar`, `Segmented Control`, `Choice Group`, `Selection Popup`, `Combobox`, `Multi-select Field`, `Choice Picker` |
32
32
  | Material | `Gem`, an attribute adapter, not a tier |
33
33
 
34
- Anything not listed is not exported. Tokens exist for component families whose
35
- implementations are not in this release; a token is not a promise of a component.
34
+ Twenty one components, every one of them drawn from a component set in the
35
+ Figma file rather than invented here. Anything not listed is not exported.
36
+ Tokens exist for families whose implementations are not in this release; a token
37
+ is not a promise of a component.
36
38
 
37
39
  ## Why this exists
38
40
 
@@ -70,8 +72,18 @@ what markup genuinely cannot express:
70
72
  - A `<button>` without an explicit `type` submits its form. Hydration pins it.
71
73
  - Links cannot be natively disabled, so they get `aria-disabled` and their
72
74
  activation is swallowed.
73
-
74
- Hydration is idempotent, running it twice changes nothing.
75
+ - Tabs and Segmented Control move one tab stop across their items with the
76
+ arrow keys, which is a roving focus model markup cannot describe.
77
+ - Combobox keeps real focus on its input and carries the active option as a
78
+ virtual cursor, so the cursor has to be moved in script.
79
+ - Multi-select Field walks its committed values with the arrow keys and takes
80
+ two presses to remove one, so a stray Backspace cannot drop a value.
81
+ - Choice Picker in Apply mode commits on Enter and restores on Escape, and
82
+ leaves a composing input method alone.
83
+
84
+ Fifteen `hydrate*` functions ship, one per family that needs one. Hydration is
85
+ idempotent, running it twice changes nothing, and a page that never calls it
86
+ still renders and still reads correctly.
75
87
 
76
88
  ## Styles
77
89
 
package/ai-context.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$comment": "GENERATED by packages/sync/src/build-package-context.mjs — do not edit.",
3
3
  "system": "OneLi8",
4
- "version": "1.0.0-beta.5",
4
+ "version": "1.0.0-beta.7",
5
5
  "meaning": "OneLi8 means One Light.",
6
6
  "foundations": {
7
7
  "package": "@oneli8/tokens",
@@ -13,7 +13,7 @@
13
13
  "color-primary-* is an alias ramp onto color-blue-* — identical values, distinct names. Prefer the semantic role over either.",
14
14
  "Dimension scale is 3px-atomic. Even multiples for structural layout; odd multiples are deliberate optical or micro-gap choices.",
15
15
  "Elevation ink is cyan-840 rgb(22,63,75) — never default black.",
16
- "Typography roles are composite. Use the .ol8-type-<role> class, not the individual font vars.",
16
+ "Typography roles are composite. Apply a whole role, all five of --ol8-typography-<role>-{font-family,font-size,font-weight,line-height,letter-spacing}, rather than picking loose font vars. This package ships custom properties and no class names.",
17
17
  "shape-radius-full (999px) is reserved for pills and circles. Soft hexagons are component-specific recipes, not a radius token."
18
18
  ]
19
19
  },
@@ -46,6 +46,14 @@
46
46
  ],
47
47
  "constraint": "Composes no other component. Native elements only."
48
48
  },
49
+ {
50
+ "tier": "atom",
51
+ "module": "atoms/navigation-badge/navigation-badge.js",
52
+ "exports": [
53
+ "renderNavigationBadge"
54
+ ],
55
+ "constraint": "Composes no other component. Native elements only."
56
+ },
49
57
  {
50
58
  "tier": "atom",
51
59
  "module": "atoms/selection-indicator/selection-indicator.js",
@@ -72,6 +80,17 @@
72
80
  ],
73
81
  "constraint": "Composed of atoms. Reusable and more specialised."
74
82
  },
83
+ {
84
+ "tier": "molecule",
85
+ "module": "molecules/choice-chip/choice-chip.js",
86
+ "exports": [
87
+ "OL8_CHIP_SIZES",
88
+ "renderChoiceChip",
89
+ "hydrateChoiceChips",
90
+ "syncChip"
91
+ ],
92
+ "constraint": "Composed of atoms. Reusable and more specialised."
93
+ },
75
94
  {
76
95
  "tier": "molecule",
77
96
  "module": "molecules/choice-item/choice-item.js",
@@ -87,6 +106,17 @@
87
106
  ],
88
107
  "constraint": "Composed of atoms. Reusable and more specialised."
89
108
  },
109
+ {
110
+ "tier": "molecule",
111
+ "module": "molecules/form-message/form-message.js",
112
+ "exports": [
113
+ "OL8_FORM_MESSAGE_TONES",
114
+ "OL8_FORM_MESSAGE_ICONS",
115
+ "renderFormMessage",
116
+ "hydrateFormMessages"
117
+ ],
118
+ "constraint": "Composed of atoms. Reusable and more specialised."
119
+ },
90
120
  {
91
121
  "tier": "molecule",
92
122
  "module": "molecules/icon-button/icon-button.js",
@@ -100,6 +130,30 @@
100
130
  ],
101
131
  "constraint": "Composed of atoms. Reusable and more specialised."
102
132
  },
133
+ {
134
+ "tier": "molecule",
135
+ "module": "molecules/link/link.js",
136
+ "exports": [
137
+ "OL8_LINK_FORMS",
138
+ "OL8_LINK_SIZES",
139
+ "OL8_LINK_MOTIONS",
140
+ "renderLink",
141
+ "hydrateLinks"
142
+ ],
143
+ "constraint": "Composed of atoms. Reusable and more specialised."
144
+ },
145
+ {
146
+ "tier": "molecule",
147
+ "module": "molecules/select/select.js",
148
+ "exports": [
149
+ "OL8_SELECT_SIZES",
150
+ "OL8_SELECT_APPEARANCES",
151
+ "OL8_SELECT_MATERIALS",
152
+ "OL8_SELECT_NATIVE_POLICIES",
153
+ "renderSelect"
154
+ ],
155
+ "constraint": "Composed of atoms. Reusable and more specialised."
156
+ },
103
157
  {
104
158
  "tier": "molecule",
105
159
  "module": "molecules/selection-option/selection-option.js",
@@ -111,6 +165,34 @@
111
165
  ],
112
166
  "constraint": "Composed of atoms. Reusable and more specialised."
113
167
  },
168
+ {
169
+ "tier": "molecule",
170
+ "module": "molecules/text-field/text-field.js",
171
+ "exports": [
172
+ "OL8_TEXT_FIELD_SIZES",
173
+ "OL8_TEXT_FIELD_APPEARANCES",
174
+ "OL8_TEXT_FIELD_MATERIALS",
175
+ "OL8_MESSAGE_TONES",
176
+ "OL8_VALIDATION_STATUSES",
177
+ "OL8_CHARACTER_LIMIT_BEHAVIORS",
178
+ "renderTextField",
179
+ "hydrateTextFields",
180
+ "countGraphemes",
181
+ "clipToGraphemes"
182
+ ],
183
+ "constraint": "Composed of atoms. Reusable and more specialised."
184
+ },
185
+ {
186
+ "tier": "molecule",
187
+ "module": "molecules/token/token.js",
188
+ "exports": [
189
+ "OL8_TOKEN_SIZES",
190
+ "OL8_TOKEN_MARKS",
191
+ "resolveTokenMark",
192
+ "renderToken"
193
+ ],
194
+ "constraint": "Composed of atoms. Reusable and more specialised."
195
+ },
114
196
  {
115
197
  "tier": "organism",
116
198
  "module": "organisms/choice-group/choice-group.js",
@@ -122,6 +204,91 @@
122
204
  "hydrateChoiceGroups"
123
205
  ],
124
206
  "constraint": "Composed of atoms, molecules or organisms. May hold application state."
207
+ },
208
+ {
209
+ "tier": "organism",
210
+ "module": "organisms/choice-picker/choice-picker.js",
211
+ "exports": [
212
+ "OL8_PICKER_SIZES",
213
+ "OL8_PICKER_MATERIALS",
214
+ "OL8_COMMIT_BEHAVIORS",
215
+ "OL8_PICKER_PRESENTATIONS",
216
+ "OL8_PICKER_MARKS",
217
+ "renderChoicePicker",
218
+ "hydrateChoicePickers"
219
+ ],
220
+ "constraint": "Composed of atoms, molecules or organisms. May hold application state."
221
+ },
222
+ {
223
+ "tier": "organism",
224
+ "module": "organisms/combobox/combobox.js",
225
+ "exports": [
226
+ "OL8_COMBOBOX_SIZES",
227
+ "OL8_COMBOBOX_APPEARANCES",
228
+ "OL8_COMBOBOX_MATERIALS",
229
+ "OL8_AUTOCOMPLETE_MODES",
230
+ "OL8_FILTER_SOURCES",
231
+ "renderCombobox",
232
+ "hydrateComboboxes"
233
+ ],
234
+ "constraint": "Composed of atoms, molecules or organisms. May hold application state."
235
+ },
236
+ {
237
+ "tier": "organism",
238
+ "module": "organisms/multi-select-field/multi-select-field.js",
239
+ "exports": [
240
+ "OL8_MULTI_SELECT_SIZES",
241
+ "OL8_MULTI_SELECT_APPEARANCES",
242
+ "OL8_MULTI_SELECT_MATERIALS",
243
+ "renderMultiSelectField",
244
+ "hydrateMultiSelectFields"
245
+ ],
246
+ "constraint": "Composed of atoms, molecules or organisms. May hold application state."
247
+ },
248
+ {
249
+ "tier": "organism",
250
+ "module": "organisms/segmented-control/segmented-control.js",
251
+ "exports": [
252
+ "OL8_SEGMENT_BEHAVIORS",
253
+ "OL8_SEGMENT_PRESENTATIONS",
254
+ "renderSegmentedControl",
255
+ "hydrateSegmentedControls"
256
+ ],
257
+ "constraint": "Composed of atoms, molecules or organisms. May hold application state."
258
+ },
259
+ {
260
+ "tier": "organism",
261
+ "module": "organisms/selection-popup/selection-popup.js",
262
+ "exports": [
263
+ "OL8_POPUP_WIDTHS",
264
+ "OL8_POPUP_SIZES",
265
+ "OL8_POPUP_STATUSES",
266
+ "renderSelectionPopup"
267
+ ],
268
+ "constraint": "Composed of atoms, molecules or organisms. May hold application state."
269
+ },
270
+ {
271
+ "tier": "organism",
272
+ "module": "organisms/tab-bar/tab-bar.js",
273
+ "exports": [
274
+ "OL8_TAB_BAR_PRESENTATIONS",
275
+ "renderTabBar",
276
+ "hydrateTabBars"
277
+ ],
278
+ "constraint": "Composed of atoms, molecules or organisms. May hold application state."
279
+ },
280
+ {
281
+ "tier": "organism",
282
+ "module": "organisms/tabs/tabs.js",
283
+ "exports": [
284
+ "OL8_TABS_HIERARCHIES",
285
+ "OL8_TABS_ORIENTATIONS",
286
+ "OL8_TABS_ACTIVATIONS",
287
+ "renderTabs",
288
+ "renderTabPanel",
289
+ "hydrateTabs"
290
+ ],
291
+ "constraint": "Composed of atoms, molecules or organisms. May hold application state."
125
292
  }
126
293
  ],
127
294
  "buildingWhatIsNotHere": {
@@ -140,7 +307,7 @@
140
307
  "A literal colour, size or font value anywhere in a component.",
141
308
  "A primitive token in a component. Primitives are hidden from the Figma pickers for the same reason.",
142
309
  "An invented icon name. Choose from OL8_ICONS.",
143
- "A per-size icon stroke. The 1.8 stroke is shared, and the 18px master is an exact 0.75 scale of the 24px one.",
310
+ "A per-size icon stroke. The 1.8 stroke is shared, and an Icon Frame instance is an exact scale of the 24px master, so an 18px one carries a 1.35 stroke. The selection masters are the exception: Icon / Selection / Check keeps the same 10.2 x 6.9 artwork at 1.8 in both its 18 and 24 boxes, so it is not scaled.",
144
311
  "Mixing a filled treatment with an outlined one across selection controls. Checkbox and Radio share one language."
145
312
  ]
146
313
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oneli8/core",
3
- "version": "1.0.0-beta.5",
3
+ "version": "1.0.0-beta.7",
4
4
  "description": "OneLi8 Design System, framework agnostic. renderX returns an HTML string, hydrateX wires existing DOM. Ships the design language and the rules it follows.",
5
5
  "keywords": [
6
6
  "design-system",
@@ -46,12 +46,27 @@
46
46
  },
47
47
  "./styles.css": "./styles.css",
48
48
  "./styles/icon.css": "./src/atoms/icon/icon.css",
49
+ "./styles/navigation-badge.css": "./src/atoms/navigation-badge/navigation-badge.css",
49
50
  "./styles/selection-indicator.css": "./src/atoms/selection-indicator/selection-indicator.css",
50
51
  "./styles/button.css": "./src/molecules/button/button.css",
51
- "./styles/icon-button.css": "./src/molecules/icon-button/icon-button.css",
52
+ "./styles/choice-chip.css": "./src/molecules/choice-chip/choice-chip.css",
52
53
  "./styles/choice-item.css": "./src/molecules/choice-item/choice-item.css",
54
+ "./styles/form-message.css": "./src/molecules/form-message/form-message.css",
55
+ "./styles/icon-button.css": "./src/molecules/icon-button/icon-button.css",
56
+ "./styles/link.css": "./src/molecules/link/link.css",
57
+ "./styles/navigation-item.css": "./src/molecules/navigation-item/navigation-item.css",
58
+ "./styles/select.css": "./src/molecules/select/select.css",
53
59
  "./styles/selection-option.css": "./src/molecules/selection-option/selection-option.css",
60
+ "./styles/text-field.css": "./src/molecules/text-field/text-field.css",
61
+ "./styles/token.css": "./src/molecules/token/token.css",
54
62
  "./styles/choice-group.css": "./src/organisms/choice-group/choice-group.css",
63
+ "./styles/choice-picker.css": "./src/organisms/choice-picker/choice-picker.css",
64
+ "./styles/combobox.css": "./src/organisms/combobox/combobox.css",
65
+ "./styles/multi-select-field.css": "./src/organisms/multi-select-field/multi-select-field.css",
66
+ "./styles/segmented-control.css": "./src/organisms/segmented-control/segmented-control.css",
67
+ "./styles/selection-popup.css": "./src/organisms/selection-popup/selection-popup.css",
68
+ "./styles/tab-bar.css": "./src/organisms/tab-bar/tab-bar.css",
69
+ "./styles/tabs.css": "./src/organisms/tabs/tabs.css",
55
70
  "./styles/gem.css": "./src/materials/gem/gem.css",
56
71
  "./package.json": "./package.json",
57
72
  "./ai-context.json": "./ai-context.json",
@@ -74,7 +89,7 @@
74
89
  "NOTICE"
75
90
  ],
76
91
  "dependencies": {
77
- "@oneli8/tokens": "1.0.0-beta.5"
92
+ "@oneli8/tokens": "1.0.0-beta.7"
78
93
  },
79
94
  "engines": {
80
95
  "node": ">=20"
@@ -1,2 +1,3 @@
1
1
  export * from './icon/index.js';
2
2
  export * from './selection-indicator/index.js';
3
+ export * from './navigation-badge/index.js';
@@ -1,2 +1,3 @@
1
1
  export * from './icon/index.js';
2
2
  export * from './selection-indicator/index.js';
3
+ export * from './navigation-badge/index.js';
@@ -0,0 +1 @@
1
+ export * from './navigation-badge.js';
@@ -0,0 +1 @@
1
+ export { renderNavigationBadge } from './navigation-badge.js';
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Oneli8 · Navigation Badge (ATOM)
3
+ * Figma: Atom / Navigation Badge 989:484 — a full radius pill at the standard
4
+ * indicator height, padded by the related inline step, whose surface and
5
+ * content are the only things that change when the item is selected or gem.
6
+ */
7
+
8
+ .ol8-nav-badge {
9
+ display: inline-flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+ block-size: var(--ol8-size-indicator-standard);
13
+ padding-inline: var(--ol8-spacing-inline-related);
14
+ border-radius: var(--ol8-shape-radius-full);
15
+ background: var(--ol8-color-navigation-badge-surface-default);
16
+ color: var(--ol8-color-navigation-badge-content-default);
17
+ font-family: var(--ol8-font-family-functional);
18
+ font-size: var(--ol8-font-size-012);
19
+ font-weight: var(--ol8-font-weight-semibold);
20
+ line-height: var(--ol8-font-line-018);
21
+ font-variant-numeric: tabular-nums;
22
+ }
23
+
24
+ /* Selection and material reach the badge through the item that owns it. */
25
+ [data-ol8-selected="true"] > * > .ol8-nav-badge,
26
+ [aria-selected="true"] > * > .ol8-nav-badge,
27
+ [aria-current] > * > .ol8-nav-badge {
28
+ background: var(--ol8-color-navigation-badge-surface-selected);
29
+ color: var(--ol8-color-navigation-badge-content-selected);
30
+ }
31
+
32
+ [data-ol8-material="gem"] .ol8-nav-badge {
33
+ background: var(--ol8-color-navigation-badge-surface-gem);
34
+ color: var(--ol8-color-navigation-badge-content-gem);
35
+ }
@@ -0,0 +1,5 @@
1
+ /** Navigation Badge (ATOM). Figma 989:484. One atom serves all three navigation organisms. */
2
+ export interface Ol8NavigationBadgeOptions { className?: string }
3
+
4
+ /** @throws when there is nothing to say. */
5
+ export declare function renderNavigationBadge(label: string | number, options?: Ol8NavigationBadgeOptions): string;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Oneli8 · Navigation Badge — headless behavior.
3
+ *
4
+ * Figma (989:484): "Canonical optional navigation badge. Content comes from
5
+ * item.badge; geometry and colors are semantic token controlled."
6
+ *
7
+ * One atom serves Tabs, Segmented Control and Tab Bar. None of them owns a
8
+ * private copy of it.
9
+ */
10
+ export function renderNavigationBadge(label, options = {}) {
11
+ const { className } = options;
12
+ const text = String(label ?? '');
13
+ if (text === '') throw new Error('[ol8] a navigation badge with nothing to say should not be rendered at all.');
14
+
15
+ // The count is decoration beside a label that already names the destination,
16
+ // so the badge is not announced twice. A product that needs it announced
17
+ // spells it into the item's own accessible name.
18
+ return `<span class="ol8-nav-badge${className ? ` ${className}` : ''}" aria-hidden="true">${escapeText(text)}</span>`;
19
+ }
20
+
21
+ function escapeText(v) { return String(v).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
@@ -71,12 +71,21 @@
71
71
  --_ink: var(--ol8-color-icon-disabled);
72
72
  }
73
73
 
74
- /* The check glyph scales geometrically with the box (18px master is an exact
75
- 0.75x of the 24px one), so a single 24-unit path serves both sizes. */
74
+ /* The check glyph does NOT scale with the box, for the same reason the Mixed
75
+ bar below does not. Figma's `Icon / Selection / Check` is a governed two size
76
+ master rather than an Icon Frame instance: Size=Small (18) and Size=Standard
77
+ (24) both carry the same 10.2 x 6.9 artwork at the same 1.8 stroke, centred
78
+ in their box. So the source keeps its 24 unit box at both sizes and the
79
+ smaller box simply lets the empty margin overflow. The artwork is well
80
+ inside 18, so nothing is clipped.
81
+
82
+ This previously scaled with the box, which drew the compact check a quarter
83
+ smaller than Figma at a 1.35 stroke, off the 1.8 system. */
76
84
  .ol8-selection--checkbox > .ol8-selection__mark > svg {
77
85
  display: block;
78
- inline-size: var(--_box);
79
- block-size: var(--_box);
86
+ inline-size: var(--ol8-size-icon-standard);
87
+ block-size: var(--ol8-size-icon-standard);
88
+ overflow: visible;
80
89
  }
81
90
 
82
91
  /* The Mixed bar does NOT scale. Both the 18px and 24px masters carry the same
@@ -32,7 +32,8 @@
32
32
  */
33
33
 
34
34
  .ol8-btn[data-ol8-material="gem"],
35
- .ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface {
35
+ .ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface,
36
+ .ol8-field[data-ol8-material="gem"] > .ol8-field__control-stack > .ol8-field__control {
36
37
  position: relative;
37
38
  isolation: isolate;
38
39
  --_rim: var(--ol8-material-control-gem-rim-default);
@@ -41,7 +42,8 @@
41
42
  /* The regular opaque background stays underneath on purpose — it is what
42
43
  shows through when the gem layer is suppressed for Reduced Transparency. */
43
44
  .ol8-btn[data-ol8-material="gem"]::before,
44
- .ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface::before {
45
+ .ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface::before,
46
+ .ol8-field[data-ol8-material="gem"] .ol8-field__control::before {
45
47
  content: "";
46
48
  position: absolute;
47
49
  inset: 0;
@@ -58,7 +60,8 @@
58
60
  }
59
61
 
60
62
  .ol8-btn[data-ol8-material="gem"]::after,
61
- .ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface::after {
63
+ .ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface::after,
64
+ .ol8-field[data-ol8-material="gem"] .ol8-field__control::after {
62
65
  content: "";
63
66
  position: absolute;
64
67
  inset: 0;
@@ -143,9 +146,48 @@
143
146
  --_g3: var(--ol8-material-control-gem-secondary-loadinglow);
144
147
  }
145
148
 
149
+ /* ---- Text Field · the same Secondary body, per Figma 211:36 and 215:226 ---
150
+ Outline follows the Cut Edge, Filled sits on the Stabilized Face and follows
151
+ the Inner Thin Cut. Hover moves Filled onto the Cut Edge. Invalid keeps the
152
+ critical boundary, because a material must never hide a validation result. */
153
+ .ol8-field[data-ol8-material="gem"] .ol8-field__control {
154
+ --_g1: var(--ol8-material-control-gem-secondary-high);
155
+ --_g2: var(--ol8-material-control-gem-secondary-mid);
156
+ --_g3: var(--ol8-material-control-gem-secondary-low);
157
+ --_surface: transparent;
158
+ --_border: var(--ol8-material-gem-color-cutedge-high);
159
+ }
160
+ .ol8-field[data-ol8-material="gem"]:hover:not([data-ol8-disabled]) .ol8-field__control {
161
+ --_g1: var(--ol8-material-control-gem-secondary-hoverhigh);
162
+ --_g2: var(--ol8-material-control-gem-secondary-hovermid);
163
+ --_g3: var(--ol8-material-control-gem-secondary-hoverlow);
164
+ --_rim: var(--ol8-material-control-gem-rim-hover);
165
+ --_border: var(--ol8-material-gem-color-cutedge-high);
166
+ }
167
+ .ol8-field[data-ol8-material="gem"][data-ol8-appearance="filled"] .ol8-field__control {
168
+ --_surface: var(--ol8-material-gem-color-quiet-stabilizedface);
169
+ --_border: var(--ol8-material-gem-color-innerthincut-high);
170
+ }
171
+ .ol8-field[data-ol8-material="gem"][data-ol8-readonly][data-ol8-appearance="filled"] .ol8-field__control {
172
+ --_border: var(--ol8-material-gem-color-innerthincut-high);
173
+ }
174
+ .ol8-field[data-ol8-material="gem"]:has(.ol8-field__input[aria-invalid="true"]) .ol8-field__control,
175
+ .ol8-field[data-ol8-material="gem"][data-ol8-status="invalid"] .ol8-field__control {
176
+ --_border: var(--ol8-color-feedbackcritical-inlinecontent);
177
+ }
178
+ /* Disabled resolves to the opaque equivalent, which is the same answer Reduced
179
+ Transparency gives. */
180
+ .ol8-field[data-ol8-material="gem"][data-ol8-disabled] .ol8-field__control {
181
+ --_surface: var(--ol8-material-gem-color-quiet-opaqueequivalent);
182
+ --_border: var(--ol8-color-border-disabled);
183
+ }
184
+ .ol8-field[data-ol8-material="gem"][data-ol8-disabled] .ol8-field__control::before,
185
+ .ol8-field[data-ol8-material="gem"][data-ol8-disabled] .ol8-field__control::after { display: none; }
186
+
146
187
  /* Content must sit above the ::before body. */
147
188
  .ol8-btn[data-ol8-material="gem"] > *,
148
- .ol8-icon-btn[data-ol8-material="gem"] .ol8-icon { position: relative; z-index: 0; }
189
+ .ol8-icon-btn[data-ol8-material="gem"] .ol8-icon,
190
+ .ol8-field[data-ol8-material="gem"] .ol8-field__control > * { position: relative; z-index: 0; }
149
191
 
150
192
  /* ---- Disabled inherits the regular opaque treatment ------------------ */
151
193
  .ol8-btn[data-ol8-material="gem"]:disabled::before,
@@ -160,7 +202,12 @@
160
202
  .ol8-btn[data-ol8-material="gem"]::before,
161
203
  .ol8-btn[data-ol8-material="gem"]::after,
162
204
  .ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface::before,
163
- .ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface::after { display: none; }
205
+ .ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface::after,
206
+ .ol8-field[data-ol8-material="gem"] .ol8-field__control::before,
207
+ .ol8-field[data-ol8-material="gem"] .ol8-field__control::after { display: none; }
208
+ .ol8-field[data-ol8-material="gem"] .ol8-field__control {
209
+ --_surface: var(--ol8-material-gem-color-quiet-opaqueequivalent);
210
+ }
164
211
  .ol8-btn--secondary[data-ol8-material="gem"] {
165
212
  --_bw: var(--ol8-component-button-secondaryboundary);
166
213
  --_bd: var(--ol8-color-actionsecondary-contentdefault);