actual-css 0.9.0 → 0.9.1
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 +25 -0
- package/components.json +2 -0
- package/dist/actual.css +10 -3
- package/dist/actual.full.css +238 -71
- package/dist/actual.full.js +1 -1
- package/dist/actual.full.min.css +1 -1
- package/dist/actual.min.css +1 -1
- package/docs/pages/foundations/tokens.md +1 -1
- package/docs/pages/guides/progressive-enhancement.md +12 -8
- package/package.json +159 -159
- package/src/css/components/accordion.css +2 -0
- package/src/css/components/actions.css +2 -0
- package/src/css/components/alert.css +2 -0
- package/src/css/components/app-nav.css +2 -0
- package/src/css/components/avatar.css +2 -0
- package/src/css/components/badge.css +2 -0
- package/src/css/components/breadcrumb.css +2 -0
- package/src/css/components/busy.css +2 -0
- package/src/css/components/button.css +2 -0
- package/src/css/components/card.css +2 -0
- package/src/css/components/chat.css +2 -0
- package/src/css/components/data-list.css +2 -0
- package/src/css/components/drawer.css +35 -22
- package/src/css/components/fab.css +2 -0
- package/src/css/components/flyout.css +2 -0
- package/src/css/components/indicator.css +2 -0
- package/src/css/components/join.css +2 -0
- package/src/css/components/key.css +2 -0
- package/src/css/components/list.css +2 -0
- package/src/css/components/meter.css +2 -0
- package/src/css/components/modal.css +47 -40
- package/src/css/components/navbar.css +2 -0
- package/src/css/components/pagination.css +2 -0
- package/src/css/components/progress.css +2 -0
- package/src/css/components/rating.css +2 -0
- package/src/css/components/scrollspy.css +2 -0
- package/src/css/components/skeleton.css +2 -0
- package/src/css/components/spinner.css +2 -0
- package/src/css/components/status-bar.css +2 -0
- package/src/css/components/steps.css +2 -0
- package/src/css/components/tab.css +2 -0
- package/src/css/components/table.css +2 -0
- package/src/css/components/tooltip.css +2 -0
- package/src/css/core/print.css +3 -0
- package/src/css/core/variants.css +7 -3
- package/src/css/effects/aura.css +2 -0
- package/src/css/forms/choice-card.css +4 -0
- package/src/css/forms/control.css +2 -0
- package/src/css/forms/floating-field.css +5 -1
- package/src/css/forms/form-actions.css +2 -0
- package/src/css/forms/form.css +2 -0
- package/src/css/forms/input-icon.css +2 -0
- package/src/css/forms/native.css +2 -0
- package/src/css/forms/otp.css +10 -0
- package/src/css/forms/range.css +2 -0
- package/src/css/forms/select.css +2 -0
- package/src/css/forms/switch.css +2 -0
- package/src/css/forms/validation.css +2 -0
- package/src/css/layout/app-layout.css +2 -0
- package/src/css/layout/app-shell.css +5 -1
- package/src/css/layout/center.css +2 -0
- package/src/css/layout/cluster.css +2 -0
- package/src/css/layout/column-layout.css +2 -0
- package/src/css/layout/frame.css +2 -0
- package/src/css/layout/grid.css +2 -0
- package/src/css/layout/media.css +2 -1
- package/src/css/layout/scroll-snap.css +2 -0
- package/src/css/layout/scroll-target.css +2 -0
- package/src/css/layout/sidebar-layout.css +2 -0
- package/src/css/layout/stack.css +2 -1
- package/src/css/layout/switcher.css +2 -0
- package/src/css/layout/topbar.css +2 -0
- package/src/css/typography/fluid.css +2 -0
- package/src/css/typography/lead.css +2 -1
- package/src/css/typography/overline.css +2 -0
- package/src/css/typography/prose.css +2 -0
- package/src/css/utilities/base.css +6 -1
- package/src/js/context-menu.js +56 -15
- package/src/js/enhancement-loader.js +5 -1
- package/src/js/escape.js +11 -1
- package/src/js/flyout.js +6 -1
- package/src/js/focus.js +8 -3
- package/src/js/surface.js +28 -0
- package/src/js/tooltip.js +10 -4
package/dist/actual.full.css
CHANGED
|
@@ -748,9 +748,9 @@ small {
|
|
|
748
748
|
}
|
|
749
749
|
|
|
750
750
|
/* Shared control scale. Wrappers are listed when their descendants form one
|
|
751
|
-
component (field, input-icon, join, pagination,
|
|
752
|
-
declaration beats inherited density because it
|
|
753
|
-
component itself. */
|
|
751
|
+
component (field, choice-card, floating-field, input-icon, join, pagination,
|
|
752
|
+
tabs, flyout). A local size declaration beats inherited density because it
|
|
753
|
+
is declared on the sized component itself. */
|
|
754
754
|
:where(
|
|
755
755
|
.btn,
|
|
756
756
|
.input,
|
|
@@ -760,7 +760,9 @@ small {
|
|
|
760
760
|
.file,
|
|
761
761
|
.field,
|
|
762
762
|
.choice,
|
|
763
|
+
.choice-card,
|
|
763
764
|
.input-icon,
|
|
765
|
+
.floating-field,
|
|
764
766
|
.otp,
|
|
765
767
|
.join,
|
|
766
768
|
.pagination,
|
|
@@ -782,7 +784,9 @@ small {
|
|
|
782
784
|
.file,
|
|
783
785
|
.field,
|
|
784
786
|
.choice,
|
|
787
|
+
.choice-card,
|
|
785
788
|
.input-icon,
|
|
789
|
+
.floating-field,
|
|
786
790
|
.otp,
|
|
787
791
|
.join,
|
|
788
792
|
.pagination,
|
|
@@ -842,6 +846,9 @@ small {
|
|
|
842
846
|
|
|
843
847
|
@media print {
|
|
844
848
|
:root,
|
|
849
|
+
/* The doubled attribute intentionally outranks single-attribute preset
|
|
850
|
+
islands (data-theme="dark", paired themes) loaded after the core. */
|
|
851
|
+
[data-theme][data-theme],
|
|
845
852
|
body {
|
|
846
853
|
/* Save some ink in dark mode */
|
|
847
854
|
color-scheme: light;
|
|
@@ -863,6 +870,8 @@ small {
|
|
|
863
870
|
/*
|
|
864
871
|
* Prose is the opt-in rich text scope for authored content.
|
|
865
872
|
*
|
|
873
|
+
* Docs: docs/pages/foundations/typography.md
|
|
874
|
+
*
|
|
866
875
|
* Global element styles stay mild so app UI is not shaped accidentally. Page
|
|
867
876
|
* layout, forms, cards, navigation, and app tables remain outside this scope.
|
|
868
877
|
*
|
|
@@ -1079,7 +1088,8 @@ small {
|
|
|
1079
1088
|
|
|
1080
1089
|
/* Lead paragraph: a slightly larger, more relaxed reading-introduction text.
|
|
1081
1090
|
Non-fluid and always available — use .text-lead from the optional fluid
|
|
1082
|
-
module for marketing/display surfaces.
|
|
1091
|
+
module for marketing/display surfaces.
|
|
1092
|
+
Docs: docs/pages/foundations/typography.md */
|
|
1083
1093
|
.lead {
|
|
1084
1094
|
font-size: 1.125em;
|
|
1085
1095
|
line-height: var(--line-height-relaxed);
|
|
@@ -1088,6 +1098,8 @@ small {
|
|
|
1088
1098
|
/*
|
|
1089
1099
|
* Overline — editorial small-cap label for section openers.
|
|
1090
1100
|
*
|
|
1101
|
+
* Docs: docs/pages/foundations/typography.md
|
|
1102
|
+
*
|
|
1091
1103
|
* Base form is unstyled chrome that takes its surrounding color. Add .pill for
|
|
1092
1104
|
* the bordered rounded shape, and combine with an intent class (.primary,
|
|
1093
1105
|
* .success, etc.) to tint the text, border, and background.
|
|
@@ -1137,6 +1149,8 @@ small {
|
|
|
1137
1149
|
/*
|
|
1138
1150
|
* Optional fluid typography module.
|
|
1139
1151
|
*
|
|
1152
|
+
* Docs: docs/pages/foundations/typography.md
|
|
1153
|
+
*
|
|
1140
1154
|
* Part of the typography family (typography/index.css) and the full bundle; it
|
|
1141
1155
|
* is not in the minimal core, so a core-only import adds it explicitly when
|
|
1142
1156
|
* display, title, and lead sizes must scale with the viewport. Typical use
|
|
@@ -1267,7 +1281,8 @@ small {
|
|
|
1267
1281
|
region re-spaces every nested stack, cluster and grid at any depth. Setting
|
|
1268
1282
|
the token stays correct on the element that consumes it, and
|
|
1269
1283
|
.compact/.spacious set it deliberately so a density context reaches nested layouts. See
|
|
1270
|
-
docs/pages/layout/stack.md.
|
|
1284
|
+
docs/pages/layout/stack.md.
|
|
1285
|
+
Docs: docs/pages/layout/stack.md */
|
|
1271
1286
|
.stack {
|
|
1272
1287
|
display: flex;
|
|
1273
1288
|
flex-direction: column;
|
|
@@ -1281,6 +1296,8 @@ small {
|
|
|
1281
1296
|
/*
|
|
1282
1297
|
* Inline group that wraps when space runs out.
|
|
1283
1298
|
*
|
|
1299
|
+
* Docs: docs/pages/layout/cluster.md
|
|
1300
|
+
*
|
|
1284
1301
|
* Public hooks:
|
|
1285
1302
|
* --cluster-align
|
|
1286
1303
|
* --cluster-justify
|
|
@@ -1304,6 +1321,8 @@ small {
|
|
|
1304
1321
|
/*
|
|
1305
1322
|
* Centered content column with a maximum measure.
|
|
1306
1323
|
*
|
|
1324
|
+
* Docs: docs/pages/layout/center.md
|
|
1325
|
+
*
|
|
1307
1326
|
* Public hooks:
|
|
1308
1327
|
* --center-size
|
|
1309
1328
|
* --center-pad
|
|
@@ -1319,6 +1338,8 @@ small {
|
|
|
1319
1338
|
/*
|
|
1320
1339
|
* Media frame with a fixed ratio.
|
|
1321
1340
|
*
|
|
1341
|
+
* Docs: docs/pages/layout/frame.md
|
|
1342
|
+
*
|
|
1322
1343
|
* Public hooks:
|
|
1323
1344
|
* --frame-ratio
|
|
1324
1345
|
*
|
|
@@ -1336,7 +1357,11 @@ small {
|
|
|
1336
1357
|
object-fit: cover;
|
|
1337
1358
|
}
|
|
1338
1359
|
|
|
1339
|
-
/*
|
|
1360
|
+
/*
|
|
1361
|
+
* Full-height single-column document shell.
|
|
1362
|
+
*
|
|
1363
|
+
* Docs: docs/pages/layout/app-shell.md
|
|
1364
|
+
*/
|
|
1340
1365
|
.app-shell {
|
|
1341
1366
|
min-block-size: var(--viewport-block);
|
|
1342
1367
|
display: flex;
|
|
@@ -1350,6 +1375,8 @@ small {
|
|
|
1350
1375
|
* chrome available without fixed offsets. This is not a generic page wrapper
|
|
1351
1376
|
* or a public-site layout; all three direct regions are part of the contract.
|
|
1352
1377
|
*
|
|
1378
|
+
* Docs: docs/pages/layout/app-layout.md
|
|
1379
|
+
*
|
|
1353
1380
|
* Public hook:
|
|
1354
1381
|
* --app-nav-side-size
|
|
1355
1382
|
*/
|
|
@@ -1449,7 +1476,8 @@ small {
|
|
|
1449
1476
|
/* Media object: fixed-width leading element (avatar, icon, image) + flexible
|
|
1450
1477
|
trailing content. Replaces the repeated "auto minmax(0, 1fr)" pattern in
|
|
1451
1478
|
author cards, comments, meta rows, and similar compositions.
|
|
1452
|
-
Child contract: exactly two direct children (leading media + content).
|
|
1479
|
+
Child contract: exactly two direct children (leading media + content).
|
|
1480
|
+
Docs: docs/pages/layout/media.md */
|
|
1453
1481
|
.media {
|
|
1454
1482
|
display: grid;
|
|
1455
1483
|
grid-template-columns: auto minmax(0, 1fr);
|
|
@@ -1460,6 +1488,8 @@ small {
|
|
|
1460
1488
|
/*
|
|
1461
1489
|
* Peer regions that switch as a group.
|
|
1462
1490
|
*
|
|
1491
|
+
* Docs: docs/pages/layout/switcher.md
|
|
1492
|
+
*
|
|
1463
1493
|
* All children sit side by side when they all fit; otherwise every child
|
|
1464
1494
|
* stacks. Unlike .grid, this recipe cannot create a partial row such as two
|
|
1465
1495
|
* items plus one orphan.
|
|
@@ -1485,6 +1515,8 @@ small {
|
|
|
1485
1515
|
/*
|
|
1486
1516
|
* Main content + secondary region.
|
|
1487
1517
|
*
|
|
1518
|
+
* Docs: docs/pages/layout/sidebar-layout.md
|
|
1519
|
+
*
|
|
1488
1520
|
* The children wrap according to the space allocated to this layout, without
|
|
1489
1521
|
* an ancestor opt-in or viewport threshold. The large growth ratio lets the
|
|
1490
1522
|
* main region reclaim the full row when the aside wraps instead of remaining
|
|
@@ -1539,6 +1571,8 @@ small {
|
|
|
1539
1571
|
/*
|
|
1540
1572
|
* Responsive equal-width grids.
|
|
1541
1573
|
*
|
|
1574
|
+
* Docs: docs/pages/layout/grid.md
|
|
1575
|
+
*
|
|
1542
1576
|
* .grid is space-driven: it fits as many tracks as --grid-min allows.
|
|
1543
1577
|
* .grid-N is count-driven: it declares a structural density of N and collapses
|
|
1544
1578
|
* through natural divisors of N.
|
|
@@ -1669,6 +1703,8 @@ small {
|
|
|
1669
1703
|
/*
|
|
1670
1704
|
* Explicit 12-column composition canvas.
|
|
1671
1705
|
*
|
|
1706
|
+
* Docs: docs/pages/layout/column-layout.md
|
|
1707
|
+
*
|
|
1672
1708
|
* Neither space-driven like .grid nor count-driven like .grid-N: this is a
|
|
1673
1709
|
* coordinate system. The author places regions with .column-span-N /
|
|
1674
1710
|
* .column-start-N and owns narrow-container recomposition. There is
|
|
@@ -1856,6 +1892,8 @@ small {
|
|
|
1856
1892
|
* Scroll snap — optional horizontal rails for niche card strips and simple
|
|
1857
1893
|
* touch-friendly overflow areas.
|
|
1858
1894
|
*
|
|
1895
|
+
* Docs: docs/pages/layout/scroll-snap.md
|
|
1896
|
+
*
|
|
1859
1897
|
* This is not a full carousel. For previous/next controls, pagination,
|
|
1860
1898
|
* active state, drag behavior, looping, or robust desktop mouse interaction,
|
|
1861
1899
|
* use a dedicated carousel library.
|
|
@@ -1920,6 +1958,8 @@ small {
|
|
|
1920
1958
|
* header (.topbar) or underlaps it; scroll-margin reserves space on the
|
|
1921
1959
|
* target itself so the browser stops early.
|
|
1922
1960
|
*
|
|
1961
|
+
* Docs: docs/pages/layout/scroll-target.md
|
|
1962
|
+
*
|
|
1923
1963
|
* Apply to the elements that are link targets: <section id="...">, headings,
|
|
1924
1964
|
* etc. The scrollspy enhancer only reads ids — this offset is what actually
|
|
1925
1965
|
* shapes where the page lands, and the two are independent.
|
|
@@ -1937,6 +1977,8 @@ small {
|
|
|
1937
1977
|
* background only — row content (search, breadcrumb, actions) stays local
|
|
1938
1978
|
* to the page.
|
|
1939
1979
|
*
|
|
1980
|
+
* Docs: docs/pages/layout/topbar.md
|
|
1981
|
+
*
|
|
1940
1982
|
* Not for marketing/editorial site headers — those are one-off visual
|
|
1941
1983
|
* identity per page; see docs/pages/patterns/overview.md's Header Navigation
|
|
1942
1984
|
* section. This is for the structural app-shell topbar, which recurs unchanged
|
|
@@ -1990,6 +2032,8 @@ small {
|
|
|
1990
2032
|
/*
|
|
1991
2033
|
* Form layout and supporting text.
|
|
1992
2034
|
*
|
|
2035
|
+
* Docs: docs/pages/forms/overview.md
|
|
2036
|
+
*
|
|
1993
2037
|
* Individual controls keep their own files; .choice label layout lives in
|
|
1994
2038
|
* choice.css; .form-actions lives in form-actions.css.
|
|
1995
2039
|
*
|
|
@@ -2052,6 +2096,8 @@ small {
|
|
|
2052
2096
|
/*
|
|
2053
2097
|
* Text controls — input, textarea, select.
|
|
2054
2098
|
*
|
|
2099
|
+
* Docs: docs/pages/forms/overview.md
|
|
2100
|
+
*
|
|
2055
2101
|
* Neutral geometry and interaction states shared across all text-like
|
|
2056
2102
|
* controls. select shares this base recipe; its chevron/appearance layer
|
|
2057
2103
|
* lives in select.css. checkbox, radio, range, switch, and file have their
|
|
@@ -2405,6 +2451,8 @@ small {
|
|
|
2405
2451
|
* Select — chevron and appearance layer on top of the shared text-control
|
|
2406
2452
|
* recipe.
|
|
2407
2453
|
*
|
|
2454
|
+
* Docs: docs/pages/forms/overview.md
|
|
2455
|
+
*
|
|
2408
2456
|
* Base geometry and interaction states come from control.css (select shares
|
|
2409
2457
|
* the --control-* tokens). This file only removes the native arrow and draws
|
|
2410
2458
|
* the CSS chevron, so the picker (appearance: base-select) enhancement in
|
|
@@ -2463,6 +2511,8 @@ small {
|
|
|
2463
2511
|
* custom styling, so the framework stays out of the way and just keeps them
|
|
2464
2512
|
* readable and theme-aware.
|
|
2465
2513
|
*
|
|
2514
|
+
* Docs: docs/pages/forms/controls.md
|
|
2515
|
+
*
|
|
2466
2516
|
* Range has its own custom-styled file (range.css) so the focus ring can
|
|
2467
2517
|
* target the thumb pseudo-element rather than the rectangular CSS box.
|
|
2468
2518
|
*/
|
|
@@ -2563,6 +2613,8 @@ small {
|
|
|
2563
2613
|
/*
|
|
2564
2614
|
* Form validation states.
|
|
2565
2615
|
*
|
|
2616
|
+
* Docs: docs/pages/forms/validation.md
|
|
2617
|
+
*
|
|
2566
2618
|
* Imported by default through forms/index.css. Custom builds may omit this
|
|
2567
2619
|
* file when an app wants to own validation styling completely.
|
|
2568
2620
|
*
|
|
@@ -2654,6 +2706,8 @@ small {
|
|
|
2654
2706
|
/*
|
|
2655
2707
|
* Form actions — bottom form bar with sticky variant.
|
|
2656
2708
|
*
|
|
2709
|
+
* Docs: docs/pages/forms/form-actions.md
|
|
2710
|
+
*
|
|
2657
2711
|
* A flex cluster of buttons/actions that closes a form. The sticky variant
|
|
2658
2712
|
* docks to the viewport bottom with a translucent blur where supported; at
|
|
2659
2713
|
* rest it stays visually neutral so it does not double as a separator.
|
|
@@ -2710,6 +2764,8 @@ small {
|
|
|
2710
2764
|
/*
|
|
2711
2765
|
* Input Icon — decorative icon inside a relative wrapper, leading or trailing.
|
|
2712
2766
|
*
|
|
2767
|
+
* Docs: docs/pages/patterns/icon-input.md
|
|
2768
|
+
*
|
|
2713
2769
|
* Icon-library agnostic: detects position by DOM order. First child that is
|
|
2714
2770
|
* not .input = leading icon; icon after .input = trailing. The icon follows
|
|
2715
2771
|
* local typography; density contexts shrink the control geometry around it
|
|
@@ -2771,6 +2827,8 @@ small {
|
|
|
2771
2827
|
/*
|
|
2772
2828
|
* Switch controls for checkbox inputs with switch semantics.
|
|
2773
2829
|
*
|
|
2830
|
+
* Docs: docs/pages/forms/switch.md
|
|
2831
|
+
*
|
|
2774
2832
|
* The switch remains an input control, not a decorative toggle. Checked, hover,
|
|
2775
2833
|
* focus-visible, and disabled states stay clear.
|
|
2776
2834
|
*
|
|
@@ -2923,6 +2981,8 @@ small {
|
|
|
2923
2981
|
* Range slider — custom-styled so the focus ring always targets the
|
|
2924
2982
|
* user-manipulated surface (the thumb) rather than the CSS box.
|
|
2925
2983
|
*
|
|
2984
|
+
* Docs: docs/pages/forms/range.md
|
|
2985
|
+
*
|
|
2926
2986
|
* appearance: none gives us full control over track and thumb; without it,
|
|
2927
2987
|
* pseudo-element rules for ::-webkit-slider-thumb and ::-moz-range-thumb
|
|
2928
2988
|
* are not reliably applied across browsers.
|
|
@@ -3038,6 +3098,8 @@ small {
|
|
|
3038
3098
|
/*
|
|
3039
3099
|
* Choice cards — card-style radio and checkbox labels.
|
|
3040
3100
|
*
|
|
3101
|
+
* Docs: docs/pages/forms/choice-cards.md
|
|
3102
|
+
*
|
|
3041
3103
|
* Each .choice-card is a <label> wrapping a native radio or checkbox. When
|
|
3042
3104
|
* :has() is supported the input is visually hidden and the card mirrors its
|
|
3043
3105
|
* state: checked border and tint, checkmark badge (round for radio, rounded
|
|
@@ -3074,6 +3136,8 @@ small {
|
|
|
3074
3136
|
border-radius: var(--choice-card-radius);
|
|
3075
3137
|
background: var(--choice-card-bg);
|
|
3076
3138
|
color: var(--text);
|
|
3139
|
+
/* Consumed so .sm/.lg reach the card; the badge's em sizing follows. */
|
|
3140
|
+
font-size: var(--control-font-size);
|
|
3077
3141
|
cursor: pointer;
|
|
3078
3142
|
transition:
|
|
3079
3143
|
border-color var(--duration),
|
|
@@ -3282,6 +3346,8 @@ small {
|
|
|
3282
3346
|
* Floating labels — optional composition over the text controls
|
|
3283
3347
|
* (.input, .textarea, .select) and .field-label.
|
|
3284
3348
|
*
|
|
3349
|
+
* Docs: docs/pages/forms/floating-field.md
|
|
3350
|
+
*
|
|
3285
3351
|
* The cell owns a control plus its label; the label overlays the control and
|
|
3286
3352
|
* floats to the top when the control is focused or non-empty. Help and error
|
|
3287
3353
|
* messages stay in the surrounding .field, not inside the cell, so the
|
|
@@ -3323,7 +3389,9 @@ small {
|
|
|
3323
3389
|
|
|
3324
3390
|
.floating-field > .field-label {
|
|
3325
3391
|
position: absolute;
|
|
3326
|
-
|
|
3392
|
+
/* Same start edge as the control's text: --control-pad-x owns that shared
|
|
3393
|
+
geometry, so .sm/.lg keep label and value aligned. */
|
|
3394
|
+
inset-inline-start: calc(var(--control-pad-x) + var(--border-width));
|
|
3327
3395
|
color: var(--text-muted);
|
|
3328
3396
|
font-size: var(--control-font-size);
|
|
3329
3397
|
font-weight: var(--font-weight-normal);
|
|
@@ -3389,6 +3457,8 @@ small {
|
|
|
3389
3457
|
/*
|
|
3390
3458
|
* One-time code input with visual cells and one native input.
|
|
3391
3459
|
*
|
|
3460
|
+
* Docs: docs/pages/forms/otp.md
|
|
3461
|
+
*
|
|
3392
3462
|
* Add one direct span per accepted character. The cells determine the
|
|
3393
3463
|
* component width; the overlaid input remains the only interactive control.
|
|
3394
3464
|
*
|
|
@@ -3517,12 +3587,22 @@ small {
|
|
|
3517
3587
|
.needs-validation.was-validated .otp > input:invalid ~ span {
|
|
3518
3588
|
--otp-cell-border: Mark;
|
|
3519
3589
|
}
|
|
3590
|
+
|
|
3591
|
+
/* biome-ignore lint/style/noDescendingSpecificity: the :user-invalid route
|
|
3592
|
+
mirrors the non-forced-colors block above and must come last — the three
|
|
3593
|
+
invalid markers write the same value, so source order is inert. An engine
|
|
3594
|
+
without the pseudo drops this rule alone. */
|
|
3595
|
+
.needs-validation .otp > input:user-invalid ~ span {
|
|
3596
|
+
--otp-cell-border: Mark;
|
|
3597
|
+
}
|
|
3520
3598
|
}
|
|
3521
3599
|
|
|
3522
3600
|
/* Controls */
|
|
3523
3601
|
/*
|
|
3524
3602
|
* Buttons for actions and button-like links.
|
|
3525
3603
|
*
|
|
3604
|
+
* Docs: docs/pages/components/button.md
|
|
3605
|
+
*
|
|
3526
3606
|
* .btn is primarily for actionable elements: <button>, <a href>,
|
|
3527
3607
|
* input[type="button"|"submit"|"reset"].
|
|
3528
3608
|
*
|
|
@@ -3769,6 +3849,8 @@ a.btn[aria-disabled="true"] {
|
|
|
3769
3849
|
/*
|
|
3770
3850
|
* Badges for compact labels, counts, and state hints.
|
|
3771
3851
|
*
|
|
3852
|
+
* Docs: docs/pages/components/badge.md
|
|
3853
|
+
*
|
|
3772
3854
|
* Soft by default. .solid for counters and notification badges. The dot
|
|
3773
3855
|
* (.badge:empty) stays solid regardless — a soft dot is pointless.
|
|
3774
3856
|
* Shared .outline variant stays available.
|
|
@@ -4029,6 +4111,8 @@ a.badge[aria-disabled="true"] {
|
|
|
4029
4111
|
/*
|
|
4030
4112
|
* Alerts for inline feedback and status messages.
|
|
4031
4113
|
*
|
|
4114
|
+
* Docs: docs/pages/components/alert.md
|
|
4115
|
+
*
|
|
4032
4116
|
* Keeps alert defaults local while consuming the shared --ui-* variant
|
|
4033
4117
|
* plumbing for emphasis.
|
|
4034
4118
|
*
|
|
@@ -4315,6 +4399,8 @@ a.badge[aria-disabled="true"] {
|
|
|
4315
4399
|
/*
|
|
4316
4400
|
* Actions — a horizontal action-button cluster.
|
|
4317
4401
|
*
|
|
4402
|
+
* Docs: docs/pages/patterns/actions.md
|
|
4403
|
+
*
|
|
4318
4404
|
* Composes with .btn (or any inline element) and feeds layout via the shared
|
|
4319
4405
|
* --gap token.
|
|
4320
4406
|
*
|
|
@@ -4343,6 +4429,8 @@ a.badge[aria-disabled="true"] {
|
|
|
4343
4429
|
/*
|
|
4344
4430
|
* Cards provide a neutral raised surface for grouped content.
|
|
4345
4431
|
*
|
|
4432
|
+
* Docs: docs/pages/components/card.md
|
|
4433
|
+
*
|
|
4346
4434
|
* Surface, border, radius, padding, density, and elevation stay local here.
|
|
4347
4435
|
* Direct header/footer children are structural slots; a direct footer is
|
|
4348
4436
|
* anchored to the card's bottom edge when extra block space is available (so
|
|
@@ -4504,6 +4592,8 @@ a.badge[aria-disabled="true"] {
|
|
|
4504
4592
|
/*
|
|
4505
4593
|
* Modal shell styling for native dialog elements.
|
|
4506
4594
|
*
|
|
4595
|
+
* Docs: docs/pages/components/dialog.md
|
|
4596
|
+
*
|
|
4507
4597
|
* Spacing, radius, transitions, and focus treatment come from shared tokens.
|
|
4508
4598
|
* Opening behavior and intent-specific decisions stay outside this component.
|
|
4509
4599
|
*
|
|
@@ -4515,16 +4605,15 @@ a.badge[aria-disabled="true"] {
|
|
|
4515
4605
|
*
|
|
4516
4606
|
* Internal:
|
|
4517
4607
|
* --dialog-close-icon-size
|
|
4608
|
+
* --dialog-focus-bleed
|
|
4518
4609
|
* --dialog-viewport-block
|
|
4519
4610
|
* --dialog-viewport-gap-default
|
|
4520
4611
|
* --dialog-viewport-gap-2x
|
|
4521
4612
|
*/
|
|
4522
|
-
dialog.modal {
|
|
4523
|
-
/* --dialog-viewport-gap stays fallback-only
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
responsive default lives in an internal token instead, so a media query
|
|
4527
|
-
can still move it without closing the hook.
|
|
4613
|
+
:where(dialog).modal {
|
|
4614
|
+
/* --dialog-viewport-gap stays fallback-only so the responsive default can
|
|
4615
|
+
live in an internal token: a media query moves it without closing the
|
|
4616
|
+
public hook.
|
|
4528
4617
|
|
|
4529
4618
|
calc() multiplication is not in the supported browser range, so the
|
|
4530
4619
|
doubled gap is spelled out with an additive calc. */
|
|
@@ -4538,10 +4627,8 @@ dialog.modal {
|
|
|
4538
4627
|
provides the containing block for the absolutely positioned close button. */
|
|
4539
4628
|
position: fixed;
|
|
4540
4629
|
|
|
4541
|
-
/*
|
|
4542
|
-
|
|
4543
|
-
public hook would silently keep this default. Same reason choice.css never
|
|
4544
|
-
declares --choice-control-nudge. */
|
|
4630
|
+
/* The root is :where(dialog).modal (0-1-0), so a single author class can
|
|
4631
|
+
outrank these defaults — no fallback-only indirection needed here. */
|
|
4545
4632
|
max-inline-size: min(calc(100% - var(--dialog-viewport-gap-2x)), var(--modal-size, 32rem));
|
|
4546
4633
|
|
|
4547
4634
|
/* Stable viewport cap for the panel: dvh tracks the dynamic browser toolbar
|
|
@@ -4571,8 +4658,8 @@ dialog.modal {
|
|
|
4571
4658
|
transform var(--duration) var(--ease-exit);
|
|
4572
4659
|
}
|
|
4573
4660
|
|
|
4574
|
-
dialog.modal > form,
|
|
4575
|
-
dialog.modal > .stack {
|
|
4661
|
+
:where(dialog).modal > form,
|
|
4662
|
+
:where(dialog).modal > .stack {
|
|
4576
4663
|
display: grid;
|
|
4577
4664
|
gap: var(--space-40);
|
|
4578
4665
|
max-block-size: calc(var(--dialog-viewport-block) - var(--dialog-viewport-gap-2x));
|
|
@@ -4582,7 +4669,7 @@ dialog.modal > .stack {
|
|
|
4582
4669
|
}
|
|
4583
4670
|
|
|
4584
4671
|
@supports (block-size: 100svh) {
|
|
4585
|
-
dialog.modal {
|
|
4672
|
+
:where(dialog).modal {
|
|
4586
4673
|
--dialog-viewport-block: 100svh;
|
|
4587
4674
|
}
|
|
4588
4675
|
}
|
|
@@ -4590,42 +4677,50 @@ dialog.modal > .stack {
|
|
|
4590
4677
|
/* Keep the dialog off the viewport edges on small screens. Moves the internal
|
|
4591
4678
|
default, not the public hook, so an author override still wins here too. */
|
|
4592
4679
|
@media (max-width: 30rem) {
|
|
4593
|
-
dialog.modal {
|
|
4680
|
+
:where(dialog).modal {
|
|
4594
4681
|
--dialog-viewport-gap-default: var(--space-30);
|
|
4595
4682
|
}
|
|
4596
4683
|
}
|
|
4597
4684
|
|
|
4598
|
-
dialog.modal.scrollable > form,
|
|
4599
|
-
dialog.modal.scrollable > .stack {
|
|
4685
|
+
:where(dialog).modal.scrollable > form,
|
|
4686
|
+
:where(dialog).modal.scrollable > .stack {
|
|
4600
4687
|
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
4601
4688
|
overflow: hidden;
|
|
4602
4689
|
}
|
|
4603
4690
|
|
|
4604
|
-
dialog.modal.scrollable > form > :not(:is(header, footer)),
|
|
4605
|
-
dialog.modal.scrollable > .stack > :not(:is(header, footer)) {
|
|
4691
|
+
:where(dialog).modal.scrollable > form > :not(:is(header, footer)),
|
|
4692
|
+
:where(dialog).modal.scrollable > .stack > :not(:is(header, footer)) {
|
|
4693
|
+
--dialog-focus-bleed: calc(var(--focus-outline-offset) + var(--border-width) * 2);
|
|
4694
|
+
|
|
4606
4695
|
min-block-size: 0;
|
|
4696
|
+
/* Keep focus paint inside the clip without moving the body off the fixed
|
|
4697
|
+
header and footer edges. */
|
|
4698
|
+
margin-inline: calc(var(--dialog-focus-bleed) * -1);
|
|
4699
|
+
padding-inline: var(--dialog-focus-bleed);
|
|
4607
4700
|
overflow: auto;
|
|
4608
4701
|
overscroll-behavior: contain;
|
|
4609
4702
|
}
|
|
4610
4703
|
|
|
4611
|
-
|
|
4612
|
-
|
|
4704
|
+
/* Anatomy is bounded to the structural slots (direct children of the
|
|
4705
|
+
form/.stack body): a header or footer deeper in the content keeps its own
|
|
4706
|
+
rhythm and is not restyled as the modal's chrome. */
|
|
4707
|
+
:where(dialog).modal > :where(form, .stack) > :is(header, footer) > :first-child {
|
|
4613
4708
|
margin-block-start: 0;
|
|
4614
4709
|
}
|
|
4615
4710
|
|
|
4616
|
-
dialog.modal header > :last-child
|
|
4617
|
-
dialog.modal footer > :last-child {
|
|
4711
|
+
:where(dialog).modal > :where(form, .stack) > :is(header, footer) > :last-child {
|
|
4618
4712
|
margin-block-end: 0;
|
|
4619
4713
|
}
|
|
4620
4714
|
|
|
4621
4715
|
/* The close button is anchored to the dialog at top inline-end, out of the
|
|
4622
|
-
content flow; headers reserve inline room so titles never run under it.
|
|
4623
|
-
|
|
4624
|
-
|
|
4716
|
+
content flow; headers reserve inline room so titles never run under it.
|
|
4717
|
+
The form/.stack anatomy is structural, so it stays in :where() and never
|
|
4718
|
+
lends .stack's specificity to a bare <form>. */
|
|
4719
|
+
:where(dialog).modal > :where(form, .stack) > header {
|
|
4625
4720
|
padding-inline-end: calc(var(--control-size) + var(--space-30));
|
|
4626
4721
|
}
|
|
4627
4722
|
|
|
4628
|
-
dialog.modal footer {
|
|
4723
|
+
:where(dialog).modal > :where(form, .stack) > footer {
|
|
4629
4724
|
display: flex;
|
|
4630
4725
|
flex-wrap: wrap;
|
|
4631
4726
|
justify-content: flex-end;
|
|
@@ -4635,17 +4730,17 @@ dialog.modal footer {
|
|
|
4635
4730
|
/* Confirmation layout — a focused message followed by a visually distinct
|
|
4636
4731
|
action band. The media primitive owns icon/content alignment; this variant
|
|
4637
4732
|
only removes the standard form inset so both regions can own their padding. */
|
|
4638
|
-
dialog.modal.dialog-confirmation > form {
|
|
4733
|
+
:where(dialog).modal.dialog-confirmation > form {
|
|
4639
4734
|
gap: 0;
|
|
4640
4735
|
padding: 0;
|
|
4641
4736
|
}
|
|
4642
4737
|
|
|
4643
|
-
dialog.modal.dialog-confirmation > form > .media {
|
|
4738
|
+
:where(dialog).modal.dialog-confirmation > form > .media {
|
|
4644
4739
|
gap: var(--space-40);
|
|
4645
4740
|
padding: var(--space-50);
|
|
4646
4741
|
}
|
|
4647
4742
|
|
|
4648
|
-
dialog.modal.dialog-confirmation > form > footer {
|
|
4743
|
+
:where(dialog).modal.dialog-confirmation > form > footer {
|
|
4649
4744
|
padding: var(--space-40) var(--space-50);
|
|
4650
4745
|
border-end-start-radius: calc(var(--radius-lg) - var(--border-width));
|
|
4651
4746
|
border-end-end-radius: calc(var(--radius-lg) - var(--border-width));
|
|
@@ -4686,13 +4781,13 @@ dialog.modal.dialog-confirmation > form > footer {
|
|
|
4686
4781
|
}
|
|
4687
4782
|
|
|
4688
4783
|
@media (max-width: 30rem) {
|
|
4689
|
-
dialog.modal.dialog-confirmation > form > .media,
|
|
4690
|
-
dialog.modal.dialog-confirmation > form > footer {
|
|
4784
|
+
:where(dialog).modal.dialog-confirmation > form > .media,
|
|
4785
|
+
:where(dialog).modal.dialog-confirmation > form > footer {
|
|
4691
4786
|
padding: var(--space-40);
|
|
4692
4787
|
}
|
|
4693
4788
|
}
|
|
4694
4789
|
|
|
4695
|
-
dialog.modal[open] {
|
|
4790
|
+
:where(dialog).modal[open] {
|
|
4696
4791
|
opacity: 1;
|
|
4697
4792
|
transform: none;
|
|
4698
4793
|
transition:
|
|
@@ -4701,13 +4796,13 @@ dialog.modal[open] {
|
|
|
4701
4796
|
}
|
|
4702
4797
|
|
|
4703
4798
|
@starting-style {
|
|
4704
|
-
dialog.modal[open] {
|
|
4799
|
+
:where(dialog).modal[open] {
|
|
4705
4800
|
opacity: 0;
|
|
4706
4801
|
transform: translateY(0.35rem);
|
|
4707
4802
|
}
|
|
4708
4803
|
}
|
|
4709
4804
|
|
|
4710
|
-
dialog.modal::backdrop {
|
|
4805
|
+
:where(dialog).modal::backdrop {
|
|
4711
4806
|
background: var(--backdrop-fill);
|
|
4712
4807
|
opacity: 0;
|
|
4713
4808
|
/* Scrim curve stays neutral `ease`; only the panel carries the character
|
|
@@ -4716,7 +4811,7 @@ dialog.modal::backdrop {
|
|
|
4716
4811
|
}
|
|
4717
4812
|
|
|
4718
4813
|
@supports (transition-behavior: allow-discrete) {
|
|
4719
|
-
dialog.modal {
|
|
4814
|
+
:where(dialog).modal {
|
|
4720
4815
|
transition:
|
|
4721
4816
|
opacity var(--duration) var(--ease-exit),
|
|
4722
4817
|
transform var(--duration) var(--ease-exit),
|
|
@@ -4724,7 +4819,7 @@ dialog.modal::backdrop {
|
|
|
4724
4819
|
display var(--duration) allow-discrete;
|
|
4725
4820
|
}
|
|
4726
4821
|
|
|
4727
|
-
dialog.modal[open] {
|
|
4822
|
+
:where(dialog).modal[open] {
|
|
4728
4823
|
transition:
|
|
4729
4824
|
opacity var(--duration) var(--ease-enter),
|
|
4730
4825
|
transform var(--duration) var(--ease-enter),
|
|
@@ -4732,7 +4827,7 @@ dialog.modal::backdrop {
|
|
|
4732
4827
|
display var(--duration) allow-discrete;
|
|
4733
4828
|
}
|
|
4734
4829
|
|
|
4735
|
-
dialog.modal::backdrop {
|
|
4830
|
+
:where(dialog).modal::backdrop {
|
|
4736
4831
|
transition:
|
|
4737
4832
|
opacity var(--duration) ease,
|
|
4738
4833
|
overlay var(--duration) allow-discrete,
|
|
@@ -4740,7 +4835,7 @@ dialog.modal::backdrop {
|
|
|
4740
4835
|
}
|
|
4741
4836
|
}
|
|
4742
4837
|
|
|
4743
|
-
dialog.modal[open]::backdrop {
|
|
4838
|
+
:where(dialog).modal[open]::backdrop {
|
|
4744
4839
|
opacity: 1;
|
|
4745
4840
|
}
|
|
4746
4841
|
|
|
@@ -4753,7 +4848,7 @@ html.has-modal-open.had-scrollbar {
|
|
|
4753
4848
|
}
|
|
4754
4849
|
|
|
4755
4850
|
@starting-style {
|
|
4756
|
-
dialog.modal[open]::backdrop {
|
|
4851
|
+
:where(dialog).modal[open]::backdrop {
|
|
4757
4852
|
opacity: 0;
|
|
4758
4853
|
}
|
|
4759
4854
|
}
|
|
@@ -4761,7 +4856,7 @@ html.has-modal-open.had-scrollbar {
|
|
|
4761
4856
|
@media (prefers-reduced-motion: no-preference) {
|
|
4762
4857
|
/* Wrong-close feedback and view-transition transport are motion quirks, not
|
|
4763
4858
|
presence enter/exit, so they stay on the plain CSS default easing. */
|
|
4764
|
-
dialog.modal.is-static {
|
|
4859
|
+
:where(dialog).modal.is-static {
|
|
4765
4860
|
animation: actual-dialog-static var(--duration-slow) ease;
|
|
4766
4861
|
}
|
|
4767
4862
|
|
|
@@ -4842,6 +4937,12 @@ html.has-modal-open.had-scrollbar {
|
|
|
4842
4937
|
/*
|
|
4843
4938
|
* Drawer — modal side-sheet built on the native dialog element.
|
|
4844
4939
|
*
|
|
4940
|
+
* Docs: docs/pages/components/drawer.md
|
|
4941
|
+
*
|
|
4942
|
+
* Open with a native command button:
|
|
4943
|
+
* command="show-modal" commandfor="<drawer-id>".
|
|
4944
|
+
* For stacked navigation, compose with .nav-list from navbar.css.
|
|
4945
|
+
*
|
|
4845
4946
|
* Top-layer rendering, ::backdrop, Escape, and modal focus come from the
|
|
4846
4947
|
* native dialog. CSS owns progressive enter/exit motion where browsers support
|
|
4847
4948
|
* discrete display/overlay transitions. The drawer is a layout shell only —
|
|
@@ -4854,10 +4955,11 @@ html.has-modal-open.had-scrollbar {
|
|
|
4854
4955
|
*
|
|
4855
4956
|
* Internal:
|
|
4856
4957
|
* --drawer-closed-translate (side/RTL positioning state)
|
|
4958
|
+
* --drawer-focus-bleed
|
|
4857
4959
|
* --drawer-motion-duration (shared by panel and ::backdrop so exit duration
|
|
4858
4960
|
* changes in one place; entry state redefines it longer)
|
|
4859
4961
|
*/
|
|
4860
|
-
dialog.drawer {
|
|
4962
|
+
:where(dialog).drawer {
|
|
4861
4963
|
--drawer-size: 20rem;
|
|
4862
4964
|
--drawer-pad: var(--space-40);
|
|
4863
4965
|
--drawer-closed-translate: -100%;
|
|
@@ -4900,7 +5002,7 @@ dialog.drawer {
|
|
|
4900
5002
|
transform var(--drawer-motion-duration) var(--ease-exit);
|
|
4901
5003
|
}
|
|
4902
5004
|
|
|
4903
|
-
dialog.drawer[open] {
|
|
5005
|
+
:where(dialog).drawer[open] {
|
|
4904
5006
|
--drawer-motion-duration: var(--duration-slow);
|
|
4905
5007
|
display: grid;
|
|
4906
5008
|
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
@@ -4915,7 +5017,7 @@ dialog.drawer[open] {
|
|
|
4915
5017
|
/* A form is the drawer's content wrapper when actions need method="dialog".
|
|
4916
5018
|
Span the drawer tracks so the form can keep the header at the top, let the
|
|
4917
5019
|
body absorb the available height, and keep the footer at the bottom. */
|
|
4918
|
-
dialog.drawer > form {
|
|
5020
|
+
:where(dialog).drawer > form {
|
|
4919
5021
|
display: grid;
|
|
4920
5022
|
grid-row: 1 / -1;
|
|
4921
5023
|
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
@@ -4924,7 +5026,7 @@ dialog.drawer > form {
|
|
|
4924
5026
|
}
|
|
4925
5027
|
|
|
4926
5028
|
/* The shared backdrop token keeps drawer and modal scrims consistent. */
|
|
4927
|
-
dialog.drawer::backdrop {
|
|
5029
|
+
:where(dialog).drawer::backdrop {
|
|
4928
5030
|
background: var(--backdrop-fill);
|
|
4929
5031
|
opacity: 0;
|
|
4930
5032
|
/* Scrim curve stays neutral `ease`; only the panel's character curves
|
|
@@ -4932,7 +5034,7 @@ dialog.drawer::backdrop {
|
|
|
4932
5034
|
transition: opacity var(--drawer-motion-duration) ease;
|
|
4933
5035
|
}
|
|
4934
5036
|
|
|
4935
|
-
dialog.drawer[data-side="end"] {
|
|
5037
|
+
:where(dialog).drawer[data-side="end"] {
|
|
4936
5038
|
--drawer-closed-translate: 100%;
|
|
4937
5039
|
|
|
4938
5040
|
margin-inline-start: auto;
|
|
@@ -4941,17 +5043,17 @@ dialog.drawer[data-side="end"] {
|
|
|
4941
5043
|
border-inline-start: var(--border-width) solid var(--border);
|
|
4942
5044
|
}
|
|
4943
5045
|
|
|
4944
|
-
dialog.drawer[open]::backdrop {
|
|
5046
|
+
:where(dialog).drawer[open]::backdrop {
|
|
4945
5047
|
opacity: 1;
|
|
4946
5048
|
}
|
|
4947
5049
|
|
|
4948
5050
|
@starting-style {
|
|
4949
|
-
dialog.drawer[open] {
|
|
5051
|
+
:where(dialog).drawer[open] {
|
|
4950
5052
|
opacity: 0;
|
|
4951
5053
|
transform: translateX(var(--drawer-closed-translate));
|
|
4952
5054
|
}
|
|
4953
5055
|
|
|
4954
|
-
dialog.drawer[open]::backdrop {
|
|
5056
|
+
:where(dialog).drawer[open]::backdrop {
|
|
4955
5057
|
opacity: 0;
|
|
4956
5058
|
}
|
|
4957
5059
|
}
|
|
@@ -4961,15 +5063,21 @@ dialog.drawer[open]::backdrop {
|
|
|
4961
5063
|
modal, which sizes to its content and opts in with .scrollable. Header and
|
|
4962
5064
|
footer keep their own tracks; .drawer-close is named because a generic
|
|
4963
5065
|
exclusion list is easier to trust than its `position: absolute`. */
|
|
4964
|
-
dialog.drawer > :not(:is(header, footer, form, .drawer-close)),
|
|
4965
|
-
dialog.drawer > form > :not(:is(header, footer, .drawer-close)) {
|
|
5066
|
+
:where(dialog).drawer > :not(:is(header, footer, form, .drawer-close)),
|
|
5067
|
+
:where(dialog).drawer > form > :not(:is(header, footer, .drawer-close)) {
|
|
5068
|
+
--drawer-focus-bleed: calc(var(--focus-outline-offset) + var(--border-width) * 2);
|
|
5069
|
+
|
|
4966
5070
|
min-block-size: 0;
|
|
5071
|
+
/* Keep focus paint inside the clip without moving the body off the fixed
|
|
5072
|
+
header and footer edges. */
|
|
5073
|
+
margin-inline: calc(var(--drawer-focus-bleed) * -1);
|
|
5074
|
+
padding-inline: var(--drawer-focus-bleed);
|
|
4967
5075
|
overflow: auto;
|
|
4968
5076
|
overscroll-behavior: contain;
|
|
4969
5077
|
}
|
|
4970
5078
|
|
|
4971
5079
|
@supports (transition-behavior: allow-discrete) {
|
|
4972
|
-
dialog.drawer {
|
|
5080
|
+
:where(dialog).drawer {
|
|
4973
5081
|
transition:
|
|
4974
5082
|
opacity var(--drawer-motion-duration) var(--ease-exit),
|
|
4975
5083
|
transform var(--drawer-motion-duration) var(--ease-exit),
|
|
@@ -4977,7 +5085,7 @@ dialog.drawer > form > :not(:is(header, footer, .drawer-close)) {
|
|
|
4977
5085
|
display var(--drawer-motion-duration) allow-discrete;
|
|
4978
5086
|
}
|
|
4979
5087
|
|
|
4980
|
-
dialog.drawer[open] {
|
|
5088
|
+
:where(dialog).drawer[open] {
|
|
4981
5089
|
transition:
|
|
4982
5090
|
opacity var(--drawer-motion-duration) var(--ease-enter),
|
|
4983
5091
|
transform var(--drawer-motion-duration) var(--ease-enter),
|
|
@@ -4985,7 +5093,7 @@ dialog.drawer > form > :not(:is(header, footer, .drawer-close)) {
|
|
|
4985
5093
|
display var(--drawer-motion-duration) allow-discrete;
|
|
4986
5094
|
}
|
|
4987
5095
|
|
|
4988
|
-
dialog.drawer::backdrop {
|
|
5096
|
+
:where(dialog).drawer::backdrop {
|
|
4989
5097
|
transition:
|
|
4990
5098
|
opacity var(--drawer-motion-duration) ease,
|
|
4991
5099
|
overlay var(--drawer-motion-duration) allow-discrete,
|
|
@@ -4999,40 +5107,40 @@ dialog.drawer > form > :not(:is(header, footer, .drawer-close)) {
|
|
|
4999
5107
|
directionality and handles nested direction changes; the explicit ltr rules
|
|
5000
5108
|
neutralize an outer RTL fallback selector. Each specificity group keeps the
|
|
5001
5109
|
fallback before the :dir() rules. */
|
|
5002
|
-
[dir="rtl"] dialog.drawer {
|
|
5110
|
+
[dir="rtl"] :where(dialog).drawer {
|
|
5003
5111
|
--drawer-closed-translate: 100%;
|
|
5004
5112
|
}
|
|
5005
5113
|
|
|
5006
|
-
dialog.drawer:dir(ltr) {
|
|
5114
|
+
:where(dialog).drawer:dir(ltr) {
|
|
5007
5115
|
--drawer-closed-translate: -100%;
|
|
5008
5116
|
}
|
|
5009
5117
|
|
|
5010
|
-
dialog.drawer:dir(rtl) {
|
|
5118
|
+
:where(dialog).drawer:dir(rtl) {
|
|
5011
5119
|
--drawer-closed-translate: 100%;
|
|
5012
5120
|
}
|
|
5013
5121
|
|
|
5014
|
-
[dir="rtl"] dialog.drawer[data-side="end"] {
|
|
5122
|
+
[dir="rtl"] :where(dialog).drawer[data-side="end"] {
|
|
5015
5123
|
--drawer-closed-translate: -100%;
|
|
5016
5124
|
}
|
|
5017
5125
|
|
|
5018
|
-
dialog.drawer[data-side="end"]:dir(ltr) {
|
|
5126
|
+
:where(dialog).drawer[data-side="end"]:dir(ltr) {
|
|
5019
5127
|
--drawer-closed-translate: 100%;
|
|
5020
5128
|
}
|
|
5021
5129
|
|
|
5022
|
-
dialog.drawer[data-side="end"]:dir(rtl) {
|
|
5130
|
+
:where(dialog).drawer[data-side="end"]:dir(rtl) {
|
|
5023
5131
|
--drawer-closed-translate: -100%;
|
|
5024
5132
|
}
|
|
5025
5133
|
|
|
5026
5134
|
@media (prefers-reduced-motion: no-preference) {
|
|
5027
|
-
dialog.drawer.is-static {
|
|
5135
|
+
:where(dialog).drawer.is-static {
|
|
5028
5136
|
animation: actual-dialog-static var(--duration-slow) ease;
|
|
5029
5137
|
}
|
|
5030
5138
|
}
|
|
5031
5139
|
|
|
5032
5140
|
/* The header reserves inline room so the title never runs under the close
|
|
5033
5141
|
control. */
|
|
5034
|
-
dialog.drawer > header,
|
|
5035
|
-
dialog.drawer > form > header {
|
|
5142
|
+
:where(dialog).drawer > header,
|
|
5143
|
+
:where(dialog).drawer > form > header {
|
|
5036
5144
|
padding-inline-end: calc(var(--control-size) + var(--space-30));
|
|
5037
5145
|
}
|
|
5038
5146
|
|
|
@@ -5091,6 +5199,8 @@ dialog.drawer > form > header {
|
|
|
5091
5199
|
/*
|
|
5092
5200
|
* Flyout — floating panel for actions and links.
|
|
5093
5201
|
*
|
|
5202
|
+
* Docs: docs/pages/enhancements/flyout.md
|
|
5203
|
+
*
|
|
5094
5204
|
* JavaScript promotes the panel to the top layer and chooses viewport or
|
|
5095
5205
|
* document coordinates from its anchor. It also owns state (.is-open), focus,
|
|
5096
5206
|
* and keyboard behavior. CSS owns the surfaced panel appearance, item styles,
|
|
@@ -5393,6 +5503,8 @@ dialog.drawer > form > header {
|
|
|
5393
5503
|
/*
|
|
5394
5504
|
* Tooltip — supplemental label overlaid near a trigger.
|
|
5395
5505
|
*
|
|
5506
|
+
* Docs: docs/pages/enhancements/tooltip.md
|
|
5507
|
+
*
|
|
5396
5508
|
* Position, placement, and arrow position are managed by JavaScript via left,
|
|
5397
5509
|
* top, data-placement, and --arrow-x. The runtime also owns `position`: it
|
|
5398
5510
|
* writes `absolute` for a tip whose trigger scrolls with the page, so the
|
|
@@ -5535,6 +5647,8 @@ dialog.drawer > form > header {
|
|
|
5535
5647
|
/*
|
|
5536
5648
|
* Accordion disclosures built on native details and summary.
|
|
5537
5649
|
*
|
|
5650
|
+
* Docs: docs/pages/components/accordion.md
|
|
5651
|
+
*
|
|
5538
5652
|
* Keeps the clickable summary affordance aligned and usable without JavaScript
|
|
5539
5653
|
* or demo styles. Decorative palettes and custom markers belong outside the
|
|
5540
5654
|
* base component.
|
|
@@ -5662,6 +5776,8 @@ dialog.drawer > form > header {
|
|
|
5662
5776
|
/*
|
|
5663
5777
|
* Spinner — compact loading feedback.
|
|
5664
5778
|
*
|
|
5779
|
+
* Docs: docs/pages/components/spinner.md
|
|
5780
|
+
*
|
|
5665
5781
|
* Uses currentColor so it adapts to buttons, alerts, badges, and text. The
|
|
5666
5782
|
* [aria-busy] overlay composition lives in components/busy.css.
|
|
5667
5783
|
*
|
|
@@ -5727,6 +5843,8 @@ dialog.drawer > form > header {
|
|
|
5727
5843
|
/*
|
|
5728
5844
|
* Skeleton placeholders for loading states.
|
|
5729
5845
|
*
|
|
5846
|
+
* Docs: docs/pages/components/skeleton.md
|
|
5847
|
+
*
|
|
5730
5848
|
* Shows approximate loading structure without implying real content hierarchy.
|
|
5731
5849
|
* Geometry, animation, and color stay theme-aware.
|
|
5732
5850
|
*
|
|
@@ -5813,6 +5931,8 @@ dialog.drawer > form > header {
|
|
|
5813
5931
|
/*
|
|
5814
5932
|
* Busy overlay — non-button loading state.
|
|
5815
5933
|
*
|
|
5934
|
+
* Docs: docs/pages/components/spinner.md
|
|
5935
|
+
*
|
|
5816
5936
|
* A non-button element with aria-busy="true" and a direct last-child .spinner
|
|
5817
5937
|
* shows that spinner as a centered overlay over a faded surface. Buttons keep
|
|
5818
5938
|
* their spinners inline (e.g. .btn[aria-busy] > .spinner stays inline-flex).
|
|
@@ -5865,6 +5985,8 @@ dialog.drawer > form > header {
|
|
|
5865
5985
|
/*
|
|
5866
5986
|
* Progress — native progress bar with theme-aware track and value.
|
|
5867
5987
|
*
|
|
5988
|
+
* Docs: docs/pages/components/progress.md
|
|
5989
|
+
*
|
|
5868
5990
|
* Consumes the shared --intent/--primary for the value bar and --bar-height
|
|
5869
5991
|
* for thickness. Indeterminate state animates a shimmer using --duration-shimmer.
|
|
5870
5992
|
*
|
|
@@ -5979,6 +6101,8 @@ dialog.drawer > form > header {
|
|
|
5979
6101
|
/*
|
|
5980
6102
|
* Status bar — singleton floating status area.
|
|
5981
6103
|
*
|
|
6104
|
+
* Docs: docs/pages/enhancements/status-bar.md
|
|
6105
|
+
*
|
|
5982
6106
|
* One element in the HTML, empty by default. .is-open is the visual state, set
|
|
5983
6107
|
* by status.js like any other surface: it lands after the message is written
|
|
5984
6108
|
* and lifts before the bar is cleared, so the bar leaves in the exact state it
|
|
@@ -6070,6 +6194,8 @@ dialog.drawer > form > header {
|
|
|
6070
6194
|
/*
|
|
6071
6195
|
* Avatars for initials, images, links, and buttons.
|
|
6072
6196
|
*
|
|
6197
|
+
* Docs: docs/pages/components/avatar.md
|
|
6198
|
+
*
|
|
6073
6199
|
* The visual box stays the same across semantic elements. Grouping and status
|
|
6074
6200
|
* dots (an empty .badge child) compose around the avatar without changing its
|
|
6075
6201
|
* meaning.
|
|
@@ -6292,6 +6418,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
6292
6418
|
/*
|
|
6293
6419
|
* Indicator — generic corner positioning for overlaid component content.
|
|
6294
6420
|
*
|
|
6421
|
+
* Docs: docs/pages/components/indicator.md
|
|
6422
|
+
*
|
|
6295
6423
|
* The container only establishes the containing block. The item owns no
|
|
6296
6424
|
* paint, size, or ring so badges and other components keep their own visual
|
|
6297
6425
|
* contract.
|
|
@@ -6361,6 +6489,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
6361
6489
|
/*
|
|
6362
6490
|
* Key — keyboard keys and shortcut tokens for UI chrome.
|
|
6363
6491
|
*
|
|
6492
|
+
* Docs: docs/pages/components/key.md
|
|
6493
|
+
*
|
|
6364
6494
|
* Use .key outside prose. Plain kbd inside .prose gets the same visual
|
|
6365
6495
|
* treatment from prose.css so authored content stays convenient.
|
|
6366
6496
|
*
|
|
@@ -6401,6 +6531,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
6401
6531
|
/*
|
|
6402
6532
|
* Message rows composed from an optional avatar, metadata, and bubble.
|
|
6403
6533
|
*
|
|
6534
|
+
* Docs: docs/pages/components/chat.md
|
|
6535
|
+
*
|
|
6404
6536
|
* Public hooks:
|
|
6405
6537
|
* --chat-gap
|
|
6406
6538
|
* --chat-max-size
|
|
@@ -6504,6 +6636,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
6504
6636
|
* Button owns the trigger's size, shape, and intent; FAB owns placement and
|
|
6505
6637
|
* the speed dial's structural open/close markers (--icon-plus / --icon-close).
|
|
6506
6638
|
*
|
|
6639
|
+
* Docs: docs/pages/components/fab.md
|
|
6640
|
+
*
|
|
6507
6641
|
* Public hooks:
|
|
6508
6642
|
* --fab-offset
|
|
6509
6643
|
* --fab-gap
|
|
@@ -6631,6 +6765,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
6631
6765
|
/*
|
|
6632
6766
|
* Rating — integer star rating drawn on native radio inputs.
|
|
6633
6767
|
*
|
|
6768
|
+
* Docs: docs/pages/components/rating.md
|
|
6769
|
+
*
|
|
6634
6770
|
* The checked radio remains the functional baseline. :has() progressively
|
|
6635
6771
|
* fills preceding stars without reversing DOM or keyboard order. Forced
|
|
6636
6772
|
* colors restores the platform radio presentation.
|
|
@@ -6733,6 +6869,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
6733
6869
|
/*
|
|
6734
6870
|
* Steps — semantic ordered-list progression for a multi-step process.
|
|
6735
6871
|
*
|
|
6872
|
+
* Docs: docs/pages/components/steps.md
|
|
6873
|
+
*
|
|
6736
6874
|
* One primitive, two explicit compositions. Both classes are required:
|
|
6737
6875
|
*
|
|
6738
6876
|
* <ol class="steps steps-horizontal"> horizontal, 2 to 5 stages
|
|
@@ -7081,6 +7219,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
7081
7219
|
* and item states; app-layout owns whether that surface is a bottom bar or
|
|
7082
7220
|
* labelled side navigation.
|
|
7083
7221
|
*
|
|
7222
|
+
* Docs: docs/pages/components/app-navigation.md
|
|
7223
|
+
*
|
|
7084
7224
|
* Public hooks:
|
|
7085
7225
|
* --app-nav-size
|
|
7086
7226
|
* --app-nav-gap
|
|
@@ -7166,6 +7306,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
7166
7306
|
/*
|
|
7167
7307
|
* Navbar — top-level navigation bar.
|
|
7168
7308
|
*
|
|
7309
|
+
* Docs: docs/pages/components/navbar.md
|
|
7310
|
+
*
|
|
7169
7311
|
* .navbar is a horizontal bar; .navbar-brand is the brand link; .navbar-nav is
|
|
7170
7312
|
* the inline link list using .nav-link items. .nav-list is the vertical nav
|
|
7171
7313
|
* list shared with .drawer, fed by the shared --gap token.
|
|
@@ -7298,6 +7440,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
7298
7440
|
/*
|
|
7299
7441
|
* Breadcrumb — current-location trail from the site root.
|
|
7300
7442
|
*
|
|
7443
|
+
* Docs: docs/pages/components/breadcrumb.md
|
|
7444
|
+
*
|
|
7301
7445
|
* Inline list separated by "/". Mark the current page with `aria-current="page"`
|
|
7302
7446
|
* either on the item or on the current link.
|
|
7303
7447
|
*/
|
|
@@ -7347,6 +7491,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
7347
7491
|
/*
|
|
7348
7492
|
* Pagination — page navigation for paginated lists.
|
|
7349
7493
|
*
|
|
7494
|
+
* Docs: docs/pages/components/pagination.md
|
|
7495
|
+
*
|
|
7350
7496
|
* Render .btn elements inside a .pagination list. The active page is marked
|
|
7351
7497
|
* with aria-current="page"; .btn owns the active state styling.
|
|
7352
7498
|
*
|
|
@@ -7391,6 +7537,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
7391
7537
|
/*
|
|
7392
7538
|
* Tabs — in-place panel switcher with real tab semantics.
|
|
7393
7539
|
*
|
|
7540
|
+
* Docs: docs/pages/enhancements/tabs.md
|
|
7541
|
+
*
|
|
7394
7542
|
* JavaScript owns roving tabindex, aria-selected, panel visibility,
|
|
7395
7543
|
* and arrow-key navigation. CSS owns the tab strip appearance and
|
|
7396
7544
|
* active indicator.
|
|
@@ -7512,6 +7660,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
7512
7660
|
* Scrollspy — behavior hook for navigation that marks the active section
|
|
7513
7661
|
* while the page scrolls.
|
|
7514
7662
|
*
|
|
7663
|
+
* Docs: docs/pages/enhancements/scrollspy.md
|
|
7664
|
+
*
|
|
7515
7665
|
* Does not add visual styles by itself. scrollspy.js watches
|
|
7516
7666
|
* <nav class="scrollspy">
|
|
7517
7667
|
* containing <a href="#section"> links and toggles aria-current="location" on
|
|
@@ -7541,6 +7691,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
7541
7691
|
/*
|
|
7542
7692
|
* Data list — term/value pairs for the properties of a single object.
|
|
7543
7693
|
*
|
|
7694
|
+
* Docs: docs/pages/components/data-list.md
|
|
7695
|
+
*
|
|
7544
7696
|
* Expects alternating <dt>/<dd> children: one term, one value per property.
|
|
7545
7697
|
* A plain <dl> handles richer relationships (several terms or descriptions)
|
|
7546
7698
|
* and stays untouched by this component.
|
|
@@ -7571,6 +7723,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
7571
7723
|
* Content determines the number of lines; the component only owns the row
|
|
7572
7724
|
* geometry, dividers, and supporting-text treatment.
|
|
7573
7725
|
*
|
|
7726
|
+
* Docs: docs/pages/components/list.md
|
|
7727
|
+
*
|
|
7574
7728
|
* Public hooks:
|
|
7575
7729
|
* --list-item-min-size
|
|
7576
7730
|
* --list-item-pad-block
|
|
@@ -7691,6 +7845,8 @@ a.list-item[aria-current]::before {
|
|
|
7691
7845
|
/*
|
|
7692
7846
|
* Table — structured tabular data.
|
|
7693
7847
|
*
|
|
7848
|
+
* Docs: docs/pages/components/table.md
|
|
7849
|
+
*
|
|
7694
7850
|
* .table-wrap provides the scroll container and outer chrome; .table owns row
|
|
7695
7851
|
* and cell rules. Tokens are sourced from the shared scale.
|
|
7696
7852
|
*
|
|
@@ -7772,6 +7928,8 @@ a.list-item[aria-current]::before {
|
|
|
7772
7928
|
/*
|
|
7773
7929
|
* Meter — native and segmented gauges for known-range values.
|
|
7774
7930
|
*
|
|
7931
|
+
* Docs: docs/pages/components/meter.md
|
|
7932
|
+
*
|
|
7775
7933
|
* Maps the three standard meter zones (optimum / suboptimum / even-less-good)
|
|
7776
7934
|
* to the shared success / warning / danger intent colors.
|
|
7777
7935
|
*
|
|
@@ -7864,6 +8022,8 @@ a.list-item[aria-current]::before {
|
|
|
7864
8022
|
/*
|
|
7865
8023
|
* Join — visually groups adjacent controls into a single unit.
|
|
7866
8024
|
*
|
|
8025
|
+
* Docs: docs/pages/forms/joined-controls.md
|
|
8026
|
+
*
|
|
7867
8027
|
* This is a cross-cutting control primitive, not a forms-only component:
|
|
7868
8028
|
* it applies equally to inputs, selects, buttons, and button-like links.
|
|
7869
8029
|
*
|
|
@@ -7997,6 +8157,8 @@ a.list-item[aria-current]::before {
|
|
|
7997
8157
|
/*
|
|
7998
8158
|
* Decorative intent-colored frame for one direct child.
|
|
7999
8159
|
*
|
|
8160
|
+
* Docs: docs/pages/components/aura.md
|
|
8161
|
+
*
|
|
8000
8162
|
* The effect is built from three layers with distinct roles: a frame carrying
|
|
8001
8163
|
* a short accent of light, a small halo that belongs to the frame and keeps
|
|
8002
8164
|
* that light attached to the edge, and a wide static halo that belongs to the
|
|
@@ -8315,7 +8477,12 @@ a.list-item[aria-current]::before {
|
|
|
8315
8477
|
text-decoration: none;
|
|
8316
8478
|
}
|
|
8317
8479
|
|
|
8318
|
-
|
|
8480
|
+
/* :focus-visible stays in its own rule: in a mixed selector list an engine
|
|
8481
|
+
without it would drop the whole rule, taking the :hover styling with it. */
|
|
8482
|
+
.link-muted:hover {
|
|
8483
|
+
color: var(--text);
|
|
8484
|
+
}
|
|
8485
|
+
|
|
8319
8486
|
.link-muted:focus-visible {
|
|
8320
8487
|
color: var(--text);
|
|
8321
8488
|
}
|