@oneli8/core 1.0.0-beta.4 → 1.0.0-beta.6

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 +51 -13
  2. package/ai-context.json +168 -1
  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
@@ -1,6 +1,6 @@
1
1
  # @oneli8/core
2
2
 
3
- OneLi8 Design System framework-agnostic components.
3
+ OneLi8 Design System, framework agnostic components.
4
4
 
5
5
  Every component is two functions: `renderX(...)` returns an HTML **string**, and
6
6
  `hydrateX(root)` enforces the runtime contract on markup that already exists.
@@ -26,26 +26,64 @@ 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) |
32
- | Material | `Gem` an attribute adapter, not a tier |
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
+ | 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.
38
+
39
+ ## Why this exists
40
+
41
+ OneLi8 means One Light. It was created by Akshay Dhore, intended for broad human
42
+ benefit and conceived as service to Guruji Shrii Arnav, whose teachings inspire
43
+ it. Service, clarity, harmony, proportion, restraint and evolution guide its
44
+ decisions. `PRINCIPLES.md` ships inside this package and sets them out in full.
45
+
46
+ The practical intention: most design systems hand you components and hope you
47
+ stay inside them. This one hands you the reasoning too, so when you need
48
+ something it does not ship you can build it and have it still belong.
49
+
50
+ ## Naming
51
+
52
+ The structure is borrowed from Sanskrit grammar rather than a CSS convention.
53
+
54
+ **A compound naming one thing is one word.** `actionprimary` is not action plus
55
+ primary, it is a single role. `cutedge` is one feature of the Gem material.
56
+
57
+ **A qualifier stays separate from what it qualifies.** In
58
+ `--ol8-material-control-gem--primary-hover-start`, `hover` is a state applied to
59
+ `start`, so it keeps its hyphen. That is why `disabledcontent` is joined and
60
+ `hover-start` is not.
61
+
62
+ Hyphens separate levels of the hierarchy, never words inside a level. The words
63
+ themselves stay readable: no abbreviations and no truncation.
36
64
 
37
65
  ## Rendering and hydration
38
66
 
39
67
  `render*` covers everything expressible in markup. `hydrate*` exists only for
40
68
  what markup genuinely cannot express:
41
69
 
42
- - `indeterminate` on a checkbox is a JavaScript-only property, so Mixed needs
70
+ - `indeterminate` on a checkbox is a JavaScript only property, so Mixed needs
43
71
  hydration and every other state does not.
44
72
  - A `<button>` without an explicit `type` submits its form. Hydration pins it.
45
73
  - Links cannot be natively disabled, so they get `aria-disabled` and their
46
74
  activation is swallowed.
47
-
48
- 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.
49
87
 
50
88
  ## Styles
51
89
 
@@ -57,7 +95,7 @@ import '@oneli8/tokens/css';
57
95
  import '@oneli8/core/styles/button.css';
58
96
  ```
59
97
 
60
- Tokens must always come first every component rule reads them.
98
+ Tokens must always come first, every component rule reads them.
61
99
 
62
100
  ## Accessibility is enforced, not suggested
63
101
 
@@ -66,11 +104,11 @@ Tokens must always come first — every component rule reads them.
66
104
  Primary and Secondary only. These are errors rather than review comments on
67
105
  purpose.
68
106
 
69
- Test Gem against its real backdrop. Near-white content alone does not guarantee
107
+ Test Gem against its real backdrop. Content close to white content alone does not guarantee
70
108
  readable contrast.
71
109
 
72
110
  ## Licence
73
111
 
74
- Apache-2.0. Created by Akshay Dhore. See `LICENSE` and `NOTICE`; preserve the
112
+ Apache 2.0. Created by Akshay Dhore. See `LICENSE` and `NOTICE`; preserve the
75
113
  attribution notices when redistributing. This does not require a visible badge
76
114
  in your product, and implies no OneLi8 endorsement.
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.4",
4
+ "version": "1.0.0-beta.6",
5
5
  "meaning": "OneLi8 means One Light.",
6
6
  "foundations": {
7
7
  "package": "@oneli8/tokens",
@@ -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": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oneli8/core",
3
- "version": "1.0.0-beta.4",
3
+ "version": "1.0.0-beta.6",
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.4"
92
+ "@oneli8/tokens": "1.0.0-beta.6"
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);