@godxjp/ui 18.15.4 → 19.0.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/dist/app/date-format-labels.d.ts +1 -1
- package/dist/app/time-format-labels.d.ts +1 -1
- package/dist/components/data-display/badge.d.ts +1 -1
- package/dist/components/data-display/card.d.ts +4 -13
- package/dist/components/data-display/card.js +2 -15
- package/dist/components/data-display/data-table.d.ts +3 -8
- package/dist/components/data-display/data-table.js +106 -68
- package/dist/components/data-display/descriptions.js +34 -5
- package/dist/components/data-display/empty-state.js +1 -1
- package/dist/components/data-display/permission-matrix.js +7 -7
- package/dist/components/data-display/popover.js +2 -9
- package/dist/components/data-display/qr-code.d.ts +1 -1
- package/dist/components/data-display/scroll-area.d.ts +3 -1
- package/dist/components/data-display/scroll-area.js +150 -16
- package/dist/components/data-display/table.js +12 -3
- package/dist/components/data-entry/branch-scope-picker.js +4 -4
- package/dist/components/data-entry/calendar.js +15 -24
- package/dist/components/data-entry/cascader.js +67 -71
- package/dist/components/data-entry/checkbox.js +9 -1
- package/dist/components/data-entry/command.d.ts +6 -6
- package/dist/components/data-entry/date-picker.js +28 -15
- package/dist/components/data-entry/date-range-picker.js +10 -10
- package/dist/components/data-entry/form-field.js +7 -3
- package/dist/components/data-entry/index.d.ts +2 -1
- package/dist/components/data-entry/input.js +8 -19
- package/dist/components/data-entry/label.js +4 -2
- package/dist/components/data-entry/month-picker.js +16 -15
- package/dist/components/data-entry/month-range-picker.js +17 -16
- package/dist/components/data-entry/radio.js +6 -1
- package/dist/components/data-entry/search-select.js +29 -29
- package/dist/components/data-entry/select.js +15 -18
- package/dist/components/data-entry/switch.js +7 -1
- package/dist/components/data-entry/textarea.d.ts +56 -0
- package/dist/components/data-entry/textarea.js +103 -40
- package/dist/components/data-entry/time-picker.js +52 -52
- package/dist/components/data-entry/toggle-group.d.ts +1 -0
- package/dist/components/data-entry/toggle.d.ts +1 -1
- package/dist/components/data-entry/transfer.js +14 -15
- package/dist/components/data-entry/tree-select.js +16 -22
- package/dist/components/data-entry/upload-crop-dialog.js +4 -4
- package/dist/components/data-entry/upload.d.ts +1 -1
- package/dist/components/data-entry/upload.js +46 -46
- package/dist/components/feedback/alert.js +7 -14
- package/dist/components/feedback/dialog.d.ts +23 -32
- package/dist/components/feedback/dialog.js +16 -13
- package/dist/components/feedback/sheet.d.ts +1 -1
- package/dist/components/feedback/sheet.js +14 -14
- package/dist/components/feedback/skeleton.js +13 -10
- package/dist/components/feedback/sonner.d.ts +1 -1
- package/dist/components/feedback/sonner.js +17 -8
- package/dist/components/feedback/tooltip.js +1 -1
- package/dist/components/general/activity.d.ts +44 -0
- package/dist/components/general/activity.js +65 -0
- package/dist/components/general/button.d.ts +2 -2
- package/dist/components/general/button.js +11 -24
- package/dist/components/general/index.d.ts +2 -0
- package/dist/components/general/index.js +2 -0
- package/dist/components/layout/app-shell.js +5 -3
- package/dist/components/layout/auth-divider.d.ts +9 -2
- package/dist/components/layout/auth-divider.js +7 -11
- package/dist/components/layout/index.d.ts +1 -0
- package/dist/components/layout/page-container.d.ts +1 -1
- package/dist/components/layout/page-container.js +32 -20
- package/dist/components/layout/resizable.d.ts +12 -12
- package/dist/components/layout/separator.d.ts +1 -0
- package/dist/components/layout/service-role-panel.js +47 -42
- package/dist/components/layout/sidebar.js +13 -1
- package/dist/components/layout/split-pane.d.ts +12 -1
- package/dist/components/layout/split-pane.js +13 -4
- package/dist/components/navigation/app-setting-picker.js +5 -14
- package/dist/components/navigation/dropdown-menu.js +10 -16
- package/dist/components/navigation/menubar.d.ts +3 -6
- package/dist/components/navigation/pagination-utils.d.ts +15 -2
- package/dist/components/navigation/pagination-utils.js +27 -21
- package/dist/components/navigation/pagination.d.ts +4 -4
- package/dist/components/navigation/pagination.js +2 -2
- package/dist/components/navigation/steps.js +31 -67
- package/dist/components/navigation/tabs.js +17 -5
- package/dist/components/query/infinite-query-state.js +2 -2
- package/dist/components/ui/accordion.d.ts +1 -1
- package/dist/components/ui/avatar.d.ts +1 -1
- package/dist/components/ui/avatar.js +29 -10
- package/dist/components/ui/input-otp.d.ts +8 -6
- package/dist/components/ui/rating.js +1 -1
- package/dist/components/ui/separator.d.ts +27 -2
- package/dist/components/ui/separator.js +42 -11
- package/dist/components/ui/toggle-group.d.ts +15 -5
- package/dist/components/ui/toggle-group.js +43 -18
- package/dist/components/ui/toggle.d.ts +66 -5
- package/dist/components/ui/toggle.js +64 -9
- package/dist/i18n/messages/en.json +8 -0
- package/dist/i18n/messages/ja.json +8 -0
- package/dist/i18n/messages/vi.json +8 -0
- package/dist/lib/control-styles.d.ts +14 -0
- package/dist/lib/control-styles.js +2 -0
- package/dist/props/components/data-display.prop.d.ts +85 -3
- package/dist/props/components/data-entry.prop.d.ts +17 -0
- package/dist/props/components/general.prop.d.ts +52 -1
- package/dist/props/components/layout.prop.d.ts +137 -2
- package/dist/props/registry.d.ts +116 -5
- package/dist/props/registry.js +165 -4
- package/dist/props/vocabulary/index.d.ts +2 -2
- package/dist/props/vocabulary/interaction.prop.d.ts +15 -0
- package/dist/props/vocabulary/layout.prop.d.ts +8 -0
- package/dist/styles/alert-layout.css +69 -3
- package/dist/styles/badge-layout.css +2 -2
- package/dist/styles/base.css +6 -0
- package/dist/styles/card-layout.css +25 -8
- package/dist/styles/control.css +1150 -29
- package/dist/styles/data-display-layout.css +185 -11
- package/dist/styles/data-entry-layout.css +352 -1
- package/dist/styles/density.css +2 -2
- package/dist/styles/dialog-layout.css +203 -4
- package/dist/styles/focus-ring.css +155 -0
- package/dist/styles/form-layout.css +50 -5
- package/dist/styles/index.css +1 -0
- package/dist/styles/layout.css +412 -23
- package/dist/styles/motion.css +178 -0
- package/dist/styles/navigation-layout.css +398 -22
- package/dist/styles/shell-layout.css +137 -39
- package/dist/styles/table-layout.css +153 -22
- package/dist/styles/toggle.css +59 -0
- package/dist/tokens/base.css +5 -0
- package/dist/tokens/components/activity.css +64 -0
- package/dist/tokens/components/badge.css +7 -0
- package/dist/tokens/components/card.css +14 -10
- package/dist/tokens/components/chart.css +1 -1
- package/dist/tokens/components/control.css +358 -8
- package/dist/tokens/components/data-display.css +75 -0
- package/dist/tokens/components/data-entry.css +25 -0
- package/dist/tokens/components/descriptions.css +22 -0
- package/dist/tokens/components/feedback.css +143 -4
- package/dist/tokens/components/form.css +16 -0
- package/dist/tokens/components/legal-document.css +5 -1
- package/dist/tokens/components/navigation.css +109 -0
- package/dist/tokens/components/scroll-area.css +21 -0
- package/dist/tokens/components/separator.css +52 -0
- package/dist/tokens/components/shell.css +132 -20
- package/dist/tokens/components/table.css +52 -3
- package/dist/tokens/components/toggle.css +82 -0
- package/dist/tokens/components/upload.css +107 -0
- package/dist/tokens/foundation.css +171 -10
- package/dist/tokens/semantic/layout.css +120 -1
- package/package.json +84 -68
- package/scripts/ui-audit.mjs +51 -2
package/dist/styles/control.css
CHANGED
|
@@ -54,11 +54,6 @@
|
|
|
54
54
|
color: hsl(var(--primary-foreground));
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
.ui-toggle:focus-visible {
|
|
58
|
-
outline: none;
|
|
59
|
-
box-shadow: 0 0 0 3px hsl(var(--focus-ring-color, var(--ring)) / 0.35);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
57
|
.ui-toggle:disabled,
|
|
63
58
|
.ui-toggle[data-disabled] {
|
|
64
59
|
cursor: not-allowed;
|
|
@@ -83,14 +78,19 @@
|
|
|
83
78
|
transition-duration: 150ms;
|
|
84
79
|
}
|
|
85
80
|
|
|
86
|
-
.ui-button
|
|
81
|
+
.ui-button {
|
|
82
|
+
flex-shrink: 0;
|
|
83
|
+
font-size: var(--font-size-sm);
|
|
87
84
|
outline: none;
|
|
88
|
-
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color, var(--ring)));
|
|
89
85
|
}
|
|
90
86
|
|
|
91
|
-
.ui-button:disabled
|
|
87
|
+
.ui-button:disabled,
|
|
88
|
+
.ui-button[data-loading] {
|
|
92
89
|
pointer-events: none;
|
|
93
|
-
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.ui-button:disabled {
|
|
93
|
+
opacity: var(--disabled-opacity);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
.ui-button svg {
|
|
@@ -249,13 +249,95 @@
|
|
|
249
249
|
|
|
250
250
|
.ui-control-multiline {
|
|
251
251
|
min-height: calc(var(--control-height) * 2.75);
|
|
252
|
-
padding: var(--
|
|
252
|
+
padding-block: var(--control-multiline-padding-block);
|
|
253
|
+
padding-inline: var(--control-padding-x);
|
|
253
254
|
font-size: var(--control-font-size);
|
|
254
255
|
border-width: var(--control-border-width);
|
|
255
256
|
border-style: solid;
|
|
256
257
|
box-shadow: var(--control-shadow);
|
|
257
258
|
}
|
|
258
259
|
|
|
260
|
+
/* TEXTAREA AUTO-GROW (gh#310) — the box grows with its content, bounded in TEXT ROWS.
|
|
261
|
+
*
|
|
262
|
+
* TECHNIQUE: the replicated-content grid. The wrapper is a one-cell grid; a hidden `::after`
|
|
263
|
+
* carrying the same text in the same typography occupies that cell and SIZES the row, and the
|
|
264
|
+
* textarea (a grid item, `align-self: stretch` by default) fills whatever the row became. The
|
|
265
|
+
* JS side therefore only ever WRITES one attribute — it never reads `scrollHeight`, never sets
|
|
266
|
+
* `style.height`, and so never forces a synchronous reflow per keystroke, and never touches
|
|
267
|
+
* `scrollTop` (which is what makes typing in the middle of a long value jump).
|
|
268
|
+
*
|
|
269
|
+
* Both bounds live on the PSEUDO-ELEMENT rather than on the textarea, deliberately: a
|
|
270
|
+
* pseudo-element is out of reach of Tailwind utilities, so `variant="ghost"`'s `min-h-0`
|
|
271
|
+
* (@layer utilities, which beats anything here in @layer components) cannot silently defeat
|
|
272
|
+
* the floor the way it would if the bounds sat on the control itself. */
|
|
273
|
+
.ui-textarea-autogrow {
|
|
274
|
+
position: relative;
|
|
275
|
+
display: grid;
|
|
276
|
+
/* `minmax(0, 1fr)`, never the default `auto`: an auto column is sized by the replica's
|
|
277
|
+
* MAX-CONTENT, so a single unbroken 2,000-character token would widen the column — and with
|
|
278
|
+
* it the control — to 18,000px instead of wrapping. Measured, not assumed. */
|
|
279
|
+
grid-template-columns: minmax(0, 1fr);
|
|
280
|
+
inline-size: 100%;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.ui-textarea-autogrow > .ui-control-multiline,
|
|
284
|
+
.ui-textarea-autogrow::after {
|
|
285
|
+
grid-area: 1 / 1 / 2 / 2;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.ui-textarea-autogrow::after {
|
|
289
|
+
content: attr(data-autogrow-value) " ";
|
|
290
|
+
visibility: hidden;
|
|
291
|
+
/* Explicit, not inherited from preflight: the replica's bounds are BORDER-box measures, and a
|
|
292
|
+
* content-box replica overshoots every height by the box inset. */
|
|
293
|
+
box-sizing: border-box;
|
|
294
|
+
margin: 0;
|
|
295
|
+
/* `pre-wrap` + `break-word` is the UA treatment of a textarea's own text: newlines are kept,
|
|
296
|
+
* an unbroken 2,000-character token wraps instead of widening the box. The trailing " " is
|
|
297
|
+
* what keeps a value ending in a newline one row taller, exactly as the control renders it. */
|
|
298
|
+
white-space: pre-wrap;
|
|
299
|
+
overflow-wrap: break-word;
|
|
300
|
+
font-size: var(--control-font-size);
|
|
301
|
+
line-height: var(--textarea-autogrow-line-height, var(--line-height-normal));
|
|
302
|
+
padding-block: var(--control-multiline-padding-block);
|
|
303
|
+
padding-inline: var(--control-padding-x);
|
|
304
|
+
border: var(--control-border-width) solid transparent;
|
|
305
|
+
/* The floor never undercuts the control tier, so a resting one-row composer still lines up
|
|
306
|
+
* with the Input / Button on its row; above it the box counts in rows. */
|
|
307
|
+
min-block-size: max(
|
|
308
|
+
var(--control-height),
|
|
309
|
+
calc(
|
|
310
|
+
var(--textarea-autogrow-min-height-rows) *
|
|
311
|
+
var(--textarea-autogrow-line-height, var(--line-height-normal)) *
|
|
312
|
+
var(--control-font-size) + var(--textarea-autogrow-box-inset)
|
|
313
|
+
)
|
|
314
|
+
);
|
|
315
|
+
max-block-size: calc(
|
|
316
|
+
var(--textarea-autogrow-max-height-rows) *
|
|
317
|
+
var(--textarea-autogrow-line-height, var(--line-height-normal)) * var(--control-font-size) +
|
|
318
|
+
var(--textarea-autogrow-box-inset)
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/* The ghost variant draws no border, so the row maths must not add one back. */
|
|
323
|
+
.ui-textarea-autogrow--ghost {
|
|
324
|
+
--textarea-autogrow-box-inset: calc(var(--control-multiline-padding-block) * 2);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.ui-textarea-autogrow--ghost::after {
|
|
328
|
+
border-width: 0;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.ui-textarea-autogrow > .ui-control-multiline {
|
|
332
|
+
/* The mirror owns the floor; the control must be free to stretch down to it. */
|
|
333
|
+
min-block-size: 0;
|
|
334
|
+
/* Growth is the box's job, not a drag handle's, and past the ceiling the control scrolls
|
|
335
|
+
* itself rather than pushing the page. */
|
|
336
|
+
resize: none;
|
|
337
|
+
overflow-y: auto;
|
|
338
|
+
line-height: var(--textarea-autogrow-line-height, var(--line-height-normal));
|
|
339
|
+
}
|
|
340
|
+
|
|
259
341
|
.ui-table-row {
|
|
260
342
|
height: var(--table-row-height);
|
|
261
343
|
}
|
|
@@ -309,6 +391,36 @@
|
|
|
309
391
|
font-size: var(--choice-description-font-size);
|
|
310
392
|
}
|
|
311
393
|
|
|
394
|
+
/* Label primitive (#319). Its box and type were Tailwind literals on label.tsx
|
|
395
|
+
* (`flex items-center gap-2 text-sm leading-none select-none`), so a service theme could not
|
|
396
|
+
* retune the label type ramp or the label→addon gap without forking the component.
|
|
397
|
+
*
|
|
398
|
+
* SOURCE ORDER IS LOAD-BEARING — this rule MUST stay AFTER `.ui-choice-label` above.
|
|
399
|
+
* `Field` renders `<Label className="ui-choice-label">`, so that element carries both classes at
|
|
400
|
+
* equal specificity (0,1,0) in the same layer and the LATER rule wins. Today the `text-sm`
|
|
401
|
+
* utility out-ranks `.ui-choice-label`'s `font-size: var(--font-size-base)` outright (utilities
|
|
402
|
+
* are layered after components); putting `.ui-label` EARLIER would hand every checkbox/radio
|
|
403
|
+
* label back to `.ui-choice-label` and silently change its type. `font-weight` is deliberately
|
|
404
|
+
* NOT declared here for the same reason: `.ui-choice-label` sets `--font-weight-normal` and
|
|
405
|
+
* Label's `font-medium` utility beats it today, so `font-medium` stays a utility.
|
|
406
|
+
*
|
|
407
|
+
* The disabled states stay utilities too (`peer-disabled:` / `group-data-[disabled=true]:`):
|
|
408
|
+
* a state must out-rank the resting rule, which in Tailwind v4 means the utilities layer. So
|
|
409
|
+
* does `line-height` — the `leading-none` it replaces existed precisely to beat the companion
|
|
410
|
+
* line-height that every `text-*` utility carries, and only a utility can still do that when a
|
|
411
|
+
* consumer passes their own `text-lg`. Its value is --control-label-line-height all the same.
|
|
412
|
+
*
|
|
413
|
+
* Not a regression for the auth shell: the canonical field-label override in shell-layout.css is
|
|
414
|
+
* INTENTIONALLY UNLAYERED (gh#263) and unlayered author CSS still beats every layer, so it keeps
|
|
415
|
+
* winning over these declarations exactly as it won over the utilities. */
|
|
416
|
+
.ui-label {
|
|
417
|
+
display: flex;
|
|
418
|
+
align-items: center;
|
|
419
|
+
gap: var(--control-label-space-gap);
|
|
420
|
+
font-size: var(--control-label-font-size);
|
|
421
|
+
user-select: none;
|
|
422
|
+
}
|
|
423
|
+
|
|
312
424
|
.ui-checkbox,
|
|
313
425
|
.ui-radio {
|
|
314
426
|
width: var(--checkbox-size);
|
|
@@ -327,20 +439,19 @@
|
|
|
327
439
|
border-radius: var(--radius-pill);
|
|
328
440
|
}
|
|
329
441
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
outline: none;
|
|
335
|
-
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color, var(--ring)));
|
|
336
|
-
}
|
|
337
|
-
|
|
442
|
+
/* `:disabled` only matches form elements, so a purely visual checkbox rendered as a <span>
|
|
443
|
+
* (Cascader's CheckboxVisual, Transfer's row marks) never picked this up and had to repeat
|
|
444
|
+
* `cursor-not-allowed opacity-50` on the component instead (#319). Matching [data-disabled]
|
|
445
|
+
* too lets those share the one rule. The alpha is the system token, not a literal 0.5. */
|
|
338
446
|
.ui-checkbox:disabled,
|
|
339
447
|
.ui-radio:disabled,
|
|
340
448
|
.ui-slider-thumb:disabled,
|
|
341
|
-
.ui-switch:disabled
|
|
449
|
+
.ui-switch:disabled,
|
|
450
|
+
.ui-checkbox[data-disabled],
|
|
451
|
+
.ui-radio[data-disabled],
|
|
452
|
+
.ui-switch[data-disabled] {
|
|
342
453
|
cursor: not-allowed;
|
|
343
|
-
opacity:
|
|
454
|
+
opacity: var(--disabled-opacity);
|
|
344
455
|
}
|
|
345
456
|
|
|
346
457
|
.ui-checkbox[data-state="checked"] {
|
|
@@ -385,7 +496,7 @@
|
|
|
385
496
|
}
|
|
386
497
|
|
|
387
498
|
.ui-switch[data-state="unchecked"] {
|
|
388
|
-
background: hsl(var(--input));
|
|
499
|
+
background: var(--switch-unchecked-background, hsl(var(--input)));
|
|
389
500
|
}
|
|
390
501
|
|
|
391
502
|
.ui-switch-thumb {
|
|
@@ -534,8 +645,6 @@
|
|
|
534
645
|
}
|
|
535
646
|
|
|
536
647
|
.ui-command-input:focus-visible {
|
|
537
|
-
box-shadow: none;
|
|
538
|
-
outline: none;
|
|
539
648
|
}
|
|
540
649
|
|
|
541
650
|
.ui-command-list {
|
|
@@ -806,8 +915,8 @@
|
|
|
806
915
|
justify-content: center;
|
|
807
916
|
}
|
|
808
917
|
.ui-otp-caret {
|
|
809
|
-
width:
|
|
810
|
-
height:
|
|
918
|
+
width: var(--otp-caret-inline-size);
|
|
919
|
+
height: var(--otp-caret-block-size);
|
|
811
920
|
background: hsl(var(--foreground));
|
|
812
921
|
animation: ui-otp-blink 1s step-end infinite;
|
|
813
922
|
}
|
|
@@ -817,8 +926,8 @@
|
|
|
817
926
|
}
|
|
818
927
|
}
|
|
819
928
|
.ui-otp-separator-icon {
|
|
820
|
-
width:
|
|
821
|
-
height:
|
|
929
|
+
width: var(--otp-separator-icon-size);
|
|
930
|
+
height: var(--otp-separator-icon-size);
|
|
822
931
|
color: hsl(var(--muted-foreground));
|
|
823
932
|
}
|
|
824
933
|
|
|
@@ -861,8 +970,6 @@
|
|
|
861
970
|
fill: currentColor;
|
|
862
971
|
}
|
|
863
972
|
.ui-rating-star:focus-visible {
|
|
864
|
-
outline: var(--focus-ring-width) solid hsl(var(--focus-ring-color, var(--ring)));
|
|
865
|
-
outline-offset: 2px;
|
|
866
973
|
border-radius: var(--radius-sm);
|
|
867
974
|
}
|
|
868
975
|
|
|
@@ -1003,3 +1110,1017 @@
|
|
|
1003
1110
|
inline-size: 0.75rem;
|
|
1004
1111
|
block-size: 0.75rem;
|
|
1005
1112
|
}
|
|
1113
|
+
|
|
1114
|
+
@layer components {
|
|
1115
|
+
/* SELECT-FAMILY TRAILING AFFIX — the clear ✕ / chevron overlay parked at a trigger's inline
|
|
1116
|
+
* end. Select, SearchSelect and TagInput each repeated the same literal stack (#319); these
|
|
1117
|
+
* are the one shared set of knobs. */
|
|
1118
|
+
.ui-control-affix {
|
|
1119
|
+
position: absolute;
|
|
1120
|
+
inset-block: 0;
|
|
1121
|
+
inset-inline-end: var(--control-affix-inset-inline-end);
|
|
1122
|
+
display: flex;
|
|
1123
|
+
align-items: center;
|
|
1124
|
+
pointer-events: none;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
.ui-control-affix-action {
|
|
1128
|
+
pointer-events: auto;
|
|
1129
|
+
display: flex;
|
|
1130
|
+
inline-size: var(--control-affix-action-size);
|
|
1131
|
+
block-size: var(--control-affix-action-size);
|
|
1132
|
+
align-items: center;
|
|
1133
|
+
justify-content: center;
|
|
1134
|
+
border-radius: var(--control-affix-action-radius);
|
|
1135
|
+
opacity: var(--control-affix-rest-alpha);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
.ui-control-affix-action:hover,
|
|
1139
|
+
.ui-control-affix-action:focus-visible {
|
|
1140
|
+
opacity: 1;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
.ui-control-affix-icon {
|
|
1144
|
+
inline-size: var(--control-affix-icon-size);
|
|
1145
|
+
block-size: var(--control-affix-icon-size);
|
|
1146
|
+
flex-shrink: 0;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
/* The chevron is decorative and never interactive, so it keeps the resting weight. */
|
|
1150
|
+
.ui-control-affix-indicator {
|
|
1151
|
+
opacity: var(--control-affix-rest-alpha);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
.ui-control-trigger-affixed {
|
|
1155
|
+
padding-inline-end: var(--control-trigger-space-inline-end);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
/* SEARCH-SELECT — Popover + Command combobox. */
|
|
1159
|
+
.ui-search-select-panel {
|
|
1160
|
+
display: flex;
|
|
1161
|
+
flex-direction: column;
|
|
1162
|
+
max-block-size: var(--radix-popover-content-available-height);
|
|
1163
|
+
inline-size: max-content;
|
|
1164
|
+
max-inline-size: min(
|
|
1165
|
+
var(--search-select-panel-max-width),
|
|
1166
|
+
calc(100vw - var(--search-select-panel-viewport-inset))
|
|
1167
|
+
);
|
|
1168
|
+
min-inline-size: var(--radix-popover-trigger-width);
|
|
1169
|
+
padding: 0;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
.ui-search-select-command {
|
|
1173
|
+
display: flex;
|
|
1174
|
+
min-block-size: 0;
|
|
1175
|
+
flex-direction: column;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
.ui-search-select-search {
|
|
1179
|
+
flex-shrink: 0;
|
|
1180
|
+
border-block-end: 1px solid hsl(var(--border));
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
/* The search field is the panel's own header, so it drops the standalone input chrome. */
|
|
1184
|
+
.ui-search-select-search-input {
|
|
1185
|
+
border: 0;
|
|
1186
|
+
border-radius: 0;
|
|
1187
|
+
box-shadow: none;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
/* The panel itself is the focused surface, so the inner search field suppresses its own RING
|
|
1191
|
+
* (this replaces a `focus-visible:ring-0` utility). It must NOT suppress the outline as well —
|
|
1192
|
+
* that would leave keyboard users with no focus affordance at all inside the panel. */
|
|
1193
|
+
.ui-search-select-search-input:focus-visible {
|
|
1194
|
+
box-shadow: none;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
.ui-search-select-list {
|
|
1198
|
+
max-block-size: none;
|
|
1199
|
+
min-block-size: 0;
|
|
1200
|
+
flex: 1 1 0%;
|
|
1201
|
+
overflow-y: auto;
|
|
1202
|
+
padding: var(--search-select-list-space-inset);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
/* The consumer's renderOption output — it must be able to truncate inside the row, which is
|
|
1206
|
+
* what min-inline-size:0 buys in a flex child. */
|
|
1207
|
+
.ui-search-select-option-slot {
|
|
1208
|
+
min-inline-size: 0;
|
|
1209
|
+
flex: 1 1 0%;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
.ui-search-select-option-body {
|
|
1213
|
+
display: flex;
|
|
1214
|
+
min-inline-size: 0;
|
|
1215
|
+
flex: 1 1 0%;
|
|
1216
|
+
align-items: center;
|
|
1217
|
+
gap: var(--search-select-option-space-gap);
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
.ui-search-select-option-text {
|
|
1221
|
+
display: flex;
|
|
1222
|
+
min-inline-size: 0;
|
|
1223
|
+
flex-direction: column;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
.ui-search-select-option-label {
|
|
1227
|
+
overflow: hidden;
|
|
1228
|
+
text-overflow: ellipsis;
|
|
1229
|
+
white-space: nowrap;
|
|
1230
|
+
font-size: var(--search-select-option-font-size);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
.ui-search-select-option-sublabel {
|
|
1234
|
+
overflow: hidden;
|
|
1235
|
+
text-overflow: ellipsis;
|
|
1236
|
+
white-space: nowrap;
|
|
1237
|
+
font-size: var(--search-select-option-sublabel-font-size);
|
|
1238
|
+
color: hsl(var(--muted-foreground));
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
.ui-search-select-status {
|
|
1242
|
+
display: flex;
|
|
1243
|
+
align-items: center;
|
|
1244
|
+
gap: var(--search-select-option-space-gap);
|
|
1245
|
+
padding-inline: var(--search-select-status-space-inline);
|
|
1246
|
+
padding-block: var(--search-select-status-space-block);
|
|
1247
|
+
font-size: var(--search-select-status-font-size);
|
|
1248
|
+
color: hsl(var(--muted-foreground));
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
.ui-search-select-spinner {
|
|
1252
|
+
inline-size: var(--search-select-spinner-size);
|
|
1253
|
+
block-size: var(--search-select-spinner-size);
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
/* Empty / error occupy the panel alone, so they get a taller centred block. */
|
|
1257
|
+
.ui-search-select-placeholder {
|
|
1258
|
+
padding-inline: var(--search-select-status-space-inline);
|
|
1259
|
+
padding-block: var(--search-select-placeholder-space-block);
|
|
1260
|
+
text-align: center;
|
|
1261
|
+
font-size: var(--search-select-status-font-size);
|
|
1262
|
+
color: hsl(var(--muted-foreground));
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
.ui-search-select-placeholder[data-tone="destructive"] {
|
|
1266
|
+
color: hsl(var(--destructive));
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
.ui-search-select-footer {
|
|
1270
|
+
flex-shrink: 0;
|
|
1271
|
+
padding: var(--search-select-footer-space-inset);
|
|
1272
|
+
border-block-start: 1px solid hsl(var(--border));
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
@layer components {
|
|
1277
|
+
/* CASCADER — column browser (desktop) + flat search list (query mode). */
|
|
1278
|
+
.ui-cascader-columns {
|
|
1279
|
+
display: flex;
|
|
1280
|
+
max-block-size: var(--cascader-columns-max-height);
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
.ui-cascader-column {
|
|
1284
|
+
min-inline-size: var(--cascader-column-min-width);
|
|
1285
|
+
border-inline-end: 1px solid hsl(var(--border));
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
.ui-cascader-column:last-child {
|
|
1289
|
+
border-inline-end: 0;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
.ui-cascader-option {
|
|
1293
|
+
display: flex;
|
|
1294
|
+
inline-size: 100%;
|
|
1295
|
+
align-items: center;
|
|
1296
|
+
gap: var(--cascader-option-space-gap);
|
|
1297
|
+
padding-inline: var(--cascader-option-space-inline);
|
|
1298
|
+
padding-block: var(--cascader-option-space-block);
|
|
1299
|
+
font-size: var(--cascader-option-font-size);
|
|
1300
|
+
outline: none;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
.ui-cascader-option-icon {
|
|
1304
|
+
margin-inline-end: var(--cascader-option-space-gap);
|
|
1305
|
+
inline-size: var(--cascader-option-icon-size);
|
|
1306
|
+
block-size: var(--cascader-option-icon-size);
|
|
1307
|
+
flex-shrink: 0;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
/* The chevron marks "has children"; it is decorative, so it keeps the resting weight. */
|
|
1311
|
+
.ui-cascader-option-chevron {
|
|
1312
|
+
inline-size: var(--cascader-option-icon-size);
|
|
1313
|
+
block-size: var(--cascader-option-icon-size);
|
|
1314
|
+
flex-shrink: 0;
|
|
1315
|
+
opacity: var(--control-affix-rest-alpha);
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
.ui-cascader-list {
|
|
1319
|
+
max-block-size: var(--cascader-list-max-height);
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
.ui-cascader-panel {
|
|
1323
|
+
padding: var(--cascader-panel-space-inset);
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
.ui-cascader-popover {
|
|
1327
|
+
inline-size: auto;
|
|
1328
|
+
min-inline-size: var(--radix-popover-trigger-width);
|
|
1329
|
+
padding: 0;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
.ui-cascader-empty {
|
|
1333
|
+
padding-block: var(--cascader-empty-space-block);
|
|
1334
|
+
text-align: center;
|
|
1335
|
+
font-size: var(--cascader-option-font-size);
|
|
1336
|
+
color: hsl(var(--muted-foreground));
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
/* Flat search result row — denser than a column row (no chevron to balance). */
|
|
1340
|
+
.ui-cascader-result {
|
|
1341
|
+
display: flex;
|
|
1342
|
+
inline-size: 100%;
|
|
1343
|
+
align-items: center;
|
|
1344
|
+
border-radius: var(--cascader-result-radius);
|
|
1345
|
+
padding-inline: var(--cascader-result-space-inline);
|
|
1346
|
+
padding-block: var(--cascader-result-space-block);
|
|
1347
|
+
font-size: var(--cascader-option-font-size);
|
|
1348
|
+
outline: none;
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
.ui-cascader-result-icon {
|
|
1352
|
+
margin-inline-end: var(--cascader-result-icon-space-inline-end);
|
|
1353
|
+
inline-size: var(--cascader-option-icon-size);
|
|
1354
|
+
block-size: var(--cascader-option-icon-size);
|
|
1355
|
+
flex-shrink: 0;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
/* Reserve the check column even when unselected so labels do not jump on select. */
|
|
1359
|
+
.ui-cascader-result-icon[data-selected="false"] {
|
|
1360
|
+
opacity: 0;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
/* Cascader parks a SMALLER affix closer to the edge than SearchSelect does. Same shared knobs,
|
|
1364
|
+
* component-scoped values — the vocabulary is shared, the calibration is not. */
|
|
1365
|
+
.ui-cascader-affix {
|
|
1366
|
+
--control-affix-inset-inline-end: var(--space-3);
|
|
1367
|
+
--control-affix-action-size: var(--control-affix-icon-size);
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
/* Disabled option rows keep pointer-events off and take the one system alpha. */
|
|
1371
|
+
.ui-cascader-option[data-disabled],
|
|
1372
|
+
.ui-cascader-trigger[data-disabled] {
|
|
1373
|
+
pointer-events: none;
|
|
1374
|
+
opacity: var(--disabled-opacity);
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
@layer components {
|
|
1379
|
+
/* SELECT — the Radix listbox popup. Its row rhythm, separator and label come from the shared
|
|
1380
|
+
* menu rules in navigation-layout.css (the same surface ContextMenu/Menubar/DropdownMenu use);
|
|
1381
|
+
* only what is genuinely Select's own lives here (#319). */
|
|
1382
|
+
.ui-select-content {
|
|
1383
|
+
position: relative;
|
|
1384
|
+
z-index: var(--overlay-z-index);
|
|
1385
|
+
max-height: var(--select-content-max-height);
|
|
1386
|
+
min-width: var(--menu-content-min-width);
|
|
1387
|
+
overflow: hidden;
|
|
1388
|
+
border-radius: var(--radius-md);
|
|
1389
|
+
border: 1px solid hsl(var(--border));
|
|
1390
|
+
background: hsl(var(--popover));
|
|
1391
|
+
color: hsl(var(--popover-foreground));
|
|
1392
|
+
box-shadow: var(--shadow-md);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
.ui-select-viewport {
|
|
1396
|
+
padding: var(--menu-content-space-inset);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
/* A listbox row has no leading icon column, so it takes a slightly wider inline inset to read
|
|
1400
|
+
* level with a menu row that does. */
|
|
1401
|
+
.ui-select-item {
|
|
1402
|
+
position: relative;
|
|
1403
|
+
inline-size: 100%;
|
|
1404
|
+
cursor: default;
|
|
1405
|
+
padding-inline: var(--select-item-space-inline);
|
|
1406
|
+
outline: none;
|
|
1407
|
+
user-select: none;
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
.ui-select-item[data-state="checked"] {
|
|
1411
|
+
background: hsl(var(--accent));
|
|
1412
|
+
color: hsl(var(--foreground));
|
|
1413
|
+
font-weight: var(--font-weight-medium);
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
.ui-select-item[data-highlighted] {
|
|
1417
|
+
background: hsl(var(--accent));
|
|
1418
|
+
color: hsl(var(--accent-foreground));
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
.ui-select-item[data-disabled] {
|
|
1422
|
+
pointer-events: none;
|
|
1423
|
+
opacity: var(--disabled-opacity);
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
.ui-select-item svg {
|
|
1427
|
+
pointer-events: none;
|
|
1428
|
+
flex-shrink: 0;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
.ui-select-label {
|
|
1432
|
+
font-weight: var(--font-weight-medium);
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
.ui-select-scroll-button {
|
|
1436
|
+
display: flex;
|
|
1437
|
+
cursor: default;
|
|
1438
|
+
align-items: center;
|
|
1439
|
+
justify-content: center;
|
|
1440
|
+
padding-block: var(--select-scroll-button-space-block);
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
.ui-select-scroll-icon,
|
|
1444
|
+
.ui-select-chevron {
|
|
1445
|
+
inline-size: var(--control-affix-icon-size);
|
|
1446
|
+
block-size: var(--control-affix-icon-size);
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
.ui-select-chevron {
|
|
1450
|
+
flex-shrink: 0;
|
|
1451
|
+
opacity: var(--control-affix-rest-alpha);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
@layer components {
|
|
1456
|
+
/* TIME PICKER — hour/minute/second scroll columns + an inline affix pair. */
|
|
1457
|
+
.ui-time-picker-panel {
|
|
1458
|
+
inline-size: var(--time-picker-panel-width);
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
.ui-time-picker-panel[data-hour-cycle="h12"] {
|
|
1462
|
+
inline-size: var(--time-picker-panel-width-12h);
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
.ui-time-picker-column {
|
|
1466
|
+
display: flex;
|
|
1467
|
+
min-inline-size: 0;
|
|
1468
|
+
flex: 1 1 0%;
|
|
1469
|
+
flex-direction: column;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
.ui-time-picker-column-heading {
|
|
1473
|
+
padding-inline: var(--time-picker-heading-space-inline);
|
|
1474
|
+
padding-block: var(--time-picker-heading-space-block);
|
|
1475
|
+
border-block-end: 1px solid hsl(var(--border));
|
|
1476
|
+
text-align: center;
|
|
1477
|
+
font-size: var(--time-picker-heading-font-size);
|
|
1478
|
+
font-weight: var(--font-weight-medium);
|
|
1479
|
+
color: hsl(var(--muted-foreground));
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
.ui-time-picker-column-scroll {
|
|
1483
|
+
block-size: var(--time-picker-column-height);
|
|
1484
|
+
padding: var(--time-picker-column-space-inset);
|
|
1485
|
+
overflow-y: scroll;
|
|
1486
|
+
overscroll-behavior: contain;
|
|
1487
|
+
scrollbar-width: thin;
|
|
1488
|
+
scrollbar-gutter: stable;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
.ui-time-picker-option {
|
|
1492
|
+
display: flex;
|
|
1493
|
+
inline-size: 100%;
|
|
1494
|
+
align-items: center;
|
|
1495
|
+
justify-content: center;
|
|
1496
|
+
border-radius: var(--time-picker-option-radius);
|
|
1497
|
+
padding-block: var(--time-picker-option-space-block);
|
|
1498
|
+
font-size: var(--time-picker-option-font-size);
|
|
1499
|
+
font-variant-numeric: tabular-nums;
|
|
1500
|
+
transition: background-color 150ms ease;
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
.ui-time-picker-option:hover {
|
|
1504
|
+
background: hsl(var(--accent));
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
.ui-time-picker-option[data-selected="true"] {
|
|
1508
|
+
background: hsl(var(--primary));
|
|
1509
|
+
color: hsl(var(--primary-foreground));
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
.ui-time-picker-option[data-selected="true"]:hover {
|
|
1513
|
+
background: hsl(var(--primary) / 0.9);
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
.ui-time-picker-footer {
|
|
1517
|
+
padding: var(--time-picker-footer-space-inset);
|
|
1518
|
+
border-block-start: 1px solid hsl(var(--border));
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
.ui-time-picker-popover {
|
|
1522
|
+
inline-size: auto;
|
|
1523
|
+
padding: 0;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
/* Two affix controls (clear + clock), so a wider reserved end than the single-affix triggers. */
|
|
1527
|
+
/* A popover whose content owns its own padding (a Calendar, a month grid) — the panel just
|
|
1528
|
+
* hugs it. Shared by the date/time pickers. */
|
|
1529
|
+
.ui-control-panel-flush {
|
|
1530
|
+
inline-size: auto;
|
|
1531
|
+
padding: 0;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
.ui-control-inline-affix-pair-affixed {
|
|
1535
|
+
padding-inline-end: var(--control-inline-affix-pair-space-inline-end);
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
.ui-time-picker-affix {
|
|
1539
|
+
display: inline-flex;
|
|
1540
|
+
align-items: center;
|
|
1541
|
+
gap: var(--time-picker-affix-space-gap);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
/* Sits INSIDE the field rather than overlaying it, so it rests a touch heavier than the
|
|
1545
|
+
* overlay affix Select/SearchSelect use. Shared by Input and TimePicker. */
|
|
1546
|
+
.ui-control-inline-affix-action {
|
|
1547
|
+
display: inline-flex;
|
|
1548
|
+
inline-size: var(--control-inline-affix-size);
|
|
1549
|
+
block-size: var(--control-inline-affix-size);
|
|
1550
|
+
align-items: center;
|
|
1551
|
+
justify-content: center;
|
|
1552
|
+
border-radius: var(--control-affix-action-radius);
|
|
1553
|
+
color: hsl(var(--muted-foreground));
|
|
1554
|
+
opacity: var(--control-inline-affix-rest-alpha);
|
|
1555
|
+
transition: opacity 150ms ease;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
.ui-control-inline-affix-action:hover,
|
|
1559
|
+
.ui-control-inline-affix-action:focus-visible {
|
|
1560
|
+
color: hsl(var(--foreground));
|
|
1561
|
+
opacity: 1;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
.ui-control-inline-affix-icon {
|
|
1565
|
+
inline-size: var(--control-inline-affix-icon-size);
|
|
1566
|
+
block-size: var(--control-inline-affix-icon-size);
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
/* The browser-owned file button, restyled to read as a control inside the control. */
|
|
1570
|
+
.ui-input::file-selector-button {
|
|
1571
|
+
display: inline-flex;
|
|
1572
|
+
block-size: var(--input-file-button-height);
|
|
1573
|
+
border: 0;
|
|
1574
|
+
background: transparent;
|
|
1575
|
+
color: hsl(var(--foreground));
|
|
1576
|
+
font-size: var(--input-file-button-font-size);
|
|
1577
|
+
font-weight: var(--font-weight-medium);
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
/* A field in a flex row must be allowed to shrink below its content, or a long value pushes
|
|
1581
|
+
* the row wider instead of the text truncating. */
|
|
1582
|
+
.ui-input {
|
|
1583
|
+
min-inline-size: 0;
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
.ui-input:disabled {
|
|
1587
|
+
pointer-events: none;
|
|
1588
|
+
cursor: not-allowed;
|
|
1589
|
+
opacity: var(--disabled-opacity);
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
/* INPUT affix slots — leading/trailing controls parked inside the field box. */
|
|
1593
|
+
.ui-input-affix-wrapper {
|
|
1594
|
+
position: relative;
|
|
1595
|
+
display: inline-flex;
|
|
1596
|
+
inline-size: 100%;
|
|
1597
|
+
align-items: center;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
.ui-input-leading,
|
|
1601
|
+
.ui-input-trailing {
|
|
1602
|
+
position: absolute;
|
|
1603
|
+
inset-block: 0;
|
|
1604
|
+
display: inline-flex;
|
|
1605
|
+
align-items: center;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
.ui-input-leading {
|
|
1609
|
+
inset-inline-start: var(--control-inline-affix-inset-inline);
|
|
1610
|
+
pointer-events: none;
|
|
1611
|
+
color: hsl(var(--muted-foreground));
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
.ui-input-trailing {
|
|
1615
|
+
inset-inline-end: var(--control-inline-affix-inset-inline);
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
/* A textarea has no single line to centre an affix on, so its clear control parks at the
|
|
1619
|
+
* top-end corner. Everything else — box, radius, resting alpha, hover — is the shared
|
|
1620
|
+
* inside-field affix it borrows from Input. */
|
|
1621
|
+
.ui-textarea-clear {
|
|
1622
|
+
position: absolute;
|
|
1623
|
+
inset-block-start: var(--textarea-clear-inset-block-start);
|
|
1624
|
+
inset-inline-end: var(--control-inline-affix-inset-inline);
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
/* The field reserves room on whichever side carries an affix, so its text never runs under. */
|
|
1628
|
+
.ui-input--leading-affix {
|
|
1629
|
+
padding-inline-start: var(--control-inline-affix-space-inline-end);
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
.ui-input--trailing-affix {
|
|
1633
|
+
padding-inline-end: var(--control-inline-affix-space-inline-end);
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
@layer components {
|
|
1638
|
+
/* TREE SELECT — a Select whose panel holds a tree. */
|
|
1639
|
+
.ui-tree-select-popover {
|
|
1640
|
+
inline-size: var(--radix-popover-trigger-width);
|
|
1641
|
+
padding: 0;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
.ui-tree-select-list {
|
|
1645
|
+
max-block-size: var(--tree-select-list-max-height);
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
.ui-tree-select-panel {
|
|
1649
|
+
padding: var(--menu-content-space-inset);
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
.ui-tree-select-empty {
|
|
1653
|
+
padding-block: var(--tree-select-empty-space-block);
|
|
1654
|
+
text-align: center;
|
|
1655
|
+
font-size: var(--tree-select-row-font-size);
|
|
1656
|
+
color: hsl(var(--muted-foreground));
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
/* Indent is base + depth × step, driven by `--tree-select-depth` on the row. It used to be a
|
|
1660
|
+
* magic expression inline in JSX, so no theme could reach it. */
|
|
1661
|
+
.ui-tree-select-row {
|
|
1662
|
+
display: flex;
|
|
1663
|
+
align-items: center;
|
|
1664
|
+
border-radius: var(--tree-select-row-radius);
|
|
1665
|
+
padding-block: var(--tree-select-row-space-block);
|
|
1666
|
+
padding-inline-end: var(--tree-select-row-space-inline-end);
|
|
1667
|
+
padding-inline-start: calc(
|
|
1668
|
+
var(--tree-select-depth-space-base) + var(--tree-select-depth, 0) *
|
|
1669
|
+
var(--tree-select-depth-space-step)
|
|
1670
|
+
);
|
|
1671
|
+
font-size: var(--tree-select-row-font-size);
|
|
1672
|
+
outline: none;
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
.ui-tree-select-row:hover {
|
|
1676
|
+
background: hsl(var(--accent));
|
|
1677
|
+
color: hsl(var(--accent-foreground));
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
.ui-tree-select-row[data-selected="true"] {
|
|
1681
|
+
background: hsl(var(--accent) / 0.6);
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
.ui-tree-select-row[data-disabled] {
|
|
1685
|
+
pointer-events: none;
|
|
1686
|
+
opacity: var(--disabled-opacity);
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
.ui-tree-select-toggle {
|
|
1690
|
+
display: flex;
|
|
1691
|
+
inline-size: var(--tree-select-toggle-size);
|
|
1692
|
+
block-size: var(--tree-select-toggle-size);
|
|
1693
|
+
flex-shrink: 0;
|
|
1694
|
+
align-items: center;
|
|
1695
|
+
justify-content: center;
|
|
1696
|
+
margin-inline-end: var(--tree-select-toggle-space-inline-end);
|
|
1697
|
+
border-radius: var(--tree-select-row-radius);
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
.ui-tree-select-toggle[data-leaf] {
|
|
1701
|
+
visibility: hidden;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
.ui-tree-select-toggle-icon,
|
|
1705
|
+
.ui-tree-select-chevron {
|
|
1706
|
+
inline-size: var(--control-affix-icon-size);
|
|
1707
|
+
block-size: var(--control-affix-icon-size);
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
.ui-tree-select-chevron {
|
|
1711
|
+
opacity: var(--control-affix-rest-alpha);
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
.ui-tree-select-label {
|
|
1715
|
+
display: flex;
|
|
1716
|
+
flex: 1 1 0%;
|
|
1717
|
+
cursor: pointer;
|
|
1718
|
+
align-items: center;
|
|
1719
|
+
gap: var(--tree-select-label-space-gap);
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
/* Its clear control is vertically centred against the whole field rather than laid out in a
|
|
1723
|
+
* flex overlay, so it keeps its own rule — but reads the shared affix knobs. */
|
|
1724
|
+
.ui-tree-select-clear {
|
|
1725
|
+
position: absolute;
|
|
1726
|
+
inset-inline-end: var(--space-3);
|
|
1727
|
+
inset-block-start: 50%;
|
|
1728
|
+
translate: 0 -50%;
|
|
1729
|
+
z-index: 10;
|
|
1730
|
+
display: flex;
|
|
1731
|
+
inline-size: var(--control-affix-action-size);
|
|
1732
|
+
block-size: var(--control-affix-action-size);
|
|
1733
|
+
align-items: center;
|
|
1734
|
+
justify-content: center;
|
|
1735
|
+
border-radius: var(--control-affix-action-radius);
|
|
1736
|
+
opacity: var(--control-affix-rest-alpha);
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
.ui-tree-select-clear:hover,
|
|
1740
|
+
.ui-tree-select-clear:focus-visible {
|
|
1741
|
+
opacity: 1;
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
/* Reserves room for a clear control that sits further in than the standard overlay affix. */
|
|
1745
|
+
.ui-tree-select-trigger-affixed {
|
|
1746
|
+
padding-inline-end: var(--space-10);
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
@layer components {
|
|
1751
|
+
/* CALENDAR — react-day-picker frame. The day/weekday cells keep sizing from --control-height
|
|
1752
|
+
* (a system tier decision); everything around them is now a knob. */
|
|
1753
|
+
.ui-calendar {
|
|
1754
|
+
position: relative;
|
|
1755
|
+
inline-size: fit-content;
|
|
1756
|
+
padding: var(--calendar-space-inset);
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
.ui-calendar-months {
|
|
1760
|
+
display: flex;
|
|
1761
|
+
flex-direction: column;
|
|
1762
|
+
gap: var(--calendar-month-space-gap);
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
@media (min-width: 40rem) {
|
|
1766
|
+
.ui-calendar-months {
|
|
1767
|
+
flex-direction: row;
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
.ui-calendar-month {
|
|
1772
|
+
position: relative;
|
|
1773
|
+
display: flex;
|
|
1774
|
+
flex-direction: column;
|
|
1775
|
+
gap: var(--calendar-month-space-gap);
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
.ui-calendar-caption {
|
|
1779
|
+
display: flex;
|
|
1780
|
+
block-size: var(--control-height);
|
|
1781
|
+
inline-size: 100%;
|
|
1782
|
+
align-items: center;
|
|
1783
|
+
justify-content: center;
|
|
1784
|
+
padding-inline: var(--calendar-caption-space-inline);
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
.ui-calendar-caption-label {
|
|
1788
|
+
font-size: var(--calendar-caption-font-size);
|
|
1789
|
+
font-weight: var(--font-weight-medium);
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
.ui-calendar-nav {
|
|
1793
|
+
position: absolute;
|
|
1794
|
+
inset-inline: 0;
|
|
1795
|
+
inset-block-start: var(--calendar-nav-space-block-start);
|
|
1796
|
+
z-index: 10;
|
|
1797
|
+
display: flex;
|
|
1798
|
+
align-items: center;
|
|
1799
|
+
justify-content: space-between;
|
|
1800
|
+
padding-inline: var(--calendar-nav-space-inline);
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
.ui-calendar-nav-button {
|
|
1804
|
+
background: transparent;
|
|
1805
|
+
padding: 0;
|
|
1806
|
+
opacity: var(--calendar-nav-rest-alpha);
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
.ui-calendar-nav-button:hover {
|
|
1810
|
+
opacity: 1;
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
.ui-calendar-chevron {
|
|
1814
|
+
inline-size: var(--calendar-chevron-size);
|
|
1815
|
+
block-size: var(--calendar-chevron-size);
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
.ui-calendar-grid {
|
|
1819
|
+
margin-block-start: var(--calendar-grid-space-block-start);
|
|
1820
|
+
inline-size: 100%;
|
|
1821
|
+
border-collapse: collapse;
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
.ui-calendar-weekday {
|
|
1825
|
+
inline-size: var(--control-height);
|
|
1826
|
+
border-radius: var(--calendar-weekday-radius);
|
|
1827
|
+
font-size: var(--calendar-weekday-font-size);
|
|
1828
|
+
font-weight: var(--font-weight-normal);
|
|
1829
|
+
color: hsl(var(--muted-foreground));
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
.ui-calendar-week {
|
|
1833
|
+
margin-block-start: var(--calendar-week-space-block-start);
|
|
1834
|
+
display: flex;
|
|
1835
|
+
inline-size: 100%;
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
.ui-calendar-day {
|
|
1839
|
+
position: relative;
|
|
1840
|
+
padding: 0;
|
|
1841
|
+
text-align: center;
|
|
1842
|
+
font-size: var(--calendar-day-font-size);
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
.ui-calendar-day:focus-within {
|
|
1846
|
+
position: relative;
|
|
1847
|
+
z-index: 20;
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
/* Scoped to the calendar root so it out-ranks `.ui-button--default-size`, which is declared
|
|
1851
|
+
* later in this file and would otherwise re-apply its block padding to a day cell. */
|
|
1852
|
+
.ui-calendar .ui-calendar-day-button {
|
|
1853
|
+
inline-size: var(--control-height);
|
|
1854
|
+
block-size: var(--control-height);
|
|
1855
|
+
padding: 0;
|
|
1856
|
+
font-weight: var(--font-weight-normal);
|
|
1857
|
+
border-radius: var(--calendar-day-radius);
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
.ui-calendar .ui-calendar-day-button[aria-selected] {
|
|
1861
|
+
opacity: 1;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
.ui-calendar-day-disabled {
|
|
1865
|
+
color: hsl(var(--muted-foreground));
|
|
1866
|
+
opacity: var(--disabled-opacity);
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
@layer components {
|
|
1871
|
+
/* TRANSFER — two-pane list mover. */
|
|
1872
|
+
.ui-transfer {
|
|
1873
|
+
display: flex;
|
|
1874
|
+
flex-wrap: wrap;
|
|
1875
|
+
align-items: stretch;
|
|
1876
|
+
gap: var(--transfer-panes-space-gap);
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
.ui-transfer-pane {
|
|
1880
|
+
display: flex;
|
|
1881
|
+
min-block-size: var(--transfer-pane-min-height);
|
|
1882
|
+
flex: 1 1 0%;
|
|
1883
|
+
flex-direction: column;
|
|
1884
|
+
border-radius: var(--transfer-pane-radius);
|
|
1885
|
+
border: 1px solid hsl(var(--border));
|
|
1886
|
+
background: hsl(var(--background));
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
.ui-transfer-pane-header {
|
|
1890
|
+
display: flex;
|
|
1891
|
+
align-items: center;
|
|
1892
|
+
justify-content: space-between;
|
|
1893
|
+
padding-inline: var(--transfer-header-space-inline);
|
|
1894
|
+
padding-block: var(--transfer-header-space-block);
|
|
1895
|
+
border-block-end: 1px solid hsl(var(--border));
|
|
1896
|
+
font-size: var(--transfer-header-font-size);
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
.ui-transfer-pane-check {
|
|
1900
|
+
display: flex;
|
|
1901
|
+
cursor: pointer;
|
|
1902
|
+
align-items: center;
|
|
1903
|
+
gap: var(--transfer-header-space-gap);
|
|
1904
|
+
font-weight: var(--font-weight-medium);
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
.ui-transfer-search {
|
|
1908
|
+
padding: var(--transfer-search-space-inset);
|
|
1909
|
+
border-block-end: 1px solid hsl(var(--border));
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
.ui-transfer-search[data-disabled] {
|
|
1913
|
+
pointer-events: none;
|
|
1914
|
+
opacity: var(--disabled-opacity);
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
.ui-transfer-list {
|
|
1918
|
+
padding: var(--transfer-list-space-inset);
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
.ui-transfer-empty {
|
|
1922
|
+
padding-block: var(--transfer-empty-space-block);
|
|
1923
|
+
text-align: center;
|
|
1924
|
+
font-size: var(--transfer-row-font-size);
|
|
1925
|
+
color: hsl(var(--muted-foreground));
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
.ui-transfer-row {
|
|
1929
|
+
display: flex;
|
|
1930
|
+
cursor: pointer;
|
|
1931
|
+
align-items: flex-start;
|
|
1932
|
+
gap: var(--transfer-row-space-gap);
|
|
1933
|
+
border-radius: var(--transfer-row-radius);
|
|
1934
|
+
padding-inline: var(--transfer-row-space-inline);
|
|
1935
|
+
padding-block: var(--transfer-row-space-block);
|
|
1936
|
+
font-size: var(--transfer-row-font-size);
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
/* Top-aligned against a two-line row, so it takes a hair of optical offset to sit level with
|
|
1940
|
+
* the first line's cap height rather than its box. */
|
|
1941
|
+
.ui-transfer-row-check {
|
|
1942
|
+
margin-block-start: var(--transfer-row-check-space-block-start);
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
.ui-transfer-row-body {
|
|
1946
|
+
min-inline-size: 0;
|
|
1947
|
+
flex: 1 1 0%;
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
.ui-transfer-row-description {
|
|
1951
|
+
font-size: var(--transfer-row-description-font-size);
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
.ui-transfer-actions {
|
|
1955
|
+
display: flex;
|
|
1956
|
+
flex-direction: column;
|
|
1957
|
+
justify-content: center;
|
|
1958
|
+
gap: var(--transfer-actions-space-gap);
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
.ui-transfer-action-icon {
|
|
1962
|
+
inline-size: var(--transfer-action-icon-size);
|
|
1963
|
+
block-size: var(--transfer-action-icon-size);
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
@layer components {
|
|
1968
|
+
/* MONTH PICKER — the year-nav + 3-column month grid shared by MonthPicker and
|
|
1969
|
+
* MonthRangePicker. One rule set for both; they used to hold identical copies. */
|
|
1970
|
+
.ui-control-composite-field {
|
|
1971
|
+
display: flex;
|
|
1972
|
+
inline-size: 100%;
|
|
1973
|
+
min-inline-size: 0;
|
|
1974
|
+
align-items: center;
|
|
1975
|
+
gap: var(--control-composite-field-space-gap);
|
|
1976
|
+
border-radius: var(--control-radius);
|
|
1977
|
+
border: 1px solid hsl(var(--input));
|
|
1978
|
+
background: hsl(var(--background));
|
|
1979
|
+
outline: none;
|
|
1980
|
+
transition:
|
|
1981
|
+
color 150ms ease,
|
|
1982
|
+
box-shadow 150ms ease;
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
.ui-control-composite-field[data-disabled] {
|
|
1986
|
+
pointer-events: none;
|
|
1987
|
+
cursor: not-allowed;
|
|
1988
|
+
opacity: var(--disabled-opacity);
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
.ui-month-picker-input {
|
|
1992
|
+
min-inline-size: 0;
|
|
1993
|
+
flex: 1 1 0%;
|
|
1994
|
+
background: transparent;
|
|
1995
|
+
font-variant-numeric: tabular-nums;
|
|
1996
|
+
outline: none;
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
.ui-month-picker-input::placeholder {
|
|
2000
|
+
color: hsl(var(--muted-foreground));
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
.ui-month-picker-input:disabled {
|
|
2004
|
+
cursor: not-allowed;
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
.ui-month-picker-icon {
|
|
2008
|
+
inline-size: var(--month-picker-icon-size);
|
|
2009
|
+
block-size: var(--month-picker-icon-size);
|
|
2010
|
+
flex-shrink: 0;
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
.ui-month-picker-separator-icon {
|
|
2014
|
+
inline-size: var(--month-picker-separator-icon-size);
|
|
2015
|
+
block-size: var(--month-picker-separator-icon-size);
|
|
2016
|
+
flex-shrink: 0;
|
|
2017
|
+
color: hsl(var(--muted-foreground));
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
.ui-month-picker-panel {
|
|
2021
|
+
inline-size: auto;
|
|
2022
|
+
padding: var(--month-picker-panel-space-inset);
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
.ui-month-picker-nav {
|
|
2026
|
+
display: flex;
|
|
2027
|
+
align-items: center;
|
|
2028
|
+
justify-content: space-between;
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
.ui-month-picker-nav-button {
|
|
2032
|
+
background: transparent;
|
|
2033
|
+
opacity: var(--month-picker-nav-rest-alpha);
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
.ui-month-picker-nav-button:hover {
|
|
2037
|
+
opacity: 1;
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
.ui-month-picker-nav-label {
|
|
2041
|
+
font-size: var(--month-picker-nav-font-size);
|
|
2042
|
+
font-weight: var(--font-weight-medium);
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
.ui-month-picker-grid {
|
|
2046
|
+
margin-block-start: var(--month-picker-grid-space-block-start);
|
|
2047
|
+
display: grid;
|
|
2048
|
+
/* 3 columns × 4 rows is the month grid's shape, not a service knob — 12 months only read
|
|
2049
|
+
* as a calendar year in this arrangement. */
|
|
2050
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
2051
|
+
gap: var(--month-picker-grid-space-gap);
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
.ui-month-picker-cell {
|
|
2055
|
+
padding-inline: var(--month-picker-cell-space-inline);
|
|
2056
|
+
font-weight: var(--font-weight-normal);
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
/* BUTTON size-xs + icon-adjacent padding + the count pill (#319). */
|
|
2060
|
+
.ui-button--xs {
|
|
2061
|
+
height: var(--button-xs-height);
|
|
2062
|
+
gap: var(--button-xs-space-gap);
|
|
2063
|
+
padding-inline: var(--button-xs-space-inline);
|
|
2064
|
+
font-size: var(--button-xs-font-size);
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
.ui-button--default-size,
|
|
2068
|
+
.ui-button--md {
|
|
2069
|
+
padding-block: var(--button-space-block);
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
/* An icon-only child needs less inline room than a label, or the glyph floats in a wide box. */
|
|
2073
|
+
.ui-button--default-size:has(> svg),
|
|
2074
|
+
.ui-button--md:has(> svg) {
|
|
2075
|
+
padding-inline: var(--button-icon-space-inline-md);
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
.ui-button--sm:has(> svg) {
|
|
2079
|
+
padding-inline: var(--button-icon-space-inline-sm);
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
.ui-button--lg:has(> svg) {
|
|
2083
|
+
padding-inline: var(--button-icon-space-inline-lg);
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
.ui-button--xs:has(> svg) {
|
|
2087
|
+
padding-inline: var(--button-icon-space-inline-xs);
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
/* COUNT PILL (gh#320). Opaque role fills, not a translucent tint over the button's surface: a
|
|
2091
|
+
* tint takes its contrast from whatever is behind it, and five variant×theme×hover combinations
|
|
2092
|
+
* measured below WCAG 1.4.3's 4.5:1 for small text. The measurements and the share-vs-split
|
|
2093
|
+
* reasoning live in tokens/components/control.css beside the knobs. Same treatment gh#312
|
|
2094
|
+
* validated on Toggle. */
|
|
2095
|
+
.ui-button-count {
|
|
2096
|
+
display: inline-flex;
|
|
2097
|
+
min-inline-size: var(--button-count-min-width);
|
|
2098
|
+
align-items: center;
|
|
2099
|
+
justify-content: center;
|
|
2100
|
+
border-radius: var(--radius-pill);
|
|
2101
|
+
padding-inline: var(--button-count-space-inline);
|
|
2102
|
+
background: var(--button-count-background, hsl(var(--muted)));
|
|
2103
|
+
color: var(--button-count-color, hsl(var(--foreground)));
|
|
2104
|
+
font-size: var(--button-count-font-size);
|
|
2105
|
+
line-height: 1;
|
|
2106
|
+
font-variant-numeric: tabular-nums;
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
/* Each filled variant wears its OWN label pair, swapped. That makes the pill exactly as legible
|
|
2110
|
+
* as the label it sits beside — it cannot be less legible under any theme that keeps the button
|
|
2111
|
+
* itself readable, which a fixed colour could not promise. */
|
|
2112
|
+
.ui-button--default .ui-button-count {
|
|
2113
|
+
background: var(--button-count-default-background, hsl(var(--primary-foreground)));
|
|
2114
|
+
color: var(--button-count-default-color, hsl(var(--primary)));
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
.ui-button--destructive .ui-button-count {
|
|
2118
|
+
background: var(--button-count-destructive-background, hsl(var(--destructive-foreground)));
|
|
2119
|
+
color: var(--button-count-destructive-color, hsl(var(--destructive)));
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
.ui-button--secondary .ui-button-count {
|
|
2123
|
+
background: var(--button-count-secondary-background, hsl(var(--secondary-foreground)));
|
|
2124
|
+
color: var(--button-count-secondary-color, hsl(var(--secondary)));
|
|
2125
|
+
}
|
|
2126
|
+
}
|