@oneli8/react 1.0.0-beta.10

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 (61) hide show
  1. package/AGENTS.md +16 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +8 -0
  4. package/PRINCIPLES.md +48 -0
  5. package/README.md +136 -0
  6. package/ai-context.json +264 -0
  7. package/package.json +66 -0
  8. package/skills/oneli8-figma-to-code/SKILL.md +25 -0
  9. package/skills/oneli8-icons/SKILL.md +36 -0
  10. package/skills/oneli8-ui/SKILL.md +52 -0
  11. package/src/atoms/Icon.js +34 -0
  12. package/src/atoms/NavigationBadge.js +19 -0
  13. package/src/atoms/SelectionIndicator.js +43 -0
  14. package/src/foundations/geometry.js +143 -0
  15. package/src/foundations/icons.generated.d.ts +48 -0
  16. package/src/foundations/icons.generated.js +562 -0
  17. package/src/index.d.ts +381 -0
  18. package/src/index.js +23 -0
  19. package/src/molecules/Button.js +50 -0
  20. package/src/molecules/ChoiceChip.js +101 -0
  21. package/src/molecules/ChoiceItem.js +55 -0
  22. package/src/molecules/FormMessage.js +33 -0
  23. package/src/molecules/IconButton.js +33 -0
  24. package/src/molecules/Link.js +31 -0
  25. package/src/molecules/NavigationItem.js +55 -0
  26. package/src/molecules/Select.js +109 -0
  27. package/src/molecules/SelectionOption.js +44 -0
  28. package/src/molecules/TextField.js +174 -0
  29. package/src/molecules/Token.js +49 -0
  30. package/src/organisms/ChoiceGroup.js +78 -0
  31. package/src/organisms/ChoicePicker.js +114 -0
  32. package/src/organisms/Combobox.js +122 -0
  33. package/src/organisms/MultiSelectField.js +138 -0
  34. package/src/organisms/SegmentedControl.js +114 -0
  35. package/src/organisms/SelectionPopup.js +73 -0
  36. package/src/organisms/TabBar.js +68 -0
  37. package/src/organisms/Tabs.js +123 -0
  38. package/src/styles/button.css +195 -0
  39. package/src/styles/choice-chip.css +216 -0
  40. package/src/styles/choice-group.css +95 -0
  41. package/src/styles/choice-item.css +267 -0
  42. package/src/styles/choice-picker.css +100 -0
  43. package/src/styles/combobox.css +42 -0
  44. package/src/styles/form-message.css +48 -0
  45. package/src/styles/gem.css +376 -0
  46. package/src/styles/icon-button.css +224 -0
  47. package/src/styles/icon.css +56 -0
  48. package/src/styles/link.css +116 -0
  49. package/src/styles/multi-select-field.css +78 -0
  50. package/src/styles/navigation-badge.css +37 -0
  51. package/src/styles/navigation-item.css +155 -0
  52. package/src/styles/segmented-control.css +115 -0
  53. package/src/styles/select.css +33 -0
  54. package/src/styles/selection-indicator.css +162 -0
  55. package/src/styles/selection-option.css +123 -0
  56. package/src/styles/selection-popup.css +92 -0
  57. package/src/styles/tab-bar.css +47 -0
  58. package/src/styles/tabs.css +55 -0
  59. package/src/styles/text-field.css +268 -0
  60. package/src/styles/token.css +65 -0
  61. package/styles.css +29 -0
