@oneli8/react 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.
- package/README.md +34 -4
- package/ai-context.json +116 -1
- package/package.json +29 -8
- package/src/atoms/NavigationBadge.js +19 -0
- package/src/foundations/geometry.js +71 -0
- package/src/index.d.ts +214 -0
- package/src/index.js +14 -0
- package/src/molecules/ChoiceChip.js +101 -0
- package/src/molecules/FormMessage.js +33 -0
- package/src/molecules/Link.js +31 -0
- package/src/molecules/NavigationItem.js +55 -0
- package/src/molecules/Select.js +109 -0
- package/src/molecules/SelectionOption.js +4 -1
- package/src/molecules/TextField.js +174 -0
- package/src/molecules/Token.js +49 -0
- package/src/organisms/ChoicePicker.js +114 -0
- package/src/organisms/Combobox.js +122 -0
- package/src/organisms/MultiSelectField.js +138 -0
- package/src/organisms/SegmentedControl.js +114 -0
- package/src/organisms/SelectionPopup.js +73 -0
- package/src/organisms/TabBar.js +68 -0
- package/src/organisms/Tabs.js +123 -0
- package/src/styles/choice-chip.css +216 -0
- package/src/styles/choice-picker.css +100 -0
- package/src/styles/combobox.css +42 -0
- package/src/styles/form-message.css +48 -0
- package/src/styles/gem.css +52 -5
- package/src/styles/link.css +116 -0
- package/src/styles/multi-select-field.css +78 -0
- package/src/styles/navigation-badge.css +37 -0
- package/src/styles/navigation-item.css +155 -0
- package/src/styles/segmented-control.css +115 -0
- package/src/styles/select.css +33 -0
- package/src/styles/selection-indicator.css +13 -4
- package/src/styles/selection-popup.css +92 -0
- package/src/styles/tab-bar.css +47 -0
- package/src/styles/tabs.css +55 -0
- package/src/styles/text-field.css +268 -0
- package/src/styles/token.css +65 -0
- package/styles.css +18 -2
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/* GENERATED from packages/core/src/molecules/link/link.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 · Link (MOLECULE)
|
|
5
|
+
*
|
|
6
|
+
* Figma (138:260, 139:260, 139:453): "Runtime href, native anchor behavior,
|
|
7
|
+
* visited resolution, aria-current, motion preference and navigation list
|
|
8
|
+
* hierarchy live in code." Figma's five States are visual evidence for review,
|
|
9
|
+
* not classes: they are :hover, :active, :focus-visible, :visited and
|
|
10
|
+
* [aria-current] here.
|
|
11
|
+
*
|
|
12
|
+
* "Icons remain intentionally parked and must not be added as local artwork."
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
.ol8-link {
|
|
16
|
+
position: relative;
|
|
17
|
+
color: var(--ol8-color-text-link-default);
|
|
18
|
+
font-family: var(--ol8-font-family-functional);
|
|
19
|
+
font-weight: var(--ol8-font-weight-semibold);
|
|
20
|
+
text-decoration-line: underline;
|
|
21
|
+
text-decoration-thickness: var(--ol8-component-link-underline-default);
|
|
22
|
+
text-underline-offset: var(--ol8-component-link-underline-offset);
|
|
23
|
+
transition-property: color, text-decoration-thickness, background-color, box-shadow;
|
|
24
|
+
transition-duration: var(--ol8-motion-interaction-hover-duration);
|
|
25
|
+
transition-timing-function: var(--ol8-motion-interaction-hover-timing-function);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* ---- Size ------------------------------------------------------------- */
|
|
29
|
+
.ol8-link[data-ol8-size="small"] {
|
|
30
|
+
font-size: var(--ol8-typography-body-small-font-size);
|
|
31
|
+
line-height: var(--ol8-typography-body-small-line-height);
|
|
32
|
+
letter-spacing: var(--ol8-typography-body-small-letter-spacing);
|
|
33
|
+
}
|
|
34
|
+
.ol8-link[data-ol8-size="standard"] {
|
|
35
|
+
font-size: var(--ol8-typography-body-medium-font-size);
|
|
36
|
+
line-height: var(--ol8-typography-body-medium-line-height);
|
|
37
|
+
letter-spacing: var(--ol8-typography-body-medium-letter-spacing);
|
|
38
|
+
}
|
|
39
|
+
.ol8-link[data-ol8-size="large"] {
|
|
40
|
+
font-size: var(--ol8-typography-body-large-font-size);
|
|
41
|
+
line-height: var(--ol8-typography-body-large-line-height);
|
|
42
|
+
letter-spacing: var(--ol8-typography-body-large-letter-spacing);
|
|
43
|
+
}
|
|
44
|
+
/* Not a Figma size. An inline link inside body copy that does not follow its
|
|
45
|
+
surrounding type is broken, and Figma cannot express "inherit" as a variant.
|
|
46
|
+
Flagged for the design source rather than left undocumented. */
|
|
47
|
+
.ol8-link[data-ol8-size="inherit"] {
|
|
48
|
+
font-size: inherit;
|
|
49
|
+
line-height: inherit;
|
|
50
|
+
letter-spacing: inherit;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* ---- State ------------------------------------------------------------ */
|
|
54
|
+
.ol8-link:hover,
|
|
55
|
+
.ol8-link:active {
|
|
56
|
+
text-decoration-thickness: var(--ol8-component-link-underline-emphasis);
|
|
57
|
+
}
|
|
58
|
+
.ol8-link:hover { color: var(--ol8-color-text-link-hover); }
|
|
59
|
+
.ol8-link:active { color: var(--ol8-color-text-link-pressed); }
|
|
60
|
+
|
|
61
|
+
/* Navigation links live in a list where visited carries no meaning. */
|
|
62
|
+
.ol8-link[data-ol8-form="inline"]:visited,
|
|
63
|
+
.ol8-link[data-ol8-form="standalone"]:visited {
|
|
64
|
+
color: var(--ol8-color-text-link-visited);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.ol8-link:focus-visible {
|
|
68
|
+
outline: none;
|
|
69
|
+
color: var(--ol8-color-text-primary);
|
|
70
|
+
text-decoration-color: var(--ol8-color-text-link-default);
|
|
71
|
+
border-radius: var(--ol8-shape-radius-subtle);
|
|
72
|
+
box-shadow:
|
|
73
|
+
0 0 0 var(--ol8-focus-ring-innerwidth) var(--ol8-color-focus-inner),
|
|
74
|
+
0 0 0 var(--ol8-focus-ring-totalwidth) var(--ol8-color-focus-outer);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* ---- Form ------------------------------------------------------------- */
|
|
78
|
+
/* Standalone and navigation are targets in their own right, so they take the
|
|
79
|
+
protected minimum. Inline sits in a sentence and must not. Figma pads both by
|
|
80
|
+
the minimal inline step and carries a gap for the parked icon slot. */
|
|
81
|
+
.ol8-link[data-ol8-form="standalone"],
|
|
82
|
+
.ol8-link[data-ol8-form="navigation"] {
|
|
83
|
+
display: inline-flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
gap: var(--ol8-component-link-gap-standard);
|
|
86
|
+
min-block-size: var(--ol8-component-link-targetnavigation);
|
|
87
|
+
padding-inline: var(--ol8-spacing-inline-minimal);
|
|
88
|
+
}
|
|
89
|
+
.ol8-link[data-ol8-form="standalone"][data-ol8-size="small"],
|
|
90
|
+
.ol8-link[data-ol8-form="navigation"][data-ol8-size="small"] {
|
|
91
|
+
gap: var(--ol8-component-link-gap-small);
|
|
92
|
+
}
|
|
93
|
+
.ol8-link[data-ol8-form="standalone"][data-ol8-size="large"],
|
|
94
|
+
.ol8-link[data-ol8-form="navigation"][data-ol8-size="large"] {
|
|
95
|
+
gap: var(--ol8-component-link-gap-large);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* Figma keeps the underline in every navigation state and marks the current
|
|
99
|
+
destination by thickening it to the current marker weight. */
|
|
100
|
+
.ol8-link[data-ol8-form="navigation"][aria-current] {
|
|
101
|
+
text-decoration-thickness: var(--ol8-component-link-underline-current);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* ---- Motion ----------------------------------------------------------- */
|
|
105
|
+
.ol8-link[data-ol8-motion="none"] { transition-duration: var(--ol8-motion-duration-instant); }
|
|
106
|
+
@media (prefers-reduced-motion: reduce) {
|
|
107
|
+
.ol8-link { transition-duration: var(--ol8-motion-duration-instant); }
|
|
108
|
+
}
|
|
109
|
+
@media (forced-colors: active) {
|
|
110
|
+
.ol8-link { forced-color-adjust: auto; }
|
|
111
|
+
.ol8-link:focus-visible {
|
|
112
|
+
outline: var(--ol8-focus-ring-innerwidth) solid Highlight;
|
|
113
|
+
outline-offset: calc(var(--ol8-focus-ring-innerwidth) * -1);
|
|
114
|
+
box-shadow: none;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -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
|
|
77
|
-
|
|
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(--
|
|
81
|
-
block-size: var(--
|
|
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
|
+
}
|