@oneli8/react 1.0.0-beta.5 → 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 (40) hide show
  1. package/README.md +7 -3
  2. package/ai-context.json +116 -1
  3. package/package.json +2 -2
  4. package/src/atoms/NavigationBadge.js +19 -0
  5. package/src/foundations/geometry.js +71 -0
  6. package/src/index.d.ts +214 -0
  7. package/src/index.js +14 -0
  8. package/src/molecules/ChoiceChip.js +101 -0
  9. package/src/molecules/FormMessage.js +33 -0
  10. package/src/molecules/Link.js +31 -0
  11. package/src/molecules/NavigationItem.js +55 -0
  12. package/src/molecules/Select.js +109 -0
  13. package/src/molecules/SelectionOption.js +4 -1
  14. package/src/molecules/TextField.js +174 -0
  15. package/src/molecules/Token.js +49 -0
  16. package/src/organisms/ChoicePicker.js +114 -0
  17. package/src/organisms/Combobox.js +122 -0
  18. package/src/organisms/MultiSelectField.js +138 -0
  19. package/src/organisms/SegmentedControl.js +114 -0
  20. package/src/organisms/SelectionPopup.js +73 -0
  21. package/src/organisms/TabBar.js +68 -0
  22. package/src/organisms/Tabs.js +123 -0
  23. package/src/styles/choice-chip.css +216 -0
  24. package/src/styles/choice-picker.css +100 -0
  25. package/src/styles/combobox.css +42 -0
  26. package/src/styles/form-message.css +48 -0
  27. package/src/styles/gem.css +52 -5
  28. package/src/styles/link.css +116 -0
  29. package/src/styles/multi-select-field.css +78 -0
  30. package/src/styles/navigation-badge.css +37 -0
  31. package/src/styles/navigation-item.css +155 -0
  32. package/src/styles/segmented-control.css +115 -0
  33. package/src/styles/select.css +33 -0
  34. package/src/styles/selection-indicator.css +13 -4
  35. package/src/styles/selection-popup.css +92 -0
  36. package/src/styles/tab-bar.css +47 -0
  37. package/src/styles/tabs.css +55 -0
  38. package/src/styles/text-field.css +268 -0
  39. package/src/styles/token.css +65 -0
  40. package/styles.css +18 -2