@@ -0,0 +1,162 @@
1
+ /* GENERATED from packages/core/src/atoms/selection-indicator/selection-indicator.css by packages/sync/src/build-styles.mjs — do not edit.
2
+ Edit the source in packages/core/src and re-run `npm run sync:styles`. */
3
+ /**
4
+ * Oneli8 · Selection Indicator (ATOM)
5
+ * Figma: Documentation / Choice Controls (246:3)
6
+ * Icon / Selection / Checkbox 365:2417 (12 variants)
7
+ * Icon / Selection / Radio 366:2371 (8)
8
+ * Icon / Selection / Switch 824:1304 (8)
9
+ *
10
+ * Figma names the tier itself — "Documentation / Atom / Selection Indicator" —
11
+ * and points at packages/react/src/atoms/SelectionIndicator.js (kind=switch),
12
+ * so all three kinds are one atom parameterised by kind, not three components.
13
+ *
14
+ * It composes no other component: the marks are drawn from native elements and
15
+ * one inline path, which is what keeps this an atom rather than a molecule.
16
+ *
17
+ * PURELY PRESENTATIONAL. It carries no role and no state of its own — the
18
+ * native <input> inside Choice Item owns semantics. Always aria-hidden.
19
+ *
20
+ * Axes are Figma's: Selection and Availability.
21
+ */
22
+
23
+ .ol8-selection {
24
+ --_box: var(--ol8-size-icon-small); /* 18px compact */
25
+ --_radius: var(--ol8-component-checkbox-radius-compact);
26
+ --_dot: var(--ol8-component-radio-dot-compact);
27
+ --_track-w: var(--ol8-component-switch-trackwidth-compact);
28
+ --_track-h: var(--ol8-component-switch-trackheight-compact);
29
+ --_thumb: var(--ol8-component-switch-thumb-compact);
30
+
31
+ --_surface: transparent;
32
+ --_edge: var(--ol8-color-border-strong);
33
+ --_ink: var(--ol8-color-selection-content);
34
+
35
+ box-sizing: border-box;
36
+ display: inline-flex;
37
+ flex: none;
38
+ align-items: center;
39
+ justify-content: center;
40
+ position: relative;
41
+ inline-size: var(--_box);
42
+ block-size: var(--_box);
43
+ background: var(--_surface);
44
+ color: var(--_ink);
45
+ }
46
+
47
+ .ol8-selection[data-ol8-size="comfortable"] {
48
+ --_box: var(--ol8-size-icon-standard); /* 24px */
49
+ --_radius: var(--ol8-component-checkbox-radius-comfortable);
50
+ --_dot: var(--ol8-component-radio-dot-comfortable);
51
+ --_track-w: var(--ol8-component-switch-trackwidth-comfortable);
52
+ --_track-h: var(--ol8-component-switch-trackheight-comfortable);
53
+ --_thumb: var(--ol8-component-switch-thumb-comfortable);
54
+ }
55
+
56
+ /* ---- Checkbox -------------------------------------------------------- */
57
+ .ol8-selection--checkbox {
58
+ border: var(--ol8-component-checkbox-boundary) solid var(--_edge);
59
+ border-radius: var(--_radius);
60
+ }
61
+ /* Filled states drop the boundary entirely so the mark occupies the whole
62
+ box, exactly as Figma draws it (glyph at 0,0 at full box size). */
63
+ .ol8-selection--checkbox[data-ol8-selection="checked"],
64
+ .ol8-selection--checkbox[data-ol8-selection="mixed"] {
65
+ border-width: 0;
66
+ --_surface: var(--ol8-color-actionprimary-default);
67
+ --_ink: var(--ol8-color-actionprimary-content);
68
+ }
69
+ .ol8-selection--checkbox[data-ol8-availability="disabled"] { --_edge: var(--ol8-color-border-disabled); }
70
+ .ol8-selection--checkbox[data-ol8-availability="disabled"][data-ol8-selection="checked"],
71
+ .ol8-selection--checkbox[data-ol8-availability="disabled"][data-ol8-selection="mixed"] {
72
+ --_surface: var(--ol8-color-surface-disabled);
73
+ --_ink: var(--ol8-color-icon-disabled);
74
+ }
75
+
76
+ /* The check glyph does NOT scale with the box, for the same reason the Mixed
77
+ bar below does not. Figma's `Icon / Selection / Check` is a governed two size
78
+ master rather than an Icon Frame instance: Size=Small (18) and Size=Standard
79
+ (24) both carry the same 10.2 x 6.9 artwork at the same 1.8 stroke, centred
80
+ in their box. So the source keeps its 24 unit box at both sizes and the
81
+ smaller box simply lets the empty margin overflow. The artwork is well
82
+ inside 18, so nothing is clipped.
83
+
84
+ This previously scaled with the box, which drew the compact check a quarter
85
+ smaller than Figma at a 1.35 stroke, off the 1.8 system. */
86
+ .ol8-selection--checkbox > .ol8-selection__mark > svg {
87
+ display: block;
88
+ inline-size: var(--ol8-size-icon-standard);
89
+ block-size: var(--ol8-size-icon-standard);
90
+ overflow: visible;
91
+ }
92
+
93
+ /* The Mixed bar does NOT scale. Both the 18px and 24px masters carry the same
94
+ 9 x 1.8px bar — an approved optical exception — so it is a fixed-size rule
95
+ here rather than a scaled glyph. */
96
+ .ol8-selection--checkbox[data-ol8-selection="mixed"] > .ol8-selection__mark {
97
+ inline-size: var(--ol8-component-checkbox-mark-mixedwidth);
98
+ block-size: var(--ol8-component-checkbox-mark-stroke);
99
+ border-radius: calc(var(--ol8-component-checkbox-mark-stroke) / 2);
100
+ background: currentColor;
101
+ }
102
+
103
+ /* ---- Radio ----------------------------------------------------------- */
104
+ .ol8-selection--radio {
105
+ border: var(--ol8-component-radio-boundary) solid var(--_edge);
106
+ border-radius: var(--ol8-shape-radius-full);
107
+ }
108
+ /* A selected radio is a checked checkbox in a circle, so it reads the same way:
109
+ the boundary drops and the whole indicator fills with the primary action
110
+ colour, carrying its own content role as the mark. Figma painted both the ring
111
+ and the dot in Selection Surface, which left the radio outlined where the
112
+ checkbox is filled and put the dot at 1.16:1 on the canvas against a 3:1
113
+ minimum. The fill is 8.81:1 and the mark 8.81:1 on the fill. */
114
+ .ol8-selection--radio[data-ol8-selection="selected"] {
115
+ border-width: 0;
116
+ --_surface: var(--ol8-color-actionprimary-default);
117
+ --_ink: var(--ol8-color-actionprimary-content);
118
+ }
119
+ .ol8-selection--radio[data-ol8-availability="disabled"] { --_edge: var(--ol8-color-border-disabled); }
120
+ .ol8-selection--radio[data-ol8-availability="disabled"][data-ol8-selection="selected"] {
121
+ border-width: 0;
122
+ --_surface: var(--ol8-color-surface-disabled);
123
+ --_ink: var(--ol8-color-icon-disabled);
124
+ }
125
+ .ol8-selection--radio > .ol8-selection__mark {
126
+ flex: none; /* never let the flex row shrink it oval */
127
+ inline-size: var(--_dot);
128
+ block-size: var(--_dot);
129
+ border-radius: var(--ol8-shape-radius-full);
130
+ background: currentColor;
131
+ }
132
+
133
+ /* ---- Switch ---------------------------------------------------------- */
134
+ .ol8-selection--switch {
135
+ inline-size: var(--_track-w);
136
+ block-size: var(--_track-h);
137
+ border-radius: var(--ol8-shape-radius-full);
138
+ --_surface: var(--ol8-color-surface-sunken);
139
+ justify-content: flex-start;
140
+ }
141
+ .ol8-selection--switch[data-ol8-selection="on"] { --_surface: var(--ol8-color-selection-surface); }
142
+ .ol8-selection--switch[data-ol8-availability="disabled"] { --_surface: var(--ol8-color-surface-disabled); }
143
+
144
+ .ol8-selection--switch > .ol8-selection__mark {
145
+ position: absolute;
146
+ inset-block-start: var(--ol8-component-switch-inset);
147
+ inset-inline-start: var(--ol8-component-switch-inset);
148
+ inline-size: var(--_thumb);
149
+ block-size: var(--_thumb);
150
+ border-radius: var(--ol8-shape-radius-full);
151
+ background: var(--ol8-color-control-thumb);
152
+ display: inline-flex;
153
+ align-items: center;
154
+ justify-content: center;
155
+ overflow: hidden;
156
+ }
157
+ .ol8-selection--switch[data-ol8-selection="on"] > .ol8-selection__mark {
158
+ inset-inline-start: calc(var(--_track-w) - var(--_thumb) - var(--ol8-component-switch-inset));
159
+ }
160
+ /* Optional artwork inside the thumb: 9px compact, 12px comfortable. */
161
+ .ol8-selection--switch .ol8-icon { --_ol8-icon-size: var(--ol8-size-icon-micro); }
162
+ .ol8-selection--switch[data-ol8-size="comfortable"] .ol8-icon { --_ol8-icon-size: var(--ol8-size-icon-compact); }
@@ -0,0 +1,123 @@
1
+ /* GENERATED from packages/core/src/molecules/selection-option/selection-option.css by packages/sync/src/build-styles.mjs — do not edit.
2
+ Edit the source in packages/core/src and re-run `npm run sync:styles`. */
3
+ /**
4
+ * Oneli8 · Selection Option (MOLECULE)
5
+ * Figma: Select and Combobox -> 01 · Atomic Selection Building Blocks
6
+ * Building Blocks / Selection Option 570:122 (8 variants)
7
+ * Size Standard|Large x Selection x Availability
8
+ *
9
+ * From the Figma description (570:122):
10
+ * "Description is associated through aria-describedby and REMAINS OUTSIDE
11
+ * the option accessible name. Selection Popup composes this owner;
12
+ * KEYBOARD FOCUS REMAINS ON THE COMBOBOX INPUT through
13
+ * aria-activedescendant."
14
+ *
15
+ * Two consequences that shape everything here:
16
+ * - the option is never focusable and carries no tabindex
17
+ * - "active" (the aria-activedescendant highlight) is a DIFFERENT thing from
18
+ * :hover and from selected, so it gets its own attribute
19
+ *
20
+ * Row geometry: 48px (Standard) / 60px (Large) minimum, but the content box
21
+ * stays 24px tall in both — the padding absorbs the difference.
22
+ * padding-block = (minimum - 24) / 2 -> 12px and 18px
23
+ */
24
+
25
+ .ol8-option {
26
+ --_min: var(--ol8-component-selectionoption-minimum-standard);
27
+ --_inset: var(--ol8-component-selectionoption-inset-standard);
28
+
29
+ box-sizing: border-box;
30
+ display: flex;
31
+ align-items: center;
32
+ min-block-size: var(--_min);
33
+ font-family: var(--ol8-font-family-functional);
34
+ font-variation-settings: "ARRR" var(--ol8-font-axis-arrr-baseline);
35
+ list-style: none;
36
+ cursor: pointer;
37
+ }
38
+ .ol8-option[data-ol8-size="large"] {
39
+ --_min: var(--ol8-component-selectionoption-minimum-large);
40
+ --_inset: var(--ol8-component-selectionoption-inset-large);
41
+ }
42
+ .ol8-option[aria-disabled="true"] { cursor: not-allowed; }
43
+
44
+ /* The surface carries every wash and the focus ring; the row itself stays
45
+ clean so a popup can lay options out without fighting them. */
46
+ .ol8-option__surface {
47
+ box-sizing: border-box;
48
+ position: relative;
49
+ display: flex;
50
+ align-items: center;
51
+ gap: var(--ol8-component-choice-gap-standard);
52
+ flex: 1 0 0;
53
+ min-inline-size: 0;
54
+ block-size: 100%;
55
+ padding-inline: var(--_inset);
56
+ padding-block: calc((var(--_min) - var(--ol8-font-line-024)) / 2);
57
+ border-radius: var(--ol8-shape-radius-medium);
58
+ overflow: clip;
59
+ }
60
+
61
+ /* Selected, hover and active all paint the same wash. Selected is a state of
62
+ the data; active is the aria-activedescendant cursor; hover is the pointer.
63
+ They coincide visually by design. */
64
+ .ol8-option[aria-selected="true"] > .ol8-option__surface,
65
+ .ol8-option[data-ol8-active="true"] > .ol8-option__surface,
66
+ .ol8-option:hover:not([aria-disabled="true"]) > .ol8-option__surface {
67
+ background: var(--ol8-color-selection-surface);
68
+ }
69
+ .ol8-option:active:not([aria-disabled="true"]) > .ol8-option__surface {
70
+ background: var(--ol8-color-selection-surface);
71
+ opacity: 0.84;
72
+ }
73
+
74
+ /* Focus lives on the combobox input, never here — but the option still shows
75
+ the active ring when a popup marks it, so the two-tone ring is reproduced. */
76
+ .ol8-option[data-ol8-focus-ring="true"] > .ol8-option__surface {
77
+ border: var(--ol8-focus-ring-outerwidth) solid var(--ol8-color-focus-outer);
78
+ }
79
+ .ol8-option[data-ol8-focus-ring="true"] > .ol8-option__surface::after {
80
+ content: "";
81
+ position: absolute;
82
+ inset: 2px;
83
+ border: var(--ol8-focus-ring-innerwidth) solid var(--ol8-color-focus-inner);
84
+ border-radius: 7px;
85
+ pointer-events: none;
86
+ }
87
+
88
+ .ol8-option__text {
89
+ display: flex;
90
+ flex-direction: column;
91
+ flex: 1 0 0;
92
+ min-inline-size: 0;
93
+ }
94
+ .ol8-option__label {
95
+ font-size: var(--ol8-font-size-016);
96
+ line-height: var(--ol8-font-line-024);
97
+ font-weight: var(--ol8-font-weight-regular);
98
+ color: var(--ol8-color-text-primary);
99
+ overflow-wrap: break-word;
100
+ }
101
+ .ol8-option__description {
102
+ font-size: var(--ol8-font-size-012);
103
+ line-height: var(--ol8-font-line-018);
104
+ letter-spacing: 0.12px;
105
+ color: var(--ol8-color-text-secondary);
106
+ overflow-wrap: break-word;
107
+ }
108
+ .ol8-option[aria-disabled="true"] .ol8-option__label,
109
+ .ol8-option[aria-disabled="true"] .ol8-option__description {
110
+ color: var(--ol8-color-text-disabled);
111
+ }
112
+
113
+ /* The trailing slot is ALWAYS reserved, selected or not, so the label column
114
+ never shifts when selection changes. Figma keeps an empty 18px node there. */
115
+ .ol8-option__check {
116
+ flex: none;
117
+ inline-size: var(--ol8-size-icon-small);
118
+ block-size: var(--ol8-size-icon-small);
119
+ color: var(--ol8-color-selection-content);
120
+ }
121
+ .ol8-option__check > svg { display: block; inline-size: 100%; block-size: 100%; }
122
+
123
+ .ol8-option .ol8-icon { --_ol8-icon-size: var(--ol8-size-icon-small); }
@@ -0,0 +1,92 @@
1
+ /* GENERATED from packages/core/src/organisms/selection-popup/selection-popup.css by packages/sync/src/build-styles.mjs — do not edit.
2
+ Edit the source in packages/core/src and re-run `npm run sync:styles`. */
3
+ /**
4
+ * Oneli8 · Selection Popup (ORGANISM)
5
+ * Figma: Building Blocks / Selection Popup 574:534 — Width, Size.
6
+ *
7
+ * The rail is a div with role="listbox" rather than a list element, because a
8
+ * list carries a user agent margin that a component may not zero here, and the
9
+ * role already carries every semantic the list element would have added.
10
+ *
11
+ * Read from Figma: a raised floating surface at the standard radius with a
12
+ * default border, padded by the minimal step inline and the related step
13
+ * block, holding one Option Rail. "Width and Size govern layout only; content
14
+ * must be canonical Selection Option instances."
15
+ *
16
+ * The maximum block is a suggestion, not a cage: the region scrolls rather than
17
+ * clipping, because an option a person cannot reach is an option that does not
18
+ * exist.
19
+ */
20
+
21
+ .ol8-popup {
22
+ box-sizing: border-box;
23
+ padding-block: var(--ol8-component-selectionpopup-paddingblock);
24
+ padding-inline: var(--ol8-component-selectionpopup-paddinginline);
25
+ border: var(--ol8-border-width-hairline) solid var(--ol8-color-border-default);
26
+ border-radius: var(--ol8-shape-radius-standard);
27
+ background: var(--ol8-color-surface-raised);
28
+ box-shadow: var(--ol8-elevation-surface-floating);
29
+ }
30
+
31
+ .ol8-popup__rail {
32
+ display: flex;
33
+ flex-direction: column;
34
+ gap: var(--ol8-component-selectionpopup-gap);
35
+ padding-inline: var(--ol8-component-selectionpopup-railinsetinline);
36
+ overflow-y: auto;
37
+ overscroll-behavior: contain;
38
+ }
39
+
40
+ /* ---- Size ------------------------------------------------------------- */
41
+ .ol8-popup[data-ol8-size="standard"] .ol8-popup__rail {
42
+ max-block-size: var(--ol8-component-selectionpopup-maximumblock-standard);
43
+ }
44
+ .ol8-popup[data-ol8-size="large"] .ol8-popup__rail {
45
+ max-block-size: var(--ol8-component-selectionpopup-maximumblock-large);
46
+ }
47
+
48
+ /* ---- Width · layout only, never meaning -------------------------------
49
+ Content hugs, Anchor matches the control it belongs to, and Wide may exceed
50
+ it when the options are longer than the field. */
51
+ .ol8-popup[data-ol8-width="content"] { inline-size: max-content; }
52
+ .ol8-popup[data-ol8-width="anchor"] { inline-size: 100%; }
53
+ .ol8-popup[data-ol8-width="wide"] { inline-size: max-content; min-inline-size: 100%; }
54
+
55
+ /* ---- Group labels ------------------------------------------------------ */
56
+ .ol8-popup__group-label {
57
+ padding-block: var(--ol8-component-selectionpopup-gap);
58
+ padding-inline: var(--ol8-component-selectionoption-inset-standard);
59
+ color: var(--ol8-color-text-secondary);
60
+ font-family: var(--ol8-font-family-functional);
61
+ font-size: var(--ol8-font-size-012);
62
+ font-weight: var(--ol8-font-weight-semibold);
63
+ line-height: var(--ol8-font-line-018);
64
+ letter-spacing: var(--ol8-font-tracking-open);
65
+ }
66
+
67
+ /* ---- Status ------------------------------------------------------------
68
+ "No results is never represented by a selectable option", so a status is a
69
+ plain region, never an option, and it can never be chosen by mistake. */
70
+ .ol8-popup__status {
71
+ display: flex;
72
+ align-items: center;
73
+ gap: var(--ol8-component-textfield-gap-status);
74
+ padding-block: var(--ol8-component-selectionpopup-paddingblock);
75
+ padding-inline: var(--ol8-component-selectionoption-inset-standard);
76
+ color: var(--ol8-color-text-secondary);
77
+ font-family: var(--ol8-font-family-functional);
78
+ font-size: var(--ol8-font-size-012);
79
+ font-weight: var(--ol8-font-weight-regular);
80
+ line-height: var(--ol8-font-line-018);
81
+ }
82
+ .ol8-popup__status[data-ol8-status="error"] { color: var(--ol8-color-feedbackcritical-inlinecontent); }
83
+
84
+ @media (prefers-reduced-motion: no-preference) {
85
+ .ol8-popup__status[data-ol8-status="loading"] .ol8-icon > svg {
86
+ animation: ol8-spin 900ms linear infinite;
87
+ }
88
+ }
89
+
90
+ @media (forced-colors: active) {
91
+ .ol8-popup { border-color: CanvasText; box-shadow: none; }
92
+ }
@@ -0,0 +1,47 @@
1
+ /* GENERATED from packages/core/src/organisms/tab-bar/tab-bar.css by packages/sync/src/build-styles.mjs — do not edit.
2
+ Edit the source in packages/core/src and re-run `npm run sync:styles`. */
3
+ /**
4
+ * Oneli8 · Tab Bar (ORGANISM)
5
+ * Figma: Navigation / Tab Bar 701:460 — Bottom, Inline, Sidebar, Spatial Rail.
6
+ *
7
+ * "Tab Bar contains destinations, never a central action disguised as a
8
+ * destination." Every presentation below keeps the same route identity and the
9
+ * same current destination; only the container changes.
10
+ *
11
+ * Read from Figma at Standard, Regular: Bottom, Sidebar and Spatial Rail all
12
+ * sit on a raised surface with a subtle border at the tab bar radius, protected
13
+ * by the tab bar inset. Inline has no surface and no border, and uses the
14
+ * tighter inline inset on its horizontal edges. Destinations meet with no gap.
15
+ */
16
+
17
+ .ol8-tab-bar {
18
+ display: flex;
19
+ align-items: stretch;
20
+ box-sizing: border-box;
21
+ gap: var(--ol8-component-tab-bar-gap);
22
+ padding: var(--ol8-component-tab-bar-inset);
23
+ border: var(--ol8-border-width-hairline) solid var(--ol8-color-border-subtle);
24
+ border-radius: var(--ol8-component-tab-bar-radius);
25
+ background: var(--ol8-color-surface-raised);
26
+ }
27
+
28
+ .ol8-tab-bar > .ol8-nav-item { flex: 1 1 0; }
29
+
30
+ /* Inline sits in the page rather than on it, so it drops the surface, the
31
+ border and the radius, and protects only its horizontal edges. */
32
+ .ol8-tab-bar[data-ol8-presentation="inline"] {
33
+ padding-block: var(--ol8-component-tab-bar-inset);
34
+ padding-inline: var(--ol8-component-tab-bar-inline-inset);
35
+ border: 0;
36
+ border-radius: var(--ol8-shape-radius-none);
37
+ background: none;
38
+ }
39
+
40
+ .ol8-tab-bar[data-ol8-presentation="sidebar"],
41
+ .ol8-tab-bar[data-ol8-presentation="spatial-rail"] {
42
+ flex-direction: column;
43
+ align-items: stretch;
44
+ }
45
+
46
+ /* The spatial rail carries no labels, so its destinations are square targets. */
47
+ .ol8-tab-bar[data-ol8-presentation="spatial-rail"] > .ol8-nav-item { flex: none; }
@@ -0,0 +1,55 @@
1
+ /* GENERATED from packages/core/src/organisms/tabs/tabs.css by packages/sync/src/build-styles.mjs — do not edit.
2
+ Edit the source in packages/core/src and re-run `npm run sync:styles`. */
3
+ /**
4
+ * Oneli8 · Tabs (ORGANISM)
5
+ * Figma: Navigation / Tabs 693:171 — Hierarchy, Material, Size.
6
+ *
7
+ * The organism owns sibling separation and the rail; the item owns its own
8
+ * inset. Read from Figma: tabs meet with no gap, and a Primary tab list carries
9
+ * an adaptive secondary rail behind them at the hairline thickness in the
10
+ * secondary text colour. Secondary and Tertiary carry no rail.
11
+ */
12
+
13
+ .ol8-tabs {
14
+ position: relative;
15
+ display: flex;
16
+ align-items: stretch;
17
+ /* "Narrow Tab lists scroll or use an explicitly governed overflow policy;
18
+ labels never wrap into competing heights." */
19
+ overflow-x: auto;
20
+ scrollbar-width: none;
21
+ }
22
+ .ol8-tabs::-webkit-scrollbar { display: none; }
23
+
24
+ .ol8-tabs[data-ol8-hierarchy="primary"]::after {
25
+ content: "";
26
+ position: absolute;
27
+ inset-block-end: 0;
28
+ inset-inline: 0;
29
+ block-size: var(--ol8-component-tab-item-rail);
30
+ background: var(--ol8-color-text-secondary);
31
+ }
32
+
33
+ /* Vertical tabs turn the rail onto the logical inline edge and let each tab
34
+ fill the width, so the indicator reads as a column marker. */
35
+ .ol8-tabs[data-ol8-orientation="vertical"] {
36
+ flex-direction: column;
37
+ overflow-x: visible;
38
+ }
39
+ .ol8-tabs[data-ol8-orientation="vertical"][data-ol8-hierarchy="primary"]::after {
40
+ inset-block: 0;
41
+ inset-inline: auto 0;
42
+ inline-size: var(--ol8-component-tab-item-rail);
43
+ block-size: auto;
44
+ }
45
+ .ol8-tabs[data-ol8-orientation="vertical"] .ol8-nav-item__indicator {
46
+ inset-block: var(--ol8-component-tab-item-indicator-inset);
47
+ inset-inline: auto 0;
48
+ inline-size: var(--ol8-component-tab-item-indicator-thickness);
49
+ block-size: auto;
50
+ }
51
+
52
+ .ol8-tabs__panel:focus-visible {
53
+ outline: none;
54
+ box-shadow: inset 0 0 0 var(--ol8-component-navigation-focus-inset) var(--ol8-color-focus-inner);
55
+ }