@oneli8/react 1.0.0-beta.2 → 1.0.0-beta.4
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/AGENTS.md +12 -0
- package/PRINCIPLES.md +48 -0
- package/README.md +61 -75
- package/ai-context.json +146 -0
- package/package.json +30 -24
- package/skills/oneli8-figma-to-code/SKILL.md +25 -0
- package/skills/oneli8-icons/SKILL.md +24 -0
- package/skills/oneli8-ui/SKILL.md +24 -0
- package/src/atoms/Icon.js +31 -29
- package/src/atoms/SelectionIndicator.js +43 -0
- package/src/foundations/geometry.js +72 -0
- package/src/foundations/icons.generated.d.ts +38 -0
- package/src/foundations/icons.generated.js +406 -0
- package/src/index.d.ts +167 -4
- package/src/index.js +9 -4
- package/src/molecules/Button.js +47 -18
- package/src/molecules/ChoiceItem.js +55 -0
- package/src/molecules/IconButton.js +31 -16
- package/src/molecules/SelectionOption.js +41 -0
- package/src/organisms/ChoiceGroup.js +78 -0
- package/src/styles/button.css +195 -0
- package/src/styles/choice-group.css +95 -0
- package/src/styles/choice-item.css +192 -0
- package/src/styles/gem.css +173 -0
- package/src/styles/icon-button.css +224 -0
- package/src/styles/icon.css +56 -0
- package/src/styles/selection-indicator.css +153 -0
- package/src/styles/selection-option.css +123 -0
- package/styles.css +12 -6
- package/src/Button.d.ts +0 -20
- package/src/Button.js +0 -1
- package/src/Icon.d.ts +0 -4
- package/src/Icon.js +0 -1
- package/src/IconButton.d.ts +0 -20
- package/src/IconButton.js +0 -1
- package/src/Link.d.ts +0 -16
- package/src/Link.js +0 -1
- package/src/atoms/AnchorControl.js +0 -7
- package/src/atoms/BusyIndicator.js +0 -6
- package/src/atoms/ButtonControl.js +0 -14
- package/src/atoms/IconFrame.js +0 -19
- package/src/atoms/Label.js +0 -5
- package/src/foundations/collection.js +0 -13
- package/src/foundations/runtime.js +0 -44
- package/src/molecules/Link.js +0 -14
- package/styles/accessibility.css +0 -1
- package/styles/button.css +0 -176
- package/styles/gem-control-focus.css +0 -33
- package/styles/icon-button.css +0 -42
- package/styles/icon-frame.css +0 -1
- package/styles/link.css +0 -20
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/* GENERATED from packages/core/src/molecules/button/button.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 · Button
|
|
5
|
+
* Figma: section "Button" (1034:9968) — Primary 15:823, Secondary 15:824,
|
|
6
|
+
* Quiet 15:825, Destructive 15:826. 4 families × 4 sizes × 6 states.
|
|
7
|
+
*
|
|
8
|
+
* The governing rule comes from the Figma component description itself:
|
|
9
|
+
*
|
|
10
|
+
* "State → deterministic visual evidence … Runtime-only native button
|
|
11
|
+
* semantics, disabled, loading, pressed, fullWidth and iconMotion remain
|
|
12
|
+
* implemented and tested in code; they must not become fake visual axes."
|
|
13
|
+
*
|
|
14
|
+
* So the six Figma States are NOT six markup variants:
|
|
15
|
+
* Default → the base rule
|
|
16
|
+
* Hover → :hover
|
|
17
|
+
* Pressed → :active (and [aria-pressed="true"] for toggles)
|
|
18
|
+
* Focus → :focus-visible
|
|
19
|
+
* Disabled → [disabled] (native, not a class)
|
|
20
|
+
* Loading → [data-ol8-loading] (which also implies disabled + aria-busy)
|
|
21
|
+
*
|
|
22
|
+
* There is no .ol8-btn--hover. Emitting one is a bug.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
.ol8-btn {
|
|
26
|
+
/* Per-variant channels. Families set these; the base rule never hardcodes. */
|
|
27
|
+
--_bg: transparent;
|
|
28
|
+
--_fg: currentColor;
|
|
29
|
+
--_bd: transparent;
|
|
30
|
+
--_bw: 0px;
|
|
31
|
+
|
|
32
|
+
/* Size channels, defaulted to Standard. */
|
|
33
|
+
--_h: var(--ol8-component-button-height-standard);
|
|
34
|
+
--_px: var(--ol8-component-button-paddinginline-standard);
|
|
35
|
+
--_r: var(--ol8-component-button-radius-standard);
|
|
36
|
+
--_gap: var(--ol8-component-button-gap-compact);
|
|
37
|
+
--_icon: var(--ol8-size-icon-small);
|
|
38
|
+
|
|
39
|
+
box-sizing: border-box; /* Figma strokes are inside the frame */
|
|
40
|
+
display: inline-flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
justify-content: center;
|
|
43
|
+
gap: var(--_gap);
|
|
44
|
+
block-size: var(--_h);
|
|
45
|
+
padding-inline: var(--_px);
|
|
46
|
+
padding-block: 0;
|
|
47
|
+
border: var(--_bw) solid var(--_bd);
|
|
48
|
+
border-radius: var(--_r);
|
|
49
|
+
background: var(--_bg);
|
|
50
|
+
color: var(--_fg);
|
|
51
|
+
|
|
52
|
+
font-family: var(--ol8-font-family-functional);
|
|
53
|
+
font-size: var(--ol8-font-size-016);
|
|
54
|
+
line-height: var(--ol8-font-line-024);
|
|
55
|
+
font-weight: var(--ol8-font-weight-regular);
|
|
56
|
+
letter-spacing: 0;
|
|
57
|
+
/* AR One Sans variable axis. Figma sets this on every functional text node;
|
|
58
|
+
omitting it shifts glyph advances and the button measures narrow. */
|
|
59
|
+
font-variation-settings: "ARRR" var(--ol8-font-axis-arrr-baseline);
|
|
60
|
+
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
user-select: none;
|
|
63
|
+
white-space: nowrap;
|
|
64
|
+
text-decoration: none; /* when rendered as <a class="ol8-btn"> */
|
|
65
|
+
-webkit-appearance: none;
|
|
66
|
+
appearance: none;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.ol8-btn__label { white-space: nowrap; }
|
|
70
|
+
|
|
71
|
+
/* Button size governs icon size — the Figma Icon Frame is driven by the
|
|
72
|
+
button, never set on the glyph. Higher specificity than .ol8-icon's own
|
|
73
|
+
[data-ol8-icon-size] rule, deliberately. */
|
|
74
|
+
.ol8-btn .ol8-icon { --_ol8-icon-size: var(--_icon); }
|
|
75
|
+
|
|
76
|
+
/* ---- Size (Figma: Size variant axis) --------------------------------- */
|
|
77
|
+
.ol8-btn[data-ol8-size="compact"] {
|
|
78
|
+
--_h: var(--ol8-component-button-height-compact);
|
|
79
|
+
--_px: var(--ol8-component-button-paddinginline-compact);
|
|
80
|
+
--_r: var(--ol8-component-button-radius-compact);
|
|
81
|
+
--_gap: var(--ol8-component-button-gap-compact);
|
|
82
|
+
--_icon: var(--ol8-size-icon-small);
|
|
83
|
+
}
|
|
84
|
+
.ol8-btn[data-ol8-size="standard"] {
|
|
85
|
+
--_h: var(--ol8-component-button-height-standard);
|
|
86
|
+
--_px: var(--ol8-component-button-paddinginline-standard);
|
|
87
|
+
--_r: var(--ol8-component-button-radius-standard);
|
|
88
|
+
--_gap: var(--ol8-component-button-gap-compact); /* shared, by design */
|
|
89
|
+
--_icon: var(--ol8-size-icon-small);
|
|
90
|
+
}
|
|
91
|
+
.ol8-btn[data-ol8-size="comfortable"] {
|
|
92
|
+
--_h: var(--ol8-component-button-height-comfortable);
|
|
93
|
+
--_px: var(--ol8-component-button-paddinginline-comfortable);
|
|
94
|
+
--_r: var(--ol8-component-button-radius-comfortable);
|
|
95
|
+
--_gap: var(--ol8-component-button-gap-comfortable);
|
|
96
|
+
--_icon: var(--ol8-size-icon-standard);
|
|
97
|
+
}
|
|
98
|
+
.ol8-btn[data-ol8-size="large"] {
|
|
99
|
+
--_h: var(--ol8-component-button-height-large);
|
|
100
|
+
--_px: var(--ol8-component-button-paddinginline-large);
|
|
101
|
+
--_r: var(--ol8-component-button-radius-large);
|
|
102
|
+
--_gap: var(--ol8-component-button-gap-comfortable); /* shared, by design */
|
|
103
|
+
--_icon: var(--ol8-size-icon-standard);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* ---- Family: Primary ------------------------------------------------- */
|
|
107
|
+
.ol8-btn--primary { --_bg: var(--ol8-color-actionprimary-default); --_fg: var(--ol8-color-actionprimary-content); }
|
|
108
|
+
.ol8-btn--primary:hover:not(:disabled):not([data-ol8-loading]) { --_bg: var(--ol8-color-actionprimary-hover); }
|
|
109
|
+
.ol8-btn--primary:active:not(:disabled):not([data-ol8-loading]),
|
|
110
|
+
.ol8-btn--primary[aria-pressed="true"]:not(:disabled) { --_bg: var(--ol8-color-actionprimary-pressed); }
|
|
111
|
+
.ol8-btn--primary:disabled, .ol8-btn--primary[data-ol8-loading] {
|
|
112
|
+
--_bg: var(--ol8-color-actionprimary-disabled);
|
|
113
|
+
--_fg: var(--ol8-color-actionprimary-disabledcontent);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* ---- Family: Destructive (same shape as Primary) --------------------- */
|
|
117
|
+
.ol8-btn--destructive { --_bg: var(--ol8-color-actiondestructive-default); --_fg: var(--ol8-color-actiondestructive-content); }
|
|
118
|
+
.ol8-btn--destructive:hover:not(:disabled):not([data-ol8-loading]) { --_bg: var(--ol8-color-actiondestructive-hover); }
|
|
119
|
+
.ol8-btn--destructive:active:not(:disabled):not([data-ol8-loading]),
|
|
120
|
+
.ol8-btn--destructive[aria-pressed="true"]:not(:disabled) { --_bg: var(--ol8-color-actiondestructive-pressed); }
|
|
121
|
+
.ol8-btn--destructive:disabled, .ol8-btn--destructive[data-ol8-loading] {
|
|
122
|
+
--_bg: var(--ol8-color-actiondestructive-disabled);
|
|
123
|
+
--_fg: var(--ol8-color-actiondestructive-disabledcontent);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* ---- Family: Secondary ----------------------------------------------
|
|
127
|
+
Secondary and Quiet carry byte-identical color roles in Figma. The ONLY
|
|
128
|
+
differentiator is this boundary: Secondary has a 2px stroke whose color
|
|
129
|
+
tracks the content color; Quiet has none. Verified — Quiet's node binds no
|
|
130
|
+
--ol8-component-button-secondaryboundary at all. */
|
|
131
|
+
.ol8-btn--secondary {
|
|
132
|
+
--_bw: var(--ol8-component-button-secondaryboundary);
|
|
133
|
+
--_bg: var(--ol8-color-actionsecondary-surfacedefault);
|
|
134
|
+
--_fg: var(--ol8-color-actionsecondary-contentdefault);
|
|
135
|
+
--_bd: var(--ol8-color-actionsecondary-contentdefault);
|
|
136
|
+
}
|
|
137
|
+
.ol8-btn--secondary:hover:not(:disabled):not([data-ol8-loading]) {
|
|
138
|
+
--_bg: var(--ol8-color-actionsecondary-surfacehover);
|
|
139
|
+
--_fg: var(--ol8-color-actionsecondary-contenthover);
|
|
140
|
+
--_bd: var(--ol8-color-actionsecondary-contenthover);
|
|
141
|
+
}
|
|
142
|
+
.ol8-btn--secondary:active:not(:disabled):not([data-ol8-loading]),
|
|
143
|
+
.ol8-btn--secondary[aria-pressed="true"]:not(:disabled) {
|
|
144
|
+
--_bg: var(--ol8-color-actionsecondary-surfacepressed);
|
|
145
|
+
--_fg: var(--ol8-color-actionsecondary-contentpressed);
|
|
146
|
+
--_bd: var(--ol8-color-actionsecondary-contentpressed);
|
|
147
|
+
}
|
|
148
|
+
.ol8-btn--secondary:disabled, .ol8-btn--secondary[data-ol8-loading] {
|
|
149
|
+
--_bg: var(--ol8-color-actionsecondary-surfacedisabled);
|
|
150
|
+
--_fg: var(--ol8-color-actionsecondary-contentdisabled);
|
|
151
|
+
--_bd: var(--ol8-color-actionsecondary-contentdisabled);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* ---- Family: Quiet (identical colors, no boundary) ------------------- */
|
|
155
|
+
.ol8-btn--quiet {
|
|
156
|
+
--_bg: var(--ol8-color-actionquiet-surfacedefault);
|
|
157
|
+
--_fg: var(--ol8-color-actionquiet-contentdefault);
|
|
158
|
+
}
|
|
159
|
+
.ol8-btn--quiet:hover:not(:disabled):not([data-ol8-loading]) {
|
|
160
|
+
--_bg: var(--ol8-color-actionquiet-surfacehover);
|
|
161
|
+
--_fg: var(--ol8-color-actionquiet-contenthover);
|
|
162
|
+
}
|
|
163
|
+
.ol8-btn--quiet:active:not(:disabled):not([data-ol8-loading]),
|
|
164
|
+
.ol8-btn--quiet[aria-pressed="true"]:not(:disabled) {
|
|
165
|
+
--_bg: var(--ol8-color-actionquiet-surfacepressed);
|
|
166
|
+
--_fg: var(--ol8-color-actionquiet-contentpressed);
|
|
167
|
+
}
|
|
168
|
+
.ol8-btn--quiet:disabled, .ol8-btn--quiet[data-ol8-loading] {
|
|
169
|
+
--_bg: var(--ol8-color-actionquiet-surfacedisabled);
|
|
170
|
+
--_fg: var(--ol8-color-actionquiet-contentdisabled);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* ---- Focus ----------------------------------------------------------
|
|
174
|
+
Figma draws the focus ring as an INSIDE stroke, so the focused variant is
|
|
175
|
+
exactly the same size as the unfocused one (83×42 either way, Standard).
|
|
176
|
+
An inset box-shadow reproduces that without disturbing layout or fighting
|
|
177
|
+
Secondary's own 2px boundary — `outline` would sit outside and grow it. */
|
|
178
|
+
.ol8-btn:focus-visible {
|
|
179
|
+
outline: none;
|
|
180
|
+
box-shadow: inset 0 0 0 var(--ol8-focus-ring-innerwidth) var(--ol8-color-focus-outer);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* ---- Disabled / Loading --------------------------------------------- */
|
|
184
|
+
.ol8-btn:disabled, .ol8-btn[data-ol8-loading] { cursor: not-allowed; }
|
|
185
|
+
.ol8-btn[data-ol8-loading] { cursor: progress; }
|
|
186
|
+
|
|
187
|
+
/* "Platform motion adapters may rotate it; Reduced Motion keeps the static
|
|
188
|
+
busy mark." — Figma 379:7. Rotation is opt-in and motion-guarded. */
|
|
189
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
190
|
+
.ol8-btn[data-ol8-loading] .ol8-btn__spinner > svg { animation: ol8-spin 900ms linear infinite; }
|
|
191
|
+
}
|
|
192
|
+
@keyframes ol8-spin { to { transform: rotate(360deg); } }
|
|
193
|
+
|
|
194
|
+
/* ---- fullWidth (runtime-only, per the Figma description) ------------- */
|
|
195
|
+
.ol8-btn[data-ol8-fullwidth] { display: flex; inline-size: 100%; }
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* GENERATED from packages/core/src/organisms/choice-group/choice-group.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 · Choice Group (ORGANISM)
|
|
5
|
+
* Figma: Documentation / Organism / Choice Group
|
|
6
|
+
* Checkbox Hierarchy 273:603 Aggregate = None | Some | All
|
|
7
|
+
* Radio Group 859:5180 Behavior = Safe Default | No Default |
|
|
8
|
+
* Horizontal | Invalid
|
|
9
|
+
*
|
|
10
|
+
* First organism in the system, and the first tier allowed to hold logic:
|
|
11
|
+
* the checkbox group computes its parent from its children, and the radio
|
|
12
|
+
* group owns group-level validation.
|
|
13
|
+
*
|
|
14
|
+
* From Figma (273:603): "Aggregate None/Some/All is computed from nested
|
|
15
|
+
* Checkbox molecules; MIXED IS NEVER A THIRD PREFERENCE. Runtime recursion,
|
|
16
|
+
* parent-child synchronization, group naming, required behavior, reset, and
|
|
17
|
+
* announcements live in code."
|
|
18
|
+
*
|
|
19
|
+
* So Aggregate is an OUTPUT, never an input. Nothing may author it.
|
|
20
|
+
*
|
|
21
|
+
* The two shells differ, deliberately:
|
|
22
|
+
* checkbox bordered, radius-container (24px), legend Title/Small
|
|
23
|
+
* radio unbordered, radius-standard (12px), legend Body/Medium
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
.ol8-choice-group {
|
|
27
|
+
box-sizing: border-box;
|
|
28
|
+
display: flex;
|
|
29
|
+
flex-direction: column;
|
|
30
|
+
gap: var(--ol8-spacing-stack-related);
|
|
31
|
+
padding: var(--ol8-spacing-inset-standard);
|
|
32
|
+
background: var(--ol8-color-surface-raised);
|
|
33
|
+
border: 0;
|
|
34
|
+
border-radius: var(--ol8-shape-radius-standard);
|
|
35
|
+
font-family: var(--ol8-font-family-functional);
|
|
36
|
+
font-variation-settings: "ARRR" var(--ol8-font-axis-arrr-baseline);
|
|
37
|
+
min-inline-size: 0; /* fieldset defaults to min-content; this lets it shrink */
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.ol8-choice-group--checkbox {
|
|
41
|
+
border: 1px solid var(--ol8-color-border-subtle);
|
|
42
|
+
border-radius: var(--ol8-shape-radius-container);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* <legend> is taken out of flow by default, so it is laid out as a normal
|
|
46
|
+
flex child instead — the group label is a real legend for assistive tech. */
|
|
47
|
+
.ol8-choice-group__legend {
|
|
48
|
+
display: block;
|
|
49
|
+
float: none;
|
|
50
|
+
padding: 0;
|
|
51
|
+
inline-size: 100%;
|
|
52
|
+
color: var(--ol8-color-text-primary);
|
|
53
|
+
}
|
|
54
|
+
.ol8-choice-group--checkbox > .ol8-choice-group__legend {
|
|
55
|
+
font-size: var(--ol8-font-size-021);
|
|
56
|
+
line-height: var(--ol8-font-line-030);
|
|
57
|
+
font-weight: var(--ol8-font-weight-semibold);
|
|
58
|
+
}
|
|
59
|
+
.ol8-choice-group--radio > .ol8-choice-group__legend {
|
|
60
|
+
font-size: var(--ol8-font-size-016);
|
|
61
|
+
line-height: var(--ol8-font-line-024);
|
|
62
|
+
font-weight: var(--ol8-font-weight-regular);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.ol8-choice-group__instruction {
|
|
66
|
+
font-size: var(--ol8-font-size-012);
|
|
67
|
+
line-height: var(--ol8-font-line-018);
|
|
68
|
+
letter-spacing: 0.12px;
|
|
69
|
+
color: var(--ol8-color-text-secondary);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.ol8-choice-group__options { display: flex; flex-direction: column; }
|
|
73
|
+
.ol8-choice-group[data-ol8-orientation="horizontal"] > .ol8-choice-group__options {
|
|
74
|
+
flex-direction: row;
|
|
75
|
+
gap: var(--ol8-spacing-stack-related);
|
|
76
|
+
flex-wrap: wrap;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* Children of a hierarchy sit under their parent, indented one 24px step. */
|
|
80
|
+
.ol8-choice-group__children {
|
|
81
|
+
display: flex;
|
|
82
|
+
flex-direction: column;
|
|
83
|
+
padding-inline-start: var(--ol8-dimension-scale-024);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* Group-level validation message. */
|
|
87
|
+
.ol8-choice-group__message {
|
|
88
|
+
font-size: var(--ol8-font-size-012);
|
|
89
|
+
line-height: var(--ol8-font-line-018);
|
|
90
|
+
letter-spacing: 0.12px;
|
|
91
|
+
color: var(--ol8-color-text-secondary);
|
|
92
|
+
}
|
|
93
|
+
.ol8-choice-group[data-ol8-invalid="true"] > .ol8-choice-group__message {
|
|
94
|
+
color: var(--ol8-color-feedbackcritical-inlinecontent);
|
|
95
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/* GENERATED from packages/core/src/molecules/choice-item/choice-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 · Choice Item (MOLECULE)
|
|
5
|
+
* Figma: Documentation / Molecule / Choice Item
|
|
6
|
+
* Checkbox 252:301 (24 variants) · Radio 331:1309 (16) · Switch 825:1591 (16)
|
|
7
|
+
*
|
|
8
|
+
* From the Figma description (252:301):
|
|
9
|
+
* "Selection maps Unchecked/Checked/Mixed to checked/indeterminate;
|
|
10
|
+
* Availability maps enabled/disabled ... The indicator is a shared
|
|
11
|
+
* selection atom, not local artwork. Native input, name/value, required,
|
|
12
|
+
* reset, form submission, and onCheckedChange live in code."
|
|
13
|
+
*
|
|
14
|
+
* So the molecule is: a real <input>, the selection-indicator atom, a label,
|
|
15
|
+
* an optional description — and NOTHING that duplicates native behaviour.
|
|
16
|
+
*
|
|
17
|
+
* ANATOMY (Figma 266:257 / 252:74 / 252:77)
|
|
18
|
+
* <label class=ol8-choice> the whole row is the target
|
|
19
|
+
* input visually hidden, still focusable
|
|
20
|
+
* __surface hover / pressed / gem wash, bleeds 9px inline
|
|
21
|
+
* __indicator slot: indicator wide, label-line tall
|
|
22
|
+
* __content label + optional description
|
|
23
|
+
*
|
|
24
|
+
* ROW GEOMETRY — every size resolves to the same 48px minimum row:
|
|
25
|
+
* size gap indicator slot h label padding-block
|
|
26
|
+
* compact 6 18 18 Body/Small 15
|
|
27
|
+
* standard 9 18 24 Body/Medium 12
|
|
28
|
+
* comfortable 9 24 24 Body/Medium 12
|
|
29
|
+
* large 9 24 30 Body/Large 9
|
|
30
|
+
* padding-block is derived: (48 - slot height) / 2.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
.ol8-choice {
|
|
34
|
+
--_gap: var(--ol8-component-choice-gap-standard);
|
|
35
|
+
--_slot-h: var(--ol8-font-line-024);
|
|
36
|
+
--_pad: calc((var(--ol8-component-choice-rowminimum) - var(--_slot-h)) / 2);
|
|
37
|
+
--_label-size: var(--ol8-font-size-016);
|
|
38
|
+
--_label-line: var(--ol8-font-line-024);
|
|
39
|
+
--_indicator: var(--ol8-size-icon-small);
|
|
40
|
+
/* The hover / pressed / focus wash bleeds outside the text column. */
|
|
41
|
+
--_bleed: var(--ol8-dimension-scale-009);
|
|
42
|
+
|
|
43
|
+
box-sizing: border-box;
|
|
44
|
+
position: relative;
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: flex-start;
|
|
47
|
+
gap: var(--_gap);
|
|
48
|
+
min-block-size: var(--ol8-component-choice-rowminimum);
|
|
49
|
+
padding-block: var(--_pad);
|
|
50
|
+
font-family: var(--ol8-font-family-functional);
|
|
51
|
+
font-variation-settings: "ARRR" var(--ol8-font-axis-arrr-baseline);
|
|
52
|
+
cursor: pointer;
|
|
53
|
+
}
|
|
54
|
+
.ol8-choice[data-ol8-availability="disabled"] { cursor: not-allowed; }
|
|
55
|
+
|
|
56
|
+
/* ---- Size ------------------------------------------------------------ */
|
|
57
|
+
.ol8-choice[data-ol8-size="compact"] {
|
|
58
|
+
--_gap: var(--ol8-component-choice-gap-compact);
|
|
59
|
+
--_slot-h: var(--ol8-font-line-018);
|
|
60
|
+
--_label-size: var(--ol8-font-size-012);
|
|
61
|
+
--_label-line: var(--ol8-font-line-018);
|
|
62
|
+
--_indicator: var(--ol8-size-icon-small);
|
|
63
|
+
}
|
|
64
|
+
.ol8-choice[data-ol8-size="standard"] {
|
|
65
|
+
--_slot-h: var(--ol8-font-line-024);
|
|
66
|
+
--_label-size: var(--ol8-font-size-016);
|
|
67
|
+
--_label-line: var(--ol8-font-line-024);
|
|
68
|
+
--_indicator: var(--ol8-size-icon-small);
|
|
69
|
+
}
|
|
70
|
+
.ol8-choice[data-ol8-size="comfortable"] {
|
|
71
|
+
--_slot-h: var(--ol8-font-line-024);
|
|
72
|
+
--_label-size: var(--ol8-font-size-016);
|
|
73
|
+
--_label-line: var(--ol8-font-line-024);
|
|
74
|
+
--_indicator: var(--ol8-size-icon-standard);
|
|
75
|
+
}
|
|
76
|
+
.ol8-choice[data-ol8-size="large"] {
|
|
77
|
+
--_slot-h: var(--ol8-font-line-030);
|
|
78
|
+
--_label-size: var(--ol8-font-size-021);
|
|
79
|
+
--_label-line: var(--ol8-font-line-030);
|
|
80
|
+
--_indicator: var(--ol8-size-icon-standard);
|
|
81
|
+
}
|
|
82
|
+
/* Compact's label carries the Body/Small tracking. */
|
|
83
|
+
.ol8-choice[data-ol8-size="compact"] .ol8-choice__label { letter-spacing: 1px; }
|
|
84
|
+
|
|
85
|
+
/* ---- The native input owns everything semantic ----------------------- */
|
|
86
|
+
.ol8-choice__input {
|
|
87
|
+
position: absolute;
|
|
88
|
+
inline-size: 1px;
|
|
89
|
+
block-size: 1px;
|
|
90
|
+
opacity: 0;
|
|
91
|
+
pointer-events: none;
|
|
92
|
+
/* No margin reset needed — it is absolutely positioned, so margin cannot
|
|
93
|
+
affect layout. The structure lint would reject one regardless. */
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ---- Wash: hover, pressed, focus, gem -------------------------------- */
|
|
97
|
+
.ol8-choice__surface {
|
|
98
|
+
position: absolute;
|
|
99
|
+
inset-block: 0;
|
|
100
|
+
inset-inline: calc(-1 * var(--_bleed));
|
|
101
|
+
border-radius: var(--ol8-shape-radius-medium);
|
|
102
|
+
pointer-events: none;
|
|
103
|
+
}
|
|
104
|
+
.ol8-choice:hover:not([data-ol8-availability="disabled"]) > .ol8-choice__surface,
|
|
105
|
+
.ol8-choice:active:not([data-ol8-availability="disabled"]) > .ol8-choice__surface {
|
|
106
|
+
background: var(--ol8-color-selection-surface);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* Two nested rings, both visible — outer 3px, inner 3px at a 2px inset.
|
|
110
|
+
This is the same two-tone ring the Icon Button focus tokens describe. */
|
|
111
|
+
.ol8-choice:has(> .ol8-choice__input:focus-visible) > .ol8-choice__surface {
|
|
112
|
+
border: var(--ol8-focus-ring-outerwidth) solid var(--ol8-color-focus-outer);
|
|
113
|
+
}
|
|
114
|
+
.ol8-choice:has(> .ol8-choice__input:focus-visible) > .ol8-choice__surface::after {
|
|
115
|
+
content: "";
|
|
116
|
+
position: absolute;
|
|
117
|
+
inset: 2px;
|
|
118
|
+
border: var(--ol8-focus-ring-innerwidth) solid var(--ol8-color-focus-inner);
|
|
119
|
+
border-radius: 7px;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* ---- Indicator slot -------------------------------------------------- */
|
|
123
|
+
.ol8-choice__indicator {
|
|
124
|
+
display: flex;
|
|
125
|
+
align-items: center;
|
|
126
|
+
justify-content: center;
|
|
127
|
+
position: relative;
|
|
128
|
+
flex: none;
|
|
129
|
+
inline-size: var(--_indicator);
|
|
130
|
+
block-size: var(--_slot-h);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* Invalid draws an extra boundary over the indicator, same shape. */
|
|
134
|
+
.ol8-choice[data-ol8-invalid="true"] .ol8-choice__indicator::after {
|
|
135
|
+
content: "";
|
|
136
|
+
position: absolute;
|
|
137
|
+
inline-size: var(--_indicator);
|
|
138
|
+
block-size: var(--_indicator);
|
|
139
|
+
border: var(--ol8-component-checkbox-boundary) solid var(--ol8-color-feedbackcritical-inlinecontent);
|
|
140
|
+
border-radius: var(--ol8-component-checkbox-radius-compact);
|
|
141
|
+
pointer-events: none;
|
|
142
|
+
}
|
|
143
|
+
.ol8-choice[data-ol8-size="comfortable"][data-ol8-invalid="true"] .ol8-choice__indicator::after,
|
|
144
|
+
.ol8-choice[data-ol8-size="large"][data-ol8-invalid="true"] .ol8-choice__indicator::after {
|
|
145
|
+
border-radius: var(--ol8-component-checkbox-radius-comfortable);
|
|
146
|
+
}
|
|
147
|
+
.ol8-choice--radio[data-ol8-invalid="true"] .ol8-choice__indicator::after {
|
|
148
|
+
border-radius: var(--ol8-shape-radius-full);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* ---- Content --------------------------------------------------------- */
|
|
152
|
+
.ol8-choice__content {
|
|
153
|
+
display: flex;
|
|
154
|
+
flex-direction: column;
|
|
155
|
+
gap: var(--ol8-dimension-scale-003);
|
|
156
|
+
flex: 1 0 0;
|
|
157
|
+
min-inline-size: 0;
|
|
158
|
+
position: relative;
|
|
159
|
+
}
|
|
160
|
+
.ol8-choice__label {
|
|
161
|
+
font-size: var(--_label-size);
|
|
162
|
+
line-height: var(--_label-line);
|
|
163
|
+
font-weight: var(--ol8-font-weight-regular);
|
|
164
|
+
color: var(--ol8-color-text-primary);
|
|
165
|
+
overflow-wrap: break-word;
|
|
166
|
+
}
|
|
167
|
+
.ol8-choice__description {
|
|
168
|
+
font-size: var(--ol8-font-size-012);
|
|
169
|
+
line-height: var(--ol8-font-line-018);
|
|
170
|
+
letter-spacing: 0.12px;
|
|
171
|
+
color: var(--ol8-color-text-secondary);
|
|
172
|
+
overflow-wrap: break-word;
|
|
173
|
+
}
|
|
174
|
+
.ol8-choice[data-ol8-availability="disabled"] .ol8-choice__label,
|
|
175
|
+
.ol8-choice[data-ol8-availability="disabled"] .ol8-choice__description {
|
|
176
|
+
color: var(--ol8-color-text-disabled);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* ---- Gem material ----------------------------------------------------
|
|
180
|
+
"Gem is one Boolean material adapter over the same anatomy; Regular
|
|
181
|
+
remains the default." A wash on the row, not a different component. */
|
|
182
|
+
.ol8-choice[data-ol8-material="gem"] > .ol8-choice__surface {
|
|
183
|
+
background: var(--ol8-material-gem-color-quiet-environmentalface);
|
|
184
|
+
border: 1px solid var(--ol8-material-gem-color-cutedge-high);
|
|
185
|
+
border-radius: var(--ol8-shape-radius-standard);
|
|
186
|
+
inset-inline: 0;
|
|
187
|
+
}
|
|
188
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
189
|
+
.ol8-choice[data-ol8-material="gem"] > .ol8-choice__surface {
|
|
190
|
+
background: var(--ol8-material-gem-color-quiet-opaqueequivalent);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/* GENERATED from packages/core/src/materials/gem/gem.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 · Gem material adapter
|
|
5
|
+
* Figma: section "Gem Button" (1034:9969) — 144 components across
|
|
6
|
+
* Button/Primary/Gem 100:3, Button/Secondary/Gem 100:208,
|
|
7
|
+
* Icon Button ±Circle Primary/Secondary Gem 102:78, 102:151, 119:202, 119:299.
|
|
8
|
+
*
|
|
9
|
+
* From the Figma description (100:3):
|
|
10
|
+
* "Material adapter — NOT a fifth semantic variant. Geometry, meaning,
|
|
11
|
+
* accessibility, protected target, Size, State, label/icon content and
|
|
12
|
+
* native behavior are inherited from the regular molecule. Material changes
|
|
13
|
+
* only the token-bound surface and inner focus edge. Reduced Transparency
|
|
14
|
+
* resolves to the regular opaque equivalent. Quiet and Destructive use the
|
|
15
|
+
* regular material treatment until a distinct Gem treatment is explicitly
|
|
16
|
+
* approved."
|
|
17
|
+
*
|
|
18
|
+
* So Gem is an ATTRIBUTE on an existing button, never a new class:
|
|
19
|
+
* <button class="ol8-btn ol8-btn--primary" data-ol8-material="gem">
|
|
20
|
+
*
|
|
21
|
+
* It adds exactly two painted layers and touches nothing else:
|
|
22
|
+
* ::before the three-stop body + backdrop blur
|
|
23
|
+
* ::after the rim (specular top edge + integrated light rim)
|
|
24
|
+
*
|
|
25
|
+
* THE GRADIENT ANGLE
|
|
26
|
+
* Figma bakes a per-instance angle: 153.1595deg at Standard (83x42),
|
|
27
|
+
* 151.6609 at Comfortable (89x48), 149.2872 at Large (101x60). Those are not
|
|
28
|
+
* three different designs — every one satisfies
|
|
29
|
+
* theta = 90deg + atan(W / H)
|
|
30
|
+
* to four decimal places, which is exactly CSS's `to bottom right` corner
|
|
31
|
+
* keyword (gradient line perpendicular to the other diagonal). Using the
|
|
32
|
+
* keyword reproduces Figma at every size AND at any label width, where a baked
|
|
33
|
+
* degree value would only be correct for the one instance it was measured on.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
.ol8-btn[data-ol8-material="gem"],
|
|
37
|
+
.ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface {
|
|
38
|
+
position: relative;
|
|
39
|
+
isolation: isolate;
|
|
40
|
+
--_rim: var(--ol8-material-control-gem-rim-default);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* The regular opaque background stays underneath on purpose — it is what
|
|
44
|
+
shows through when the gem layer is suppressed for Reduced Transparency. */
|
|
45
|
+
.ol8-btn[data-ol8-material="gem"]::before,
|
|
46
|
+
.ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface::before {
|
|
47
|
+
content: "";
|
|
48
|
+
position: absolute;
|
|
49
|
+
inset: 0;
|
|
50
|
+
z-index: -1;
|
|
51
|
+
border-radius: inherit;
|
|
52
|
+
pointer-events: none;
|
|
53
|
+
backdrop-filter: blur(var(--ol8-material-control-gem-blur));
|
|
54
|
+
-webkit-backdrop-filter: blur(var(--ol8-material-control-gem-blur));
|
|
55
|
+
background-image: linear-gradient(
|
|
56
|
+
to bottom right,
|
|
57
|
+
var(--_g1) var(--ol8-material-control-gem-gradient-start),
|
|
58
|
+
var(--_g2) var(--ol8-material-control-gem-gradient-mid),
|
|
59
|
+
var(--_g3) var(--ol8-material-control-gem-gradient-end));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.ol8-btn[data-ol8-material="gem"]::after,
|
|
63
|
+
.ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface::after {
|
|
64
|
+
content: "";
|
|
65
|
+
position: absolute;
|
|
66
|
+
inset: 0;
|
|
67
|
+
border-radius: inherit;
|
|
68
|
+
pointer-events: none;
|
|
69
|
+
box-shadow:
|
|
70
|
+
inset 0 2px 1px 0 var(--ol8-material-control-gem-rim-specular),
|
|
71
|
+
inset 0 0 0 0 var(--_rim);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* ---- Primary · three-stop same-hue body ------------------------------ */
|
|
75
|
+
.ol8-btn--primary[data-ol8-material="gem"],
|
|
76
|
+
.ol8-icon-btn--primary[data-ol8-material="gem"] > .ol8-icon-btn__surface {
|
|
77
|
+
--_g1: var(--ol8-material-control-gem-primary-start);
|
|
78
|
+
--_g2: var(--ol8-material-control-gem-primary-mid);
|
|
79
|
+
--_g3: var(--ol8-material-control-gem-primary-end);
|
|
80
|
+
--_fg: var(--ol8-material-control-gem-primary-content);
|
|
81
|
+
--_ink: var(--ol8-material-control-gem-primary-content);
|
|
82
|
+
}
|
|
83
|
+
.ol8-btn--primary[data-ol8-material="gem"]:hover:not(:disabled):not([data-ol8-loading]),
|
|
84
|
+
.ol8-icon-btn--primary[data-ol8-material="gem"]:hover:not(:disabled):not([data-ol8-loading]) > .ol8-icon-btn__surface {
|
|
85
|
+
--_g1: var(--ol8-material-control-gem-primary-hoverstart);
|
|
86
|
+
--_g2: var(--ol8-material-control-gem-primary-hovermid);
|
|
87
|
+
--_g3: var(--ol8-material-control-gem-primary-hoverend);
|
|
88
|
+
--_rim: var(--ol8-material-control-gem-rim-hover);
|
|
89
|
+
}
|
|
90
|
+
/* Pressed reuses the HOVER stops and adds the cut-edge drop shadow. */
|
|
91
|
+
.ol8-btn--primary[data-ol8-material="gem"]:active:not(:disabled),
|
|
92
|
+
.ol8-btn--primary[data-ol8-material="gem"][aria-pressed="true"]:not(:disabled),
|
|
93
|
+
.ol8-icon-btn--primary[data-ol8-material="gem"]:active:not(:disabled) > .ol8-icon-btn__surface,
|
|
94
|
+
.ol8-icon-btn--primary[data-ol8-material="gem"][aria-pressed="true"]:not(:disabled) > .ol8-icon-btn__surface {
|
|
95
|
+
--_g1: var(--ol8-material-control-gem-primary-hoverstart);
|
|
96
|
+
--_g2: var(--ol8-material-control-gem-primary-hovermid);
|
|
97
|
+
--_g3: var(--ol8-material-control-gem-primary-hoverend);
|
|
98
|
+
--_rim: var(--ol8-material-control-gem-rim-hover);
|
|
99
|
+
}
|
|
100
|
+
.ol8-btn--primary[data-ol8-material="gem"]:active:not(:disabled),
|
|
101
|
+
.ol8-btn--primary[data-ol8-material="gem"][aria-pressed="true"]:not(:disabled) {
|
|
102
|
+
filter: drop-shadow(0 9px 12px var(--ol8-material-gem-color-cutedge-shadow));
|
|
103
|
+
}
|
|
104
|
+
.ol8-btn--primary[data-ol8-material="gem"][data-ol8-loading],
|
|
105
|
+
.ol8-icon-btn--primary[data-ol8-material="gem"][data-ol8-loading] > .ol8-icon-btn__surface {
|
|
106
|
+
--_g1: var(--ol8-material-control-gem-primary-loadingstart);
|
|
107
|
+
--_g2: var(--ol8-material-control-gem-primary-loadingmid);
|
|
108
|
+
--_g3: var(--ol8-material-control-gem-primary-loadingend);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* ---- Secondary · transparent body with an integrated light rim -------
|
|
112
|
+
The regular Secondary's 2px boundary is dropped in Gem; the rim replaces
|
|
113
|
+
it. Reduced Transparency restores the boundary along with the opaque body. */
|
|
114
|
+
.ol8-btn--secondary[data-ol8-material="gem"] { --_bw: 0px; }
|
|
115
|
+
.ol8-btn--secondary[data-ol8-material="gem"],
|
|
116
|
+
.ol8-icon-btn--secondary[data-ol8-material="gem"] > .ol8-icon-btn__surface {
|
|
117
|
+
--_g1: var(--ol8-material-control-gem-secondary-high);
|
|
118
|
+
--_g2: var(--ol8-material-control-gem-secondary-mid);
|
|
119
|
+
--_g3: var(--ol8-material-control-gem-secondary-low);
|
|
120
|
+
--_fg: var(--ol8-material-control-gem-secondary-content);
|
|
121
|
+
--_ink: var(--ol8-material-control-gem-secondary-content);
|
|
122
|
+
--_bd: transparent;
|
|
123
|
+
}
|
|
124
|
+
.ol8-btn--secondary[data-ol8-material="gem"]:hover:not(:disabled):not([data-ol8-loading]),
|
|
125
|
+
.ol8-icon-btn--secondary[data-ol8-material="gem"]:hover:not(:disabled):not([data-ol8-loading]) > .ol8-icon-btn__surface {
|
|
126
|
+
--_g1: var(--ol8-material-control-gem-secondary-hoverhigh);
|
|
127
|
+
--_g2: var(--ol8-material-control-gem-secondary-hovermid);
|
|
128
|
+
--_g3: var(--ol8-material-control-gem-secondary-hoverlow);
|
|
129
|
+
--_rim: var(--ol8-material-control-gem-rim-hover);
|
|
130
|
+
--_fg: var(--ol8-material-control-gem-secondary-content);
|
|
131
|
+
}
|
|
132
|
+
.ol8-btn--secondary[data-ol8-material="gem"]:active:not(:disabled),
|
|
133
|
+
.ol8-btn--secondary[data-ol8-material="gem"][aria-pressed="true"]:not(:disabled),
|
|
134
|
+
.ol8-icon-btn--secondary[data-ol8-material="gem"]:active:not(:disabled) > .ol8-icon-btn__surface {
|
|
135
|
+
--_g1: var(--ol8-material-control-gem-secondary-hoverhigh);
|
|
136
|
+
--_g2: var(--ol8-material-control-gem-secondary-hovermid);
|
|
137
|
+
--_g3: var(--ol8-material-control-gem-secondary-hoverlow);
|
|
138
|
+
--_rim: var(--ol8-material-control-gem-rim-hover);
|
|
139
|
+
--_fg: var(--ol8-material-control-gem-secondary-content);
|
|
140
|
+
}
|
|
141
|
+
.ol8-btn--secondary[data-ol8-material="gem"][data-ol8-loading],
|
|
142
|
+
.ol8-icon-btn--secondary[data-ol8-material="gem"][data-ol8-loading] > .ol8-icon-btn__surface {
|
|
143
|
+
--_g1: var(--ol8-material-control-gem-secondary-loadinghigh);
|
|
144
|
+
--_g2: var(--ol8-material-control-gem-secondary-loadingmid);
|
|
145
|
+
--_g3: var(--ol8-material-control-gem-secondary-loadinglow);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Content must sit above the ::before body. */
|
|
149
|
+
.ol8-btn[data-ol8-material="gem"] > *,
|
|
150
|
+
.ol8-icon-btn[data-ol8-material="gem"] .ol8-icon { position: relative; z-index: 0; }
|
|
151
|
+
|
|
152
|
+
/* ---- Disabled inherits the regular opaque treatment ------------------ */
|
|
153
|
+
.ol8-btn[data-ol8-material="gem"]:disabled::before,
|
|
154
|
+
.ol8-btn[data-ol8-material="gem"]:disabled::after,
|
|
155
|
+
.ol8-icon-btn[data-ol8-material="gem"]:disabled > .ol8-icon-btn__surface::before,
|
|
156
|
+
.ol8-icon-btn[data-ol8-material="gem"]:disabled > .ol8-icon-btn__surface::after { display: none; }
|
|
157
|
+
|
|
158
|
+
/* ---- Reduced Transparency -> the regular opaque equivalent -----------
|
|
159
|
+
Explicitly required by the Figma description. Hiding the gem layers
|
|
160
|
+
reveals the regular molecule's own background, which was never removed. */
|
|
161
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
162
|
+
.ol8-btn[data-ol8-material="gem"]::before,
|
|
163
|
+
.ol8-btn[data-ol8-material="gem"]::after,
|
|
164
|
+
.ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface::before,
|
|
165
|
+
.ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface::after { display: none; }
|
|
166
|
+
.ol8-btn--secondary[data-ol8-material="gem"] {
|
|
167
|
+
--_bw: var(--ol8-component-button-secondaryboundary);
|
|
168
|
+
--_bd: var(--ol8-color-actionsecondary-contentdefault);
|
|
169
|
+
--_fg: var(--ol8-color-actionsecondary-contentdefault);
|
|
170
|
+
}
|
|
171
|
+
.ol8-btn--primary[data-ol8-material="gem"] { --_fg: var(--ol8-color-actionprimary-content); }
|
|
172
|
+
.ol8-btn[data-ol8-material="gem"]:active { filter: none; }
|
|
173
|
+
}
|