actual-css 0.8.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 +76 -0
- package/components.json +2 -0
- package/dist/actual.css +10 -3
- package/dist/actual.full.css +360 -109
- 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/components/alert.md +6 -1
- package/docs/pages/components/dialog.md +5 -0
- package/docs/pages/components/drawer.md +13 -0
- package/docs/pages/components/skeleton.md +3 -1
- package/docs/pages/enhancements/flyout.md +16 -2
- package/docs/pages/forms/choice-cards.md +5 -4
- package/docs/pages/foundations/tokens.md +1 -1
- package/docs/pages/guides/progressive-enhancement.md +12 -8
- package/package.json +3 -2
- 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 +55 -27
- 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 +17 -4
- 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 +10 -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/choice.css +70 -26
- 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 +11 -3
- 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
|
|
@@ -2164,7 +2210,7 @@ small {
|
|
|
2164
2210
|
* geometry and states. switch.css is imported after this file.
|
|
2165
2211
|
*
|
|
2166
2212
|
* Public hooks:
|
|
2167
|
-
* --choice-control-
|
|
2213
|
+
* --choice-control-nudge
|
|
2168
2214
|
* --choice-border
|
|
2169
2215
|
* --choice-bg
|
|
2170
2216
|
* --choice-mark
|
|
@@ -2196,45 +2242,89 @@ small {
|
|
|
2196
2242
|
cursor: pointer;
|
|
2197
2243
|
}
|
|
2198
2244
|
|
|
2199
|
-
/* Optical alignment:
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2245
|
+
/* Optical alignment: the control reads level with the first line's cap height,
|
|
2246
|
+
not with the top of its line box. Every inline choice runs the same formula
|
|
2247
|
+
over its own block size, so a checkbox and a taller switch stay level with
|
|
2248
|
+
each other under any line-height or --choice-control-size — something a
|
|
2249
|
+
per-control constant cannot do.
|
|
2250
|
+
|
|
2251
|
+
Three tiers, each the best the engine can express:
|
|
2252
|
+
|
|
2253
|
+
- `cap` unit: exact, and the only font-independent answer. Where the cap
|
|
2254
|
+
center sits inside a line box depends on the font's ascent/descent/cap
|
|
2255
|
+
ratios, so no constant can serve both Segoe UI and Roboto — the same rule
|
|
2256
|
+
was a pixel high on Windows and two pixels low on Android. Baseline
|
|
2257
|
+
alignment puts the control's border-box bottom on the text baseline, and
|
|
2258
|
+
one inset drops it by half the difference between its own height and the
|
|
2259
|
+
cap height. Both classed controls synthesize that baseline from their
|
|
2260
|
+
border box because their mark and knob are out of flow.
|
|
2261
|
+
- `lh` only: center the control on the line box, which is within a pixel of
|
|
2262
|
+
the cap center for the usual UI faces.
|
|
2263
|
+
- neither: the constants that formula yields at the default 1.5
|
|
2264
|
+
line-height, 0.1875em for a checkbox and 0.125em for the switch track.
|
|
2265
|
+
|
|
2266
|
+
--choice-control-nudge adds to whichever tier applies, rather than replacing
|
|
2267
|
+
it: the derived value is exact now, so a design has a correction to make,
|
|
2268
|
+
not a formula to restate — and one number cannot stand in for three
|
|
2269
|
+
different references anyway. It stays fallback-only rather than declared
|
|
2270
|
+
here, so an override placed anywhere up the tree — :root included — reaches
|
|
2271
|
+
the control instead of losing to a component-level declaration. */
|
|
2216
2272
|
.choice > :is(input[type="checkbox"]:not([role="switch"]), input[type="radio"]) {
|
|
2217
2273
|
flex: 0 0 auto;
|
|
2218
|
-
margin-block-start: var(--choice-control-
|
|
2274
|
+
margin-block-start: calc(0.1875em + var(--choice-control-nudge, 0px));
|
|
2219
2275
|
}
|
|
2220
2276
|
|
|
2221
2277
|
.choice > .switch {
|
|
2222
2278
|
flex: 0 0 auto;
|
|
2223
|
-
margin-block-start: var(--choice-control-
|
|
2279
|
+
margin-block-start: calc(0.125em + var(--choice-control-nudge, 0px));
|
|
2224
2280
|
}
|
|
2225
2281
|
|
|
2226
2282
|
@supports (margin-block-start: 1lh) {
|
|
2227
2283
|
.choice > :is(input[type="checkbox"]:not([role="switch"]), input[type="radio"]) {
|
|
2228
|
-
margin-block-start:
|
|
2229
|
-
--choice-control-
|
|
2230
|
-
|
|
2284
|
+
margin-block-start: calc(
|
|
2285
|
+
(1lh - var(--choice-control-size)) /
|
|
2286
|
+
2 +
|
|
2287
|
+
var(--choice-control-nudge, 0px)
|
|
2231
2288
|
);
|
|
2232
2289
|
}
|
|
2233
2290
|
|
|
2234
2291
|
.choice > .switch {
|
|
2235
|
-
margin-block-start:
|
|
2236
|
-
--
|
|
2237
|
-
|
|
2292
|
+
margin-block-start: calc(
|
|
2293
|
+
(1lh - var(--switch-block-size)) /
|
|
2294
|
+
2 +
|
|
2295
|
+
var(--choice-control-nudge, 0px)
|
|
2296
|
+
);
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
@supports (margin-block-start: 1cap) {
|
|
2301
|
+
.choice {
|
|
2302
|
+
align-items: baseline;
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
/* Baseline alignment owns the block position now; the tier above would only
|
|
2306
|
+
grow the row. An unclassed native input keeps the UA's own baseline, which
|
|
2307
|
+
is already tuned for text — the formula below describes the framework's
|
|
2308
|
+
geometry and would not fit it. */
|
|
2309
|
+
.choice > :is(input[type="checkbox"], input[type="radio"]) {
|
|
2310
|
+
margin-block-start: 0;
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
/* inset-block-start rides the `position: relative` these controls already
|
|
2314
|
+
carry for their mark and knob. */
|
|
2315
|
+
.choice > :is(.check, .radio) {
|
|
2316
|
+
inset-block-start: calc(
|
|
2317
|
+
(var(--choice-control-size) - 1cap) /
|
|
2318
|
+
2 +
|
|
2319
|
+
var(--choice-control-nudge, 0px)
|
|
2320
|
+
);
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
.choice > .switch {
|
|
2324
|
+
inset-block-start: calc(
|
|
2325
|
+
(var(--switch-block-size) - 1cap) /
|
|
2326
|
+
2 +
|
|
2327
|
+
var(--choice-control-nudge, 0px)
|
|
2238
2328
|
);
|
|
2239
2329
|
}
|
|
2240
2330
|
}
|
|
@@ -2361,6 +2451,8 @@ small {
|
|
|
2361
2451
|
* Select — chevron and appearance layer on top of the shared text-control
|
|
2362
2452
|
* recipe.
|
|
2363
2453
|
*
|
|
2454
|
+
* Docs: docs/pages/forms/overview.md
|
|
2455
|
+
*
|
|
2364
2456
|
* Base geometry and interaction states come from control.css (select shares
|
|
2365
2457
|
* the --control-* tokens). This file only removes the native arrow and draws
|
|
2366
2458
|
* the CSS chevron, so the picker (appearance: base-select) enhancement in
|
|
@@ -2419,6 +2511,8 @@ small {
|
|
|
2419
2511
|
* custom styling, so the framework stays out of the way and just keeps them
|
|
2420
2512
|
* readable and theme-aware.
|
|
2421
2513
|
*
|
|
2514
|
+
* Docs: docs/pages/forms/controls.md
|
|
2515
|
+
*
|
|
2422
2516
|
* Range has its own custom-styled file (range.css) so the focus ring can
|
|
2423
2517
|
* target the thumb pseudo-element rather than the rectangular CSS box.
|
|
2424
2518
|
*/
|
|
@@ -2519,6 +2613,8 @@ small {
|
|
|
2519
2613
|
/*
|
|
2520
2614
|
* Form validation states.
|
|
2521
2615
|
*
|
|
2616
|
+
* Docs: docs/pages/forms/validation.md
|
|
2617
|
+
*
|
|
2522
2618
|
* Imported by default through forms/index.css. Custom builds may omit this
|
|
2523
2619
|
* file when an app wants to own validation styling completely.
|
|
2524
2620
|
*
|
|
@@ -2610,6 +2706,8 @@ small {
|
|
|
2610
2706
|
/*
|
|
2611
2707
|
* Form actions — bottom form bar with sticky variant.
|
|
2612
2708
|
*
|
|
2709
|
+
* Docs: docs/pages/forms/form-actions.md
|
|
2710
|
+
*
|
|
2613
2711
|
* A flex cluster of buttons/actions that closes a form. The sticky variant
|
|
2614
2712
|
* docks to the viewport bottom with a translucent blur where supported; at
|
|
2615
2713
|
* rest it stays visually neutral so it does not double as a separator.
|
|
@@ -2666,6 +2764,8 @@ small {
|
|
|
2666
2764
|
/*
|
|
2667
2765
|
* Input Icon — decorative icon inside a relative wrapper, leading or trailing.
|
|
2668
2766
|
*
|
|
2767
|
+
* Docs: docs/pages/patterns/icon-input.md
|
|
2768
|
+
*
|
|
2669
2769
|
* Icon-library agnostic: detects position by DOM order. First child that is
|
|
2670
2770
|
* not .input = leading icon; icon after .input = trailing. The icon follows
|
|
2671
2771
|
* local typography; density contexts shrink the control geometry around it
|
|
@@ -2727,6 +2827,8 @@ small {
|
|
|
2727
2827
|
/*
|
|
2728
2828
|
* Switch controls for checkbox inputs with switch semantics.
|
|
2729
2829
|
*
|
|
2830
|
+
* Docs: docs/pages/forms/switch.md
|
|
2831
|
+
*
|
|
2730
2832
|
* The switch remains an input control, not a decorative toggle. Checked, hover,
|
|
2731
2833
|
* focus-visible, and disabled states stay clear.
|
|
2732
2834
|
*
|
|
@@ -2782,8 +2884,6 @@ small {
|
|
|
2782
2884
|
|
|
2783
2885
|
position: relative;
|
|
2784
2886
|
display: inline-grid;
|
|
2785
|
-
align-items: center;
|
|
2786
|
-
justify-content: start;
|
|
2787
2887
|
inline-size: var(--switch-width);
|
|
2788
2888
|
block-size: var(--switch-block-size);
|
|
2789
2889
|
margin: 0;
|
|
@@ -2802,9 +2902,17 @@ small {
|
|
|
2802
2902
|
against the track is what makes the knob legible. */
|
|
2803
2903
|
.switch::before {
|
|
2804
2904
|
content: "";
|
|
2905
|
+
/* Out of flow like the checkbox mark, so the track carries no in-flow item
|
|
2906
|
+
and the switch synthesizes its baseline from its own border box. A knob
|
|
2907
|
+
laid out as a grid item makes the baseline the knob's bottom edge instead,
|
|
2908
|
+
which is what .choice measures its cap-height offset from. The inset is
|
|
2909
|
+
the same --switch-knob-margin on both axes: it resolves against the
|
|
2910
|
+
padding box, so the border is already out of the way. */
|
|
2911
|
+
position: absolute;
|
|
2912
|
+
inset-block-start: var(--switch-knob-margin);
|
|
2913
|
+
inset-inline-start: var(--switch-knob-margin);
|
|
2805
2914
|
inline-size: var(--switch-knob-size);
|
|
2806
2915
|
block-size: var(--switch-knob-size);
|
|
2807
|
-
margin-inline: var(--switch-knob-margin);
|
|
2808
2916
|
border-radius: var(--radius-full);
|
|
2809
2917
|
background: var(--switch-knob);
|
|
2810
2918
|
transition:
|
|
@@ -2873,6 +2981,8 @@ small {
|
|
|
2873
2981
|
* Range slider — custom-styled so the focus ring always targets the
|
|
2874
2982
|
* user-manipulated surface (the thumb) rather than the CSS box.
|
|
2875
2983
|
*
|
|
2984
|
+
* Docs: docs/pages/forms/range.md
|
|
2985
|
+
*
|
|
2876
2986
|
* appearance: none gives us full control over track and thumb; without it,
|
|
2877
2987
|
* pseudo-element rules for ::-webkit-slider-thumb and ::-moz-range-thumb
|
|
2878
2988
|
* are not reliably applied across browsers.
|
|
@@ -2988,6 +3098,8 @@ small {
|
|
|
2988
3098
|
/*
|
|
2989
3099
|
* Choice cards — card-style radio and checkbox labels.
|
|
2990
3100
|
*
|
|
3101
|
+
* Docs: docs/pages/forms/choice-cards.md
|
|
3102
|
+
*
|
|
2991
3103
|
* Each .choice-card is a <label> wrapping a native radio or checkbox. When
|
|
2992
3104
|
* :has() is supported the input is visually hidden and the card mirrors its
|
|
2993
3105
|
* state: checked border and tint, checkmark badge (round for radio, rounded
|
|
@@ -3024,6 +3136,8 @@ small {
|
|
|
3024
3136
|
border-radius: var(--choice-card-radius);
|
|
3025
3137
|
background: var(--choice-card-bg);
|
|
3026
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);
|
|
3027
3141
|
cursor: pointer;
|
|
3028
3142
|
transition:
|
|
3029
3143
|
border-color var(--duration),
|
|
@@ -3232,6 +3346,8 @@ small {
|
|
|
3232
3346
|
* Floating labels — optional composition over the text controls
|
|
3233
3347
|
* (.input, .textarea, .select) and .field-label.
|
|
3234
3348
|
*
|
|
3349
|
+
* Docs: docs/pages/forms/floating-field.md
|
|
3350
|
+
*
|
|
3235
3351
|
* The cell owns a control plus its label; the label overlays the control and
|
|
3236
3352
|
* floats to the top when the control is focused or non-empty. Help and error
|
|
3237
3353
|
* messages stay in the surrounding .field, not inside the cell, so the
|
|
@@ -3273,7 +3389,9 @@ small {
|
|
|
3273
3389
|
|
|
3274
3390
|
.floating-field > .field-label {
|
|
3275
3391
|
position: absolute;
|
|
3276
|
-
|
|
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));
|
|
3277
3395
|
color: var(--text-muted);
|
|
3278
3396
|
font-size: var(--control-font-size);
|
|
3279
3397
|
font-weight: var(--font-weight-normal);
|
|
@@ -3339,6 +3457,8 @@ small {
|
|
|
3339
3457
|
/*
|
|
3340
3458
|
* One-time code input with visual cells and one native input.
|
|
3341
3459
|
*
|
|
3460
|
+
* Docs: docs/pages/forms/otp.md
|
|
3461
|
+
*
|
|
3342
3462
|
* Add one direct span per accepted character. The cells determine the
|
|
3343
3463
|
* component width; the overlaid input remains the only interactive control.
|
|
3344
3464
|
*
|
|
@@ -3467,12 +3587,22 @@ small {
|
|
|
3467
3587
|
.needs-validation.was-validated .otp > input:invalid ~ span {
|
|
3468
3588
|
--otp-cell-border: Mark;
|
|
3469
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
|
+
}
|
|
3470
3598
|
}
|
|
3471
3599
|
|
|
3472
3600
|
/* Controls */
|
|
3473
3601
|
/*
|
|
3474
3602
|
* Buttons for actions and button-like links.
|
|
3475
3603
|
*
|
|
3604
|
+
* Docs: docs/pages/components/button.md
|
|
3605
|
+
*
|
|
3476
3606
|
* .btn is primarily for actionable elements: <button>, <a href>,
|
|
3477
3607
|
* input[type="button"|"submit"|"reset"].
|
|
3478
3608
|
*
|
|
@@ -3719,6 +3849,8 @@ a.btn[aria-disabled="true"] {
|
|
|
3719
3849
|
/*
|
|
3720
3850
|
* Badges for compact labels, counts, and state hints.
|
|
3721
3851
|
*
|
|
3852
|
+
* Docs: docs/pages/components/badge.md
|
|
3853
|
+
*
|
|
3722
3854
|
* Soft by default. .solid for counters and notification badges. The dot
|
|
3723
3855
|
* (.badge:empty) stays solid regardless — a soft dot is pointless.
|
|
3724
3856
|
* Shared .outline variant stays available.
|
|
@@ -3979,6 +4111,8 @@ a.badge[aria-disabled="true"] {
|
|
|
3979
4111
|
/*
|
|
3980
4112
|
* Alerts for inline feedback and status messages.
|
|
3981
4113
|
*
|
|
4114
|
+
* Docs: docs/pages/components/alert.md
|
|
4115
|
+
*
|
|
3982
4116
|
* Keeps alert defaults local while consuming the shared --ui-* variant
|
|
3983
4117
|
* plumbing for emphasis.
|
|
3984
4118
|
*
|
|
@@ -4265,6 +4399,8 @@ a.badge[aria-disabled="true"] {
|
|
|
4265
4399
|
/*
|
|
4266
4400
|
* Actions — a horizontal action-button cluster.
|
|
4267
4401
|
*
|
|
4402
|
+
* Docs: docs/pages/patterns/actions.md
|
|
4403
|
+
*
|
|
4268
4404
|
* Composes with .btn (or any inline element) and feeds layout via the shared
|
|
4269
4405
|
* --gap token.
|
|
4270
4406
|
*
|
|
@@ -4293,6 +4429,8 @@ a.badge[aria-disabled="true"] {
|
|
|
4293
4429
|
/*
|
|
4294
4430
|
* Cards provide a neutral raised surface for grouped content.
|
|
4295
4431
|
*
|
|
4432
|
+
* Docs: docs/pages/components/card.md
|
|
4433
|
+
*
|
|
4296
4434
|
* Surface, border, radius, padding, density, and elevation stay local here.
|
|
4297
4435
|
* Direct header/footer children are structural slots; a direct footer is
|
|
4298
4436
|
* anchored to the card's bottom edge when extra block space is available (so
|
|
@@ -4454,6 +4592,8 @@ a.badge[aria-disabled="true"] {
|
|
|
4454
4592
|
/*
|
|
4455
4593
|
* Modal shell styling for native dialog elements.
|
|
4456
4594
|
*
|
|
4595
|
+
* Docs: docs/pages/components/dialog.md
|
|
4596
|
+
*
|
|
4457
4597
|
* Spacing, radius, transitions, and focus treatment come from shared tokens.
|
|
4458
4598
|
* Opening behavior and intent-specific decisions stay outside this component.
|
|
4459
4599
|
*
|
|
@@ -4465,16 +4605,15 @@ a.badge[aria-disabled="true"] {
|
|
|
4465
4605
|
*
|
|
4466
4606
|
* Internal:
|
|
4467
4607
|
* --dialog-close-icon-size
|
|
4608
|
+
* --dialog-focus-bleed
|
|
4468
4609
|
* --dialog-viewport-block
|
|
4469
4610
|
* --dialog-viewport-gap-default
|
|
4470
4611
|
* --dialog-viewport-gap-2x
|
|
4471
4612
|
*/
|
|
4472
|
-
dialog.modal {
|
|
4473
|
-
/* --dialog-viewport-gap stays fallback-only
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
responsive default lives in an internal token instead, so a media query
|
|
4477
|
-
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.
|
|
4478
4617
|
|
|
4479
4618
|
calc() multiplication is not in the supported browser range, so the
|
|
4480
4619
|
doubled gap is spelled out with an additive calc. */
|
|
@@ -4488,10 +4627,8 @@ dialog.modal {
|
|
|
4488
4627
|
provides the containing block for the absolutely positioned close button. */
|
|
4489
4628
|
position: fixed;
|
|
4490
4629
|
|
|
4491
|
-
/*
|
|
4492
|
-
|
|
4493
|
-
public hook would silently keep this default. Same reason choice.css never
|
|
4494
|
-
declares --choice-control-offset. */
|
|
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. */
|
|
4495
4632
|
max-inline-size: min(calc(100% - var(--dialog-viewport-gap-2x)), var(--modal-size, 32rem));
|
|
4496
4633
|
|
|
4497
4634
|
/* Stable viewport cap for the panel: dvh tracks the dynamic browser toolbar
|
|
@@ -4521,8 +4658,8 @@ dialog.modal {
|
|
|
4521
4658
|
transform var(--duration) var(--ease-exit);
|
|
4522
4659
|
}
|
|
4523
4660
|
|
|
4524
|
-
dialog.modal > form,
|
|
4525
|
-
dialog.modal > .stack {
|
|
4661
|
+
:where(dialog).modal > form,
|
|
4662
|
+
:where(dialog).modal > .stack {
|
|
4526
4663
|
display: grid;
|
|
4527
4664
|
gap: var(--space-40);
|
|
4528
4665
|
max-block-size: calc(var(--dialog-viewport-block) - var(--dialog-viewport-gap-2x));
|
|
@@ -4532,7 +4669,7 @@ dialog.modal > .stack {
|
|
|
4532
4669
|
}
|
|
4533
4670
|
|
|
4534
4671
|
@supports (block-size: 100svh) {
|
|
4535
|
-
dialog.modal {
|
|
4672
|
+
:where(dialog).modal {
|
|
4536
4673
|
--dialog-viewport-block: 100svh;
|
|
4537
4674
|
}
|
|
4538
4675
|
}
|
|
@@ -4540,42 +4677,50 @@ dialog.modal > .stack {
|
|
|
4540
4677
|
/* Keep the dialog off the viewport edges on small screens. Moves the internal
|
|
4541
4678
|
default, not the public hook, so an author override still wins here too. */
|
|
4542
4679
|
@media (max-width: 30rem) {
|
|
4543
|
-
dialog.modal {
|
|
4680
|
+
:where(dialog).modal {
|
|
4544
4681
|
--dialog-viewport-gap-default: var(--space-30);
|
|
4545
4682
|
}
|
|
4546
4683
|
}
|
|
4547
4684
|
|
|
4548
|
-
dialog.modal.scrollable > form,
|
|
4549
|
-
dialog.modal.scrollable > .stack {
|
|
4685
|
+
:where(dialog).modal.scrollable > form,
|
|
4686
|
+
:where(dialog).modal.scrollable > .stack {
|
|
4550
4687
|
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
4551
4688
|
overflow: hidden;
|
|
4552
4689
|
}
|
|
4553
4690
|
|
|
4554
|
-
dialog.modal.scrollable > form > :not(:is(header, footer)),
|
|
4555
|
-
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
|
+
|
|
4556
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);
|
|
4557
4700
|
overflow: auto;
|
|
4558
4701
|
overscroll-behavior: contain;
|
|
4559
4702
|
}
|
|
4560
4703
|
|
|
4561
|
-
|
|
4562
|
-
|
|
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 {
|
|
4563
4708
|
margin-block-start: 0;
|
|
4564
4709
|
}
|
|
4565
4710
|
|
|
4566
|
-
dialog.modal header > :last-child
|
|
4567
|
-
dialog.modal footer > :last-child {
|
|
4711
|
+
:where(dialog).modal > :where(form, .stack) > :is(header, footer) > :last-child {
|
|
4568
4712
|
margin-block-end: 0;
|
|
4569
4713
|
}
|
|
4570
4714
|
|
|
4571
4715
|
/* The close button is anchored to the dialog at top inline-end, out of the
|
|
4572
|
-
content flow; headers reserve inline room so titles never run under it.
|
|
4573
|
-
|
|
4574
|
-
|
|
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 {
|
|
4575
4720
|
padding-inline-end: calc(var(--control-size) + var(--space-30));
|
|
4576
4721
|
}
|
|
4577
4722
|
|
|
4578
|
-
dialog.modal footer {
|
|
4723
|
+
:where(dialog).modal > :where(form, .stack) > footer {
|
|
4579
4724
|
display: flex;
|
|
4580
4725
|
flex-wrap: wrap;
|
|
4581
4726
|
justify-content: flex-end;
|
|
@@ -4585,17 +4730,17 @@ dialog.modal footer {
|
|
|
4585
4730
|
/* Confirmation layout — a focused message followed by a visually distinct
|
|
4586
4731
|
action band. The media primitive owns icon/content alignment; this variant
|
|
4587
4732
|
only removes the standard form inset so both regions can own their padding. */
|
|
4588
|
-
dialog.modal.dialog-confirmation > form {
|
|
4733
|
+
:where(dialog).modal.dialog-confirmation > form {
|
|
4589
4734
|
gap: 0;
|
|
4590
4735
|
padding: 0;
|
|
4591
4736
|
}
|
|
4592
4737
|
|
|
4593
|
-
dialog.modal.dialog-confirmation > form > .media {
|
|
4738
|
+
:where(dialog).modal.dialog-confirmation > form > .media {
|
|
4594
4739
|
gap: var(--space-40);
|
|
4595
4740
|
padding: var(--space-50);
|
|
4596
4741
|
}
|
|
4597
4742
|
|
|
4598
|
-
dialog.modal.dialog-confirmation > form > footer {
|
|
4743
|
+
:where(dialog).modal.dialog-confirmation > form > footer {
|
|
4599
4744
|
padding: var(--space-40) var(--space-50);
|
|
4600
4745
|
border-end-start-radius: calc(var(--radius-lg) - var(--border-width));
|
|
4601
4746
|
border-end-end-radius: calc(var(--radius-lg) - var(--border-width));
|
|
@@ -4636,13 +4781,13 @@ dialog.modal.dialog-confirmation > form > footer {
|
|
|
4636
4781
|
}
|
|
4637
4782
|
|
|
4638
4783
|
@media (max-width: 30rem) {
|
|
4639
|
-
dialog.modal.dialog-confirmation > form > .media,
|
|
4640
|
-
dialog.modal.dialog-confirmation > form > footer {
|
|
4784
|
+
:where(dialog).modal.dialog-confirmation > form > .media,
|
|
4785
|
+
:where(dialog).modal.dialog-confirmation > form > footer {
|
|
4641
4786
|
padding: var(--space-40);
|
|
4642
4787
|
}
|
|
4643
4788
|
}
|
|
4644
4789
|
|
|
4645
|
-
dialog.modal[open] {
|
|
4790
|
+
:where(dialog).modal[open] {
|
|
4646
4791
|
opacity: 1;
|
|
4647
4792
|
transform: none;
|
|
4648
4793
|
transition:
|
|
@@ -4651,13 +4796,13 @@ dialog.modal[open] {
|
|
|
4651
4796
|
}
|
|
4652
4797
|
|
|
4653
4798
|
@starting-style {
|
|
4654
|
-
dialog.modal[open] {
|
|
4799
|
+
:where(dialog).modal[open] {
|
|
4655
4800
|
opacity: 0;
|
|
4656
4801
|
transform: translateY(0.35rem);
|
|
4657
4802
|
}
|
|
4658
4803
|
}
|
|
4659
4804
|
|
|
4660
|
-
dialog.modal::backdrop {
|
|
4805
|
+
:where(dialog).modal::backdrop {
|
|
4661
4806
|
background: var(--backdrop-fill);
|
|
4662
4807
|
opacity: 0;
|
|
4663
4808
|
/* Scrim curve stays neutral `ease`; only the panel carries the character
|
|
@@ -4666,7 +4811,7 @@ dialog.modal::backdrop {
|
|
|
4666
4811
|
}
|
|
4667
4812
|
|
|
4668
4813
|
@supports (transition-behavior: allow-discrete) {
|
|
4669
|
-
dialog.modal {
|
|
4814
|
+
:where(dialog).modal {
|
|
4670
4815
|
transition:
|
|
4671
4816
|
opacity var(--duration) var(--ease-exit),
|
|
4672
4817
|
transform var(--duration) var(--ease-exit),
|
|
@@ -4674,7 +4819,7 @@ dialog.modal::backdrop {
|
|
|
4674
4819
|
display var(--duration) allow-discrete;
|
|
4675
4820
|
}
|
|
4676
4821
|
|
|
4677
|
-
dialog.modal[open] {
|
|
4822
|
+
:where(dialog).modal[open] {
|
|
4678
4823
|
transition:
|
|
4679
4824
|
opacity var(--duration) var(--ease-enter),
|
|
4680
4825
|
transform var(--duration) var(--ease-enter),
|
|
@@ -4682,7 +4827,7 @@ dialog.modal::backdrop {
|
|
|
4682
4827
|
display var(--duration) allow-discrete;
|
|
4683
4828
|
}
|
|
4684
4829
|
|
|
4685
|
-
dialog.modal::backdrop {
|
|
4830
|
+
:where(dialog).modal::backdrop {
|
|
4686
4831
|
transition:
|
|
4687
4832
|
opacity var(--duration) ease,
|
|
4688
4833
|
overlay var(--duration) allow-discrete,
|
|
@@ -4690,7 +4835,7 @@ dialog.modal::backdrop {
|
|
|
4690
4835
|
}
|
|
4691
4836
|
}
|
|
4692
4837
|
|
|
4693
|
-
dialog.modal[open]::backdrop {
|
|
4838
|
+
:where(dialog).modal[open]::backdrop {
|
|
4694
4839
|
opacity: 1;
|
|
4695
4840
|
}
|
|
4696
4841
|
|
|
@@ -4703,7 +4848,7 @@ html.has-modal-open.had-scrollbar {
|
|
|
4703
4848
|
}
|
|
4704
4849
|
|
|
4705
4850
|
@starting-style {
|
|
4706
|
-
dialog.modal[open]::backdrop {
|
|
4851
|
+
:where(dialog).modal[open]::backdrop {
|
|
4707
4852
|
opacity: 0;
|
|
4708
4853
|
}
|
|
4709
4854
|
}
|
|
@@ -4711,7 +4856,7 @@ html.has-modal-open.had-scrollbar {
|
|
|
4711
4856
|
@media (prefers-reduced-motion: no-preference) {
|
|
4712
4857
|
/* Wrong-close feedback and view-transition transport are motion quirks, not
|
|
4713
4858
|
presence enter/exit, so they stay on the plain CSS default easing. */
|
|
4714
|
-
dialog.modal.is-static {
|
|
4859
|
+
:where(dialog).modal.is-static {
|
|
4715
4860
|
animation: actual-dialog-static var(--duration-slow) ease;
|
|
4716
4861
|
}
|
|
4717
4862
|
|
|
@@ -4792,6 +4937,12 @@ html.has-modal-open.had-scrollbar {
|
|
|
4792
4937
|
/*
|
|
4793
4938
|
* Drawer — modal side-sheet built on the native dialog element.
|
|
4794
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
|
+
*
|
|
4795
4946
|
* Top-layer rendering, ::backdrop, Escape, and modal focus come from the
|
|
4796
4947
|
* native dialog. CSS owns progressive enter/exit motion where browsers support
|
|
4797
4948
|
* discrete display/overlay transitions. The drawer is a layout shell only —
|
|
@@ -4804,10 +4955,11 @@ html.has-modal-open.had-scrollbar {
|
|
|
4804
4955
|
*
|
|
4805
4956
|
* Internal:
|
|
4806
4957
|
* --drawer-closed-translate (side/RTL positioning state)
|
|
4958
|
+
* --drawer-focus-bleed
|
|
4807
4959
|
* --drawer-motion-duration (shared by panel and ::backdrop so exit duration
|
|
4808
4960
|
* changes in one place; entry state redefines it longer)
|
|
4809
4961
|
*/
|
|
4810
|
-
dialog.drawer {
|
|
4962
|
+
:where(dialog).drawer {
|
|
4811
4963
|
--drawer-size: 20rem;
|
|
4812
4964
|
--drawer-pad: var(--space-40);
|
|
4813
4965
|
--drawer-closed-translate: -100%;
|
|
@@ -4817,15 +4969,24 @@ dialog.drawer {
|
|
|
4817
4969
|
|
|
4818
4970
|
/* Keep the surface anchored to the viewport in the top layer. This also
|
|
4819
4971
|
provides the containing block for the absolutely positioned close button.
|
|
4820
|
-
|
|
4821
|
-
|
|
4972
|
+
|
|
4973
|
+
The panel reaches both viewport edges, so it has to follow the space that
|
|
4974
|
+
is actually on screen: --viewport-block resolves to 100dvh and tracks a
|
|
4975
|
+
mobile browser's collapsing URL bar. A percentage resolves against the
|
|
4976
|
+
layout viewport instead, which those browsers size as if the bar were
|
|
4977
|
+
hidden — that put the footer below the fold for as long as it showed. The
|
|
4978
|
+
modal pins the small viewport for the opposite reason: it floats, so a
|
|
4979
|
+
stable height beats a panel that breathes while the bar moves.
|
|
4980
|
+
|
|
4981
|
+
An embedded preview that fakes a viewport overrides --viewport-block on
|
|
4982
|
+
its own shell; the component does not degrade to a percentage for it. */
|
|
4822
4983
|
position: fixed;
|
|
4823
4984
|
inset-block-start: 0;
|
|
4824
4985
|
|
|
4825
4986
|
inline-size: min(calc(var(--viewport-inline) * 0.9), var(--drawer-size));
|
|
4826
|
-
block-size:
|
|
4987
|
+
block-size: var(--viewport-block);
|
|
4827
4988
|
max-inline-size: none;
|
|
4828
|
-
max-block-size:
|
|
4989
|
+
max-block-size: var(--viewport-block);
|
|
4829
4990
|
margin: 0;
|
|
4830
4991
|
padding: var(--drawer-pad);
|
|
4831
4992
|
border: 0;
|
|
@@ -4841,7 +5002,7 @@ dialog.drawer {
|
|
|
4841
5002
|
transform var(--drawer-motion-duration) var(--ease-exit);
|
|
4842
5003
|
}
|
|
4843
5004
|
|
|
4844
|
-
dialog.drawer[open] {
|
|
5005
|
+
:where(dialog).drawer[open] {
|
|
4845
5006
|
--drawer-motion-duration: var(--duration-slow);
|
|
4846
5007
|
display: grid;
|
|
4847
5008
|
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
@@ -4856,7 +5017,7 @@ dialog.drawer[open] {
|
|
|
4856
5017
|
/* A form is the drawer's content wrapper when actions need method="dialog".
|
|
4857
5018
|
Span the drawer tracks so the form can keep the header at the top, let the
|
|
4858
5019
|
body absorb the available height, and keep the footer at the bottom. */
|
|
4859
|
-
dialog.drawer > form {
|
|
5020
|
+
:where(dialog).drawer > form {
|
|
4860
5021
|
display: grid;
|
|
4861
5022
|
grid-row: 1 / -1;
|
|
4862
5023
|
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
@@ -4865,7 +5026,7 @@ dialog.drawer > form {
|
|
|
4865
5026
|
}
|
|
4866
5027
|
|
|
4867
5028
|
/* The shared backdrop token keeps drawer and modal scrims consistent. */
|
|
4868
|
-
dialog.drawer::backdrop {
|
|
5029
|
+
:where(dialog).drawer::backdrop {
|
|
4869
5030
|
background: var(--backdrop-fill);
|
|
4870
5031
|
opacity: 0;
|
|
4871
5032
|
/* Scrim curve stays neutral `ease`; only the panel's character curves
|
|
@@ -4873,7 +5034,7 @@ dialog.drawer::backdrop {
|
|
|
4873
5034
|
transition: opacity var(--drawer-motion-duration) ease;
|
|
4874
5035
|
}
|
|
4875
5036
|
|
|
4876
|
-
dialog.drawer[data-side="end"] {
|
|
5037
|
+
:where(dialog).drawer[data-side="end"] {
|
|
4877
5038
|
--drawer-closed-translate: 100%;
|
|
4878
5039
|
|
|
4879
5040
|
margin-inline-start: auto;
|
|
@@ -4882,29 +5043,41 @@ dialog.drawer[data-side="end"] {
|
|
|
4882
5043
|
border-inline-start: var(--border-width) solid var(--border);
|
|
4883
5044
|
}
|
|
4884
5045
|
|
|
4885
|
-
dialog.drawer[open]::backdrop {
|
|
5046
|
+
:where(dialog).drawer[open]::backdrop {
|
|
4886
5047
|
opacity: 1;
|
|
4887
5048
|
}
|
|
4888
5049
|
|
|
4889
5050
|
@starting-style {
|
|
4890
|
-
dialog.drawer[open] {
|
|
5051
|
+
:where(dialog).drawer[open] {
|
|
4891
5052
|
opacity: 0;
|
|
4892
5053
|
transform: translateX(var(--drawer-closed-translate));
|
|
4893
5054
|
}
|
|
4894
5055
|
|
|
4895
|
-
dialog.drawer[open]::backdrop {
|
|
5056
|
+
:where(dialog).drawer[open]::backdrop {
|
|
4896
5057
|
opacity: 0;
|
|
4897
5058
|
}
|
|
4898
5059
|
}
|
|
4899
5060
|
|
|
4900
|
-
/*
|
|
4901
|
-
|
|
5061
|
+
/* The body region absorbs the panel's height and scrolls. A drawer is a
|
|
5062
|
+
full-height shell by definition, so it always scrolls its body — unlike the
|
|
5063
|
+
modal, which sizes to its content and opts in with .scrollable. Header and
|
|
5064
|
+
footer keep their own tracks; .drawer-close is named because a generic
|
|
5065
|
+
exclusion list is easier to trust than its `position: absolute`. */
|
|
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
|
+
|
|
4902
5070
|
min-block-size: 0;
|
|
4903
|
-
|
|
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);
|
|
5075
|
+
overflow: auto;
|
|
5076
|
+
overscroll-behavior: contain;
|
|
4904
5077
|
}
|
|
4905
5078
|
|
|
4906
5079
|
@supports (transition-behavior: allow-discrete) {
|
|
4907
|
-
dialog.drawer {
|
|
5080
|
+
:where(dialog).drawer {
|
|
4908
5081
|
transition:
|
|
4909
5082
|
opacity var(--drawer-motion-duration) var(--ease-exit),
|
|
4910
5083
|
transform var(--drawer-motion-duration) var(--ease-exit),
|
|
@@ -4912,7 +5085,7 @@ dialog.drawer nav {
|
|
|
4912
5085
|
display var(--drawer-motion-duration) allow-discrete;
|
|
4913
5086
|
}
|
|
4914
5087
|
|
|
4915
|
-
dialog.drawer[open] {
|
|
5088
|
+
:where(dialog).drawer[open] {
|
|
4916
5089
|
transition:
|
|
4917
5090
|
opacity var(--drawer-motion-duration) var(--ease-enter),
|
|
4918
5091
|
transform var(--drawer-motion-duration) var(--ease-enter),
|
|
@@ -4920,7 +5093,7 @@ dialog.drawer nav {
|
|
|
4920
5093
|
display var(--drawer-motion-duration) allow-discrete;
|
|
4921
5094
|
}
|
|
4922
5095
|
|
|
4923
|
-
dialog.drawer::backdrop {
|
|
5096
|
+
:where(dialog).drawer::backdrop {
|
|
4924
5097
|
transition:
|
|
4925
5098
|
opacity var(--drawer-motion-duration) ease,
|
|
4926
5099
|
overlay var(--drawer-motion-duration) allow-discrete,
|
|
@@ -4934,40 +5107,40 @@ dialog.drawer nav {
|
|
|
4934
5107
|
directionality and handles nested direction changes; the explicit ltr rules
|
|
4935
5108
|
neutralize an outer RTL fallback selector. Each specificity group keeps the
|
|
4936
5109
|
fallback before the :dir() rules. */
|
|
4937
|
-
[dir="rtl"] dialog.drawer {
|
|
5110
|
+
[dir="rtl"] :where(dialog).drawer {
|
|
4938
5111
|
--drawer-closed-translate: 100%;
|
|
4939
5112
|
}
|
|
4940
5113
|
|
|
4941
|
-
dialog.drawer:dir(ltr) {
|
|
5114
|
+
:where(dialog).drawer:dir(ltr) {
|
|
4942
5115
|
--drawer-closed-translate: -100%;
|
|
4943
5116
|
}
|
|
4944
5117
|
|
|
4945
|
-
dialog.drawer:dir(rtl) {
|
|
5118
|
+
:where(dialog).drawer:dir(rtl) {
|
|
4946
5119
|
--drawer-closed-translate: 100%;
|
|
4947
5120
|
}
|
|
4948
5121
|
|
|
4949
|
-
[dir="rtl"] dialog.drawer[data-side="end"] {
|
|
5122
|
+
[dir="rtl"] :where(dialog).drawer[data-side="end"] {
|
|
4950
5123
|
--drawer-closed-translate: -100%;
|
|
4951
5124
|
}
|
|
4952
5125
|
|
|
4953
|
-
dialog.drawer[data-side="end"]:dir(ltr) {
|
|
5126
|
+
:where(dialog).drawer[data-side="end"]:dir(ltr) {
|
|
4954
5127
|
--drawer-closed-translate: 100%;
|
|
4955
5128
|
}
|
|
4956
5129
|
|
|
4957
|
-
dialog.drawer[data-side="end"]:dir(rtl) {
|
|
5130
|
+
:where(dialog).drawer[data-side="end"]:dir(rtl) {
|
|
4958
5131
|
--drawer-closed-translate: -100%;
|
|
4959
5132
|
}
|
|
4960
5133
|
|
|
4961
5134
|
@media (prefers-reduced-motion: no-preference) {
|
|
4962
|
-
dialog.drawer.is-static {
|
|
5135
|
+
:where(dialog).drawer.is-static {
|
|
4963
5136
|
animation: actual-dialog-static var(--duration-slow) ease;
|
|
4964
5137
|
}
|
|
4965
5138
|
}
|
|
4966
5139
|
|
|
4967
5140
|
/* The header reserves inline room so the title never runs under the close
|
|
4968
5141
|
control. */
|
|
4969
|
-
dialog.drawer > header,
|
|
4970
|
-
dialog.drawer > form > header {
|
|
5142
|
+
:where(dialog).drawer > header,
|
|
5143
|
+
:where(dialog).drawer > form > header {
|
|
4971
5144
|
padding-inline-end: calc(var(--control-size) + var(--space-30));
|
|
4972
5145
|
}
|
|
4973
5146
|
|
|
@@ -5026,6 +5199,8 @@ dialog.drawer > form > header {
|
|
|
5026
5199
|
/*
|
|
5027
5200
|
* Flyout — floating panel for actions and links.
|
|
5028
5201
|
*
|
|
5202
|
+
* Docs: docs/pages/enhancements/flyout.md
|
|
5203
|
+
*
|
|
5029
5204
|
* JavaScript promotes the panel to the top layer and chooses viewport or
|
|
5030
5205
|
* document coordinates from its anchor. It also owns state (.is-open), focus,
|
|
5031
5206
|
* and keyboard behavior. CSS owns the surfaced panel appearance, item styles,
|
|
@@ -5328,6 +5503,8 @@ dialog.drawer > form > header {
|
|
|
5328
5503
|
/*
|
|
5329
5504
|
* Tooltip — supplemental label overlaid near a trigger.
|
|
5330
5505
|
*
|
|
5506
|
+
* Docs: docs/pages/enhancements/tooltip.md
|
|
5507
|
+
*
|
|
5331
5508
|
* Position, placement, and arrow position are managed by JavaScript via left,
|
|
5332
5509
|
* top, data-placement, and --arrow-x. The runtime also owns `position`: it
|
|
5333
5510
|
* writes `absolute` for a tip whose trigger scrolls with the page, so the
|
|
@@ -5470,6 +5647,8 @@ dialog.drawer > form > header {
|
|
|
5470
5647
|
/*
|
|
5471
5648
|
* Accordion disclosures built on native details and summary.
|
|
5472
5649
|
*
|
|
5650
|
+
* Docs: docs/pages/components/accordion.md
|
|
5651
|
+
*
|
|
5473
5652
|
* Keeps the clickable summary affordance aligned and usable without JavaScript
|
|
5474
5653
|
* or demo styles. Decorative palettes and custom markers belong outside the
|
|
5475
5654
|
* base component.
|
|
@@ -5597,6 +5776,8 @@ dialog.drawer > form > header {
|
|
|
5597
5776
|
/*
|
|
5598
5777
|
* Spinner — compact loading feedback.
|
|
5599
5778
|
*
|
|
5779
|
+
* Docs: docs/pages/components/spinner.md
|
|
5780
|
+
*
|
|
5600
5781
|
* Uses currentColor so it adapts to buttons, alerts, badges, and text. The
|
|
5601
5782
|
* [aria-busy] overlay composition lives in components/busy.css.
|
|
5602
5783
|
*
|
|
@@ -5662,6 +5843,8 @@ dialog.drawer > form > header {
|
|
|
5662
5843
|
/*
|
|
5663
5844
|
* Skeleton placeholders for loading states.
|
|
5664
5845
|
*
|
|
5846
|
+
* Docs: docs/pages/components/skeleton.md
|
|
5847
|
+
*
|
|
5665
5848
|
* Shows approximate loading structure without implying real content hierarchy.
|
|
5666
5849
|
* Geometry, animation, and color stay theme-aware.
|
|
5667
5850
|
*
|
|
@@ -5672,16 +5855,24 @@ dialog.drawer > form > header {
|
|
|
5672
5855
|
* --skeleton-track
|
|
5673
5856
|
* --skeleton-highlight
|
|
5674
5857
|
*/
|
|
5858
|
+
/* A placeholder fills its container up to --skeleton-width; the preset widths
|
|
5859
|
+
are a cap, never an intrinsic size. Stated the other way round — a rem
|
|
5860
|
+
`inline-size` capped by a percentage `max-inline-size` — the cap is ignored
|
|
5861
|
+
during intrinsic sizing, so every shrink-to-fit ancestor inherits the
|
|
5862
|
+
preset as a min-content floor: a card holding a title placeholder could not
|
|
5863
|
+
go below 18rem and pushed a phone's layout sideways. A percentage
|
|
5864
|
+
`inline-size` contributes nothing intrinsically and a rem cap only lowers
|
|
5865
|
+
the contribution, so the same geometry costs no floor. */
|
|
5675
5866
|
.skeleton {
|
|
5676
5867
|
--skeleton-size: 1rem;
|
|
5677
|
-
--skeleton-width:
|
|
5868
|
+
--skeleton-width: none;
|
|
5678
5869
|
--skeleton-radius: var(--radius);
|
|
5679
5870
|
--skeleton-track: var(--surface-subtle);
|
|
5680
5871
|
--skeleton-highlight: var(--surface-raised);
|
|
5681
5872
|
|
|
5682
5873
|
display: block;
|
|
5683
|
-
inline-size:
|
|
5684
|
-
max-inline-size:
|
|
5874
|
+
inline-size: 100%;
|
|
5875
|
+
max-inline-size: var(--skeleton-width);
|
|
5685
5876
|
block-size: var(--skeleton-size);
|
|
5686
5877
|
border-radius: var(--skeleton-radius);
|
|
5687
5878
|
background:
|
|
@@ -5705,11 +5896,14 @@ dialog.drawer > form > header {
|
|
|
5705
5896
|
--skeleton-width: 18rem;
|
|
5706
5897
|
}
|
|
5707
5898
|
|
|
5899
|
+
/* The one preset with a real intrinsic width: a disc is square, so it sizes
|
|
5900
|
+
rather than caps. The floor it does carry is the avatar size, which is small
|
|
5901
|
+
enough never to widen a layout. */
|
|
5708
5902
|
.skeleton[data-shape="avatar"] {
|
|
5709
5903
|
--skeleton-size: var(--avatar-size, 2.25rem);
|
|
5710
|
-
--skeleton-width: var(--skeleton-size);
|
|
5711
5904
|
--skeleton-radius: var(--radius-full);
|
|
5712
5905
|
|
|
5906
|
+
inline-size: var(--skeleton-size);
|
|
5713
5907
|
flex: 0 0 auto;
|
|
5714
5908
|
}
|
|
5715
5909
|
|
|
@@ -5737,6 +5931,8 @@ dialog.drawer > form > header {
|
|
|
5737
5931
|
/*
|
|
5738
5932
|
* Busy overlay — non-button loading state.
|
|
5739
5933
|
*
|
|
5934
|
+
* Docs: docs/pages/components/spinner.md
|
|
5935
|
+
*
|
|
5740
5936
|
* A non-button element with aria-busy="true" and a direct last-child .spinner
|
|
5741
5937
|
* shows that spinner as a centered overlay over a faded surface. Buttons keep
|
|
5742
5938
|
* their spinners inline (e.g. .btn[aria-busy] > .spinner stays inline-flex).
|
|
@@ -5789,6 +5985,8 @@ dialog.drawer > form > header {
|
|
|
5789
5985
|
/*
|
|
5790
5986
|
* Progress — native progress bar with theme-aware track and value.
|
|
5791
5987
|
*
|
|
5988
|
+
* Docs: docs/pages/components/progress.md
|
|
5989
|
+
*
|
|
5792
5990
|
* Consumes the shared --intent/--primary for the value bar and --bar-height
|
|
5793
5991
|
* for thickness. Indeterminate state animates a shimmer using --duration-shimmer.
|
|
5794
5992
|
*
|
|
@@ -5903,6 +6101,8 @@ dialog.drawer > form > header {
|
|
|
5903
6101
|
/*
|
|
5904
6102
|
* Status bar — singleton floating status area.
|
|
5905
6103
|
*
|
|
6104
|
+
* Docs: docs/pages/enhancements/status-bar.md
|
|
6105
|
+
*
|
|
5906
6106
|
* One element in the HTML, empty by default. .is-open is the visual state, set
|
|
5907
6107
|
* by status.js like any other surface: it lands after the message is written
|
|
5908
6108
|
* and lifts before the bar is cleared, so the bar leaves in the exact state it
|
|
@@ -5994,6 +6194,8 @@ dialog.drawer > form > header {
|
|
|
5994
6194
|
/*
|
|
5995
6195
|
* Avatars for initials, images, links, and buttons.
|
|
5996
6196
|
*
|
|
6197
|
+
* Docs: docs/pages/components/avatar.md
|
|
6198
|
+
*
|
|
5997
6199
|
* The visual box stays the same across semantic elements. Grouping and status
|
|
5998
6200
|
* dots (an empty .badge child) compose around the avatar without changing its
|
|
5999
6201
|
* meaning.
|
|
@@ -6216,6 +6418,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
6216
6418
|
/*
|
|
6217
6419
|
* Indicator — generic corner positioning for overlaid component content.
|
|
6218
6420
|
*
|
|
6421
|
+
* Docs: docs/pages/components/indicator.md
|
|
6422
|
+
*
|
|
6219
6423
|
* The container only establishes the containing block. The item owns no
|
|
6220
6424
|
* paint, size, or ring so badges and other components keep their own visual
|
|
6221
6425
|
* contract.
|
|
@@ -6285,6 +6489,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
6285
6489
|
/*
|
|
6286
6490
|
* Key — keyboard keys and shortcut tokens for UI chrome.
|
|
6287
6491
|
*
|
|
6492
|
+
* Docs: docs/pages/components/key.md
|
|
6493
|
+
*
|
|
6288
6494
|
* Use .key outside prose. Plain kbd inside .prose gets the same visual
|
|
6289
6495
|
* treatment from prose.css so authored content stays convenient.
|
|
6290
6496
|
*
|
|
@@ -6325,6 +6531,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
6325
6531
|
/*
|
|
6326
6532
|
* Message rows composed from an optional avatar, metadata, and bubble.
|
|
6327
6533
|
*
|
|
6534
|
+
* Docs: docs/pages/components/chat.md
|
|
6535
|
+
*
|
|
6328
6536
|
* Public hooks:
|
|
6329
6537
|
* --chat-gap
|
|
6330
6538
|
* --chat-max-size
|
|
@@ -6428,6 +6636,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
6428
6636
|
* Button owns the trigger's size, shape, and intent; FAB owns placement and
|
|
6429
6637
|
* the speed dial's structural open/close markers (--icon-plus / --icon-close).
|
|
6430
6638
|
*
|
|
6639
|
+
* Docs: docs/pages/components/fab.md
|
|
6640
|
+
*
|
|
6431
6641
|
* Public hooks:
|
|
6432
6642
|
* --fab-offset
|
|
6433
6643
|
* --fab-gap
|
|
@@ -6555,6 +6765,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
6555
6765
|
/*
|
|
6556
6766
|
* Rating — integer star rating drawn on native radio inputs.
|
|
6557
6767
|
*
|
|
6768
|
+
* Docs: docs/pages/components/rating.md
|
|
6769
|
+
*
|
|
6558
6770
|
* The checked radio remains the functional baseline. :has() progressively
|
|
6559
6771
|
* fills preceding stars without reversing DOM or keyboard order. Forced
|
|
6560
6772
|
* colors restores the platform radio presentation.
|
|
@@ -6657,6 +6869,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
6657
6869
|
/*
|
|
6658
6870
|
* Steps — semantic ordered-list progression for a multi-step process.
|
|
6659
6871
|
*
|
|
6872
|
+
* Docs: docs/pages/components/steps.md
|
|
6873
|
+
*
|
|
6660
6874
|
* One primitive, two explicit compositions. Both classes are required:
|
|
6661
6875
|
*
|
|
6662
6876
|
* <ol class="steps steps-horizontal"> horizontal, 2 to 5 stages
|
|
@@ -7005,6 +7219,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
7005
7219
|
* and item states; app-layout owns whether that surface is a bottom bar or
|
|
7006
7220
|
* labelled side navigation.
|
|
7007
7221
|
*
|
|
7222
|
+
* Docs: docs/pages/components/app-navigation.md
|
|
7223
|
+
*
|
|
7008
7224
|
* Public hooks:
|
|
7009
7225
|
* --app-nav-size
|
|
7010
7226
|
* --app-nav-gap
|
|
@@ -7090,6 +7306,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
7090
7306
|
/*
|
|
7091
7307
|
* Navbar — top-level navigation bar.
|
|
7092
7308
|
*
|
|
7309
|
+
* Docs: docs/pages/components/navbar.md
|
|
7310
|
+
*
|
|
7093
7311
|
* .navbar is a horizontal bar; .navbar-brand is the brand link; .navbar-nav is
|
|
7094
7312
|
* the inline link list using .nav-link items. .nav-list is the vertical nav
|
|
7095
7313
|
* list shared with .drawer, fed by the shared --gap token.
|
|
@@ -7222,6 +7440,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
7222
7440
|
/*
|
|
7223
7441
|
* Breadcrumb — current-location trail from the site root.
|
|
7224
7442
|
*
|
|
7443
|
+
* Docs: docs/pages/components/breadcrumb.md
|
|
7444
|
+
*
|
|
7225
7445
|
* Inline list separated by "/". Mark the current page with `aria-current="page"`
|
|
7226
7446
|
* either on the item or on the current link.
|
|
7227
7447
|
*/
|
|
@@ -7271,6 +7491,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
7271
7491
|
/*
|
|
7272
7492
|
* Pagination — page navigation for paginated lists.
|
|
7273
7493
|
*
|
|
7494
|
+
* Docs: docs/pages/components/pagination.md
|
|
7495
|
+
*
|
|
7274
7496
|
* Render .btn elements inside a .pagination list. The active page is marked
|
|
7275
7497
|
* with aria-current="page"; .btn owns the active state styling.
|
|
7276
7498
|
*
|
|
@@ -7315,6 +7537,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
7315
7537
|
/*
|
|
7316
7538
|
* Tabs — in-place panel switcher with real tab semantics.
|
|
7317
7539
|
*
|
|
7540
|
+
* Docs: docs/pages/enhancements/tabs.md
|
|
7541
|
+
*
|
|
7318
7542
|
* JavaScript owns roving tabindex, aria-selected, panel visibility,
|
|
7319
7543
|
* and arrow-key navigation. CSS owns the tab strip appearance and
|
|
7320
7544
|
* active indicator.
|
|
@@ -7436,6 +7660,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
7436
7660
|
* Scrollspy — behavior hook for navigation that marks the active section
|
|
7437
7661
|
* while the page scrolls.
|
|
7438
7662
|
*
|
|
7663
|
+
* Docs: docs/pages/enhancements/scrollspy.md
|
|
7664
|
+
*
|
|
7439
7665
|
* Does not add visual styles by itself. scrollspy.js watches
|
|
7440
7666
|
* <nav class="scrollspy">
|
|
7441
7667
|
* containing <a href="#section"> links and toggles aria-current="location" on
|
|
@@ -7465,6 +7691,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
7465
7691
|
/*
|
|
7466
7692
|
* Data list — term/value pairs for the properties of a single object.
|
|
7467
7693
|
*
|
|
7694
|
+
* Docs: docs/pages/components/data-list.md
|
|
7695
|
+
*
|
|
7468
7696
|
* Expects alternating <dt>/<dd> children: one term, one value per property.
|
|
7469
7697
|
* A plain <dl> handles richer relationships (several terms or descriptions)
|
|
7470
7698
|
* and stays untouched by this component.
|
|
@@ -7495,6 +7723,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
7495
7723
|
* Content determines the number of lines; the component only owns the row
|
|
7496
7724
|
* geometry, dividers, and supporting-text treatment.
|
|
7497
7725
|
*
|
|
7726
|
+
* Docs: docs/pages/components/list.md
|
|
7727
|
+
*
|
|
7498
7728
|
* Public hooks:
|
|
7499
7729
|
* --list-item-min-size
|
|
7500
7730
|
* --list-item-pad-block
|
|
@@ -7615,6 +7845,8 @@ a.list-item[aria-current]::before {
|
|
|
7615
7845
|
/*
|
|
7616
7846
|
* Table — structured tabular data.
|
|
7617
7847
|
*
|
|
7848
|
+
* Docs: docs/pages/components/table.md
|
|
7849
|
+
*
|
|
7618
7850
|
* .table-wrap provides the scroll container and outer chrome; .table owns row
|
|
7619
7851
|
* and cell rules. Tokens are sourced from the shared scale.
|
|
7620
7852
|
*
|
|
@@ -7623,6 +7855,14 @@ a.list-item[aria-current]::before {
|
|
|
7623
7855
|
* --table-cell-pad
|
|
7624
7856
|
*/
|
|
7625
7857
|
.table-wrap {
|
|
7858
|
+
/* Containing block for anything absolutely positioned inside the table —
|
|
7859
|
+
`.sr-only` on a column header above all. Without it that box resolves
|
|
7860
|
+
against the initial containing block, so it keeps the static position of a
|
|
7861
|
+
cell that may be a thousand pixels along the scrolled row, escapes this
|
|
7862
|
+
scroller, and extends the *document's* scrollable width. The page then
|
|
7863
|
+
scrolls sideways on a phone with nothing painted out there. */
|
|
7864
|
+
position: relative;
|
|
7865
|
+
|
|
7626
7866
|
inline-size: 100%;
|
|
7627
7867
|
max-inline-size: 100%;
|
|
7628
7868
|
min-inline-size: 0;
|
|
@@ -7688,6 +7928,8 @@ a.list-item[aria-current]::before {
|
|
|
7688
7928
|
/*
|
|
7689
7929
|
* Meter — native and segmented gauges for known-range values.
|
|
7690
7930
|
*
|
|
7931
|
+
* Docs: docs/pages/components/meter.md
|
|
7932
|
+
*
|
|
7691
7933
|
* Maps the three standard meter zones (optimum / suboptimum / even-less-good)
|
|
7692
7934
|
* to the shared success / warning / danger intent colors.
|
|
7693
7935
|
*
|
|
@@ -7780,6 +8022,8 @@ a.list-item[aria-current]::before {
|
|
|
7780
8022
|
/*
|
|
7781
8023
|
* Join — visually groups adjacent controls into a single unit.
|
|
7782
8024
|
*
|
|
8025
|
+
* Docs: docs/pages/forms/joined-controls.md
|
|
8026
|
+
*
|
|
7783
8027
|
* This is a cross-cutting control primitive, not a forms-only component:
|
|
7784
8028
|
* it applies equally to inputs, selects, buttons, and button-like links.
|
|
7785
8029
|
*
|
|
@@ -7913,6 +8157,8 @@ a.list-item[aria-current]::before {
|
|
|
7913
8157
|
/*
|
|
7914
8158
|
* Decorative intent-colored frame for one direct child.
|
|
7915
8159
|
*
|
|
8160
|
+
* Docs: docs/pages/components/aura.md
|
|
8161
|
+
*
|
|
7916
8162
|
* The effect is built from three layers with distinct roles: a frame carrying
|
|
7917
8163
|
* a short accent of light, a small halo that belongs to the frame and keeps
|
|
7918
8164
|
* that light attached to the edge, and a wide static halo that belongs to the
|
|
@@ -8231,7 +8477,12 @@ a.list-item[aria-current]::before {
|
|
|
8231
8477
|
text-decoration: none;
|
|
8232
8478
|
}
|
|
8233
8479
|
|
|
8234
|
-
|
|
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
|
+
|
|
8235
8486
|
.link-muted:focus-visible {
|
|
8236
8487
|
color: var(--text);
|
|
8237
8488
|
}
|