actual-css 0.9.2 → 0.10.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/CHANGELOG.md +96 -0
- package/README.md +65 -44
- package/components.json +33 -13
- package/dist/actual.full.css +585 -499
- package/dist/actual.full.js +1 -1
- package/dist/actual.full.min.css +1 -1
- package/docs/design-notes/app-ui.md +1 -1
- package/docs/design-notes/column-layout.md +103 -520
- package/docs/design-notes/forced-colors.md +12 -1
- package/docs/design-notes/naming.md +1 -1
- package/docs/design-notes/surface-reparenting.md +8 -9
- package/docs/navigation.json +2 -0
- package/docs/pages/components/accordion.md +55 -1
- package/docs/pages/components/alert.md +9 -15
- package/docs/pages/components/badge.md +6 -7
- package/docs/pages/components/bleed.md +42 -0
- package/docs/pages/components/button.md +30 -4
- package/docs/pages/components/card.md +21 -86
- package/docs/pages/components/close.md +48 -0
- package/docs/pages/components/data-list.md +26 -1
- package/docs/pages/components/dialog.md +16 -16
- package/docs/pages/components/drawer.md +9 -9
- package/docs/pages/components/navbar.md +5 -5
- package/docs/pages/components/spinner.md +3 -3
- package/docs/pages/enhancements/overview.md +5 -0
- package/docs/pages/enhancements/tooltip.md +3 -1
- package/docs/pages/examples/overview.md +2 -1
- package/docs/pages/forms/choice-cards.md +39 -0
- package/docs/pages/forms/combobox.md +23 -11
- package/docs/pages/forms/joined-controls.md +2 -5
- package/docs/pages/forms/overview.md +6 -0
- package/docs/pages/foundations/theming.md +64 -18
- package/docs/pages/foundations/tokens.md +28 -10
- package/docs/pages/guides/introduction.md +15 -23
- package/docs/pages/guides/modular-import.md +53 -6
- package/docs/pages/guides/progressive-enhancement.md +6 -3
- package/docs/pages/guides/tailwind.md +17 -5
- package/docs/pages/layout/column-layout.md +92 -54
- package/docs/pages/layout/media.md +1 -1
- package/docs/pages/layout/overview.md +1 -1
- package/docs/pages/utilities/overview.md +1 -1
- package/docs/pages/utilities/sizing-wrapping.md +5 -3
- package/llms.txt +24 -4
- package/package.json +6 -6
- package/reserved-classes.json +5 -30
- package/skills/actual-css/PROJECT.md.example +65 -0
- package/skills/actual-css/README.md +64 -0
- package/skills/actual-css/SKILL.md +234 -0
- package/skills/actual-css/references/accessibility.md +44 -0
- package/skills/actual-css/references/api-and-vocabulary.md +94 -0
- package/skills/actual-css/references/enhancements.md +59 -0
- package/skills/actual-css/references/forms.md +49 -0
- package/skills/actual-css/references/layout-and-theming.md +82 -0
- package/skills/actual-css/references/review-checklist.md +51 -0
- package/skills/actual-css/scripts/inspect-actual-css.mjs +171 -0
- package/src/css/components/accordion.css +44 -7
- package/src/css/components/alert.css +23 -43
- package/src/css/components/app-nav.css +9 -0
- package/src/css/components/badge.css +11 -39
- package/src/css/components/bleed.css +79 -0
- package/src/css/components/button.css +6 -17
- package/src/css/components/card.css +7 -40
- package/src/css/components/close.css +91 -0
- package/src/css/components/data-list.css +18 -0
- package/src/css/components/drawer.css +15 -56
- package/src/css/components/fab.css +10 -0
- package/src/css/components/flyout.css +1 -0
- package/src/css/components/index.css +2 -0
- package/src/css/components/join.css +36 -20
- package/src/css/components/menu.css +12 -0
- package/src/css/components/modal.css +10 -57
- package/src/css/components/navbar.css +27 -0
- package/src/css/core/focus.css +9 -3
- package/src/css/core/theme.css +25 -43
- package/src/css/core/tokens.css +1 -1
- package/src/css/core/variants.css +3 -33
- package/src/css/forms/choice-card.css +74 -4
- package/src/css/forms/choice.css +2 -3
- package/src/css/forms/control.css +15 -8
- package/src/css/forms/custom-select.css +4 -4
- package/src/css/forms/native.css +4 -4
- package/src/css/forms/range.css +23 -8
- package/src/css/forms/switch.css +2 -3
- package/src/css/layout/app-layout.css +1 -1
- package/src/css/layout/column-layout.css +26 -111
- package/src/css/utilities/extra.css +7 -6
- package/src/js/dialog.js +95 -24
- package/src/js/enhance.js +7 -3
- package/src/js/escape.js +1 -0
- package/src/js/flyout.js +10 -1
- package/src/js/focus-group.js +2 -1
- package/src/js/keys.js +14 -0
- package/src/js/menu.js +2 -1
- package/src/js/status.js +19 -0
- package/src/js/surface.js +12 -8
- package/src/js/tab.js +2 -1
- package/src/js/tooltip.js +18 -1
- package/src/js/validation.js +5 -6
- package/dist/actual.css +0 -876
- package/dist/actual.js +0 -1
- package/dist/actual.min.css +0 -1
package/dist/actual.full.css
CHANGED
|
@@ -134,7 +134,7 @@ select {
|
|
|
134
134
|
--focus-outline-color: currentColor;
|
|
135
135
|
--focus-outline: calc(var(--border-width) * 2) solid var(--focus-outline-color);
|
|
136
136
|
--focus-outline-offset: 2px;
|
|
137
|
-
--focus-ring-width:
|
|
137
|
+
--focus-ring-width: 2px;
|
|
138
138
|
|
|
139
139
|
/* Status dot positioning — used by .avatar > .badge:empty. The offset is
|
|
140
140
|
the circle-aware inset (1 - 1/√2) / 2 ≈ 14.65%; the ring surrounds the
|
|
@@ -356,10 +356,10 @@ select {
|
|
|
356
356
|
|
|
357
357
|
/* Lines and focus */
|
|
358
358
|
--border: hsl(260 15% 88%);
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
--focus
|
|
359
|
+
/* A primary-hued mid-tone on purpose, one value for both schemes: focus
|
|
360
|
+
lines must hold 3:1 against --surface and --surface-solid alike (page,
|
|
361
|
+
fields, a solid band), which rules out the primary itself. */
|
|
362
|
+
--focus: hsl(268 30% 55%);
|
|
363
363
|
--hover-overlay: hsl(260 20% 10% / 0.04);
|
|
364
364
|
|
|
365
365
|
/* Radius scale — themes may override the whole scale (e.g. [data-theme="square"]) */
|
|
@@ -382,7 +382,6 @@ select {
|
|
|
382
382
|
--heading: var(--text);
|
|
383
383
|
--selection-bg: var(--primary);
|
|
384
384
|
--selection-fg: var(--primary-fg);
|
|
385
|
-
--focus-ring-shadow: 0 0 0 var(--focus-ring-width) var(--focus-ring);
|
|
386
385
|
}
|
|
387
386
|
|
|
388
387
|
/* Soft foreground hooks — the soft ink is the intent rebated toward the local
|
|
@@ -391,18 +390,17 @@ select {
|
|
|
391
390
|
default theme: a custom property bakes its var() references where it is
|
|
392
391
|
declared, so these resolve against root/light/dark tokens on every boundary.
|
|
393
392
|
Presets and custom islands keep their --soft-fg-mix / raw-intent ink unless
|
|
394
|
-
they declare hooks themselves
|
|
395
|
-
|
|
396
|
-
:where(
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
:root,
|
|
393
|
+
they declare hooks themselves.
|
|
394
|
+
|
|
395
|
+
Why :where(:root) and why the reset comes second: both rules match when a
|
|
396
|
+
custom theme sits on <html> itself. A bare :root is (0,1,0) and would
|
|
397
|
+
outrank the zero-specificity reset, so the same theme resolved a calibrated
|
|
398
|
+
Ink & Terra ink on <html data-theme="brand"> and the raw --soft-fg-mix ink
|
|
399
|
+
on <div data-theme="brand"> — one attribute, two soft palettes. Levelling
|
|
400
|
+
both rules to zero specificity makes source order the tiebreaker, so the
|
|
401
|
+
reset must stay below. Zero specificity is also what lets a theme declare
|
|
402
|
+
its own hooks (e.g. brutalist's 45% soft-fg-mix) and win at (0,1,0). */
|
|
403
|
+
:where(:root),
|
|
406
404
|
[data-theme="light"],
|
|
407
405
|
[data-theme="dark"] {
|
|
408
406
|
--secondary-soft-fg: color-mix(in oklab, var(--text) 10%, var(--secondary) 90%);
|
|
@@ -412,6 +410,15 @@ select {
|
|
|
412
410
|
--neutral-soft-fg: color-mix(in oklab, var(--text) 20%, var(--neutral) 80%);
|
|
413
411
|
}
|
|
414
412
|
|
|
413
|
+
:where([data-theme]:not([data-theme="light"]):not([data-theme="dark"])) {
|
|
414
|
+
--primary-soft-fg: initial;
|
|
415
|
+
--secondary-soft-fg: initial;
|
|
416
|
+
--success-soft-fg: initial;
|
|
417
|
+
--warning-soft-fg: initial;
|
|
418
|
+
--danger-soft-fg: initial;
|
|
419
|
+
--neutral-soft-fg: initial;
|
|
420
|
+
}
|
|
421
|
+
|
|
415
422
|
/* Native platform controls (checkbox, radio, range, progress) that are not
|
|
416
423
|
restyled by a component class still follow the theme accent. Declared on
|
|
417
424
|
every island: accent-color inherits as a computed value, so islands must
|
|
@@ -433,20 +440,6 @@ select {
|
|
|
433
440
|
--tone-fg: var(--text);
|
|
434
441
|
}
|
|
435
442
|
|
|
436
|
-
/* Focus ring derivation — color-mix tier. Runs whether or not light-dark()
|
|
437
|
-
is also supported, so it covers the color-mix-without-light-dark browser
|
|
438
|
-
tier on its own; the light-dark block below refines it further with the
|
|
439
|
-
light/dark alpha split once both features are available. Declared on every
|
|
440
|
-
[data-theme] island (not just the built-in light/dark pair) so custom
|
|
441
|
-
islands recolor the ring for free; a preset that re-declares --focus-ring
|
|
442
|
-
later in source order intentionally pins its own ring instead. */
|
|
443
|
-
@supports (color: color-mix(in oklch, red, white)) {
|
|
444
|
-
:root,
|
|
445
|
-
[data-theme] {
|
|
446
|
-
--focus-ring: color-mix(in oklch, var(--focus) 30%, transparent);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
443
|
/* Auto dark mode via light-dark(). Resolves based on the element's color-scheme:
|
|
451
444
|
:root (light dark) follows the OS, [data-theme="light"] forces light,
|
|
452
445
|
[data-theme="dark"] forces dark by overriding color-scheme.
|
|
@@ -483,22 +476,11 @@ select {
|
|
|
483
476
|
--text-subtle: light-dark(hsl(258 7% 57%), hsl(258 7% 54%));
|
|
484
477
|
|
|
485
478
|
--border: light-dark(hsl(260 15% 88%), hsl(262 12% 24%));
|
|
486
|
-
--focus-ring: light-dark(hsl(258 7% 43% / 0.3), hsl(258 10% 72% / 0.28));
|
|
487
479
|
--hover-overlay: light-dark(hsl(260 20% 10% / 0.04), hsl(0 0% 100% / 0.06));
|
|
488
480
|
--shadow-color: light-dark(hsl(260 25% 10%), hsl(262 40% 2%));
|
|
489
481
|
}
|
|
490
482
|
}
|
|
491
483
|
|
|
492
|
-
@supports (color: color-mix(in oklch, red, white)) and (color: light-dark(red, blue)) {
|
|
493
|
-
:root,
|
|
494
|
-
[data-theme] {
|
|
495
|
-
--focus-ring: light-dark(
|
|
496
|
-
color-mix(in oklch, var(--focus) 30%, transparent),
|
|
497
|
-
color-mix(in oklch, var(--focus) 28%, transparent)
|
|
498
|
-
);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
484
|
@media (prefers-contrast: more) {
|
|
503
485
|
:root,
|
|
504
486
|
/* The repeated attribute intentionally outranks single-attribute preset
|
|
@@ -506,7 +488,7 @@ select {
|
|
|
506
488
|
[data-theme][data-theme] {
|
|
507
489
|
--border-width: 2px;
|
|
508
490
|
--focus-outline-offset: 3px;
|
|
509
|
-
--focus-ring-width:
|
|
491
|
+
--focus-ring-width: 3px;
|
|
510
492
|
--text-muted: var(--text);
|
|
511
493
|
--text-subtle: var(--text);
|
|
512
494
|
}
|
|
@@ -662,13 +644,9 @@ small {
|
|
|
662
644
|
* Cross-cutting variant token mapping.
|
|
663
645
|
*
|
|
664
646
|
* Variant scope: .solid/.soft/.outline/.surface set --ui-* for components that
|
|
665
|
-
* opt into shared variants.
|
|
666
|
-
*
|
|
667
|
-
*
|
|
668
|
-
* (.card, .navbar, .alert, .badge) invert through their own rules. Loaded
|
|
669
|
-
* before components, so a component with its own explicit surface (e.g.
|
|
670
|
-
* .accordion) naturally keeps that surface — .inverted never forces a
|
|
671
|
-
* non-participant.
|
|
647
|
+
* opt into shared variants. A subtree that needs another palette or scheme is
|
|
648
|
+
* a data-theme island, never a variant: only an island recomputes every token
|
|
649
|
+
* (fields, states, intents), where a remap of a few --ui-* tokens cannot.
|
|
672
650
|
* Component-specific defaults (.btn solid-by-default, .badge
|
|
673
651
|
* soft-by-default, .alert soft-by-default) live with their components:
|
|
674
652
|
* components/button.css — .btn default solid + .btn.ghost + .btn.link
|
|
@@ -724,28 +702,6 @@ small {
|
|
|
724
702
|
--ui-border: var(--border);
|
|
725
703
|
}
|
|
726
704
|
|
|
727
|
-
/* Inverse surface treatment. Paints the element directly (so it works on any
|
|
728
|
-
bare block) and remaps the shared --ui-* tokens so opt-in components
|
|
729
|
-
(.card, .navbar) invert through their own rules. Because this loads before
|
|
730
|
-
component base surfaces, components with their own explicit surface
|
|
731
|
-
(e.g. .accordion) keep it — participation is opt-in, never forced. --heading
|
|
732
|
-
is set so global headings (var(--heading, inherit)) do not inherit an outside
|
|
733
|
-
heading color onto the dark surface; surface-owning descendants derive
|
|
734
|
-
their contextual aliases from their own local surface. */
|
|
735
|
-
.inverted {
|
|
736
|
-
--ui-bg: var(--surface-solid);
|
|
737
|
-
--ui-fg: var(--surface);
|
|
738
|
-
--ui-border: transparent;
|
|
739
|
-
--ui-hover-bg: var(--surface-solid);
|
|
740
|
-
|
|
741
|
-
--heading: var(--surface);
|
|
742
|
-
--busy-overlay-bg: var(--surface-solid);
|
|
743
|
-
|
|
744
|
-
background: var(--ui-bg);
|
|
745
|
-
color: var(--ui-fg);
|
|
746
|
-
border-color: var(--ui-border);
|
|
747
|
-
}
|
|
748
|
-
|
|
749
705
|
.compact {
|
|
750
706
|
--gap: var(--space-20);
|
|
751
707
|
--density-space: var(--space-20);
|
|
@@ -829,10 +785,6 @@ small {
|
|
|
829
785
|
);
|
|
830
786
|
/* @sync soft-recipe */
|
|
831
787
|
}
|
|
832
|
-
|
|
833
|
-
.inverted {
|
|
834
|
-
--ui-hover-bg: color-mix(in oklch, var(--surface-solid) 88%, black);
|
|
835
|
-
}
|
|
836
788
|
}
|
|
837
789
|
|
|
838
790
|
/* Interactive hover recipes live in the components that consume them:
|
|
@@ -843,9 +795,15 @@ small {
|
|
|
843
795
|
pointer/mouse focus and interactive components layer their ring on top.
|
|
844
796
|
|
|
845
797
|
Invariant: an interactive component must never rely on box-shadow alone
|
|
846
|
-
for its keyboard focus indicator
|
|
847
|
-
|
|
848
|
-
|
|
798
|
+
for its keyboard focus indicator; forced colors keep outlines and drop
|
|
799
|
+
shadows.
|
|
800
|
+
|
|
801
|
+
Component indicators are a solid --focus-ring-width line in --focus: inset
|
|
802
|
+
on fields (inside their border box), offset by --focus-outline-offset on
|
|
803
|
+
discrete actions. The color never derives from an intent or a local
|
|
804
|
+
surface — the theme guarantees --focus at 3:1 against both --surface and
|
|
805
|
+
--surface-solid, so one token reads on the page, on a solid band and on
|
|
806
|
+
any nested surface. */
|
|
849
807
|
:focus {
|
|
850
808
|
outline: var(--focus-outline);
|
|
851
809
|
outline-offset: var(--focus-outline-offset);
|
|
@@ -1390,7 +1348,7 @@ small {
|
|
|
1390
1348
|
*
|
|
1391
1349
|
* Docs: docs/pages/layout/app-layout.md
|
|
1392
1350
|
*
|
|
1393
|
-
* Public
|
|
1351
|
+
* Public hooks:
|
|
1394
1352
|
* --app-nav-side-size
|
|
1395
1353
|
*/
|
|
1396
1354
|
.app-layout {
|
|
@@ -1719,12 +1677,12 @@ small {
|
|
|
1719
1677
|
* Docs: docs/pages/layout/column-layout.md
|
|
1720
1678
|
*
|
|
1721
1679
|
* Neither space-driven like .grid nor count-driven like .grid-N: this is a
|
|
1722
|
-
* coordinate system.
|
|
1723
|
-
*
|
|
1724
|
-
* deliberately no automatic collapse — no threshold,
|
|
1725
|
-
* .container-query behavior, no breakpoint variants. A single
|
|
1726
|
-
* threshold cannot be right for arbitrary per-region compositions,
|
|
1727
|
-
* hidden default would have to be undone rather than extended.
|
|
1680
|
+
* coordinate system. Each direct child places itself with two numbers,
|
|
1681
|
+
* --column-start and --column-span, and the author owns narrow-container
|
|
1682
|
+
* recomposition. There is deliberately no automatic collapse — no threshold,
|
|
1683
|
+
* no hidden .container-query behavior, no breakpoint variants. A single
|
|
1684
|
+
* framework threshold cannot be right for arbitrary per-region compositions,
|
|
1685
|
+
* and a hidden default would have to be undone rather than extended.
|
|
1728
1686
|
*
|
|
1729
1687
|
* See docs/design-notes/column-layout.md.
|
|
1730
1688
|
*
|
|
@@ -1736,11 +1694,12 @@ small {
|
|
|
1736
1694
|
* media + flexible content -> .media
|
|
1737
1695
|
* exact custom track templates -> --grid-columns on .grid
|
|
1738
1696
|
*
|
|
1739
|
-
* Child contract: each direct child is placed on the canvas via its own
|
|
1740
|
-
* .column-span-N / .column-start-N.
|
|
1741
|
-
*
|
|
1742
1697
|
* Unlike .grid-N, the track template does not read --gap, so the gap
|
|
1743
1698
|
* utilities are exact here at every width.
|
|
1699
|
+
*
|
|
1700
|
+
* Public hooks:
|
|
1701
|
+
* --column-start (the start line, 1-12; unset = auto-placed)
|
|
1702
|
+
* --column-span (the width in canvas units, 1-12; unset = to the canvas end)
|
|
1744
1703
|
*/
|
|
1745
1704
|
.column-layout {
|
|
1746
1705
|
display: grid;
|
|
@@ -1748,116 +1707,30 @@ small {
|
|
|
1748
1707
|
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
1749
1708
|
}
|
|
1750
1709
|
|
|
1751
|
-
/*
|
|
1752
|
-
*
|
|
1753
|
-
*
|
|
1754
|
-
*
|
|
1755
|
-
*
|
|
1756
|
-
* 3. .column-span-* the end only, so it refines either of the above
|
|
1710
|
+
/* One rule covers the four placements:
|
|
1711
|
+
* neither auto-placed, spanning the whole canvas
|
|
1712
|
+
* span only auto-placed over N units, so 8 + 4 flows onto one row
|
|
1713
|
+
* start only from line S to the canvas end (span 13 - S)
|
|
1714
|
+
* start and span explicit; keep S + N - 1 <= 12
|
|
1757
1715
|
*
|
|
1758
|
-
*
|
|
1759
|
-
*
|
|
1760
|
-
* are silent. tests/css-audit.test.js locks the order and
|
|
1761
|
-
* tests/browser/column-layout.test.js locks the resulting geometry.
|
|
1716
|
+
* The start stays `auto` unless set: a definite start on every child would
|
|
1717
|
+
* take all of them out of auto-placement, and an 8 + 4 pair would stack.
|
|
1762
1718
|
*
|
|
1763
|
-
*
|
|
1764
|
-
*
|
|
1765
|
-
*
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
/* Tier 1. A child with no placement class stays a readable full-width region
|
|
1769
|
-
* instead of collapsing to one twelfth. Expressed as a span, not as
|
|
1770
|
-
* `1 / -1`: a definite start line would give every child a definite column
|
|
1771
|
-
* position and disable auto-placement, so `.column-span-8` next to
|
|
1772
|
-
* `.column-span-4` would stack instead of composing 8 + 4.
|
|
1719
|
+
* The reset makes the two hooks inputs of this grid item rather than an
|
|
1720
|
+
* inherited context: a nested .column-layout keeps the coordinates it was
|
|
1721
|
+
* given for its own placement, and its children start from nothing. Zero
|
|
1722
|
+
* specificity, so any author rule on the child wins.
|
|
1773
1723
|
*
|
|
1774
1724
|
* min-inline-size: 0 keeps the item box inside its track so the canvas never
|
|
1775
1725
|
* grows past its container. It does not stop content from painting outside a
|
|
1776
1726
|
* narrow track — that distinction is the primitive's contract, not a bug. */
|
|
1777
1727
|
:where(.column-layout) > * {
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
}
|
|
1728
|
+
--column-start: initial;
|
|
1729
|
+
--column-span: initial;
|
|
1781
1730
|
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
:where(.column-start-1) {
|
|
1786
|
-
grid-column: 1 / -1;
|
|
1787
|
-
}
|
|
1788
|
-
:where(.column-start-2) {
|
|
1789
|
-
grid-column: 2 / -1;
|
|
1790
|
-
}
|
|
1791
|
-
:where(.column-start-3) {
|
|
1792
|
-
grid-column: 3 / -1;
|
|
1793
|
-
}
|
|
1794
|
-
:where(.column-start-4) {
|
|
1795
|
-
grid-column: 4 / -1;
|
|
1796
|
-
}
|
|
1797
|
-
:where(.column-start-5) {
|
|
1798
|
-
grid-column: 5 / -1;
|
|
1799
|
-
}
|
|
1800
|
-
:where(.column-start-6) {
|
|
1801
|
-
grid-column: 6 / -1;
|
|
1802
|
-
}
|
|
1803
|
-
:where(.column-start-7) {
|
|
1804
|
-
grid-column: 7 / -1;
|
|
1805
|
-
}
|
|
1806
|
-
:where(.column-start-8) {
|
|
1807
|
-
grid-column: 8 / -1;
|
|
1808
|
-
}
|
|
1809
|
-
:where(.column-start-9) {
|
|
1810
|
-
grid-column: 9 / -1;
|
|
1811
|
-
}
|
|
1812
|
-
:where(.column-start-10) {
|
|
1813
|
-
grid-column: 10 / -1;
|
|
1814
|
-
}
|
|
1815
|
-
:where(.column-start-11) {
|
|
1816
|
-
grid-column: 11 / -1;
|
|
1817
|
-
}
|
|
1818
|
-
:where(.column-start-12) {
|
|
1819
|
-
grid-column: 12 / -1;
|
|
1820
|
-
}
|
|
1821
|
-
|
|
1822
|
-
/* Tier 3. The end only. Combined with a start, keep the placement inside the
|
|
1823
|
-
* canvas: S + N - 1 <= 12. Exceeding it adds an auto-sized implicit column,
|
|
1824
|
-
* which reshapes every 1fr track — the whole canvas loses its alignment, not
|
|
1825
|
-
* just the offending region. */
|
|
1826
|
-
:where(.column-span-1) {
|
|
1827
|
-
grid-column-end: span 1;
|
|
1828
|
-
}
|
|
1829
|
-
:where(.column-span-2) {
|
|
1830
|
-
grid-column-end: span 2;
|
|
1831
|
-
}
|
|
1832
|
-
:where(.column-span-3) {
|
|
1833
|
-
grid-column-end: span 3;
|
|
1834
|
-
}
|
|
1835
|
-
:where(.column-span-4) {
|
|
1836
|
-
grid-column-end: span 4;
|
|
1837
|
-
}
|
|
1838
|
-
:where(.column-span-5) {
|
|
1839
|
-
grid-column-end: span 5;
|
|
1840
|
-
}
|
|
1841
|
-
:where(.column-span-6) {
|
|
1842
|
-
grid-column-end: span 6;
|
|
1843
|
-
}
|
|
1844
|
-
:where(.column-span-7) {
|
|
1845
|
-
grid-column-end: span 7;
|
|
1846
|
-
}
|
|
1847
|
-
:where(.column-span-8) {
|
|
1848
|
-
grid-column-end: span 8;
|
|
1849
|
-
}
|
|
1850
|
-
:where(.column-span-9) {
|
|
1851
|
-
grid-column-end: span 9;
|
|
1852
|
-
}
|
|
1853
|
-
:where(.column-span-10) {
|
|
1854
|
-
grid-column-end: span 10;
|
|
1855
|
-
}
|
|
1856
|
-
:where(.column-span-11) {
|
|
1857
|
-
grid-column-end: span 11;
|
|
1858
|
-
}
|
|
1859
|
-
:where(.column-span-12) {
|
|
1860
|
-
grid-column-end: span 12;
|
|
1731
|
+
grid-column-start: var(--column-start, auto);
|
|
1732
|
+
grid-column-end: span var(--column-span, calc(13 - var(--column-start, 1)));
|
|
1733
|
+
min-inline-size: 0;
|
|
1861
1734
|
}
|
|
1862
1735
|
|
|
1863
1736
|
/*
|
|
@@ -2138,6 +2011,8 @@ small {
|
|
|
2138
2011
|
* --control-cursor
|
|
2139
2012
|
* --control-border-block-end-color
|
|
2140
2013
|
* --control-border-block-end-width
|
|
2014
|
+
* --control-hover-border (input and textarea)
|
|
2015
|
+
* --control-hover-bg (select)
|
|
2141
2016
|
*/
|
|
2142
2017
|
.input,
|
|
2143
2018
|
.textarea,
|
|
@@ -2182,19 +2057,24 @@ small {
|
|
|
2182
2057
|
|
|
2183
2058
|
.input:hover:not(:disabled),
|
|
2184
2059
|
.textarea:hover:not(:disabled) {
|
|
2185
|
-
--control-border: var(--text-subtle);
|
|
2060
|
+
--control-border: var(--control-hover-border, var(--text-subtle));
|
|
2186
2061
|
}
|
|
2187
2062
|
|
|
2188
2063
|
.select:hover:not(:disabled) {
|
|
2189
|
-
--control-bg: var(--surface-subtle);
|
|
2190
|
-
}
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
.
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2064
|
+
--control-bg: var(--control-hover-bg, var(--surface-subtle));
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
/* Field focus is an inset outline: offset = -width keeps every pixel inside
|
|
2068
|
+
the control's border box (any smaller offset bleeds outward), so it covers
|
|
2069
|
+
the border, never touches a .join neighbour or a scrollport edge, and needs
|
|
2070
|
+
no group treatment. Width follows --focus-ring-width, including its
|
|
2071
|
+
prefers-contrast step. Border = shape and validation, outline = focus. */
|
|
2072
|
+
.input:focus-visible:not(:disabled),
|
|
2073
|
+
.textarea:focus-visible:not(:disabled),
|
|
2074
|
+
.select:focus-visible:not(:disabled) {
|
|
2075
|
+
outline: var(--focus-ring-width) solid var(--form-invalid-border, var(--focus));
|
|
2076
|
+
outline-offset: calc(var(--focus-ring-width) * -1);
|
|
2077
|
+
box-shadow: none;
|
|
2198
2078
|
}
|
|
2199
2079
|
|
|
2200
2080
|
.input[readonly],
|
|
@@ -2447,9 +2327,8 @@ small {
|
|
|
2447
2327
|
|
|
2448
2328
|
.check:focus-visible,
|
|
2449
2329
|
.radio:focus-visible {
|
|
2450
|
-
--
|
|
2451
|
-
outline:
|
|
2452
|
-
box-shadow: var(--focus-ring-shadow);
|
|
2330
|
+
outline: var(--focus-ring-width) solid var(--focus);
|
|
2331
|
+
outline-offset: var(--focus-outline-offset);
|
|
2453
2332
|
}
|
|
2454
2333
|
|
|
2455
2334
|
.check:disabled,
|
|
@@ -2571,8 +2450,8 @@ small {
|
|
|
2571
2450
|
}
|
|
2572
2451
|
|
|
2573
2452
|
.file:focus-visible {
|
|
2574
|
-
outline:
|
|
2575
|
-
|
|
2453
|
+
outline: var(--focus-ring-width) solid var(--focus);
|
|
2454
|
+
outline-offset: var(--focus-outline-offset);
|
|
2576
2455
|
}
|
|
2577
2456
|
|
|
2578
2457
|
/* Native color picker — keep the browser's dialog and selected-color UI. The
|
|
@@ -2608,8 +2487,8 @@ small {
|
|
|
2608
2487
|
}
|
|
2609
2488
|
|
|
2610
2489
|
.color:focus-visible {
|
|
2611
|
-
outline:
|
|
2612
|
-
|
|
2490
|
+
outline: var(--focus-ring-width) solid var(--focus);
|
|
2491
|
+
outline-offset: var(--focus-outline-offset);
|
|
2613
2492
|
}
|
|
2614
2493
|
|
|
2615
2494
|
@media (forced-colors: active) {
|
|
@@ -2973,9 +2852,8 @@ small {
|
|
|
2973
2852
|
}
|
|
2974
2853
|
|
|
2975
2854
|
.switch:focus-visible {
|
|
2976
|
-
--
|
|
2977
|
-
outline:
|
|
2978
|
-
box-shadow: var(--focus-ring-shadow);
|
|
2855
|
+
outline: var(--focus-ring-width) solid var(--focus);
|
|
2856
|
+
outline-offset: var(--focus-outline-offset);
|
|
2979
2857
|
}
|
|
2980
2858
|
|
|
2981
2859
|
.switch:disabled {
|
|
@@ -3073,20 +2951,22 @@ small {
|
|
|
3073
2951
|
}
|
|
3074
2952
|
|
|
3075
2953
|
/*
|
|
3076
|
-
*
|
|
3077
|
-
*
|
|
3078
|
-
*
|
|
2954
|
+
* A thumb pseudo-element takes no reliable outline, so its ring is a solid
|
|
2955
|
+
* --focus shadow (the transparent outline above covers forced colors). The
|
|
2956
|
+
* 1px surface-coloured separator keeps the ring visually distinct from the
|
|
2957
|
+
* small, coloured thumb — unlike a full-size input where the same gap would
|
|
2958
|
+
* read as an unwanted void.
|
|
3079
2959
|
*/
|
|
3080
2960
|
.range:focus-visible::-webkit-slider-thumb {
|
|
3081
2961
|
box-shadow:
|
|
3082
2962
|
0 0 0 1px var(--surface),
|
|
3083
|
-
0 0 0 calc(1px + var(--focus-ring-width)) var(--focus
|
|
2963
|
+
0 0 0 calc(1px + var(--focus-ring-width)) var(--focus);
|
|
3084
2964
|
}
|
|
3085
2965
|
|
|
3086
2966
|
.range:focus-visible::-moz-range-thumb {
|
|
3087
2967
|
box-shadow:
|
|
3088
2968
|
0 0 0 1px var(--surface),
|
|
3089
|
-
0 0 0 calc(1px + var(--focus-ring-width)) var(--focus
|
|
2969
|
+
0 0 0 calc(1px + var(--focus-ring-width)) var(--focus);
|
|
3090
2970
|
}
|
|
3091
2971
|
|
|
3092
2972
|
.range:disabled {
|
|
@@ -3096,20 +2976,33 @@ small {
|
|
|
3096
2976
|
}
|
|
3097
2977
|
|
|
3098
2978
|
/* Forced colors: the track must stay a visible line against the page (Canvas
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
2979
|
+
would merge with it) and the thumb needs a real boundary to remain
|
|
2980
|
+
distinguishable from the track. Disabled keeps GrayText via :disabled.
|
|
2981
|
+
Focus stays on the transparent outline of :focus-visible. Track and thumb
|
|
2982
|
+
fills are written on the properties, not only the hooks: an application
|
|
2983
|
+
hook override at the same specificity loads after this file and would win
|
|
2984
|
+
the variable, leaving a Canvas-remapped fill. */
|
|
3102
2985
|
@media (forced-colors: active) {
|
|
3103
2986
|
.range {
|
|
3104
2987
|
--range-track-bg: CanvasText;
|
|
3105
2988
|
--range-thumb-bg: ButtonFace;
|
|
3106
2989
|
}
|
|
3107
2990
|
|
|
2991
|
+
.range::-webkit-slider-runnable-track {
|
|
2992
|
+
background: CanvasText;
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2995
|
+
.range::-moz-range-track {
|
|
2996
|
+
background: CanvasText;
|
|
2997
|
+
}
|
|
2998
|
+
|
|
3108
2999
|
.range::-webkit-slider-thumb {
|
|
3000
|
+
background: ButtonFace;
|
|
3109
3001
|
border: var(--border-width) solid ButtonText;
|
|
3110
3002
|
}
|
|
3111
3003
|
|
|
3112
3004
|
.range::-moz-range-thumb {
|
|
3005
|
+
background: ButtonFace;
|
|
3113
3006
|
border: var(--border-width) solid ButtonText;
|
|
3114
3007
|
}
|
|
3115
3008
|
}
|
|
@@ -3129,12 +3022,18 @@ small {
|
|
|
3129
3022
|
* overflow: hidden will clip it. Pair with grid or flex utilities for group
|
|
3130
3023
|
* layout.
|
|
3131
3024
|
*
|
|
3025
|
+
* .chip is the compact pill form (filter chips, selectable tags): control
|
|
3026
|
+
* height, and a leading dot that turns into the check in a fixed slot, so
|
|
3027
|
+
* checking never moves the label.
|
|
3028
|
+
*
|
|
3132
3029
|
* Public hooks:
|
|
3133
3030
|
* --choice-card-pad
|
|
3134
3031
|
* --choice-card-radius
|
|
3135
3032
|
* --choice-card-border
|
|
3136
3033
|
* --choice-card-bg
|
|
3137
3034
|
* --choice-card-check-size
|
|
3035
|
+
* --choice-chip-dot (read-only, on .chip ::before: the check-slot centre as a
|
|
3036
|
+
* circle() position, for a theme that grows the dot into a fill)
|
|
3138
3037
|
*/
|
|
3139
3038
|
:where(.choice-card) {
|
|
3140
3039
|
--intent: initial;
|
|
@@ -3184,8 +3083,8 @@ small {
|
|
|
3184
3083
|
|
|
3185
3084
|
/* Focus ring on the label follows the hidden input's focus. */
|
|
3186
3085
|
.choice-card:has(:focus-visible) {
|
|
3187
|
-
outline:
|
|
3188
|
-
|
|
3086
|
+
outline: var(--focus-ring-width) solid var(--focus);
|
|
3087
|
+
outline-offset: var(--focus-outline-offset);
|
|
3189
3088
|
}
|
|
3190
3089
|
|
|
3191
3090
|
.choice-card:has(:disabled) {
|
|
@@ -3230,11 +3129,13 @@ small {
|
|
|
3230
3129
|
z-index: 1;
|
|
3231
3130
|
}
|
|
3232
3131
|
|
|
3233
|
-
|
|
3132
|
+
/* :where() keeps the shape at single-class weight, so .chip can reshape the
|
|
3133
|
+
layer without out-arguing the input-type test. */
|
|
3134
|
+
.choice-card:where(:has(input[type="radio"]))::before {
|
|
3234
3135
|
border-radius: var(--radius-full);
|
|
3235
3136
|
}
|
|
3236
3137
|
|
|
3237
|
-
.choice-card:has(input[type="checkbox"])::before {
|
|
3138
|
+
.choice-card:where(:has(input[type="checkbox"]))::before {
|
|
3238
3139
|
border-radius: var(--radius-sm);
|
|
3239
3140
|
}
|
|
3240
3141
|
|
|
@@ -3251,6 +3152,54 @@ small {
|
|
|
3251
3152
|
transform: scale(1);
|
|
3252
3153
|
}
|
|
3253
3154
|
|
|
3155
|
+
/* Chip — the rules above stay in force; this section only relocates the
|
|
3156
|
+
indicator. Must follow them: several ties are won by source order. */
|
|
3157
|
+
.choice-card.chip {
|
|
3158
|
+
--choice-card-pad: 0 var(--control-pad-x);
|
|
3159
|
+
--choice-card-radius: var(--radius-full);
|
|
3160
|
+
--choice-card-check-size: 1em;
|
|
3161
|
+
|
|
3162
|
+
display: inline-flex;
|
|
3163
|
+
align-items: center;
|
|
3164
|
+
min-block-size: var(--control-size);
|
|
3165
|
+
line-height: 1.2;
|
|
3166
|
+
/* The dot layer sits under the label at z-index -1. */
|
|
3167
|
+
isolation: isolate;
|
|
3168
|
+
}
|
|
3169
|
+
|
|
3170
|
+
/* ::before is a full-bleed layer clipped to a dot centred on the check slot,
|
|
3171
|
+
so a theme can grow the same clip into a selected fill. The chip itself
|
|
3172
|
+
must not clip (its focus ring is an outline), hence the inherited radius. */
|
|
3173
|
+
.choice-card.chip::before {
|
|
3174
|
+
--choice-chip-dot: calc(var(--control-pad-x) + var(--choice-card-check-size) / 2) 50%;
|
|
3175
|
+
|
|
3176
|
+
inset: 0;
|
|
3177
|
+
inline-size: auto;
|
|
3178
|
+
block-size: auto;
|
|
3179
|
+
border-radius: inherit;
|
|
3180
|
+
box-shadow: none;
|
|
3181
|
+
background: var(--text-subtle);
|
|
3182
|
+
transform: none;
|
|
3183
|
+
clip-path: circle(0.1875em at var(--choice-chip-dot));
|
|
3184
|
+
z-index: -1;
|
|
3185
|
+
transition: clip-path var(--duration);
|
|
3186
|
+
}
|
|
3187
|
+
|
|
3188
|
+
.choice-card.chip:has(:checked)::before {
|
|
3189
|
+
clip-path: circle(0 at var(--choice-chip-dot));
|
|
3190
|
+
}
|
|
3191
|
+
|
|
3192
|
+
/* The check keeps the card's scale-in, in flow ahead of the label. */
|
|
3193
|
+
.choice-card.chip::after {
|
|
3194
|
+
position: static;
|
|
3195
|
+
order: -1;
|
|
3196
|
+
flex: none;
|
|
3197
|
+
margin-inline-end: 0.375em;
|
|
3198
|
+
background: var(--intent, var(--state-selected));
|
|
3199
|
+
-webkit-mask-size: contain;
|
|
3200
|
+
mask-size: contain;
|
|
3201
|
+
}
|
|
3202
|
+
|
|
3254
3203
|
/* The native input is hidden and the checked marker is custom-drawn: without
|
|
3255
3204
|
this repair, forced colors pushes disc and check to the same system color
|
|
3256
3205
|
and the state disappears. Highlight/Canvas/HighlightText are preserved as-is,
|
|
@@ -3265,6 +3214,20 @@ small {
|
|
|
3265
3214
|
.choice-card:has(:checked)::after {
|
|
3266
3215
|
background: HighlightText;
|
|
3267
3216
|
}
|
|
3217
|
+
|
|
3218
|
+
/* A chip has no disc behind its check: the check carries the state alone,
|
|
3219
|
+
and dropping the checked layer also drops any theme fill. */
|
|
3220
|
+
.choice-card.chip::before {
|
|
3221
|
+
background: CanvasText;
|
|
3222
|
+
}
|
|
3223
|
+
|
|
3224
|
+
.choice-card.chip:has(:checked)::before {
|
|
3225
|
+
content: none;
|
|
3226
|
+
}
|
|
3227
|
+
|
|
3228
|
+
.choice-card.chip:has(:checked)::after {
|
|
3229
|
+
background: CanvasText;
|
|
3230
|
+
}
|
|
3268
3231
|
}
|
|
3269
3232
|
|
|
3270
3233
|
/*
|
|
@@ -3278,7 +3241,6 @@ small {
|
|
|
3278
3241
|
* Arbitrary menu and popover behavior belongs elsewhere.
|
|
3279
3242
|
*
|
|
3280
3243
|
* Internal:
|
|
3281
|
-
* --control-border
|
|
3282
3244
|
* --control-bg
|
|
3283
3245
|
*/
|
|
3284
3246
|
/* This enhancement is excluded from forced-colors, where the native/shared
|
|
@@ -3303,11 +3265,12 @@ small {
|
|
|
3303
3265
|
box-shadow: var(--shadow-popout);
|
|
3304
3266
|
}
|
|
3305
3267
|
|
|
3268
|
+
/* Same inset outline as a focused field (control.css). */
|
|
3306
3269
|
&:open {
|
|
3307
|
-
--control-border: var(--border);
|
|
3308
3270
|
--control-bg: var(--surface-raised);
|
|
3309
|
-
outline:
|
|
3310
|
-
|
|
3271
|
+
outline: var(--focus-ring-width) solid var(--form-invalid-border, var(--focus));
|
|
3272
|
+
outline-offset: calc(var(--focus-ring-width) * -1);
|
|
3273
|
+
box-shadow: none;
|
|
3311
3274
|
}
|
|
3312
3275
|
|
|
3313
3276
|
&::picker-icon {
|
|
@@ -3636,7 +3599,6 @@ small {
|
|
|
3636
3599
|
* --btn-min-size
|
|
3637
3600
|
* --btn-pad-x
|
|
3638
3601
|
* --btn-radius
|
|
3639
|
-
* --btn-focus-color
|
|
3640
3602
|
* --btn-gap
|
|
3641
3603
|
* --btn-hover-overlay
|
|
3642
3604
|
* --btn-default-bg
|
|
@@ -3649,7 +3611,6 @@ small {
|
|
|
3649
3611
|
* --btn-fg
|
|
3650
3612
|
* --btn-border
|
|
3651
3613
|
* --btn-default-hover-bg
|
|
3652
|
-
* --btn-focus-ring-color
|
|
3653
3614
|
* --btn-border-block-end-color
|
|
3654
3615
|
* --btn-border-block-end-width
|
|
3655
3616
|
* --spinner-width
|
|
@@ -3676,7 +3637,6 @@ small {
|
|
|
3676
3637
|
--btn-border: var(--ui-border, var(--btn-default-border, transparent));
|
|
3677
3638
|
--btn-default-hover-bg: var(--btn-bg);
|
|
3678
3639
|
--btn-radius: var(--radius);
|
|
3679
|
-
--btn-focus-color: var(--intent, var(--focus));
|
|
3680
3640
|
--btn-hover-overlay: var(--hover-overlay, rgb(0 0 0 / 0.08));
|
|
3681
3641
|
|
|
3682
3642
|
/* Positioning context for indicators, overlays, and grouped states. */
|
|
@@ -3718,12 +3678,6 @@ small {
|
|
|
3718
3678
|
padding: 0;
|
|
3719
3679
|
}
|
|
3720
3680
|
|
|
3721
|
-
@supports (color: color-mix(in oklch, red, white)) {
|
|
3722
|
-
.btn {
|
|
3723
|
-
--btn-focus-ring-color: color-mix(in oklch, var(--btn-focus-color) 28%, transparent);
|
|
3724
|
-
}
|
|
3725
|
-
}
|
|
3726
|
-
|
|
3727
3681
|
/* .solid/.soft/.outline are shared via variants.css. The baseline --btn-*
|
|
3728
3682
|
fallbacks make .btn solid by default; .ghost/.link are .btn-only variants. */
|
|
3729
3683
|
.btn.ghost {
|
|
@@ -3811,15 +3765,13 @@ small {
|
|
|
3811
3765
|
text-decoration-thickness: 0.12em;
|
|
3812
3766
|
}
|
|
3813
3767
|
|
|
3768
|
+
/* why: the ring must contrast with the surface around the button, not with
|
|
3769
|
+
the button, so it is --focus for every intent and variant — the theme
|
|
3770
|
+
guarantees --focus against --surface and --surface-solid, which covers
|
|
3771
|
+
the page, a solid band and nested surfaces without tracking any context. */
|
|
3814
3772
|
.btn:focus-visible {
|
|
3815
|
-
outline:
|
|
3816
|
-
|
|
3817
|
-
}
|
|
3818
|
-
|
|
3819
|
-
.btn.link:focus-visible {
|
|
3820
|
-
outline: var(--focus-outline);
|
|
3821
|
-
outline-offset: 0.2em;
|
|
3822
|
-
box-shadow: none;
|
|
3773
|
+
outline: var(--focus-ring-width) solid var(--focus);
|
|
3774
|
+
outline-offset: var(--focus-outline-offset);
|
|
3823
3775
|
}
|
|
3824
3776
|
|
|
3825
3777
|
.btn:disabled {
|
|
@@ -3865,6 +3817,98 @@ a.btn[aria-disabled="true"] {
|
|
|
3865
3817
|
}
|
|
3866
3818
|
}
|
|
3867
3819
|
|
|
3820
|
+
/*
|
|
3821
|
+
* Close button — the shared X that removes or closes the thing it sits in.
|
|
3822
|
+
*
|
|
3823
|
+
* Docs: docs/pages/components/close.md
|
|
3824
|
+
*
|
|
3825
|
+
* .close is always the X, painted from the --icon-close mask: an empty
|
|
3826
|
+
* <button class="close" aria-label="…"> needs no icon font and no text glyph.
|
|
3827
|
+
* The mechanism stays with the container (command="request-close",
|
|
3828
|
+
* method="dialog", command="--dismiss", application logic); the class only
|
|
3829
|
+
* names the visual action.
|
|
3830
|
+
*
|
|
3831
|
+
* The containers that host one (alert, badge) tune it from their own files by
|
|
3832
|
+
* declaring the size hooks and their geometry; this file owns the control
|
|
3833
|
+
* and the dialog-surface corner, shared by .modal and .drawer.
|
|
3834
|
+
*
|
|
3835
|
+
* Public hooks:
|
|
3836
|
+
* --close-size
|
|
3837
|
+
* --close-icon-size
|
|
3838
|
+
*/
|
|
3839
|
+
.close {
|
|
3840
|
+
display: inline-grid;
|
|
3841
|
+
place-items: center;
|
|
3842
|
+
flex: none;
|
|
3843
|
+
inline-size: var(--close-size, var(--control-size));
|
|
3844
|
+
block-size: var(--close-size, var(--control-size));
|
|
3845
|
+
padding: 0;
|
|
3846
|
+
border: 0;
|
|
3847
|
+
border-radius: var(--radius);
|
|
3848
|
+
background: transparent;
|
|
3849
|
+
color: inherit;
|
|
3850
|
+
cursor: pointer;
|
|
3851
|
+
}
|
|
3852
|
+
|
|
3853
|
+
.close::before {
|
|
3854
|
+
content: "";
|
|
3855
|
+
inline-size: var(--close-icon-size, 1rem);
|
|
3856
|
+
block-size: var(--close-icon-size, 1rem);
|
|
3857
|
+
background: currentColor;
|
|
3858
|
+
-webkit-mask: var(--icon-close) center / contain no-repeat;
|
|
3859
|
+
mask: var(--icon-close) center / contain no-repeat;
|
|
3860
|
+
}
|
|
3861
|
+
|
|
3862
|
+
/* An inset overlay, not a background: it tints whatever fill the context gave
|
|
3863
|
+
the control (the dialog corner's --surface-subtle) instead of replacing it. */
|
|
3864
|
+
@media (hover: hover) {
|
|
3865
|
+
.close:hover {
|
|
3866
|
+
box-shadow: inset 0 0 0 999px var(--hover-overlay);
|
|
3867
|
+
}
|
|
3868
|
+
}
|
|
3869
|
+
|
|
3870
|
+
.close:active {
|
|
3871
|
+
box-shadow: inset 0 0 0 999px var(--hover-overlay);
|
|
3872
|
+
}
|
|
3873
|
+
|
|
3874
|
+
.close:focus-visible {
|
|
3875
|
+
outline: var(--focus-ring-width) solid var(--focus);
|
|
3876
|
+
outline-offset: var(--focus-outline-offset);
|
|
3877
|
+
}
|
|
3878
|
+
|
|
3879
|
+
/* Dialog corner — out of the content flow, anchored to the positioned dialog
|
|
3880
|
+
surface rather than to the header or a scroll container inside it. Matched
|
|
3881
|
+
on the documented positions (the dialog, its form, its header, or a form
|
|
3882
|
+
inside that header) so a .close inside content — an alert in the body —
|
|
3883
|
+
keeps its own place. */
|
|
3884
|
+
:where(dialog):is(.modal, .drawer) > .close,
|
|
3885
|
+
:where(dialog):is(.modal, .drawer) > :is(form, header) > .close,
|
|
3886
|
+
:where(dialog):is(.modal, .drawer) > :is(form, header) > :is(form, header) > .close {
|
|
3887
|
+
position: absolute;
|
|
3888
|
+
z-index: 1;
|
|
3889
|
+
inset-block-start: var(--space-20);
|
|
3890
|
+
inset-inline-end: var(--space-20);
|
|
3891
|
+
background: var(--surface-subtle);
|
|
3892
|
+
color: var(--text-muted);
|
|
3893
|
+
}
|
|
3894
|
+
|
|
3895
|
+
@media (hover: hover) {
|
|
3896
|
+
:where(dialog):is(.modal, .drawer) > .close:hover,
|
|
3897
|
+
:where(dialog):is(.modal, .drawer) > :is(form, header) > .close:hover,
|
|
3898
|
+
:where(dialog):is(.modal, .drawer) > :is(form, header) > :is(form, header) > .close:hover {
|
|
3899
|
+
color: var(--text);
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
|
|
3903
|
+
@media (forced-colors: active) {
|
|
3904
|
+
/* The X is a currentColor mask fill: without this the system remaps the
|
|
3905
|
+
background to Canvas and the glyph disappears. Kept on the pseudo so the
|
|
3906
|
+
host keeps its native color and focus adaptation. */
|
|
3907
|
+
.close::before {
|
|
3908
|
+
forced-color-adjust: none;
|
|
3909
|
+
}
|
|
3910
|
+
}
|
|
3911
|
+
|
|
3868
3912
|
/*
|
|
3869
3913
|
* Badges for compact labels, counts, and state hints.
|
|
3870
3914
|
*
|
|
@@ -3881,7 +3925,6 @@ a.btn[aria-disabled="true"] {
|
|
|
3881
3925
|
* --badge-icon-size
|
|
3882
3926
|
* --badge-dot-size
|
|
3883
3927
|
* --badge-radius
|
|
3884
|
-
* --badge-dismiss-icon-size
|
|
3885
3928
|
*
|
|
3886
3929
|
* Internal:
|
|
3887
3930
|
* --badge-default-bg
|
|
@@ -3912,7 +3955,10 @@ a.btn[aria-disabled="true"] {
|
|
|
3912
3955
|
--badge-font-size: var(--font-size-sm);
|
|
3913
3956
|
--badge-icon-size: 1em;
|
|
3914
3957
|
--badge-dot-size: 0.5rem;
|
|
3915
|
-
|
|
3958
|
+
/* Declared on the badge, not the button, so an author can still set them
|
|
3959
|
+
on either. Same element as --badge-size, so .sm/.lg carry through. */
|
|
3960
|
+
--close-size: max(var(--badge-size), 24px);
|
|
3961
|
+
--close-icon-size: 0.875em;
|
|
3916
3962
|
--badge-default-bg: var(--surface-subtle);
|
|
3917
3963
|
--badge-default-fg: var(--intent, var(--text));
|
|
3918
3964
|
--badge-default-border: var(--border);
|
|
@@ -3962,8 +4008,8 @@ a.btn[aria-disabled="true"] {
|
|
|
3962
4008
|
--badge-dot-size: 0.625rem;
|
|
3963
4009
|
}
|
|
3964
4010
|
|
|
3965
|
-
/* Decorative children follow the badge's optical size. Text and
|
|
3966
|
-
|
|
4011
|
+
/* Decorative children follow the badge's optical size. Text and the .close
|
|
4012
|
+
button retain their own boxes. */
|
|
3967
4013
|
.badge > :where(svg, img, [aria-hidden="true"]:not(.dot)) {
|
|
3968
4014
|
flex: none;
|
|
3969
4015
|
/* The shared rule maps --badge-icon-size onto font-size. One em is therefore
|
|
@@ -4016,45 +4062,15 @@ a.btn[aria-disabled="true"] {
|
|
|
4016
4062
|
background-color: currentColor;
|
|
4017
4063
|
}
|
|
4018
4064
|
|
|
4019
|
-
.
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
keep the badge visually compact through the negative margins, but never
|
|
4025
|
-
shrink its action target below that literal floor. */
|
|
4026
|
-
min-inline-size: max(var(--badge-size), 24px);
|
|
4027
|
-
min-block-size: max(var(--badge-size), 24px);
|
|
4065
|
+
/* Removable tag — a .close (components/close.css) inside the pill. WCAG 2.5.8
|
|
4066
|
+
floors pointer targets at 24 CSS px: density and local size may keep the
|
|
4067
|
+
badge visually compact through the negative margins, but never shrink the
|
|
4068
|
+
target below that literal floor. */
|
|
4069
|
+
.badge > .close {
|
|
4028
4070
|
margin-block: -0.25rem;
|
|
4029
4071
|
margin-inline-start: -0.15em;
|
|
4030
4072
|
margin-inline-end: -0.45em;
|
|
4031
|
-
padding: 0.25rem;
|
|
4032
|
-
border: 0;
|
|
4033
4073
|
border-radius: var(--radius-full);
|
|
4034
|
-
background: transparent;
|
|
4035
|
-
color: inherit;
|
|
4036
|
-
font: inherit;
|
|
4037
|
-
line-height: 1;
|
|
4038
|
-
cursor: pointer;
|
|
4039
|
-
}
|
|
4040
|
-
|
|
4041
|
-
/* Dismiss mark — an empty dismiss button paints its own X from the shared
|
|
4042
|
-
--icon-close mask, so no icon font or text glyph is required in the markup
|
|
4043
|
-
(same contract as .alert-dismiss and .dialog-close). A mask box is a block
|
|
4044
|
-
box, so place-items centers the ink itself; a text glyph would only center
|
|
4045
|
-
its line box and land the ink low, because a lowercase x has no descender
|
|
4046
|
-
and only x-height of ascent. A button with its own content keeps it. */
|
|
4047
|
-
.badge > button:empty::before {
|
|
4048
|
-
content: "";
|
|
4049
|
-
inline-size: var(--badge-dismiss-icon-size);
|
|
4050
|
-
block-size: var(--badge-dismiss-icon-size);
|
|
4051
|
-
background: currentColor;
|
|
4052
|
-
-webkit-mask: var(--icon-close) center / contain no-repeat;
|
|
4053
|
-
mask: var(--icon-close) center / contain no-repeat;
|
|
4054
|
-
}
|
|
4055
|
-
|
|
4056
|
-
.badge > button:not(:disabled):hover {
|
|
4057
|
-
background: var(--hover-overlay);
|
|
4058
4074
|
}
|
|
4059
4075
|
|
|
4060
4076
|
/* Hover recipes — sets --ui-hover-bg consumed by the hover rule below.
|
|
@@ -4145,7 +4161,6 @@ a.badge[aria-disabled="true"] {
|
|
|
4145
4161
|
* --alert-font-size
|
|
4146
4162
|
* --alert-radius
|
|
4147
4163
|
* --alert-icon-size
|
|
4148
|
-
* --alert-dismiss-size
|
|
4149
4164
|
* --alert-border-inline-start-color
|
|
4150
4165
|
* --alert-border-inline-start-width
|
|
4151
4166
|
*
|
|
@@ -4156,7 +4171,6 @@ a.badge[aria-disabled="true"] {
|
|
|
4156
4171
|
* --alert-bg
|
|
4157
4172
|
* --alert-fg
|
|
4158
4173
|
* --alert-border
|
|
4159
|
-
* --alert-dismiss-icon-size
|
|
4160
4174
|
*/
|
|
4161
4175
|
/* Intent boundary — inherited intents do not tint this component.
|
|
4162
4176
|
Local intent classes still win because :where() has zero specificity. */
|
|
@@ -4205,13 +4219,13 @@ a.badge[aria-disabled="true"] {
|
|
|
4205
4219
|
The slots are excluded because they are anatomy, not content: an
|
|
4206
4220
|
admonition's title bar has to stay flush against its body. The list follows
|
|
4207
4221
|
the anatomy, so it grows with it. */
|
|
4208
|
-
.alert > :not(:where(.alert-icon, .
|
|
4222
|
+
.alert > :not(:where(.alert-icon, .close, .alert-title, .alert-body)) {
|
|
4209
4223
|
margin-block: 0;
|
|
4210
4224
|
}
|
|
4211
4225
|
|
|
4212
4226
|
.alert
|
|
4213
|
-
> :not(:where(.alert-icon, .
|
|
4214
|
-
+ :not(:where(.alert-icon, .
|
|
4227
|
+
> :not(:where(.alert-icon, .close, .alert-title, .alert-body))
|
|
4228
|
+
+ :not(:where(.alert-icon, .close, .alert-title, .alert-body)) {
|
|
4215
4229
|
margin-block-start: var(--space-10);
|
|
4216
4230
|
}
|
|
4217
4231
|
|
|
@@ -4249,42 +4263,24 @@ a.badge[aria-disabled="true"] {
|
|
|
4249
4263
|
vertical-align: -0.125em;
|
|
4250
4264
|
}
|
|
4251
4265
|
|
|
4252
|
-
/*
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
--
|
|
4260
|
-
--alert-dismiss-icon-size: 0.875rem;
|
|
4261
|
-
|
|
4262
|
-
display: inline-grid;
|
|
4263
|
-
place-items: center;
|
|
4264
|
-
inline-size: var(--alert-dismiss-size);
|
|
4265
|
-
block-size: var(--alert-dismiss-size);
|
|
4266
|
-
flex: none;
|
|
4267
|
-
padding: 0;
|
|
4268
|
-
border: 0;
|
|
4269
|
-
border-radius: var(--radius);
|
|
4270
|
-
background: transparent;
|
|
4271
|
-
color: currentColor;
|
|
4272
|
-
opacity: 0.65;
|
|
4273
|
-
cursor: pointer;
|
|
4266
|
+
/* Close (components/close.css) — compact, and at reduced opacity so it stays
|
|
4267
|
+
secondary inside an already-colored alert. Standard alerts place it as a
|
|
4268
|
+
direct trailing child; admonitions place it inside .alert-title (see
|
|
4269
|
+
below). The sizes are declared on the alert so an author can still set
|
|
4270
|
+
them on the alert or on the button. */
|
|
4271
|
+
.alert {
|
|
4272
|
+
--close-size: 1.75rem;
|
|
4273
|
+
--close-icon-size: 0.875rem;
|
|
4274
4274
|
}
|
|
4275
4275
|
|
|
4276
|
-
.alert
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
block-size: var(--alert-dismiss-icon-size);
|
|
4280
|
-
background: currentColor;
|
|
4281
|
-
-webkit-mask: var(--icon-close) center / contain no-repeat;
|
|
4282
|
-
mask: var(--icon-close) center / contain no-repeat;
|
|
4276
|
+
.alert > .close,
|
|
4277
|
+
.alert > .alert-title > .close {
|
|
4278
|
+
opacity: 0.65;
|
|
4283
4279
|
}
|
|
4284
4280
|
|
|
4285
4281
|
@media (hover: hover) {
|
|
4286
|
-
.alert
|
|
4287
|
-
|
|
4282
|
+
.alert > .close:hover,
|
|
4283
|
+
.alert > .alert-title > .close:hover {
|
|
4288
4284
|
opacity: 1;
|
|
4289
4285
|
}
|
|
4290
4286
|
}
|
|
@@ -4293,7 +4289,7 @@ a.badge[aria-disabled="true"] {
|
|
|
4293
4289
|
both. Icon and dismiss are pinned to the outer columns; anything else (the
|
|
4294
4290
|
message content) flows into the remaining text column. */
|
|
4295
4291
|
@supports selector(.alert:has(> *)) {
|
|
4296
|
-
.alert:has(> .alert-icon, > .
|
|
4292
|
+
.alert:has(> .alert-icon, > .close) {
|
|
4297
4293
|
display: grid;
|
|
4298
4294
|
grid-template-columns: minmax(0, 1fr);
|
|
4299
4295
|
align-items: var(--items-align, start);
|
|
@@ -4312,19 +4308,19 @@ a.badge[aria-disabled="true"] {
|
|
|
4312
4308
|
margin-block-start: 0.125em;
|
|
4313
4309
|
}
|
|
4314
4310
|
|
|
4315
|
-
.alert:has(> .alert-icon) > :not(.alert-icon, .
|
|
4311
|
+
.alert:has(> .alert-icon) > :not(.alert-icon, .close) {
|
|
4316
4312
|
grid-column: 2;
|
|
4317
4313
|
}
|
|
4318
4314
|
|
|
4319
|
-
.alert:has(> .
|
|
4315
|
+
.alert:has(> .close) {
|
|
4320
4316
|
grid-template-columns: minmax(0, 1fr) auto;
|
|
4321
4317
|
}
|
|
4322
4318
|
|
|
4323
|
-
.alert:has(> .alert-icon):has(> .
|
|
4319
|
+
.alert:has(> .alert-icon):has(> .close) {
|
|
4324
4320
|
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
4325
4321
|
}
|
|
4326
4322
|
|
|
4327
|
-
.alert:has(> .
|
|
4323
|
+
.alert:has(> .close) > .close {
|
|
4328
4324
|
grid-column: -2 / -1;
|
|
4329
4325
|
grid-row: 1;
|
|
4330
4326
|
}
|
|
@@ -4372,8 +4368,8 @@ a.badge[aria-disabled="true"] {
|
|
|
4372
4368
|
border-start-end-radius: calc(var(--alert-radius, var(--radius-lg)) - var(--border-width));
|
|
4373
4369
|
}
|
|
4374
4370
|
|
|
4375
|
-
/*
|
|
4376
|
-
.alert.admonition > .alert-title > .
|
|
4371
|
+
/* Close inside an admonition title bar rides the inline end. */
|
|
4372
|
+
.alert.admonition > .alert-title > .close {
|
|
4377
4373
|
margin-inline-start: auto;
|
|
4378
4374
|
}
|
|
4379
4375
|
|
|
@@ -4466,7 +4462,9 @@ a.badge[aria-disabled="true"] {
|
|
|
4466
4462
|
*
|
|
4467
4463
|
* Child contract:
|
|
4468
4464
|
* Direct .bleed children escape --card-pad (negative inset, wider media);
|
|
4469
|
-
* edge children inherit the card corners. .bleed must be a direct child
|
|
4465
|
+
* edge children inherit the card corners. .bleed must be a direct child, and
|
|
4466
|
+
* its rules live in components/bleed.css — the card relays --card-pad to the
|
|
4467
|
+
* shared --surface-pad that they read.
|
|
4470
4468
|
*
|
|
4471
4469
|
* Internal:
|
|
4472
4470
|
* --card-bg
|
|
@@ -4497,6 +4495,10 @@ a.badge[aria-disabled="true"] {
|
|
|
4497
4495
|
--card-bg: var(--ui-bg, var(--surface-raised));
|
|
4498
4496
|
--card-fg: var(--ui-fg, var(--text));
|
|
4499
4497
|
|
|
4498
|
+
/* The inset a direct .bleed child cancels (components/bleed.css). Relayed
|
|
4499
|
+
from --card-pad so .compact and an author override both reach it. */
|
|
4500
|
+
--surface-pad: var(--card-pad);
|
|
4501
|
+
|
|
4500
4502
|
/* Keep contextual descendants tied to the surface this card actually owns. */
|
|
4501
4503
|
--heading: var(--card-fg);
|
|
4502
4504
|
--busy-overlay-bg: var(--card-bg);
|
|
@@ -4562,45 +4564,6 @@ a.badge[aria-disabled="true"] {
|
|
|
4562
4564
|
--card-pad: var(--space-20);
|
|
4563
4565
|
}
|
|
4564
4566
|
|
|
4565
|
-
/* Full-bleed children */
|
|
4566
|
-
.card > .bleed {
|
|
4567
|
-
margin-inline: calc(var(--card-pad) * -1);
|
|
4568
|
-
max-inline-size: none;
|
|
4569
|
-
}
|
|
4570
|
-
|
|
4571
|
-
.card > .bleed:first-child {
|
|
4572
|
-
margin-block-start: calc(var(--card-pad) * -1);
|
|
4573
|
-
border-start-start-radius: inherit;
|
|
4574
|
-
border-start-end-radius: inherit;
|
|
4575
|
-
}
|
|
4576
|
-
|
|
4577
|
-
.card > .bleed:last-child {
|
|
4578
|
-
margin-block-end: calc(var(--card-pad) * -1);
|
|
4579
|
-
border-end-start-radius: inherit;
|
|
4580
|
-
border-end-end-radius: inherit;
|
|
4581
|
-
}
|
|
4582
|
-
|
|
4583
|
-
/* Media bleed */
|
|
4584
|
-
.card > :where(img, picture, video, canvas, svg).bleed {
|
|
4585
|
-
display: block;
|
|
4586
|
-
inline-size: calc(100% + var(--card-pad) * 2);
|
|
4587
|
-
max-inline-size: none;
|
|
4588
|
-
}
|
|
4589
|
-
|
|
4590
|
-
.card > picture.bleed {
|
|
4591
|
-
overflow: hidden;
|
|
4592
|
-
}
|
|
4593
|
-
|
|
4594
|
-
.card > picture.bleed > img {
|
|
4595
|
-
display: block;
|
|
4596
|
-
inline-size: 100%;
|
|
4597
|
-
block-size: auto;
|
|
4598
|
-
}
|
|
4599
|
-
|
|
4600
|
-
/* Padded structural bleed */
|
|
4601
|
-
.card > :where(header, footer, figure).bleed {
|
|
4602
|
-
padding: var(--card-pad);
|
|
4603
|
-
}
|
|
4604
4567
|
@media print {
|
|
4605
4568
|
.card {
|
|
4606
4569
|
border: var(--border-width) solid CanvasText;
|
|
@@ -4622,8 +4585,9 @@ a.badge[aria-disabled="true"] {
|
|
|
4622
4585
|
* --dialog-icon-size
|
|
4623
4586
|
* --dialog-icon-glyph-size
|
|
4624
4587
|
*
|
|
4588
|
+
* The corner close control is .close (components/close.css).
|
|
4589
|
+
*
|
|
4625
4590
|
* Internal:
|
|
4626
|
-
* --dialog-close-icon-size
|
|
4627
4591
|
* --dialog-focus-bleed
|
|
4628
4592
|
* --dialog-viewport-block
|
|
4629
4593
|
* --dialog-viewport-gap-default
|
|
@@ -4709,11 +4673,16 @@ a.badge[aria-disabled="true"] {
|
|
|
4709
4673
|
|
|
4710
4674
|
:where(dialog).modal.scrollable > form > :not(:is(header, footer)),
|
|
4711
4675
|
:where(dialog).modal.scrollable > .stack > :not(:is(header, footer)) {
|
|
4712
|
-
--dialog-focus-bleed: calc(
|
|
4676
|
+
--dialog-focus-bleed: calc(
|
|
4677
|
+
var(--focus-outline-offset) +
|
|
4678
|
+
max(var(--focus-ring-width), var(--border-width) * 2)
|
|
4679
|
+
);
|
|
4713
4680
|
|
|
4714
4681
|
min-block-size: 0;
|
|
4715
4682
|
/* Keep focus paint inside the clip without moving the body off the fixed
|
|
4716
|
-
header and footer edges.
|
|
4683
|
+
header and footer edges. The reserve covers the thicker of the two
|
|
4684
|
+
outside indicators: the :focus baseline (2 × border) and the component
|
|
4685
|
+
line (--focus-ring-width, 3px in bootstrap-v6). */
|
|
4717
4686
|
margin-inline: calc(var(--dialog-focus-bleed) * -1);
|
|
4718
4687
|
padding-inline: var(--dialog-focus-bleed);
|
|
4719
4688
|
overflow: auto;
|
|
@@ -4736,7 +4705,7 @@ a.badge[aria-disabled="true"] {
|
|
|
4736
4705
|
The form/.stack anatomy is structural, so it stays in :where() and never
|
|
4737
4706
|
lends .stack's specificity to a bare <form>. */
|
|
4738
4707
|
:where(dialog).modal > :where(form, .stack) > header {
|
|
4739
|
-
padding-inline-end: calc(var(--control-size) + var(--space-30));
|
|
4708
|
+
padding-inline-end: calc(var(--close-size, var(--control-size)) + var(--space-30));
|
|
4740
4709
|
}
|
|
4741
4710
|
|
|
4742
4711
|
:where(dialog).modal > :where(form, .stack) > footer {
|
|
@@ -4900,59 +4869,6 @@ html.has-modal-open.had-scrollbar {
|
|
|
4900
4869
|
}
|
|
4901
4870
|
}
|
|
4902
4871
|
|
|
4903
|
-
/* Close control. A close button is an action attached to a surface, so it
|
|
4904
|
-
never participates in the content flow — absolute at the panel's top
|
|
4905
|
-
inline-end, inside the surface. The glyph comes from --icon-close, masked to
|
|
4906
|
-
currentColor, so no icon font or text glyph is required in the markup. */
|
|
4907
|
-
.dialog-close {
|
|
4908
|
-
--dialog-close-icon-size: 1rem;
|
|
4909
|
-
|
|
4910
|
-
display: inline-grid;
|
|
4911
|
-
place-items: center;
|
|
4912
|
-
padding: 0;
|
|
4913
|
-
border: 0;
|
|
4914
|
-
border-radius: var(--radius);
|
|
4915
|
-
color: var(--text-muted);
|
|
4916
|
-
cursor: pointer;
|
|
4917
|
-
|
|
4918
|
-
/* Out of flow, anchored to the positioned dialog surface, not the header or
|
|
4919
|
-
any scroll container inside it. */
|
|
4920
|
-
position: absolute;
|
|
4921
|
-
z-index: 1;
|
|
4922
|
-
inset-block-start: var(--space-20);
|
|
4923
|
-
inset-inline-end: var(--space-20);
|
|
4924
|
-
|
|
4925
|
-
inline-size: var(--control-size);
|
|
4926
|
-
block-size: var(--control-size);
|
|
4927
|
-
|
|
4928
|
-
background: var(--surface-subtle);
|
|
4929
|
-
}
|
|
4930
|
-
|
|
4931
|
-
.dialog-close::before {
|
|
4932
|
-
content: "";
|
|
4933
|
-
inline-size: var(--dialog-close-icon-size);
|
|
4934
|
-
block-size: var(--dialog-close-icon-size);
|
|
4935
|
-
background: currentColor;
|
|
4936
|
-
-webkit-mask: var(--icon-close) center / contain no-repeat;
|
|
4937
|
-
mask: var(--icon-close) center / contain no-repeat;
|
|
4938
|
-
}
|
|
4939
|
-
|
|
4940
|
-
@media (hover: hover) {
|
|
4941
|
-
.dialog-close:hover {
|
|
4942
|
-
box-shadow: inset 0 0 0 999px var(--hover-overlay);
|
|
4943
|
-
color: var(--text);
|
|
4944
|
-
}
|
|
4945
|
-
}
|
|
4946
|
-
|
|
4947
|
-
.dialog-close:active {
|
|
4948
|
-
box-shadow: inset 0 0 0 999px var(--hover-overlay);
|
|
4949
|
-
}
|
|
4950
|
-
|
|
4951
|
-
.dialog-close:focus-visible {
|
|
4952
|
-
outline: 2px solid transparent;
|
|
4953
|
-
box-shadow: var(--focus-ring-shadow);
|
|
4954
|
-
}
|
|
4955
|
-
|
|
4956
4872
|
/*
|
|
4957
4873
|
* Drawer — modal side-sheet built on the native dialog element.
|
|
4958
4874
|
*
|
|
@@ -4972,6 +4888,8 @@ html.has-modal-open.had-scrollbar {
|
|
|
4972
4888
|
* --drawer-size
|
|
4973
4889
|
* --drawer-pad
|
|
4974
4890
|
*
|
|
4891
|
+
* The corner close control is .close (components/close.css).
|
|
4892
|
+
*
|
|
4975
4893
|
* Internal:
|
|
4976
4894
|
* --drawer-closed-translate (side/RTL positioning state)
|
|
4977
4895
|
* --drawer-focus-bleed
|
|
@@ -4981,6 +4899,8 @@ html.has-modal-open.had-scrollbar {
|
|
|
4981
4899
|
:where(dialog).drawer {
|
|
4982
4900
|
--drawer-size: 20rem;
|
|
4983
4901
|
--drawer-pad: var(--space-40);
|
|
4902
|
+
/* The inset a direct .bleed child cancels (components/bleed.css). */
|
|
4903
|
+
--surface-pad: var(--drawer-pad);
|
|
4984
4904
|
--drawer-closed-translate: -100%;
|
|
4985
4905
|
/* Exit is shorter than entry so the panel clears quickly; the scrim reads
|
|
4986
4906
|
the same variable through inheritance and never lags the panel. */
|
|
@@ -5080,15 +5000,20 @@ html.has-modal-open.had-scrollbar {
|
|
|
5080
5000
|
/* The body region absorbs the panel's height and scrolls. A drawer is a
|
|
5081
5001
|
full-height shell by definition, so it always scrolls its body — unlike the
|
|
5082
5002
|
modal, which sizes to its content and opts in with .scrollable. Header and
|
|
5083
|
-
footer keep their own tracks; .
|
|
5003
|
+
footer keep their own tracks; .close is named because a generic
|
|
5084
5004
|
exclusion list is easier to trust than its `position: absolute`. */
|
|
5085
|
-
:where(dialog).drawer > :not(:is(header, footer, form, .
|
|
5086
|
-
:where(dialog).drawer > form > :not(:is(header, footer, .
|
|
5087
|
-
--drawer-focus-bleed: calc(
|
|
5005
|
+
:where(dialog).drawer > :not(:is(header, footer, form, .close)),
|
|
5006
|
+
:where(dialog).drawer > form > :not(:is(header, footer, .close)) {
|
|
5007
|
+
--drawer-focus-bleed: calc(
|
|
5008
|
+
var(--focus-outline-offset) +
|
|
5009
|
+
max(var(--focus-ring-width), var(--border-width) * 2)
|
|
5010
|
+
);
|
|
5088
5011
|
|
|
5089
5012
|
min-block-size: 0;
|
|
5090
5013
|
/* Keep focus paint inside the clip without moving the body off the fixed
|
|
5091
|
-
header and footer edges.
|
|
5014
|
+
header and footer edges. The reserve covers the thicker of the two
|
|
5015
|
+
outside indicators: the :focus baseline (2 × border) and the component
|
|
5016
|
+
line (--focus-ring-width, 3px in bootstrap-v6). */
|
|
5092
5017
|
margin-inline: calc(var(--drawer-focus-bleed) * -1);
|
|
5093
5018
|
padding-inline: var(--drawer-focus-bleed);
|
|
5094
5019
|
overflow: auto;
|
|
@@ -5160,57 +5085,87 @@ html.has-modal-open.had-scrollbar {
|
|
|
5160
5085
|
control. */
|
|
5161
5086
|
:where(dialog).drawer > header,
|
|
5162
5087
|
:where(dialog).drawer > form > header {
|
|
5163
|
-
padding-inline-end: calc(var(--control-size) + var(--space-30));
|
|
5088
|
+
padding-inline-end: calc(var(--close-size, var(--control-size)) + var(--space-30));
|
|
5164
5089
|
}
|
|
5165
5090
|
|
|
5166
|
-
/*
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
.
|
|
5170
|
-
|
|
5091
|
+
/*
|
|
5092
|
+
* Full-bleed children — the shared escape hatch for a padded surface.
|
|
5093
|
+
*
|
|
5094
|
+
* Docs: docs/pages/components/bleed.md
|
|
5095
|
+
*
|
|
5096
|
+
* A surface that insets its direct children relays that inset to them as
|
|
5097
|
+
* --surface-pad; a direct .bleed child cancels it and reaches the surface
|
|
5098
|
+
* edge. Card and drawer pad at the container level, so they own the token
|
|
5099
|
+
* there; the accordion pads its summary and its panel instead, so the panel
|
|
5100
|
+
* owns it and the item does not.
|
|
5101
|
+
*
|
|
5102
|
+
* Why the token is handed to the children rather than declared on the surface:
|
|
5103
|
+
* custom properties inherit, so a surface-level declaration would also reach a
|
|
5104
|
+
* grandchild, which sits at its wrapper's edge rather than the surface's and
|
|
5105
|
+
* would pull a negative margin out of the wrong box. Relaying it one level and
|
|
5106
|
+
* clearing it below keeps the child combinator's guarantee while leaving the
|
|
5107
|
+
* .bleed rules themselves free of the surface list.
|
|
5108
|
+
*
|
|
5109
|
+
* Why the clearing rule comes first: both rules are zero-specificity, so
|
|
5110
|
+
* source order decides where they overlap, and they overlap on exactly one
|
|
5111
|
+
* element — a direct child of a surface nested inside another surface. That
|
|
5112
|
+
* child must keep the inner surface's inset, so the relay has to win.
|
|
5113
|
+
*
|
|
5114
|
+
* Public hooks:
|
|
5115
|
+
* --bleed-pad (read-only: the inset relayed to a surface's direct children;
|
|
5116
|
+
* pad a band that is not a header/footer/figure with it)
|
|
5117
|
+
*
|
|
5118
|
+
* Framework plumbing:
|
|
5119
|
+
* --surface-pad (declared by the surface)
|
|
5120
|
+
*/
|
|
5121
|
+
:where(.card, .drawer, .accordion details > summary + *) > * * {
|
|
5122
|
+
--bleed-pad: initial;
|
|
5123
|
+
}
|
|
5171
5124
|
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
border: 0;
|
|
5176
|
-
border-radius: var(--radius);
|
|
5177
|
-
color: var(--text-muted);
|
|
5178
|
-
cursor: pointer;
|
|
5125
|
+
:where(.card, .drawer, .accordion details > summary + *) > * {
|
|
5126
|
+
--bleed-pad: var(--surface-pad);
|
|
5127
|
+
}
|
|
5179
5128
|
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5129
|
+
/* Inert without a surface: the fallback resolves the negative margin to zero
|
|
5130
|
+
rather than dropping the declaration and inheriting a stray inset. */
|
|
5131
|
+
.bleed {
|
|
5132
|
+
margin-inline: calc(var(--bleed-pad, 0px) * -1);
|
|
5133
|
+
max-inline-size: none;
|
|
5134
|
+
}
|
|
5184
5135
|
|
|
5185
|
-
|
|
5186
|
-
block-
|
|
5136
|
+
.bleed:first-child {
|
|
5137
|
+
margin-block-start: calc(var(--bleed-pad, 0px) * -1);
|
|
5138
|
+
border-start-start-radius: inherit;
|
|
5139
|
+
border-start-end-radius: inherit;
|
|
5140
|
+
}
|
|
5187
5141
|
|
|
5188
|
-
|
|
5142
|
+
.bleed:last-child {
|
|
5143
|
+
margin-block-end: calc(var(--bleed-pad, 0px) * -1);
|
|
5144
|
+
border-end-start-radius: inherit;
|
|
5145
|
+
border-end-end-radius: inherit;
|
|
5189
5146
|
}
|
|
5190
5147
|
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
-webkit-mask: var(--icon-close) center / contain no-repeat;
|
|
5197
|
-
mask: var(--icon-close) center / contain no-repeat;
|
|
5148
|
+
/* Media bleed */
|
|
5149
|
+
:where(img, picture, video, canvas, svg).bleed {
|
|
5150
|
+
display: block;
|
|
5151
|
+
inline-size: calc(100% + var(--bleed-pad, 0px) * 2);
|
|
5152
|
+
max-inline-size: none;
|
|
5198
5153
|
}
|
|
5199
5154
|
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
box-shadow: inset 0 0 0 999px var(--hover-overlay);
|
|
5203
|
-
color: var(--text);
|
|
5204
|
-
}
|
|
5155
|
+
picture.bleed {
|
|
5156
|
+
overflow: hidden;
|
|
5205
5157
|
}
|
|
5206
5158
|
|
|
5207
|
-
.
|
|
5208
|
-
|
|
5159
|
+
picture.bleed > img {
|
|
5160
|
+
display: block;
|
|
5161
|
+
inline-size: 100%;
|
|
5162
|
+
block-size: auto;
|
|
5209
5163
|
}
|
|
5210
5164
|
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5165
|
+
/* Padded structural bleed — the band keeps the surface's own rhythm while its
|
|
5166
|
+
background reaches the edge. */
|
|
5167
|
+
:where(header, footer, figure).bleed {
|
|
5168
|
+
padding: var(--bleed-pad, 0px);
|
|
5214
5169
|
}
|
|
5215
5170
|
|
|
5216
5171
|
|
|
@@ -5287,6 +5242,7 @@ html.has-modal-open.had-scrollbar {
|
|
|
5287
5242
|
border: var(--border-width) solid var(--border);
|
|
5288
5243
|
border-radius: var(--radius);
|
|
5289
5244
|
background: var(--surface-raised);
|
|
5245
|
+
color: var(--text);
|
|
5290
5246
|
box-shadow: var(--shadow-popout);
|
|
5291
5247
|
list-style: none;
|
|
5292
5248
|
opacity: 1;
|
|
@@ -5519,6 +5475,18 @@ html.has-modal-open.had-scrollbar {
|
|
|
5519
5475
|
border-block-start: var(--border-width) solid var(--border);
|
|
5520
5476
|
}
|
|
5521
5477
|
|
|
5478
|
+
@media (forced-colors: active) {
|
|
5479
|
+
/* The check mark is a currentColor mask fill and the mixed/radio marks are
|
|
5480
|
+
currentColor gradients: without this the system remaps them to Canvas or
|
|
5481
|
+
drops the image and the state disappears. Kept on the pseudo so the host
|
|
5482
|
+
keeps its native color and focus adaptation. */
|
|
5483
|
+
.menu-item[role="menuitemcheckbox"][aria-checked="true"]::before,
|
|
5484
|
+
.menu-item[role="menuitemcheckbox"][aria-checked="mixed"]::before,
|
|
5485
|
+
.menu-item[role="menuitemradio"]::before {
|
|
5486
|
+
forced-color-adjust: none;
|
|
5487
|
+
}
|
|
5488
|
+
}
|
|
5489
|
+
|
|
5522
5490
|
/*
|
|
5523
5491
|
* Tooltip — supplemental label overlaid near a trigger.
|
|
5524
5492
|
*
|
|
@@ -5680,11 +5648,15 @@ html.has-modal-open.had-scrollbar {
|
|
|
5680
5648
|
*
|
|
5681
5649
|
* Public hooks:
|
|
5682
5650
|
* --accordion-radius
|
|
5651
|
+
* --accordion-pad
|
|
5683
5652
|
* --accordion-marker-color
|
|
5653
|
+
* --accordion-marker-size
|
|
5684
5654
|
*/
|
|
5685
5655
|
.accordion {
|
|
5686
5656
|
--accordion-radius: var(--radius);
|
|
5657
|
+
--accordion-pad: var(--space-40);
|
|
5687
5658
|
--accordion-marker-color: var(--text-muted);
|
|
5659
|
+
--accordion-marker-size: 1rem;
|
|
5688
5660
|
|
|
5689
5661
|
border: var(--border-width) solid var(--border);
|
|
5690
5662
|
border-radius: var(--accordion-radius);
|
|
@@ -5701,7 +5673,7 @@ html.has-modal-open.had-scrollbar {
|
|
|
5701
5673
|
gap: var(--space-30);
|
|
5702
5674
|
cursor: pointer;
|
|
5703
5675
|
list-style: none;
|
|
5704
|
-
padding: var(--
|
|
5676
|
+
padding: var(--accordion-pad);
|
|
5705
5677
|
color: var(--text);
|
|
5706
5678
|
font-weight: var(--font-weight-medium);
|
|
5707
5679
|
}
|
|
@@ -5717,12 +5689,17 @@ html.has-modal-open.had-scrollbar {
|
|
|
5717
5689
|
display: none;
|
|
5718
5690
|
}
|
|
5719
5691
|
|
|
5692
|
+
/* The marker is the chevron itself: the box is filled with
|
|
5693
|
+
--accordion-marker-color and then clipped to the glyph by the mask. Anything
|
|
5694
|
+
painted on this pseudo-element — a background, a ring, a shadow — is clipped
|
|
5695
|
+
with it, so a decorated marker (a chevron in a pill) needs a real element;
|
|
5696
|
+
see the accordion docs for the opt-out. */
|
|
5720
5697
|
.accordion summary::after {
|
|
5721
5698
|
content: "";
|
|
5722
5699
|
flex: 0 0 auto;
|
|
5723
5700
|
margin-inline-start: auto;
|
|
5724
|
-
inline-size:
|
|
5725
|
-
block-size:
|
|
5701
|
+
inline-size: var(--accordion-marker-size);
|
|
5702
|
+
block-size: var(--accordion-marker-size);
|
|
5726
5703
|
background-color: var(--accordion-marker-color);
|
|
5727
5704
|
-webkit-mask: var(--icon-chevron) center / contain no-repeat;
|
|
5728
5705
|
mask: var(--icon-chevron) center / contain no-repeat;
|
|
@@ -5733,9 +5710,14 @@ html.has-modal-open.had-scrollbar {
|
|
|
5733
5710
|
transform: rotate(180deg);
|
|
5734
5711
|
}
|
|
5735
5712
|
|
|
5713
|
+
/* The panel, not the item, owns the inset here — .separated paints a card on
|
|
5714
|
+
<details> but leaves it unpadded — so the panel is what a .bleed child
|
|
5715
|
+
escapes (components/bleed.css). */
|
|
5736
5716
|
.accordion details > summary + * {
|
|
5737
|
-
|
|
5738
|
-
|
|
5717
|
+
--surface-pad: var(--accordion-pad);
|
|
5718
|
+
|
|
5719
|
+
padding-block: 0 var(--accordion-pad);
|
|
5720
|
+
padding-inline: var(--accordion-pad);
|
|
5739
5721
|
color: var(--text-muted);
|
|
5740
5722
|
margin-block: 0;
|
|
5741
5723
|
}
|
|
@@ -5744,6 +5726,9 @@ html.has-modal-open.had-scrollbar {
|
|
|
5744
5726
|
padding-block-start: var(--space-10);
|
|
5745
5727
|
}
|
|
5746
5728
|
|
|
5729
|
+
/* Also cancels the lift of a first .bleed child (bleed.css, lower
|
|
5730
|
+
specificity): the panel's top edge is the summary, not the item edge, so a
|
|
5731
|
+
bleed reaches only the sides and the bottom. */
|
|
5747
5732
|
.accordion details > summary + * > :first-child {
|
|
5748
5733
|
margin-block-start: 0;
|
|
5749
5734
|
}
|
|
@@ -5768,6 +5753,12 @@ html.has-modal-open.had-scrollbar {
|
|
|
5768
5753
|
padding-inline: 0;
|
|
5769
5754
|
}
|
|
5770
5755
|
|
|
5756
|
+
/* Flush already sits at the embedding surface's edge, so there is no inline
|
|
5757
|
+
inset left for a .bleed child to cancel. */
|
|
5758
|
+
.accordion.flush details > summary + * {
|
|
5759
|
+
--surface-pad: 0px;
|
|
5760
|
+
}
|
|
5761
|
+
|
|
5771
5762
|
/* Separated — each item owns a full card. The item's own border beats the
|
|
5772
5763
|
shared details + details separator on specificity alone, so nothing needs
|
|
5773
5764
|
undoing for the second item onwards. */
|
|
@@ -5784,9 +5775,23 @@ html.has-modal-open.had-scrollbar {
|
|
|
5784
5775
|
background: var(--surface-raised);
|
|
5785
5776
|
}
|
|
5786
5777
|
|
|
5778
|
+
/* A last .bleed child inherits its bottom corners from the panel, but the
|
|
5779
|
+
rounded box is the item (.separated) or the group (last item) — relay the
|
|
5780
|
+
radius to the panel. Explicit rather than inherit: in browsers with
|
|
5781
|
+
::details-content the panel's flat-tree parent is that slot, not
|
|
5782
|
+
<details>. */
|
|
5783
|
+
.accordion.separated > details > summary + *,
|
|
5784
|
+
.accordion:not(.flush) > details:last-child > summary + * {
|
|
5785
|
+
border-end-start-radius: var(--accordion-radius);
|
|
5786
|
+
border-end-end-radius: var(--accordion-radius);
|
|
5787
|
+
}
|
|
5788
|
+
|
|
5787
5789
|
@media (forced-colors: active) {
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
+
/* Written on the property, not the hook: an application hook override at
|
|
5791
|
+
the same specificity loads after this file and would win the variable,
|
|
5792
|
+
leaving a Canvas-remapped fill. The property keeps CanvasText. */
|
|
5793
|
+
.accordion summary::after {
|
|
5794
|
+
background-color: CanvasText;
|
|
5790
5795
|
}
|
|
5791
5796
|
}
|
|
5792
5797
|
|
|
@@ -6781,6 +6786,16 @@ a.avatar[aria-disabled="true"] {
|
|
|
6781
6786
|
transform: none;
|
|
6782
6787
|
}
|
|
6783
6788
|
|
|
6789
|
+
@media (forced-colors: active) {
|
|
6790
|
+
/* Both toggle markers are currentColor mask fills: without this the system
|
|
6791
|
+
remaps the backgrounds to Canvas and the glyphs disappear. Kept on the
|
|
6792
|
+
pseudos so the host keeps its native color and focus adaptation. */
|
|
6793
|
+
.fab > summary::before,
|
|
6794
|
+
.fab > summary::after {
|
|
6795
|
+
forced-color-adjust: none;
|
|
6796
|
+
}
|
|
6797
|
+
}
|
|
6798
|
+
|
|
6784
6799
|
/*
|
|
6785
6800
|
* Rating — integer star rating drawn on native radio inputs.
|
|
6786
6801
|
*
|
|
@@ -7305,6 +7320,15 @@ a.avatar[aria-disabled="true"] {
|
|
|
7305
7320
|
color: var(--text);
|
|
7306
7321
|
}
|
|
7307
7322
|
|
|
7323
|
+
/* Same contextual hover as .nav-link (navbar.css): a tint of the tile's ink
|
|
7324
|
+
reads on any --ui-* bar; the baseline keeps a legible light chip. */
|
|
7325
|
+
@supports (color: color-mix(in oklch, red, white)) {
|
|
7326
|
+
.app-nav > a:hover {
|
|
7327
|
+
background: color-mix(in oklch, currentColor 10%, transparent);
|
|
7328
|
+
color: var(--ui-fg, var(--text));
|
|
7329
|
+
}
|
|
7330
|
+
}
|
|
7331
|
+
|
|
7308
7332
|
/* Current tile is already a full selected surface. Selection never changes
|
|
7309
7333
|
text metrics: the shared medium weight is kept, no permanent indicator is
|
|
7310
7334
|
added here. */
|
|
@@ -7392,11 +7416,16 @@ a.avatar[aria-disabled="true"] {
|
|
|
7392
7416
|
display: inline-flex;
|
|
7393
7417
|
align-items: center;
|
|
7394
7418
|
min-block-size: var(--control-size-sm);
|
|
7419
|
+
padding-block: 0;
|
|
7395
7420
|
padding-inline: var(--space-20);
|
|
7421
|
+
border: 0;
|
|
7396
7422
|
border-radius: var(--radius);
|
|
7423
|
+
background: transparent;
|
|
7397
7424
|
color: var(--ui-fg, var(--text-muted));
|
|
7425
|
+
font: inherit;
|
|
7398
7426
|
font-weight: var(--font-weight-medium);
|
|
7399
7427
|
text-decoration: none;
|
|
7428
|
+
cursor: pointer;
|
|
7400
7429
|
}
|
|
7401
7430
|
|
|
7402
7431
|
/* nav-link serves two contracts: plain page navigation (aria-current="page")
|
|
@@ -7421,6 +7450,28 @@ a.avatar[aria-disabled="true"] {
|
|
|
7421
7450
|
color: var(--state-selected);
|
|
7422
7451
|
}
|
|
7423
7452
|
|
|
7453
|
+
/* On a bar with its own --ui-bg/--ui-fg the fill is a tint of the link's own
|
|
7454
|
+
ink, the .btn.ghost hover recipe, so it reads on light and dark bars alike
|
|
7455
|
+
instead of snapping back to --surface-subtle. Not --ui-hover-bg: that is the
|
|
7456
|
+
bar's own hover and can equal its fill. The ink follows --ui-fg too — the
|
|
7457
|
+
selected accent has no contrast pair with an arbitrary bar. The baseline
|
|
7458
|
+
above keeps the light chip where color-mix is missing, with ink that is
|
|
7459
|
+
legible on it. */
|
|
7460
|
+
@supports (color: color-mix(in oklch, red, white)) {
|
|
7461
|
+
.nav-link:is(:hover, [aria-current]) {
|
|
7462
|
+
background: color-mix(in oklch, currentColor 10%, transparent);
|
|
7463
|
+
}
|
|
7464
|
+
|
|
7465
|
+
.nav-link:hover {
|
|
7466
|
+
color: var(--ui-fg, var(--text));
|
|
7467
|
+
}
|
|
7468
|
+
|
|
7469
|
+
.nav-link[aria-current],
|
|
7470
|
+
.nav-link[aria-current]:hover {
|
|
7471
|
+
color: var(--ui-fg, var(--state-selected));
|
|
7472
|
+
}
|
|
7473
|
+
}
|
|
7474
|
+
|
|
7424
7475
|
/* Nav list — a vertical navigation list. Used by .drawer for stacked links.
|
|
7425
7476
|
Links fill each row so the whole line is an interactive target; the
|
|
7426
7477
|
horizontal .navbar-nav links keep their intrinsic size. */
|
|
@@ -7716,6 +7767,9 @@ a.avatar[aria-disabled="true"] {
|
|
|
7716
7767
|
* A plain <dl> handles richer relationships (several terms or descriptions)
|
|
7717
7768
|
* and stays untouched by this component.
|
|
7718
7769
|
*
|
|
7770
|
+
* Variants:
|
|
7771
|
+
* .stacked — value under its term rather than beside it.
|
|
7772
|
+
*
|
|
7719
7773
|
* Why margin resets: neutralizes .prose list margins if .data-list appears
|
|
7720
7774
|
* inside an authored rich-text scope; the grid owns row/column rhythm.
|
|
7721
7775
|
*/
|
|
@@ -7737,6 +7791,21 @@ a.avatar[aria-disabled="true"] {
|
|
|
7737
7791
|
margin: 0;
|
|
7738
7792
|
}
|
|
7739
7793
|
|
|
7794
|
+
/* Stacked — the value sits under its term instead of beside it, for narrow
|
|
7795
|
+
columns and for values too long to sit in a second column.
|
|
7796
|
+
Why the pair separation rides on the term's margin: the children alternate
|
|
7797
|
+
dt/dd with no element around a pair, so the grid has no row boundary to give
|
|
7798
|
+
a larger gap to. The tight gap is the row gap; the term reopens the space. */
|
|
7799
|
+
.data-list.stacked {
|
|
7800
|
+
grid-template-columns: minmax(0, 1fr);
|
|
7801
|
+
gap: var(--space-10);
|
|
7802
|
+
}
|
|
7803
|
+
|
|
7804
|
+
.data-list.stacked > dt + dt,
|
|
7805
|
+
.data-list.stacked > dd + dt {
|
|
7806
|
+
margin-block-start: var(--space-30);
|
|
7807
|
+
}
|
|
7808
|
+
|
|
7740
7809
|
/*
|
|
7741
7810
|
* Application list — repeated rows with optional leading and trailing regions.
|
|
7742
7811
|
* Content determines the number of lines; the component only owns the row
|
|
@@ -8090,6 +8159,11 @@ a.list-item[aria-current]::before {
|
|
|
8090
8159
|
min-block-size: var(--control-size);
|
|
8091
8160
|
padding-inline: var(--space-30);
|
|
8092
8161
|
border: var(--border-width) solid var(--control-border);
|
|
8162
|
+
/* An addon is a field segment: it honours the same lower-edge extension
|
|
8163
|
+
points as text controls (control.css), so a theme's underline or accent
|
|
8164
|
+
edge reaches it instead of stopping at the field. */
|
|
8165
|
+
border-block-end-color: var(--control-border-block-end-color, var(--control-border));
|
|
8166
|
+
border-block-end-width: var(--control-border-block-end-width, var(--border-width));
|
|
8093
8167
|
border-radius: var(--radius);
|
|
8094
8168
|
background: var(--control-bg);
|
|
8095
8169
|
color: var(--control-fg);
|
|
@@ -8137,38 +8211,49 @@ a.list-item[aria-current]::before {
|
|
|
8137
8211
|
z-index: 1;
|
|
8138
8212
|
}
|
|
8139
8213
|
|
|
8140
|
-
/* Keep an active focus ring above an adjacent hovered control. This matters
|
|
8141
|
-
on platforms where clicking a button does not move focus from the input. */
|
|
8142
|
-
.join > :is(:focus, :focus-within) {
|
|
8143
|
-
z-index: 4;
|
|
8144
|
-
}
|
|
8145
|
-
|
|
8146
8214
|
/* An input's border must read as the "primary" edge against an attached
|
|
8147
8215
|
action (e.g. a password-reveal button) at all times, not only while
|
|
8148
8216
|
hovered or focused — otherwise the shared edge flips ownership as the
|
|
8149
|
-
user's pointer moves, which reads as a rendering glitch.
|
|
8150
|
-
|
|
8217
|
+
user's pointer moves, which reads as a rendering glitch. The :has()
|
|
8218
|
+
variant covers a control nested one wrapper level deep (e.g. a combobox
|
|
8219
|
+
element): the wrapper is the flex item, so it carries the z-index — a
|
|
8220
|
+
z-index on the inner control would be a no-op on a static grandchild. */
|
|
8221
|
+
.join > :is(.input, .textarea, .select),
|
|
8222
|
+
.join > :has(> :is(.input, .textarea, .select)) {
|
|
8151
8223
|
z-index: 3;
|
|
8152
8224
|
}
|
|
8153
8225
|
|
|
8154
|
-
/*
|
|
8155
|
-
|
|
8156
|
-
|
|
8157
|
-
|
|
8158
|
-
|
|
8159
|
-
|
|
8160
|
-
|
|
8226
|
+
/* Keep an active focus line above every neighbour, including an adjacent
|
|
8227
|
+
field and a hovered control (some platforms keep focus on the input while
|
|
8228
|
+
a button is clicked). It must follow the primary-edge rule above: both are
|
|
8229
|
+
(0,2,0), and when it came first a focused field stayed at 3 and the next
|
|
8230
|
+
field, painted later, covered the 1px shared edge of its line. */
|
|
8231
|
+
.join > :is(:focus, :focus-within) {
|
|
8232
|
+
z-index: 4;
|
|
8161
8233
|
}
|
|
8162
8234
|
|
|
8163
|
-
|
|
8164
|
-
|
|
8165
|
-
|
|
8235
|
+
/* One wrapper level (e.g. a combobox element around the native control) is
|
|
8236
|
+
supported generically — no widget is named here. The wrapper is the flex
|
|
8237
|
+
item that receives the corner longhands above; the inner control inherits
|
|
8238
|
+
them. The wrapper is also what align-items stretches: block-size: 100%
|
|
8239
|
+
resolves against that stretched size, so the field fills it. :hover and
|
|
8240
|
+
:focus-within already propagate to the wrapper, so the z-index rules above
|
|
8241
|
+
need no duplication. */
|
|
8242
|
+
.join > * > :is(.input, .textarea, .select) {
|
|
8243
|
+
block-size: 100%;
|
|
8244
|
+
border-radius: inherit;
|
|
8166
8245
|
}
|
|
8167
8246
|
|
|
8247
|
+
/* Text controls paint their own inset focus outline (control.css), so a
|
|
8248
|
+
.join never moves the ring to the group: button-only groups and wrapped
|
|
8249
|
+
inputs alike keep per-control focus, with no :has() group treatment. */
|
|
8250
|
+
|
|
8168
8251
|
/* A default outline action belongs to the composite field border. An explicit
|
|
8169
8252
|
intent still provides its own outline color — --intent is guaranteed-invalid
|
|
8170
|
-
for a neutral button (the :where(.btn) boundary), so the fallback applies.
|
|
8171
|
-
|
|
8253
|
+
for a neutral button (the :where(.btn) boundary), so the fallback applies.
|
|
8254
|
+
The second :has() covers a control nested one wrapper level deep. */
|
|
8255
|
+
.join:has(> :is(.input, .textarea, .select)) > .btn.outline,
|
|
8256
|
+
.join:has(> * > :is(.input, .textarea, .select)) > .btn.outline {
|
|
8172
8257
|
--btn-border-color: var(--intent, var(--border));
|
|
8173
8258
|
}
|
|
8174
8259
|
|
|
@@ -8537,9 +8622,9 @@ a.list-item[aria-current]::before {
|
|
|
8537
8622
|
}
|
|
8538
8623
|
|
|
8539
8624
|
/*
|
|
8540
|
-
*
|
|
8541
|
-
*
|
|
8542
|
-
*
|
|
8625
|
+
* Extra utility helpers — explicit property/value names beside the curated
|
|
8626
|
+
* shortcuts of base.css. Both are imported by utilities/index.css and are
|
|
8627
|
+
* equally public (docs/design-notes/naming.md § Utilities).
|
|
8543
8628
|
* They are escape hatches for layout nudges, not a full atomic scale.
|
|
8544
8629
|
*
|
|
8545
8630
|
* Core principle:
|
|
@@ -8715,8 +8800,9 @@ a.list-item[aria-current]::before {
|
|
|
8715
8800
|
overflow-y: auto;
|
|
8716
8801
|
}
|
|
8717
8802
|
|
|
8718
|
-
/* Text
|
|
8719
|
-
.
|
|
8803
|
+
/* Text. .text-wrap is the counterpart of base's .text-nowrap: it restores
|
|
8804
|
+
wrapping inside a component that sets nowrap (.badge). */
|
|
8805
|
+
.text-wrap {
|
|
8720
8806
|
white-space: normal;
|
|
8721
8807
|
}
|
|
8722
8808
|
|
|
@@ -8789,7 +8875,7 @@ a.list-item[aria-current]::before {
|
|
|
8789
8875
|
/* Surface. These are semantic surfaces, not arbitrary paint: each owns its
|
|
8790
8876
|
matching ink and rebinds --heading to that ink, so it stays readable when it
|
|
8791
8877
|
recreates a light surface inside a context that installed a dark one
|
|
8792
|
-
(.
|
|
8878
|
+
(e.g. a --surface-solid band). Intent ink is not inherited through a surface utility; use
|
|
8793
8879
|
.intent-color for that. */
|
|
8794
8880
|
.background-surface {
|
|
8795
8881
|
--heading: currentColor;
|