@@ -0,0 +1,78 @@
1
+ /* GENERATED from packages/core/src/organisms/multi-select-field/multi-select-field.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 · Multi-select Field (ORGANISM)
5
+ * Figma: Organism / Multi-select Field / Outline 904:2507 (16 variants)
6
+ * Organism / Multi-select Field / Filled 978:4472 (16 variants)
7
+ *
8
+ * Almost everything here is inherited rather than declared. The field shell,
9
+ * its four sizes, both appearances, the Gem material and the focus treatment
10
+ * all come from the Text Field stylesheet, because Figma builds this organism
11
+ * from the canonical Text Field owner and only swaps that owner between the
12
+ * Outline and Filled sets. What is left is the one thing a Text Field has never
13
+ * had to do: let committed values and the editable input share a run that
14
+ * wraps.
15
+ */
16
+
17
+ /* Figma's root is a vertical stack: the field composition, then the popup,
18
+ twelve apart. That gap is now bound in Figma to Spacing / Stack / Related. */
19
+ .ol8-multiselect {
20
+ display: flex;
21
+ flex-direction: column;
22
+ gap: var(--ol8-spacing-stack-related);
23
+ inline-size: 100%;
24
+ }
25
+
26
+ /* A Text Field pins this stack to one row's height, because a single line
27
+ input never needs more. A multi value field does: releasing the stack is what
28
+ lets the control below it grow, and without it the tokens simply spill out of
29
+ the boundary they are supposed to sit inside. */
30
+ .ol8-multiselect__field > .ol8-field__control-stack {
31
+ block-size: auto;
32
+ min-block-size: var(--_height);
33
+ }
34
+
35
+ /* The control grows instead of scrolling or shrinking its contents. The
36
+ contract is explicit: the field "starts at its approved Text Field minimum
37
+ height and grows in 6px or 12px-aligned increments as tokens wrap" and "never
38
+ compresses tokens to preserve one nominal row". Every token keeps its own
39
+ protected 48 unit interaction line, so a populated row is taller than an
40
+ empty field by design. */
41
+ .ol8-multiselect__control {
42
+ flex-wrap: wrap;
43
+ align-content: center;
44
+ block-size: auto;
45
+ min-block-size: var(--_height);
46
+ gap: var(--ol8-component-token-field-gap);
47
+ padding-block: var(--ol8-component-token-field-padding-block);
48
+ }
49
+
50
+ /* The editable input keeps a usable measure before it gives up its row. */
51
+ .ol8-multiselect__input {
52
+ min-inline-size: var(--ol8-component-token-field-minimum-input);
53
+ }
54
+
55
+ /* A token awaiting a second Backspace. The first press only activates, so this
56
+ has to be visible: a silent activation would make the second press feel like
57
+ a single key deleting a value. */
58
+ .ol8-multiselect__token[data-ol8-active] {
59
+ --_surface: var(--ol8-color-selection-surface);
60
+ --_edge: var(--ol8-color-focus-outer);
61
+ }
62
+
63
+ /* A read only field's values are labelled content, so nothing inside invites a
64
+ click that would do nothing. */
65
+ .ol8-multiselect[data-ol8-readonly="true"] .ol8-multiselect__token,
66
+ .ol8-multiselect[data-ol8-availability="disabled"] .ol8-multiselect__token {
67
+ cursor: default;
68
+ }
69
+
70
+ /* Announcements only. Removing a value says so, with the remaining count. */
71
+ .ol8-multiselect__status {
72
+ position: absolute;
73
+ inline-size: 1px;
74
+ block-size: 1px;
75
+ overflow: hidden;
76
+ clip-path: inset(50%);
77
+ white-space: nowrap;
78
+ }
@@ -0,0 +1,37 @@
1
+ /* GENERATED from packages/core/src/atoms/navigation-badge/navigation-badge.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 · Navigation Badge (ATOM)
5
+ * Figma: Atom / Navigation Badge 989:484 — a full radius pill at the standard
6
+ * indicator height, padded by the related inline step, whose surface and
7
+ * content are the only things that change when the item is selected or gem.
8
+ */
9
+
10
+ .ol8-nav-badge {
11
+ display: inline-flex;
12
+ align-items: center;
13
+ justify-content: center;
14
+ block-size: var(--ol8-size-indicator-standard);
15
+ padding-inline: var(--ol8-spacing-inline-related);
16
+ border-radius: var(--ol8-shape-radius-full);
17
+ background: var(--ol8-color-navigation-badge-surface-default);
18
+ color: var(--ol8-color-navigation-badge-content-default);
19
+ font-family: var(--ol8-font-family-functional);
20
+ font-size: var(--ol8-font-size-012);
21
+ font-weight: var(--ol8-font-weight-semibold);
22
+ line-height: var(--ol8-font-line-018);
23
+ font-variant-numeric: tabular-nums;
24
+ }
25
+
26
+ /* Selection and material reach the badge through the item that owns it. */
27
+ [data-ol8-selected="true"] > * > .ol8-nav-badge,
28
+ [aria-selected="true"] > * > .ol8-nav-badge,
29
+ [aria-current] > * > .ol8-nav-badge {
30
+ background: var(--ol8-color-navigation-badge-surface-selected);
31
+ color: var(--ol8-color-navigation-badge-content-selected);
32
+ }
33
+
34
+ [data-ol8-material="gem"] .ol8-nav-badge {
35
+ background: var(--ol8-color-navigation-badge-surface-gem);
36
+ color: var(--ol8-color-navigation-badge-content-gem);
37
+ }
@@ -0,0 +1,155 @@
1
+ /* GENERATED from packages/core/src/molecules/navigation-item/navigation-item.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 · Navigation Item (MOLECULE, internal)
5
+ * Figma: Tab Item 688:51, Segment Item 695:167, Destination Item 700:292.
6
+ *
7
+ * All three are one molecule. Read from Figma: a box at the size's control
8
+ * height with centred content, a horizontal content row at the canonical
9
+ * navigation gap, an eighteen unit icon, a Semibold label at the size's reading
10
+ * step, and an optional badge. States are identical across the three:
11
+ *
12
+ * Hover Action Quiet Surface Hover + Action Quiet Content Hover
13
+ * Pressed Action Quiet Surface Pressed + Action Quiet Content Pressed
14
+ * Focus an inward Focus Inner edge, so a parent cannot crop it
15
+ * Disabled Text Disabled, no surface
16
+ *
17
+ * Only the selection language differs. Tab Item raises an indicator bar in the
18
+ * primary colour, inset by the governed amount. Segment Item and Destination
19
+ * Item take the selection surface and content instead.
20
+ */
21
+
22
+ .ol8-nav-item {
23
+ position: relative;
24
+ display: flex;
25
+ align-items: center;
26
+ justify-content: center;
27
+ box-sizing: border-box;
28
+ block-size: var(--_height);
29
+ border: 0;
30
+ border-radius: var(--_radius);
31
+ padding-inline: var(--_inset);
32
+ background: transparent;
33
+ color: var(--ol8-color-text-secondary);
34
+ font-family: var(--ol8-font-family-functional);
35
+ font-weight: var(--ol8-font-weight-semibold);
36
+ text-decoration: none;
37
+ cursor: pointer;
38
+ transition-property: background-color, color;
39
+ transition-duration: var(--ol8-motion-interaction-hover-duration);
40
+ transition-timing-function: var(--ol8-motion-interaction-hover-timing-function);
41
+ }
42
+
43
+ .ol8-nav-item__content {
44
+ display: flex;
45
+ align-items: center;
46
+ gap: var(--ol8-component-navigation-item-gap);
47
+ min-inline-size: 0;
48
+ }
49
+
50
+ /* "Labels do not wrap." A narrow list scrolls; it never grows a second line. */
51
+ .ol8-nav-item__label {
52
+ white-space: nowrap;
53
+ font-size: var(--_label-size);
54
+ line-height: var(--_label-line);
55
+ }
56
+
57
+ .ol8-nav-item__icon { flex: none; }
58
+
59
+ .ol8-nav-item[data-ol8-layout="stacked"] .ol8-nav-item__content {
60
+ flex-direction: column;
61
+ gap: 0;
62
+ }
63
+
64
+ /* ---- Size ------------------------------------------------------------- */
65
+ .ol8-nav-item[data-ol8-size="compact"] {
66
+ --_height: var(--ol8-component-tab-item-height-compact);
67
+ --_inset: var(--ol8-component-tab-item-inset-compact);
68
+ --_label-size: var(--ol8-font-size-012);
69
+ --_label-line: var(--ol8-font-line-018);
70
+ }
71
+ .ol8-nav-item[data-ol8-size="standard"] {
72
+ --_height: var(--ol8-component-tab-item-height-standard);
73
+ --_inset: var(--ol8-component-tab-item-inset-standard);
74
+ --_label-size: var(--ol8-font-size-016);
75
+ --_label-line: var(--ol8-font-line-024);
76
+ }
77
+ .ol8-nav-item[data-ol8-size="comfortable"] {
78
+ --_height: var(--ol8-component-tab-item-height-comfortable);
79
+ --_inset: var(--ol8-component-tab-item-inset-comfortable);
80
+ --_label-size: var(--ol8-font-size-016);
81
+ --_label-line: var(--ol8-font-line-024);
82
+ }
83
+ .ol8-nav-item[data-ol8-size="large"] {
84
+ --_height: var(--ol8-component-tab-item-height-large);
85
+ --_inset: var(--ol8-component-tab-item-inset-large);
86
+ --_label-size: var(--ol8-font-size-021);
87
+ --_label-line: var(--ol8-font-line-030);
88
+ }
89
+
90
+ .ol8-nav-item--tab,
91
+ .ol8-nav-item--segment { --_radius: var(--ol8-component-tab-item-radius); }
92
+ .ol8-nav-item--destination { --_radius: var(--ol8-component-tab-bar-item-radius); }
93
+
94
+ /* ---- State ------------------------------------------------------------ */
95
+ @media (hover: hover) and (pointer: fine) {
96
+ .ol8-nav-item:hover:not(:disabled) {
97
+ background: var(--ol8-color-actionquiet-surfacehover);
98
+ color: var(--ol8-color-actionquiet-contenthover);
99
+ }
100
+ }
101
+ .ol8-nav-item:active:not(:disabled) {
102
+ background: var(--ol8-color-actionquiet-surfacepressed);
103
+ color: var(--ol8-color-actionquiet-contentpressed);
104
+ }
105
+
106
+ /* "Focus uses the approved inward treatment so parent clipping cannot crop it."
107
+ An outward ring would be cut by a scrolling tab list. */
108
+ .ol8-nav-item:focus-visible {
109
+ outline: none;
110
+ box-shadow: inset 0 0 0 var(--ol8-component-navigation-focus-inset) var(--ol8-color-focus-inner);
111
+ }
112
+
113
+ .ol8-nav-item:disabled {
114
+ color: var(--ol8-color-text-disabled);
115
+ background: transparent;
116
+ cursor: not-allowed;
117
+ }
118
+
119
+ /* ---- Selection · Tab Item raises an indicator ------------------------- */
120
+ .ol8-nav-item__indicator {
121
+ position: absolute;
122
+ inset-block-end: 0;
123
+ inset-inline: var(--ol8-component-tab-item-indicator-inset);
124
+ block-size: var(--ol8-component-tab-item-indicator-thickness);
125
+ border-radius: var(--ol8-shape-radius-full);
126
+ background: transparent;
127
+ }
128
+ .ol8-nav-item--tab[aria-selected="true"] {
129
+ color: var(--ol8-color-actionprimary-default);
130
+ }
131
+ .ol8-nav-item--tab[aria-selected="true"] > .ol8-nav-item__indicator {
132
+ background: var(--ol8-color-actionprimary-default);
133
+ }
134
+
135
+ /* ---- Selection · Segment and Destination take the surface ------------- */
136
+ .ol8-nav-item--segment[aria-pressed="true"],
137
+ .ol8-nav-item--destination[aria-current] {
138
+ background: var(--ol8-color-selection-surface);
139
+ color: var(--ol8-color-selection-content);
140
+ }
141
+
142
+ @media (prefers-reduced-motion: reduce) {
143
+ .ol8-nav-item { transition-duration: var(--ol8-motion-duration-instant); }
144
+ }
145
+
146
+ @media (forced-colors: active) {
147
+ .ol8-nav-item:focus-visible {
148
+ outline: var(--ol8-component-navigation-focus-inset) solid Highlight;
149
+ outline-offset: calc(var(--ol8-component-navigation-focus-inset) * -1);
150
+ box-shadow: none;
151
+ }
152
+ .ol8-nav-item--tab[aria-selected="true"] > .ol8-nav-item__indicator { background: Highlight; }
153
+ .ol8-nav-item--segment[aria-pressed="true"],
154
+ .ol8-nav-item--destination[aria-current] { background: Highlight; color: HighlightText; }
155
+ }
@@ -0,0 +1,115 @@
1
+ /* GENERATED from packages/core/src/organisms/segmented-control/segmented-control.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 · Segmented Control (ORGANISM)
5
+ * Figma: Navigation / Segmented Control 698:4792 — six presentations.
6
+ *
7
+ * "Presentation never changes behavior." Every presentation below is a
8
+ * treatment of the same Segment molecule, and none of them touches which
9
+ * behaviour the consumer declared.
10
+ *
11
+ * Read from Figma at Standard, Regular:
12
+ * Inset Fill sunken track, Border Default at the standard boundary,
13
+ * segments inset by the track inset, selected takes the
14
+ * selection surface
15
+ * Line Indicator no track; an adaptive secondary rail with a separate
16
+ * indicator in the primary colour, inset like a tab
17
+ * Outlined Selection no track; each segment carries its own boundary, the
18
+ * selected one in the primary colour at the strong width,
19
+ * with a deliberate gap between segments
20
+ * Soft Pill raised track at full radius; segments at full radius
21
+ * Icon-only the Inset Fill track, with icon only segments
22
+ * Stacked Label no track; segments stack their icon over their label
23
+ */
24
+
25
+ .ol8-segmented {
26
+ display: inline-flex;
27
+ align-items: center;
28
+ box-sizing: border-box;
29
+ gap: var(--ol8-component-tab-bar-gap);
30
+ }
31
+
32
+ .ol8-segmented > .ol8-nav-item { flex: 1 1 0; }
33
+
34
+ /* ---- Inset Fill and Icon-only share one track ------------------------- */
35
+ .ol8-segmented[data-ol8-presentation="inset-fill"],
36
+ .ol8-segmented[data-ol8-presentation="icon-only"] {
37
+ padding: var(--ol8-component-segmented-control-track-inset);
38
+ border: var(--ol8-border-width-standard) solid var(--ol8-color-border-default);
39
+ border-radius: var(--ol8-component-segmented-control-track-radius);
40
+ background: var(--ol8-color-surface-sunken);
41
+ }
42
+ .ol8-segmented[data-ol8-presentation="inset-fill"] > .ol8-nav-item,
43
+ .ol8-segmented[data-ol8-presentation="icon-only"] > .ol8-nav-item {
44
+ --_radius: var(--ol8-component-segmented-control-item-radius);
45
+ }
46
+
47
+ /* ---- Soft Pill -------------------------------------------------------- */
48
+ .ol8-segmented[data-ol8-presentation="soft-pill"] {
49
+ padding: var(--ol8-component-segmented-control-track-inset);
50
+ border-radius: var(--ol8-shape-radius-full);
51
+ background: var(--ol8-color-surface-raised);
52
+ }
53
+ .ol8-segmented[data-ol8-presentation="soft-pill"] > .ol8-nav-item {
54
+ --_radius: var(--ol8-shape-radius-full);
55
+ }
56
+
57
+ /* ---- Line Indicator --------------------------------------------------- */
58
+ .ol8-segmented[data-ol8-presentation="line-indicator"] {
59
+ position: relative;
60
+ gap: 0;
61
+ }
62
+ .ol8-segmented[data-ol8-presentation="line-indicator"]::after {
63
+ content: "";
64
+ position: absolute;
65
+ inset-block-end: 0;
66
+ inset-inline: 0;
67
+ block-size: var(--ol8-component-tab-item-rail);
68
+ background: var(--ol8-color-text-secondary);
69
+ }
70
+ /* The active segment raises the line itself, so the indicator travels with the
71
+ selection rather than being positioned by script. */
72
+ .ol8-segmented[data-ol8-presentation="line-indicator"] > .ol8-nav-item { --_radius: 0; }
73
+ .ol8-segmented[data-ol8-presentation="line-indicator"] > .ol8-nav-item::after {
74
+ content: "";
75
+ position: absolute;
76
+ z-index: 1;
77
+ inset-block-end: 0;
78
+ inset-inline: var(--ol8-component-tab-item-indicator-inset);
79
+ block-size: var(--ol8-component-segmented-control-line);
80
+ border-radius: var(--ol8-shape-radius-full);
81
+ background: transparent;
82
+ }
83
+ .ol8-segmented[data-ol8-presentation="line-indicator"] > .ol8-nav-item[aria-pressed="true"] {
84
+ background: transparent;
85
+ color: var(--ol8-color-actionprimary-default);
86
+ }
87
+ .ol8-segmented[data-ol8-presentation="line-indicator"] > .ol8-nav-item[aria-pressed="true"]::after {
88
+ background: var(--ol8-color-actionprimary-default);
89
+ }
90
+
91
+ /* ---- Outlined Selection ----------------------------------------------- */
92
+ .ol8-segmented[data-ol8-presentation="outlined-selection"] {
93
+ gap: var(--ol8-component-segmented-control-outlined-gap);
94
+ }
95
+ .ol8-segmented[data-ol8-presentation="outlined-selection"] > .ol8-nav-item {
96
+ --_radius: var(--ol8-component-segmented-control-item-radius);
97
+ border: var(--ol8-component-segmented-control-boundary) solid var(--ol8-color-border-default);
98
+ }
99
+ .ol8-segmented[data-ol8-presentation="outlined-selection"] > .ol8-nav-item[aria-pressed="true"] {
100
+ border-width: var(--ol8-border-width-standard);
101
+ border-color: var(--ol8-color-actionprimary-default);
102
+ background: transparent;
103
+ color: var(--ol8-color-actionprimary-default);
104
+ }
105
+
106
+ /* ---- Stacked Label ---------------------------------------------------- */
107
+ .ol8-segmented[data-ol8-presentation="stacked-label"] > .ol8-nav-item {
108
+ --_radius: var(--ol8-component-segmented-control-item-radius);
109
+ }
110
+
111
+ @media (forced-colors: active) {
112
+ .ol8-segmented[data-ol8-presentation="line-indicator"] > .ol8-nav-item[aria-pressed="true"]::after {
113
+ background: Highlight;
114
+ }
115
+ }
@@ -0,0 +1,33 @@
1
+ /* GENERATED from packages/core/src/molecules/select/select.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 · Select (MOLECULE)
5
+ * Figma: Select / Trigger 583:338, Molecule / Select 875:1778.
6
+ *
7
+ * "Geometry and appearance reuse approved Text Field tokens", and Figma proves
8
+ * it: every bound variable on the trigger control is a Text Field one. So
9
+ * Select carries the field classes and adds only what a native select needs,
10
+ * which is the disclosure mark and the removal of the platform's own arrow.
11
+ */
12
+
13
+ .ol8-select__input {
14
+ appearance: none;
15
+ cursor: pointer;
16
+ }
17
+ .ol8-select__input:disabled { cursor: not-allowed; }
18
+
19
+ /* The native control keeps its own text colour rules, so the value takes the
20
+ primary ink while an unselected placeholder option stays secondary. */
21
+ .ol8-select__input:has(option[value=""]:checked) { color: var(--ol8-color-text-secondary); }
22
+
23
+ .ol8-select__disclosure {
24
+ flex: none;
25
+ color: var(--ol8-color-icon-secondary);
26
+ pointer-events: none;
27
+ }
28
+ .ol8-select[data-ol8-disabled] .ol8-select__disclosure { color: var(--ol8-color-icon-disabled); }
29
+
30
+ /* The disclosure sits over the control, so a click anywhere still reaches the
31
+ native select underneath and opens the platform picker. */
32
+ .ol8-select .ol8-field__control { position: relative; }
33
+ .ol8-select__input { flex: 1 1 auto; min-inline-size: 0; }
@@ -73,12 +73,21 @@
73
73
  --_ink: var(--ol8-color-icon-disabled);
74
74
  }
75
75
 
76
- /* The check glyph scales geometrically with the box (18px master is an exact
77
- 0.75x of the 24px one), so a single 24-unit path serves both sizes. */
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. */
78
86
  .ol8-selection--checkbox > .ol8-selection__mark > svg {
79
87
  display: block;
80
- inline-size: var(--_box);
81
- block-size: var(--_box);
88
+ inline-size: var(--ol8-size-icon-standard);
89
+ block-size: var(--ol8-size-icon-standard);
90
+ overflow: visible;
82
91
  }
83
92
 
84
93
  /* The Mixed bar does NOT scale. Both the 18px and 24px masters carry the same
@@ -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
+ }