@threadlabs/looma 0.11.5 → 0.12.0
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/components/shared/describe.js +9 -0
- package/components/ui-button/ui-button.html +12 -24
- package/components/ui-checkbox/ui-checkbox.html +22 -1
- package/components/ui-checkbox/ui-checkbox.js +2 -0
- package/components/ui-icon-button/ui-icon-button.html +4 -14
- package/components/ui-radio/ui-radio.html +22 -1
- package/components/ui-radio/ui-radio.js +2 -0
- package/components/ui-sidebar/ui-sidebar.js +3 -1
- package/components/ui-switch/ui-switch.html +22 -1
- package/components/ui-switch/ui-switch.js +2 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/styles/ui-button.css +21 -33
- package/styles/ui-checkbox.css +21 -1
- package/styles/ui-icon-button.css +4 -13
- package/styles/ui-radio.css +21 -1
- package/styles/ui-switch.css +21 -1
- package/theme-dark.css +2 -0
- package/theme-high-contrast.css +3 -0
- package/tokens.css +3 -0
- package/vanilla/UiCheckbox.js +18 -3
- package/vanilla/UiRadio.js +18 -3
- package/vanilla/UiSwitch.js +18 -3
- package/vue/UiButton.vue +14 -24
- package/vue/UiCheckbox.d.ts +3 -1
- package/vue/UiCheckbox.vue +24 -2
- package/vue/UiIconButton.vue +3 -13
- package/vue/UiRadio.d.ts +3 -1
- package/vue/UiRadio.vue +24 -2
- package/vue/UiSwitch.d.ts +3 -1
- package/vue/UiSwitch.vue +24 -2
- package/vue/chunks/UiButton.js +1 -1
- package/vue/chunks/UiCheckbox.js +14 -4
- package/vue/chunks/UiIconButton.js +1 -1
- package/vue/chunks/UiRadio.js +14 -4
- package/vue/chunks/UiSwitch.js +14 -4
- package/vue/components.css +156 -126
package/package.json
CHANGED
package/styles/ui-button.css
CHANGED
|
@@ -182,31 +182,19 @@
|
|
|
182
182
|
var(--ui-focus-halo);
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
/* Disabled
|
|
186
|
-
disabled outline is still an outline and a disabled ghost still has no edge.
|
|
187
|
-
|
|
188
|
-
highlight and shadow, because nothing that cannot be pressed should
|
|
185
|
+
/* Disabled keeps the shape and washes out the rest: every variant keeps its own structure, so a
|
|
186
|
+
disabled outline is still an outline and a disabled ghost still has no edge. One filter fades
|
|
187
|
+
fill, border, and text together (--ui-disabled-filter), and the theme decides which way is
|
|
188
|
+
toward the page. The highlight and shadow go, because nothing that cannot be pressed should
|
|
189
|
+
look raised. */
|
|
189
190
|
:scope:is(:disabled, [aria-disabled="true"]) {
|
|
190
|
-
|
|
191
|
-
page, which desaturates it at the same time. Toward the page rather than toward white,
|
|
192
|
-
so a dark theme dims where a light one lightens — the same rule, both schemes. */
|
|
193
|
-
--_tone: color-mix(in oklab, var(--_hue) 58%, var(--ui-surface));
|
|
194
|
-
--_tone-hover: var(--_tone);
|
|
195
|
-
--_tone-active: var(--_tone);
|
|
196
|
-
--_tone-text: color-mix(in oklab, var(--_hue-text) 72%, var(--ui-surface));
|
|
197
|
-
--_on-tone: var(--_tone-text);
|
|
198
|
-
|
|
191
|
+
filter: var(--ui-button-disabled-filter, var(--ui-disabled-filter));
|
|
199
192
|
opacity: var(--ui-button-disabled-opacity, 1);
|
|
200
193
|
cursor: not-allowed;
|
|
201
194
|
background-image: none;
|
|
202
195
|
box-shadow: none;
|
|
203
196
|
}
|
|
204
197
|
|
|
205
|
-
/* A filled button keeps its fill, faded further so its text stays legible on it. */
|
|
206
|
-
:is(:scope[data-ui-button-state~="variant=solid"], :scope[data-ui-button-state~="variant=danger"]):is(:disabled, [aria-disabled="true"]) {
|
|
207
|
-
--_tone: color-mix(in oklab, var(--_hue) 34%, var(--ui-surface));
|
|
208
|
-
}
|
|
209
|
-
|
|
210
198
|
/* Ghost has no hover to fall back on, so disabled is the one state it has to state outright:
|
|
211
199
|
it takes a surface rather than reading as plain text. The surface is a wash of the faded tone,
|
|
212
200
|
the way hover washes the live one, so it stays light in every tone. An opaque mix toward the
|
|
@@ -222,14 +210,15 @@
|
|
|
222
210
|
}
|
|
223
211
|
}
|
|
224
212
|
|
|
225
|
-
/* Under touch,
|
|
226
|
-
centred on it, like IconButton's. A
|
|
227
|
-
|
|
228
|
-
|
|
213
|
+
/* Under touch, a link-style button's press lands within the control minimum: an invisible hit
|
|
214
|
+
area, centred on it, like IconButton's. A boxed button grows to the minimum itself (below);
|
|
215
|
+
a link-style one keeps its text size, so a standalone one ("See all" under a list) needs the
|
|
216
|
+
area. Only the link needs it: a hit area on every button would reach over its neighbours. */
|
|
217
|
+
html[data-ui-input-modality="touch"] :scope[data-ui-button-state~="variant=link"] {
|
|
229
218
|
position: relative;
|
|
230
219
|
}
|
|
231
220
|
|
|
232
|
-
html[data-ui-input-modality="touch"] :scope::after {
|
|
221
|
+
html[data-ui-input-modality="touch"] :scope[data-ui-button-state~="variant=link"]::after {
|
|
233
222
|
position: absolute;
|
|
234
223
|
inset: 50% auto auto 50%;
|
|
235
224
|
inline-size: max(100%, var(--ui-control-min-block-size));
|
|
@@ -272,7 +261,6 @@
|
|
|
272
261
|
:scope[data-ui-button-state~="variant=link"]:is(:disabled, [aria-disabled="true"]) {
|
|
273
262
|
background: none;
|
|
274
263
|
border-color: transparent;
|
|
275
|
-
color: var(--ui-button-disabled-text, var(--ui-disabled-text));
|
|
276
264
|
}
|
|
277
265
|
|
|
278
266
|
:scope[data-ui-button-state~="variant=link"]:focus-visible {
|
|
@@ -303,12 +291,11 @@
|
|
|
303
291
|
|
|
304
292
|
/* A soft halo that transitions in with the other box-shadow changes, layered on the raised shadow. */
|
|
305
293
|
|
|
306
|
-
/* Disabled
|
|
307
|
-
disabled outline is still an outline and a disabled ghost still has no edge.
|
|
308
|
-
|
|
309
|
-
highlight and shadow, because nothing that cannot be pressed should
|
|
310
|
-
|
|
311
|
-
/* A filled button keeps its fill, faded further so its text stays legible on it. */
|
|
294
|
+
/* Disabled keeps the shape and washes out the rest: every variant keeps its own structure, so a
|
|
295
|
+
disabled outline is still an outline and a disabled ghost still has no edge. One filter fades
|
|
296
|
+
fill, border, and text together (--ui-disabled-filter), and the theme decides which way is
|
|
297
|
+
toward the page. The highlight and shadow go, because nothing that cannot be pressed should
|
|
298
|
+
look raised. */
|
|
312
299
|
|
|
313
300
|
/* Ghost has no hover to fall back on, so disabled is the one state it has to state outright:
|
|
314
301
|
it takes a surface rather than reading as plain text. The surface is a wash of the faded tone,
|
|
@@ -318,9 +305,10 @@
|
|
|
318
305
|
@media (pointer: coarse) {
|
|
319
306
|
}
|
|
320
307
|
|
|
321
|
-
/* Under touch,
|
|
322
|
-
centred on it, like IconButton's. A
|
|
323
|
-
|
|
308
|
+
/* Under touch, a link-style button's press lands within the control minimum: an invisible hit
|
|
309
|
+
area, centred on it, like IconButton's. A boxed button grows to the minimum itself (below);
|
|
310
|
+
a link-style one keeps its text size, so a standalone one ("See all" under a list) needs the
|
|
311
|
+
area. Only the link needs it: a hit area on every button would reach over its neighbours. */
|
|
324
312
|
|
|
325
313
|
/* Link: an inline text action. No box, padding, or minimum height (inline targets are exempt under
|
|
326
314
|
WCAG 2.5.8); the inherited font; an underline that strengthens on hover; a visible focus ring. */
|
package/styles/ui-checkbox.css
CHANGED
|
@@ -9,11 +9,29 @@
|
|
|
9
9
|
cursor: pointer;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
/* The label, and under it an optional line saying what the choice means. */
|
|
13
|
+
.text {
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
gap: var(--ui-space-1);
|
|
17
|
+
min-inline-size: 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
.label {
|
|
13
21
|
min-inline-size: 0;
|
|
14
22
|
line-height: var(--ui-line-height-md);
|
|
15
23
|
}
|
|
16
24
|
|
|
25
|
+
.description {
|
|
26
|
+
color: var(--ui-text-secondary);
|
|
27
|
+
font-size: var(--ui-font-size-sm);
|
|
28
|
+
line-height: var(--ui-line-height);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.description:empty {
|
|
32
|
+
display: none;
|
|
33
|
+
}
|
|
34
|
+
|
|
17
35
|
input {
|
|
18
36
|
appearance: none;
|
|
19
37
|
-webkit-appearance: none;
|
|
@@ -95,7 +113,9 @@
|
|
|
95
113
|
}
|
|
96
114
|
}
|
|
97
115
|
@scope ([data-component~="ui-checkbox"]) to ([data-component]) {
|
|
98
|
-
/*
|
|
116
|
+
/* The label, and under it an optional line saying what the choice means. */
|
|
117
|
+
|
|
118
|
+
/* Centre the tick's box, then lift it: a rotated L's ink sits ~0.11rem below its box centre. */
|
|
99
119
|
|
|
100
120
|
/* A component that sets its display still honours the hidden attribute on its root. */
|
|
101
121
|
}
|
|
@@ -161,22 +161,12 @@
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
/* Disabled colours fall back to each variant's own; ghost takes only the icon colour. */
|
|
164
|
+
/* Disabled washes the button out the way Button does: the same shape, one filter. */
|
|
164
165
|
:scope:disabled {
|
|
166
|
+
filter: var(--ui-icon-button-disabled-filter, var(--ui-disabled-filter));
|
|
165
167
|
opacity: var(--ui-icon-button-disabled-opacity, 1);
|
|
166
|
-
color: var(--ui-icon-button-disabled-text, var(--ui-disabled-text));
|
|
167
168
|
cursor: not-allowed;
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
:scope[data-ui-icon-button-state~="variant=outline"]:disabled {
|
|
171
|
-
border-color: var(--ui-icon-button-disabled-border, var(--ui-border));
|
|
172
|
-
background-color: var(--ui-icon-button-disabled-surface, var(--ui-disabled-surface));
|
|
173
|
-
color: var(--ui-icon-button-disabled-text, var(--ui-disabled-text));
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
:scope[data-ui-icon-button-state~="variant=solid"]:disabled {
|
|
177
|
-
border-color: var(--ui-icon-button-disabled-border, var(--ui-border));
|
|
178
|
-
background-color: var(--ui-icon-button-disabled-surface, var(--ui-disabled-surface));
|
|
179
|
-
color: var(--ui-icon-button-disabled-text, var(--ui-disabled-text));
|
|
169
|
+
box-shadow: none;
|
|
180
170
|
}
|
|
181
171
|
|
|
182
172
|
:scope {
|
|
@@ -212,6 +202,7 @@
|
|
|
212
202
|
}
|
|
213
203
|
|
|
214
204
|
/* Disabled colours fall back to each variant's own; ghost takes only the icon colour. */
|
|
205
|
+
/* Disabled washes the button out the way Button does: the same shape, one filter. */
|
|
215
206
|
|
|
216
207
|
:where([data-slotted], [data-slotted] *):is(svg) {
|
|
217
208
|
inline-size: var(--ui-icon-button-icon-size, var(--_icon-button-icon-default));
|
package/styles/ui-radio.css
CHANGED
|
@@ -9,11 +9,29 @@
|
|
|
9
9
|
cursor: pointer;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
/* The label, and under it an optional line saying what the choice means. */
|
|
13
|
+
.text {
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
gap: var(--ui-space-1);
|
|
17
|
+
min-inline-size: 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
.label {
|
|
13
21
|
min-inline-size: 0;
|
|
14
22
|
line-height: var(--ui-line-height-md);
|
|
15
23
|
}
|
|
16
24
|
|
|
25
|
+
.description {
|
|
26
|
+
color: var(--ui-text-secondary);
|
|
27
|
+
font-size: var(--ui-font-size-sm);
|
|
28
|
+
line-height: var(--ui-line-height);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.description:empty {
|
|
32
|
+
display: none;
|
|
33
|
+
}
|
|
34
|
+
|
|
17
35
|
input {
|
|
18
36
|
flex: 0 0 auto;
|
|
19
37
|
inline-size: var(--ui-radio-size, 1.125rem);
|
|
@@ -42,5 +60,7 @@
|
|
|
42
60
|
}
|
|
43
61
|
}
|
|
44
62
|
@scope ([data-component~="ui-radio"]) to ([data-component]) {
|
|
45
|
-
/*
|
|
63
|
+
/* The label, and under it an optional line saying what the choice means. */
|
|
64
|
+
|
|
65
|
+
/* A component that sets its display still honours the hidden attribute on its root. */
|
|
46
66
|
}
|
package/styles/ui-switch.css
CHANGED
|
@@ -9,11 +9,29 @@
|
|
|
9
9
|
cursor: pointer;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
/* The label, and under it an optional line saying what the choice means. */
|
|
13
|
+
.text {
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
gap: var(--ui-space-1);
|
|
17
|
+
min-inline-size: 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
.label {
|
|
13
21
|
min-inline-size: 0;
|
|
14
22
|
line-height: var(--ui-line-height-md);
|
|
15
23
|
}
|
|
16
24
|
|
|
25
|
+
.description {
|
|
26
|
+
color: var(--ui-text-secondary);
|
|
27
|
+
font-size: var(--ui-font-size-sm);
|
|
28
|
+
line-height: var(--ui-line-height);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.description:empty {
|
|
32
|
+
display: none;
|
|
33
|
+
}
|
|
34
|
+
|
|
17
35
|
input {
|
|
18
36
|
appearance: none;
|
|
19
37
|
-webkit-appearance: none;
|
|
@@ -81,7 +99,9 @@
|
|
|
81
99
|
}
|
|
82
100
|
}
|
|
83
101
|
@scope ([data-component~="ui-switch"]) to ([data-component]) {
|
|
84
|
-
|
|
102
|
+
/* The label, and under it an optional line saying what the choice means. */
|
|
103
|
+
|
|
104
|
+
@media (prefers-reduced-motion: reduce) {
|
|
85
105
|
}
|
|
86
106
|
|
|
87
107
|
/* A component that sets its display still honours the hidden attribute on its root. */
|
package/theme-dark.css
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
--ui-shadow-lg: 0 10px 28px rgb(0 0 0 / 0.55), 0 4px 10px rgb(0 0 0 / 0.35);
|
|
24
24
|
--ui-control-inset: inset 0 1px 2px rgb(0 0 0 / 0.35);
|
|
25
25
|
--ui-raised: 0 1px 2px rgb(0 0 0 / 0.4), inset 0 1px 0 rgb(255 255 255 / 0.06);
|
|
26
|
+
--ui-disabled-filter: saturate(0.2) contrast(0.75) brightness(0.8);
|
|
26
27
|
--ui-raised-highlight: linear-gradient(rgb(255 255 255 / 0.04), rgb(255 255 255 / 0));
|
|
27
28
|
--ui-select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23a0a09c' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
|
28
29
|
}
|
|
@@ -51,6 +52,7 @@
|
|
|
51
52
|
--ui-shadow-lg: 0 10px 28px rgb(0 0 0 / 0.55), 0 4px 10px rgb(0 0 0 / 0.35);
|
|
52
53
|
--ui-control-inset: inset 0 1px 2px rgb(0 0 0 / 0.35);
|
|
53
54
|
--ui-raised: 0 1px 2px rgb(0 0 0 / 0.4), inset 0 1px 0 rgb(255 255 255 / 0.06);
|
|
55
|
+
--ui-disabled-filter: saturate(0.2) contrast(0.75) brightness(0.8);
|
|
54
56
|
--ui-raised-highlight: linear-gradient(rgb(255 255 255 / 0.04), rgb(255 255 255 / 0));
|
|
55
57
|
--ui-select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23a0a09c' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
|
56
58
|
}
|
package/theme-high-contrast.css
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
--ui-text-secondary: #111111;
|
|
25
25
|
--ui-text-muted: #222222;
|
|
26
26
|
--ui-disabled-text: #333333;
|
|
27
|
+
--ui-disabled-filter: saturate(0);
|
|
27
28
|
--ui-focus-ring: #0000ff;
|
|
28
29
|
}
|
|
29
30
|
}
|
|
@@ -52,6 +53,8 @@
|
|
|
52
53
|
--ui-text-secondary: #111111;
|
|
53
54
|
--ui-text-muted: #222222;
|
|
54
55
|
--ui-disabled-text: #333333;
|
|
56
|
+
/* Disabled loses its colour, never its contrast. */
|
|
57
|
+
--ui-disabled-filter: saturate(0);
|
|
55
58
|
--ui-focus-ring: #0000ff;
|
|
56
59
|
}
|
|
57
60
|
}
|
package/tokens.css
CHANGED
|
@@ -141,6 +141,9 @@
|
|
|
141
141
|
full opacity, so a disabled control reads as unavailable rather than as a faded variant. */
|
|
142
142
|
--ui-disabled-surface: var(--ui-surface-sunken);
|
|
143
143
|
--ui-disabled-text: var(--ui-text-muted);
|
|
144
|
+
/* A disabled button keeps its own look, washed out: less colour, less contrast, toward the page.
|
|
145
|
+
A filter rather than a second palette, so every variant and tone fades the same way. */
|
|
146
|
+
--ui-disabled-filter: saturate(0.2) contrast(0.75) brightness(1.25);
|
|
144
147
|
|
|
145
148
|
--ui-focus-ring: var(--ui-accent);
|
|
146
149
|
|
package/vanilla/UiCheckbox.js
CHANGED
|
@@ -3,7 +3,7 @@ import { manageComponentLifecycle } from "@nextwebwg/html-next/runtime";
|
|
|
3
3
|
import * as controller from "../components/ui-checkbox/ui-checkbox.js";
|
|
4
4
|
import "../styles/ui-checkbox.css";
|
|
5
5
|
|
|
6
|
-
const definition = {...{"contract":{"tag":"ui-checkbox","props":{"checked":{"type":"boolean","required":false,"target":{"attribute":"checked"},"default":false},"disabled":{"type":"boolean","required":false,"target":{"attribute":"disabled"},"default":false},"indeterminate":{"type":"boolean","required":false,"target":{"property":"indeterminate"},"default":false},"name":{"type":"string","required":false,"target":{"attribute":"name"},"default":""},"required":{"type":"boolean","required":false,"target":{"attribute":"required"},"default":false},"value":{"type":"string","required":false,"target":{"attribute":"value"},"default":"on"}}},"template":{"kind":"element","name":"label","attributes":[],"children":[{"kind":"element","name":"input","attributes":[{"kind":"literal","name":"type","value":"checkbox"},{"kind":"property","key":"checked","name":"checked","expression":"internalChecked","expressionPlan":{"source":"internalChecked","ast":{"kind":"id","name":"internalChecked"},"dependencies":["internalChecked"]}},{"kind":"property","key":"indeterminate","name":"indeterminate","expression":"indeterminate","expressionPlan":{"source":"indeterminate","ast":{"kind":"id","name":"indeterminate"},"dependencies":["indeterminate"]}},{"kind":"attribute","name":"disabled","expression":"disabled","expressionPlan":{"source":"disabled","ast":{"kind":"id","name":"disabled"},"dependencies":["disabled"]}},{"kind":"attribute","name":"required","expression":"required","expressionPlan":{"source":"required","ast":{"kind":"id","name":"required"},"dependencies":["required"]}},{"kind":"attribute","name":"name","expression":"name","expressionPlan":{"source":"name","ast":{"kind":"id","name":"name"},"dependencies":["name"]}},{"kind":"attribute","name":"value","expression":"value","expressionPlan":{"source":"value","ast":{"kind":"id","name":"value"},"dependencies":["value"]}}],"children":[],"ref":"input"},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"label"}],"children":[{"kind":"slot"}]}]},"declarations":[{"kind":"state","name":"internalChecked","expression":{"source":"false","ast":{"kind":"literal","value":false},"dependencies":[]}},{"kind":"event","name":"change","type":"object({ checked: boolean, value: string, trigger: keyboard | pointer | programmatic })","bubbles":true,"composed":true,"cancelable":false}],"root":{"kind":"native","element":"label","choices":["label"]}},source:{file:import.meta.url},css:""};
|
|
6
|
+
const definition = {...{"contract":{"tag":"ui-checkbox","props":{"checked":{"type":"boolean","required":false,"target":{"attribute":"checked"},"default":false},"disabled":{"type":"boolean","required":false,"target":{"attribute":"disabled"},"default":false},"indeterminate":{"type":"boolean","required":false,"target":{"property":"indeterminate"},"default":false},"name":{"type":"string","required":false,"target":{"attribute":"name"},"default":""},"required":{"type":"boolean","required":false,"target":{"attribute":"required"},"default":false},"value":{"type":"string","required":false,"target":{"attribute":"value"},"default":"on"}}},"template":{"kind":"element","name":"label","attributes":[],"children":[{"kind":"element","name":"input","attributes":[{"kind":"literal","name":"type","value":"checkbox"},{"kind":"property","key":"checked","name":"checked","expression":"internalChecked","expressionPlan":{"source":"internalChecked","ast":{"kind":"id","name":"internalChecked"},"dependencies":["internalChecked"]}},{"kind":"property","key":"indeterminate","name":"indeterminate","expression":"indeterminate","expressionPlan":{"source":"indeterminate","ast":{"kind":"id","name":"indeterminate"},"dependencies":["indeterminate"]}},{"kind":"attribute","name":"disabled","expression":"disabled","expressionPlan":{"source":"disabled","ast":{"kind":"id","name":"disabled"},"dependencies":["disabled"]}},{"kind":"attribute","name":"required","expression":"required","expressionPlan":{"source":"required","ast":{"kind":"id","name":"required"},"dependencies":["required"]}},{"kind":"attribute","name":"name","expression":"name","expressionPlan":{"source":"name","ast":{"kind":"id","name":"name"},"dependencies":["name"]}},{"kind":"attribute","name":"value","expression":"value","expressionPlan":{"source":"value","ast":{"kind":"id","name":"value"},"dependencies":["value"]}}],"children":[],"ref":"input"},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"text"}],"children":[{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"label"}],"children":[{"kind":"slot"}]},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"description"},{"kind":"literal","name":"aria-hidden","value":"true"}],"children":[{"kind":"slot","fallback":[],"name":"description"}],"ref":"description"}]}]},"declarations":[{"kind":"state","name":"internalChecked","expression":{"source":"false","ast":{"kind":"literal","value":false},"dependencies":[]}},{"kind":"event","name":"change","type":"object({ checked: boolean, value: string, trigger: keyboard | pointer | programmatic })","bubbles":true,"composed":true,"cancelable":false}],"root":{"kind":"native","element":"label","choices":["label"]}},source:{file:import.meta.url},css:""};
|
|
7
7
|
|
|
8
8
|
export function createUiCheckbox(options = {}) {
|
|
9
9
|
const { attributes = {}, children = [], slots = {}, as, ...componentProps } = options;
|
|
@@ -32,15 +32,30 @@ export function createUiCheckbox(options = {}) {
|
|
|
32
32
|
else element0.setAttribute("value", String(value4));
|
|
33
33
|
element.append(element0);
|
|
34
34
|
const element1 = document.createElement("span");
|
|
35
|
-
element1.setAttribute("class", "
|
|
35
|
+
element1.setAttribute("class", "text");
|
|
36
|
+
const element2 = document.createElement("span");
|
|
37
|
+
element2.setAttribute("class", "label");
|
|
36
38
|
if (children.length > 0) {
|
|
37
39
|
for (const child of children) {
|
|
38
40
|
const node = typeof child === "string" ? document.createTextNode(child) : child;
|
|
39
41
|
projected.push([node, ""]);
|
|
40
|
-
|
|
42
|
+
element2.append(node);
|
|
41
43
|
}
|
|
42
44
|
} else {
|
|
43
45
|
}
|
|
46
|
+
element1.append(element2);
|
|
47
|
+
const element3 = document.createElement("span");
|
|
48
|
+
element3.setAttribute("class", "description");
|
|
49
|
+
element3.setAttribute("aria-hidden", "true");
|
|
50
|
+
if (slots["description"] ?? [].length > 0) {
|
|
51
|
+
for (const child of slots["description"] ?? []) {
|
|
52
|
+
const node = typeof child === "string" ? document.createTextNode(child) : child;
|
|
53
|
+
projected.push([node, "description"]);
|
|
54
|
+
element3.append(node);
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
}
|
|
58
|
+
element1.append(element3);
|
|
44
59
|
element.append(element1);
|
|
45
60
|
manageComponentLifecycle(element, definition, { props: componentProps, projected,
|
|
46
61
|
controller,
|
package/vanilla/UiRadio.js
CHANGED
|
@@ -3,7 +3,7 @@ import { manageComponentLifecycle } from "@nextwebwg/html-next/runtime";
|
|
|
3
3
|
import * as controller from "../components/ui-radio/ui-radio.js";
|
|
4
4
|
import "../styles/ui-radio.css";
|
|
5
5
|
|
|
6
|
-
const definition = {...{"contract":{"tag":"ui-radio","props":{"checked":{"type":"boolean","required":false,"target":{"attribute":"checked"},"default":false},"disabled":{"type":"boolean","required":false,"target":{"attribute":"disabled"},"default":false},"name":{"type":"string","required":false,"target":{"attribute":"name"},"default":""},"required":{"type":"boolean","required":false,"target":{"attribute":"required"},"default":false},"value":{"type":"string","required":false,"target":{"attribute":"value"},"default":"on"}}},"template":{"kind":"element","name":"label","attributes":[],"children":[{"kind":"element","name":"input","attributes":[{"kind":"literal","name":"type","value":"radio"},{"kind":"property","key":"checked","name":"checked","expression":"internalChecked","expressionPlan":{"source":"internalChecked","ast":{"kind":"id","name":"internalChecked"},"dependencies":["internalChecked"]}},{"kind":"attribute","name":"disabled","expression":"disabled","expressionPlan":{"source":"disabled","ast":{"kind":"id","name":"disabled"},"dependencies":["disabled"]}},{"kind":"attribute","name":"required","expression":"required","expressionPlan":{"source":"required","ast":{"kind":"id","name":"required"},"dependencies":["required"]}},{"kind":"attribute","name":"value","expression":"value","expressionPlan":{"source":"value","ast":{"kind":"id","name":"value"},"dependencies":["value"]}},{"kind":"attribute","name":"name","expression":"name","expressionPlan":{"source":"name","ast":{"kind":"id","name":"name"},"dependencies":["name"]}}],"children":[],"ref":"input"},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"label"}],"children":[{"kind":"slot"}]}]},"declarations":[{"kind":"state","name":"internalChecked","expression":{"source":"false","ast":{"kind":"literal","value":false},"dependencies":[]}},{"kind":"event","name":"change","type":"object({ checked: boolean, value: string, trigger: keyboard | pointer | programmatic })","bubbles":true,"composed":true,"cancelable":false}],"root":{"kind":"native","element":"label","choices":["label"]}},source:{file:import.meta.url},css:""};
|
|
6
|
+
const definition = {...{"contract":{"tag":"ui-radio","props":{"checked":{"type":"boolean","required":false,"target":{"attribute":"checked"},"default":false},"disabled":{"type":"boolean","required":false,"target":{"attribute":"disabled"},"default":false},"name":{"type":"string","required":false,"target":{"attribute":"name"},"default":""},"required":{"type":"boolean","required":false,"target":{"attribute":"required"},"default":false},"value":{"type":"string","required":false,"target":{"attribute":"value"},"default":"on"}}},"template":{"kind":"element","name":"label","attributes":[],"children":[{"kind":"element","name":"input","attributes":[{"kind":"literal","name":"type","value":"radio"},{"kind":"property","key":"checked","name":"checked","expression":"internalChecked","expressionPlan":{"source":"internalChecked","ast":{"kind":"id","name":"internalChecked"},"dependencies":["internalChecked"]}},{"kind":"attribute","name":"disabled","expression":"disabled","expressionPlan":{"source":"disabled","ast":{"kind":"id","name":"disabled"},"dependencies":["disabled"]}},{"kind":"attribute","name":"required","expression":"required","expressionPlan":{"source":"required","ast":{"kind":"id","name":"required"},"dependencies":["required"]}},{"kind":"attribute","name":"value","expression":"value","expressionPlan":{"source":"value","ast":{"kind":"id","name":"value"},"dependencies":["value"]}},{"kind":"attribute","name":"name","expression":"name","expressionPlan":{"source":"name","ast":{"kind":"id","name":"name"},"dependencies":["name"]}}],"children":[],"ref":"input"},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"text"}],"children":[{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"label"}],"children":[{"kind":"slot"}]},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"description"},{"kind":"literal","name":"aria-hidden","value":"true"}],"children":[{"kind":"slot","fallback":[],"name":"description"}],"ref":"description"}]}]},"declarations":[{"kind":"state","name":"internalChecked","expression":{"source":"false","ast":{"kind":"literal","value":false},"dependencies":[]}},{"kind":"event","name":"change","type":"object({ checked: boolean, value: string, trigger: keyboard | pointer | programmatic })","bubbles":true,"composed":true,"cancelable":false}],"root":{"kind":"native","element":"label","choices":["label"]}},source:{file:import.meta.url},css:""};
|
|
7
7
|
|
|
8
8
|
export function createUiRadio(options = {}) {
|
|
9
9
|
const { attributes = {}, children = [], slots = {}, as, ...componentProps } = options;
|
|
@@ -30,15 +30,30 @@ export function createUiRadio(options = {}) {
|
|
|
30
30
|
else element0.setAttribute("name", String(value3));
|
|
31
31
|
element.append(element0);
|
|
32
32
|
const element1 = document.createElement("span");
|
|
33
|
-
element1.setAttribute("class", "
|
|
33
|
+
element1.setAttribute("class", "text");
|
|
34
|
+
const element2 = document.createElement("span");
|
|
35
|
+
element2.setAttribute("class", "label");
|
|
34
36
|
if (children.length > 0) {
|
|
35
37
|
for (const child of children) {
|
|
36
38
|
const node = typeof child === "string" ? document.createTextNode(child) : child;
|
|
37
39
|
projected.push([node, ""]);
|
|
38
|
-
|
|
40
|
+
element2.append(node);
|
|
39
41
|
}
|
|
40
42
|
} else {
|
|
41
43
|
}
|
|
44
|
+
element1.append(element2);
|
|
45
|
+
const element3 = document.createElement("span");
|
|
46
|
+
element3.setAttribute("class", "description");
|
|
47
|
+
element3.setAttribute("aria-hidden", "true");
|
|
48
|
+
if (slots["description"] ?? [].length > 0) {
|
|
49
|
+
for (const child of slots["description"] ?? []) {
|
|
50
|
+
const node = typeof child === "string" ? document.createTextNode(child) : child;
|
|
51
|
+
projected.push([node, "description"]);
|
|
52
|
+
element3.append(node);
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
}
|
|
56
|
+
element1.append(element3);
|
|
42
57
|
element.append(element1);
|
|
43
58
|
manageComponentLifecycle(element, definition, { props: componentProps, projected,
|
|
44
59
|
controller,
|
package/vanilla/UiSwitch.js
CHANGED
|
@@ -3,7 +3,7 @@ import { manageComponentLifecycle } from "@nextwebwg/html-next/runtime";
|
|
|
3
3
|
import * as controller from "../components/ui-switch/ui-switch.js";
|
|
4
4
|
import "../styles/ui-switch.css";
|
|
5
5
|
|
|
6
|
-
const definition = {...{"contract":{"tag":"ui-switch","props":{"checked":{"type":"boolean","required":false,"target":{"attribute":"checked"},"default":false},"disabled":{"type":"boolean","required":false,"target":{"attribute":"disabled"},"default":false},"name":{"type":"string","required":false,"target":{"attribute":"name"},"default":""},"required":{"type":"boolean","required":false,"target":{"attribute":"required"},"default":false},"value":{"type":"string","required":false,"target":{"attribute":"value"},"default":"on"}}},"template":{"kind":"element","name":"label","attributes":[],"children":[{"kind":"element","name":"input","attributes":[{"kind":"literal","name":"type","value":"checkbox"},{"kind":"literal","name":"role","value":"switch"},{"kind":"property","key":"checked","name":"checked","expression":"internalChecked","expressionPlan":{"source":"internalChecked","ast":{"kind":"id","name":"internalChecked"},"dependencies":["internalChecked"]}},{"kind":"attribute","name":"disabled","expression":"disabled","expressionPlan":{"source":"disabled","ast":{"kind":"id","name":"disabled"},"dependencies":["disabled"]}},{"kind":"attribute","name":"required","expression":"required","expressionPlan":{"source":"required","ast":{"kind":"id","name":"required"},"dependencies":["required"]}},{"kind":"attribute","name":"name","expression":"name","expressionPlan":{"source":"name","ast":{"kind":"id","name":"name"},"dependencies":["name"]}},{"kind":"attribute","name":"value","expression":"value","expressionPlan":{"source":"value","ast":{"kind":"id","name":"value"},"dependencies":["value"]}}],"children":[],"ref":"input"},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"label"}],"children":[{"kind":"slot"}]}]},"declarations":[{"kind":"state","name":"internalChecked","expression":{"source":"false","ast":{"kind":"literal","value":false},"dependencies":[]}},{"kind":"event","name":"change","type":"object({ checked: boolean, value: string, trigger: keyboard | pointer | programmatic })","bubbles":true,"composed":true,"cancelable":false}],"root":{"kind":"native","element":"label","choices":["label"]}},source:{file:import.meta.url},css:""};
|
|
6
|
+
const definition = {...{"contract":{"tag":"ui-switch","props":{"checked":{"type":"boolean","required":false,"target":{"attribute":"checked"},"default":false},"disabled":{"type":"boolean","required":false,"target":{"attribute":"disabled"},"default":false},"name":{"type":"string","required":false,"target":{"attribute":"name"},"default":""},"required":{"type":"boolean","required":false,"target":{"attribute":"required"},"default":false},"value":{"type":"string","required":false,"target":{"attribute":"value"},"default":"on"}}},"template":{"kind":"element","name":"label","attributes":[],"children":[{"kind":"element","name":"input","attributes":[{"kind":"literal","name":"type","value":"checkbox"},{"kind":"literal","name":"role","value":"switch"},{"kind":"property","key":"checked","name":"checked","expression":"internalChecked","expressionPlan":{"source":"internalChecked","ast":{"kind":"id","name":"internalChecked"},"dependencies":["internalChecked"]}},{"kind":"attribute","name":"disabled","expression":"disabled","expressionPlan":{"source":"disabled","ast":{"kind":"id","name":"disabled"},"dependencies":["disabled"]}},{"kind":"attribute","name":"required","expression":"required","expressionPlan":{"source":"required","ast":{"kind":"id","name":"required"},"dependencies":["required"]}},{"kind":"attribute","name":"name","expression":"name","expressionPlan":{"source":"name","ast":{"kind":"id","name":"name"},"dependencies":["name"]}},{"kind":"attribute","name":"value","expression":"value","expressionPlan":{"source":"value","ast":{"kind":"id","name":"value"},"dependencies":["value"]}}],"children":[],"ref":"input"},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"text"}],"children":[{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"label"}],"children":[{"kind":"slot"}]},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"description"},{"kind":"literal","name":"aria-hidden","value":"true"}],"children":[{"kind":"slot","fallback":[],"name":"description"}],"ref":"description"}]}]},"declarations":[{"kind":"state","name":"internalChecked","expression":{"source":"false","ast":{"kind":"literal","value":false},"dependencies":[]}},{"kind":"event","name":"change","type":"object({ checked: boolean, value: string, trigger: keyboard | pointer | programmatic })","bubbles":true,"composed":true,"cancelable":false}],"root":{"kind":"native","element":"label","choices":["label"]}},source:{file:import.meta.url},css:""};
|
|
7
7
|
|
|
8
8
|
export function createUiSwitch(options = {}) {
|
|
9
9
|
const { attributes = {}, children = [], slots = {}, as, ...componentProps } = options;
|
|
@@ -31,15 +31,30 @@ export function createUiSwitch(options = {}) {
|
|
|
31
31
|
else element0.setAttribute("value", String(value3));
|
|
32
32
|
element.append(element0);
|
|
33
33
|
const element1 = document.createElement("span");
|
|
34
|
-
element1.setAttribute("class", "
|
|
34
|
+
element1.setAttribute("class", "text");
|
|
35
|
+
const element2 = document.createElement("span");
|
|
36
|
+
element2.setAttribute("class", "label");
|
|
35
37
|
if (children.length > 0) {
|
|
36
38
|
for (const child of children) {
|
|
37
39
|
const node = typeof child === "string" ? document.createTextNode(child) : child;
|
|
38
40
|
projected.push([node, ""]);
|
|
39
|
-
|
|
41
|
+
element2.append(node);
|
|
40
42
|
}
|
|
41
43
|
} else {
|
|
42
44
|
}
|
|
45
|
+
element1.append(element2);
|
|
46
|
+
const element3 = document.createElement("span");
|
|
47
|
+
element3.setAttribute("class", "description");
|
|
48
|
+
element3.setAttribute("aria-hidden", "true");
|
|
49
|
+
if (slots["description"] ?? [].length > 0) {
|
|
50
|
+
for (const child of slots["description"] ?? []) {
|
|
51
|
+
const node = typeof child === "string" ? document.createTextNode(child) : child;
|
|
52
|
+
projected.push([node, "description"]);
|
|
53
|
+
element3.append(node);
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
}
|
|
57
|
+
element1.append(element3);
|
|
43
58
|
element.append(element1);
|
|
44
59
|
manageComponentLifecycle(element, definition, { props: componentProps, projected,
|
|
45
60
|
controller,
|
package/vue/UiButton.vue
CHANGED
|
@@ -259,31 +259,19 @@ function attribute(value: unknown): any {
|
|
|
259
259
|
var(--ui-focus-halo);
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
-
/* Disabled
|
|
263
|
-
disabled outline is still an outline and a disabled ghost still has no edge.
|
|
264
|
-
|
|
265
|
-
highlight and shadow, because nothing that cannot be pressed should
|
|
262
|
+
/* Disabled keeps the shape and washes out the rest: every variant keeps its own structure, so a
|
|
263
|
+
disabled outline is still an outline and a disabled ghost still has no edge. One filter fades
|
|
264
|
+
fill, border, and text together (--ui-disabled-filter), and the theme decides which way is
|
|
265
|
+
toward the page. The highlight and shadow go, because nothing that cannot be pressed should
|
|
266
|
+
look raised. */
|
|
266
267
|
[data-component~="ui-button"]:is(:disabled, [aria-disabled="true"]) {
|
|
267
|
-
|
|
268
|
-
page, which desaturates it at the same time. Toward the page rather than toward white,
|
|
269
|
-
so a dark theme dims where a light one lightens — the same rule, both schemes. */
|
|
270
|
-
--_tone: color-mix(in oklab, var(--_hue) 58%, var(--ui-surface));
|
|
271
|
-
--_tone-hover: var(--_tone);
|
|
272
|
-
--_tone-active: var(--_tone);
|
|
273
|
-
--_tone-text: color-mix(in oklab, var(--_hue-text) 72%, var(--ui-surface));
|
|
274
|
-
--_on-tone: var(--_tone-text);
|
|
275
|
-
|
|
268
|
+
filter: var(--ui-button-disabled-filter, var(--ui-disabled-filter));
|
|
276
269
|
opacity: var(--ui-button-disabled-opacity, 1);
|
|
277
270
|
cursor: not-allowed;
|
|
278
271
|
background-image: none;
|
|
279
272
|
box-shadow: none;
|
|
280
273
|
}
|
|
281
274
|
|
|
282
|
-
/* A filled button keeps its fill, faded further so its text stays legible on it. */
|
|
283
|
-
:is([data-component~="ui-button"][data-ui-button-state~="variant=solid"], [data-component~="ui-button"][data-ui-button-state~="variant=danger"]):is(:disabled, [aria-disabled="true"]) {
|
|
284
|
-
--_tone: color-mix(in oklab, var(--_hue) 34%, var(--ui-surface));
|
|
285
|
-
}
|
|
286
|
-
|
|
287
275
|
/* Ghost has no hover to fall back on, so disabled is the one state it has to state outright:
|
|
288
276
|
it takes a surface rather than reading as plain text. The surface is a wash of the faded tone,
|
|
289
277
|
the way hover washes the live one, so it stays light in every tone. An opaque mix toward the
|
|
@@ -299,14 +287,17 @@ function attribute(value: unknown): any {
|
|
|
299
287
|
}
|
|
300
288
|
}
|
|
301
289
|
|
|
302
|
-
/* Under touch,
|
|
303
|
-
centred on it, like IconButton's. A
|
|
304
|
-
|
|
305
|
-
|
|
290
|
+
/* Under touch, a link-style button's press lands within the control minimum: an invisible hit
|
|
291
|
+
area, centred on it, like IconButton's. A boxed button grows to the minimum itself (below);
|
|
292
|
+
a link-style one keeps its text size, so a standalone one ("See all" under a list) needs the
|
|
293
|
+
area. Only the link needs it: a hit area on every button would reach over its neighbours. */
|
|
294
|
+
html[data-ui-input-modality="touch"]
|
|
295
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=link"] {
|
|
306
296
|
position: relative;
|
|
307
297
|
}
|
|
308
298
|
|
|
309
|
-
html[data-ui-input-modality="touch"]
|
|
299
|
+
html[data-ui-input-modality="touch"]
|
|
300
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=link"]::after {
|
|
310
301
|
position: absolute;
|
|
311
302
|
inset: 50% auto auto 50%;
|
|
312
303
|
inline-size: max(100%, var(--ui-control-min-block-size));
|
|
@@ -351,7 +342,6 @@ html[data-ui-input-modality="touch"] [data-component~="ui-button"]::after {
|
|
|
351
342
|
[data-component~="ui-button"][data-ui-button-state~="variant=link"]:is(:disabled, [aria-disabled="true"]) {
|
|
352
343
|
background: none;
|
|
353
344
|
border-color: transparent;
|
|
354
|
-
color: var(--ui-button-disabled-text, var(--ui-disabled-text));
|
|
355
345
|
}
|
|
356
346
|
|
|
357
347
|
[data-component~="ui-button"][data-ui-button-state~="variant=link"]:focus-visible {
|
package/vue/UiCheckbox.d.ts
CHANGED
|
@@ -6,9 +6,11 @@ type __VLS_Props = {
|
|
|
6
6
|
required?: boolean;
|
|
7
7
|
value?: string;
|
|
8
8
|
};
|
|
9
|
-
declare var __VLS_1: {};
|
|
9
|
+
declare var __VLS_1: {}, __VLS_3: {};
|
|
10
10
|
type __VLS_Slots = {} & {
|
|
11
11
|
default?: (props: typeof __VLS_1) => any;
|
|
12
|
+
} & {
|
|
13
|
+
description?: (props: typeof __VLS_3) => any;
|
|
12
14
|
};
|
|
13
15
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
14
16
|
change: (detail: {
|
package/vue/UiCheckbox.vue
CHANGED
|
@@ -38,6 +38,7 @@ const emit = defineEmits<{
|
|
|
38
38
|
|
|
39
39
|
const root = ref<HTMLElement | null>(null)
|
|
40
40
|
const inputElement = useTemplateRef<HTMLElement>('input')
|
|
41
|
+
const descriptionElement = useTemplateRef<HTMLElement>('description')
|
|
41
42
|
const internalChecked = ref(false)
|
|
42
43
|
|
|
43
44
|
/** The values the styles' :host-state() rules test. */
|
|
@@ -66,7 +67,7 @@ useComponentHost(controllerModule.default, {
|
|
|
66
67
|
root,
|
|
67
68
|
dispatch,
|
|
68
69
|
props,
|
|
69
|
-
refs: { input: inputElement },
|
|
70
|
+
refs: { input: inputElement, description: descriptionElement },
|
|
70
71
|
state: { internalChecked },
|
|
71
72
|
})
|
|
72
73
|
</script>
|
|
@@ -87,7 +88,10 @@ useComponentHost(controllerModule.default, {
|
|
|
87
88
|
:value="value"
|
|
88
89
|
ref="input"
|
|
89
90
|
>
|
|
90
|
-
<span class="label"><slot /></span
|
|
91
|
+
<span class="text"><span class="label"><slot /></span>
|
|
92
|
+
<span class="description" aria-hidden="true" ref="description"><slot
|
|
93
|
+
name="description"
|
|
94
|
+
/></span></span></label>
|
|
91
95
|
</template>
|
|
92
96
|
|
|
93
97
|
<style scoped>
|
|
@@ -100,11 +104,29 @@ useComponentHost(controllerModule.default, {
|
|
|
100
104
|
cursor: pointer;
|
|
101
105
|
}
|
|
102
106
|
|
|
107
|
+
/* The label, and under it an optional line saying what the choice means. */
|
|
108
|
+
.text {
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: column;
|
|
111
|
+
gap: var(--ui-space-1);
|
|
112
|
+
min-inline-size: 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
103
115
|
.label {
|
|
104
116
|
min-inline-size: 0;
|
|
105
117
|
line-height: var(--ui-line-height-md);
|
|
106
118
|
}
|
|
107
119
|
|
|
120
|
+
.description {
|
|
121
|
+
color: var(--ui-text-secondary);
|
|
122
|
+
font-size: var(--ui-font-size-sm);
|
|
123
|
+
line-height: var(--ui-line-height);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.description:empty {
|
|
127
|
+
display: none;
|
|
128
|
+
}
|
|
129
|
+
|
|
108
130
|
input {
|
|
109
131
|
appearance: none;
|
|
110
132
|
-webkit-appearance: none;
|