actual-css 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +90 -1
- package/README.md +25 -8
- package/dist/actual-themes.min.css +1 -0
- package/dist/actual.css +1492 -931
- package/dist/actual.js +1 -3
- package/dist/actual.min.css +1 -1
- package/dist/optional.css +1048 -0
- package/dist/optional.min.css +1 -0
- package/docs/design-notes/browser-support.md +95 -6
- package/docs/design-notes/cascade-layer.md +1 -1
- package/docs/design-notes/enhancement-contract.md +101 -0
- package/docs/design-notes/forced-colors.md +105 -0
- package/docs/design-notes/naming.md +3 -1
- package/docs/design-notes/platform-alignment.md +492 -0
- package/docs/design-notes/references.md +8 -0
- package/docs/design-notes/widget-primitives.md +119 -0
- package/docs/navigation.json +135 -0
- package/docs/pages/components/accordion.md +80 -0
- package/docs/pages/components/alert.md +203 -0
- package/docs/pages/components/aura.md +35 -0
- package/docs/pages/components/avatar.md +95 -0
- package/docs/pages/components/badge.md +102 -0
- package/docs/pages/components/breadcrumb.md +19 -0
- package/docs/pages/components/button.md +205 -0
- package/docs/pages/components/card.md +183 -0
- package/docs/pages/components/chat.md +50 -0
- package/docs/pages/components/dialog.md +367 -0
- package/docs/pages/components/drawer.md +113 -0
- package/docs/pages/components/fab.md +118 -0
- package/docs/pages/components/key.md +33 -0
- package/docs/pages/components/meter.md +23 -0
- package/docs/pages/components/navbar.md +92 -0
- package/docs/pages/components/pagination.md +55 -0
- package/docs/pages/components/progress.md +29 -0
- package/docs/pages/components/skeleton.md +38 -0
- package/docs/pages/components/spinner.md +93 -0
- package/docs/pages/components/table.md +102 -0
- package/docs/pages/enhancements/flyout.md +391 -0
- package/docs/pages/enhancements/overview.md +36 -0
- package/docs/pages/enhancements/scrollspy.md +71 -0
- package/docs/pages/enhancements/status-bar.md +72 -0
- package/docs/pages/enhancements/tabs.md +80 -0
- package/docs/pages/enhancements/tooltip.md +130 -0
- package/docs/pages/examples/overview.md +41 -0
- package/docs/pages/forms/choice-cards.md +102 -0
- package/docs/pages/forms/controls.md +32 -0
- package/docs/pages/forms/detached-actions.md +59 -0
- package/docs/pages/forms/floating-field.md +88 -0
- package/docs/pages/forms/form-actions.md +56 -0
- package/docs/pages/forms/input-policy.md +91 -0
- package/docs/pages/forms/joined-controls.md +37 -0
- package/docs/pages/forms/labels.md +42 -0
- package/docs/pages/forms/otp.md +50 -0
- package/docs/pages/forms/overview.md +188 -0
- package/docs/pages/forms/password.md +40 -0
- package/docs/pages/forms/range.md +17 -0
- package/docs/pages/forms/states-and-sizes.md +321 -0
- package/docs/pages/forms/sticky-actions.md +187 -0
- package/docs/pages/forms/switch.md +31 -0
- package/docs/pages/forms/validation.md +241 -0
- package/docs/{mental-model.md → pages/foundations/philosophy.md} +33 -18
- package/docs/pages/foundations/theming.md +92 -0
- package/docs/pages/foundations/tokens.md +421 -0
- package/docs/{typography.md → pages/foundations/typography.md} +27 -9
- package/docs/pages/guides/bootstrap.md +438 -0
- package/docs/pages/guides/introduction.md +157 -0
- package/docs/pages/guides/optional-components.md +33 -0
- package/docs/pages/guides/progressive-enhancement.md +686 -0
- package/docs/pages/guides/tailwind.md +455 -0
- package/docs/pages/layout/app-shell.md +25 -0
- package/docs/pages/layout/center.md +29 -0
- package/docs/pages/layout/cluster.md +57 -0
- package/docs/pages/layout/frame.md +30 -0
- package/docs/pages/layout/grid.md +142 -0
- package/docs/pages/layout/list.md +41 -0
- package/docs/pages/layout/media.md +21 -0
- package/docs/pages/layout/overview.md +37 -0
- package/docs/pages/layout/scroll-snap.md +54 -0
- package/docs/pages/layout/sidebar-layout.md +27 -0
- package/docs/pages/layout/stack.md +18 -0
- package/docs/pages/layout/switcher.md +36 -0
- package/docs/pages/layout/topbar.md +23 -0
- package/docs/pages/patterns/actions.md +52 -0
- package/docs/pages/patterns/code-block.md +36 -0
- package/docs/pages/patterns/exclusive-accordion.md +16 -0
- package/docs/pages/patterns/hierarchical-nav.md +26 -0
- package/docs/pages/patterns/icon-input.md +25 -0
- package/docs/pages/patterns/intent-nesting.md +3 -0
- package/docs/pages/patterns/navigation-list.md +235 -0
- package/docs/pages/patterns/overview.md +58 -0
- package/docs/pages/patterns/structured-lists.md +47 -0
- package/docs/pages/utilities/accessibility.md +34 -0
- package/docs/pages/utilities/flex.md +105 -0
- package/docs/pages/utilities/links.md +18 -0
- package/docs/pages/utilities/list.md +31 -0
- package/docs/pages/utilities/overflow.md +43 -0
- package/docs/pages/utilities/overview.md +18 -0
- package/docs/pages/utilities/shape.md +25 -0
- package/docs/pages/utilities/sizing-wrapping.md +115 -0
- package/docs/pages/utilities/spacing.md +79 -0
- package/docs/pages/utilities/text.md +43 -0
- package/llms.txt +10 -4
- package/package.json +30 -16
- package/src/css/actual.css +12 -1
- package/src/css/base.css +3 -9
- package/src/css/components/accordion.css +18 -30
- package/src/css/components/actions.css +4 -1
- package/src/css/components/alert.css +162 -49
- package/src/css/components/avatar.css +16 -10
- package/src/css/components/badge.css +82 -39
- package/src/css/components/breadcrumb.css +7 -15
- package/src/css/components/busy.css +7 -0
- package/src/css/components/button.css +82 -38
- package/src/css/components/card.css +17 -37
- package/src/css/components/drawer.css +30 -13
- package/src/css/components/flyout.css +39 -110
- package/src/css/components/index.css +1 -0
- package/src/css/components/join.css +14 -3
- package/src/css/components/key.css +2 -0
- package/src/css/components/menu.css +125 -0
- package/src/css/components/meter.css +0 -23
- package/src/css/components/modal.css +162 -7
- package/src/css/components/navbar.css +17 -18
- package/src/css/components/overline.css +0 -7
- package/src/css/components/pagination.css +1 -1
- package/src/css/components/progress.css +8 -26
- package/src/css/components/scrollspy.css +2 -2
- package/src/css/components/skeleton.css +7 -0
- package/src/css/components/spinner.css +10 -11
- package/src/css/components/status-bar.css +18 -28
- package/src/css/components/surface.css +2 -8
- package/src/css/components/tab.css +20 -48
- package/src/css/components/table.css +7 -5
- package/src/css/components/tooltip.css +14 -7
- package/src/css/focus.css +16 -3
- package/src/css/forms/choice-card.css +13 -15
- package/src/css/forms/choice.css +21 -28
- package/src/css/forms/control.css +16 -26
- package/src/css/forms/custom-select.css +9 -11
- package/src/css/forms/form-actions.css +14 -3
- package/src/css/forms/form.css +7 -4
- package/src/css/forms/index.css +5 -4
- package/src/css/forms/input-icon.css +30 -12
- package/src/css/forms/native.css +48 -33
- package/src/css/forms/range.css +103 -0
- package/src/css/forms/select.css +8 -8
- package/src/css/forms/switch.css +14 -13
- package/src/css/forms/validation.css +32 -5
- package/src/css/grid.css +19 -18
- package/src/css/intents.css +2 -2
- package/src/css/layout.css +29 -1
- package/src/css/optional/aura.css +93 -0
- package/src/css/optional/chat.css +105 -0
- package/src/css/optional/fab.css +104 -0
- package/src/css/optional/floating-field.css +87 -0
- package/src/css/optional/index.css +5 -0
- package/src/css/optional/layout-extra.css +7 -4
- package/src/css/optional/otp.css +115 -0
- package/src/css/optional/scroll-snap.css +6 -0
- package/src/css/optional/scroller.css +19 -7
- package/src/css/optional/typography-fluid.css +5 -0
- package/src/css/optional/utilities-extra.css +17 -2
- package/src/css/prose.css +18 -8
- package/src/css/reset.css +0 -10
- package/src/css/theme.css +29 -13
- package/src/css/themes/cyberpunk.css +1 -1
- package/src/css/themes/dim.css +3 -3
- package/src/css/themes/edge.css +11 -37
- package/src/css/themes/index.css +3 -3
- package/src/css/themes/{dark.css → indigo.css} +5 -4
- package/src/css/themes/neon.css +5 -5
- package/src/css/themes/square.css +0 -3
- package/src/css/tokens.css +81 -21
- package/src/css/utilities.css +8 -10
- package/src/css/variants.css +37 -65
- package/src/js/command.js +1 -3
- package/src/js/context-menu.js +72 -53
- package/src/js/dialog.js +68 -79
- package/src/js/dismiss.js +15 -0
- package/src/js/enhance.js +76 -19
- package/src/js/enhancement-loader.js +301 -0
- package/src/js/events.js +3 -0
- package/src/js/filter.js +3 -3
- package/src/js/floating.js +101 -131
- package/src/js/flyout.js +106 -96
- package/src/js/focus-group.js +116 -0
- package/src/js/focus.js +6 -1
- package/src/js/index.js +4 -2
- package/src/js/input.js +1 -1
- package/src/js/keys.js +27 -0
- package/src/js/mask.js +7 -6
- package/src/js/menu.js +89 -35
- package/src/js/parse-config.js +47 -0
- package/src/js/password.js +9 -1
- package/src/js/scrollspy.js +133 -62
- package/src/js/selectors.js +14 -31
- package/src/js/status.js +16 -14
- package/src/js/surface.js +219 -125
- package/src/js/tab.js +46 -66
- package/src/js/tooltip.js +53 -31
- package/src/js/validation.js +45 -51
- package/dist/actual.js.map +0 -31
- package/docs/README.md +0 -26
- package/docs/components.md +0 -860
- package/docs/design-notes/previous-work.md +0 -5
- package/docs/forms.md +0 -1265
- package/docs/javascript.md +0 -426
- package/docs/layout.md +0 -480
- package/docs/patterns.md +0 -415
- package/docs/reserved.md +0 -7
- package/docs/tokens.md +0 -455
- package/docs/ui.md +0 -937
- package/docs/utilities.md +0 -384
- package/src/css/dialog-fallback.css +0 -56
- package/src/js/dialog-fallback.js +0 -163
|
@@ -4,13 +4,24 @@
|
|
|
4
4
|
* A flex cluster of buttons/actions that closes a form. The sticky variant
|
|
5
5
|
* docks to the viewport bottom with a translucent blur where supported; at
|
|
6
6
|
* rest it stays visually neutral so it does not double as a separator.
|
|
7
|
+
*
|
|
8
|
+
* Public hooks:
|
|
9
|
+
* --form-actions-align
|
|
10
|
+
* --form-actions-justify
|
|
11
|
+
* --form-actions-margin-block-start
|
|
12
|
+
*
|
|
13
|
+
* Internal:
|
|
14
|
+
* --form-actions-sticky-block-offset (owned by .form-actions.sticky)
|
|
15
|
+
* --form-actions-sticky-inline-offset (owned by .form-actions.sticky)
|
|
16
|
+
* --form-actions-sticky-padding (owned by .form-actions.sticky)
|
|
7
17
|
*/
|
|
8
18
|
.form-actions {
|
|
9
19
|
display: flex;
|
|
10
20
|
flex-wrap: wrap;
|
|
11
21
|
gap: var(--gap);
|
|
12
|
-
align-items: center;
|
|
13
|
-
|
|
22
|
+
align-items: var(--form-actions-align, center);
|
|
23
|
+
justify-content: var(--form-actions-justify, flex-start);
|
|
24
|
+
margin-block-start: var(--form-actions-margin-block-start, var(--space-50));
|
|
14
25
|
}
|
|
15
26
|
|
|
16
27
|
/* Opt-in sticky variant. When the bar is not stuck (at rest) the translucent
|
|
@@ -24,7 +35,7 @@
|
|
|
24
35
|
isolation: isolate;
|
|
25
36
|
|
|
26
37
|
margin-inline: calc(var(--form-actions-sticky-inline-offset, 0px) * -1);
|
|
27
|
-
padding: var(--form-actions-sticky-padding, var(--space-
|
|
38
|
+
padding: var(--form-actions-sticky-padding, var(--space-30));
|
|
28
39
|
|
|
29
40
|
border-block-start: var(--border-width) solid var(--border);
|
|
30
41
|
background: var(--surface);
|
package/src/css/forms/form.css
CHANGED
|
@@ -6,23 +6,26 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Public API: .field, .field-label, .field-help, .field-error, .field-group,
|
|
8
8
|
* .required-mark.
|
|
9
|
+
*
|
|
10
|
+
* Public hooks:
|
|
11
|
+
* --fieldset-legend-padding-inline
|
|
9
12
|
*/
|
|
10
13
|
.field {
|
|
11
14
|
display: grid;
|
|
12
|
-
gap: var(--space-
|
|
15
|
+
gap: var(--space-10);
|
|
13
16
|
color: var(--text);
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
.field-group {
|
|
17
20
|
min-inline-size: 0;
|
|
18
21
|
margin: 0;
|
|
19
|
-
padding: var(--space-
|
|
22
|
+
padding: var(--space-30);
|
|
20
23
|
border: var(--border-width) solid var(--border);
|
|
21
24
|
border-radius: var(--radius);
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
.field-group + .field-group {
|
|
25
|
-
margin-block-start: var(--space-
|
|
28
|
+
margin-block-start: var(--space-40);
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
.field-label,
|
|
@@ -33,7 +36,7 @@
|
|
|
33
36
|
|
|
34
37
|
.field-group > legend {
|
|
35
38
|
padding-block: 0;
|
|
36
|
-
padding-inline: var(--fieldset-legend-padding-inline, var(--space-
|
|
39
|
+
padding-inline: var(--fieldset-legend-padding-inline, var(--space-10));
|
|
37
40
|
font-size: var(--font-size-xs);
|
|
38
41
|
}
|
|
39
42
|
|
package/src/css/forms/index.css
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/* Forms — import order matters: switch.css
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/* Forms — import order matters: switch.css follows choice.css so its rules
|
|
2
|
+
land after the shared choice recipe. validation.css is part of the default
|
|
3
|
+
forms bundle, but custom builds may omit it when validation styling is
|
|
4
|
+
app-owned. */
|
|
5
5
|
@import "./form.css";
|
|
6
6
|
@import "./control.css";
|
|
7
7
|
@import "./input-icon.css";
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
@import "./select.css";
|
|
11
11
|
@import "./custom-select.css";
|
|
12
12
|
@import "./native.css";
|
|
13
|
+
@import "./range.css";
|
|
13
14
|
@import "./switch.css";
|
|
14
15
|
@import "./validation.css";
|
|
15
16
|
@import "./form-actions.css";
|
|
@@ -1,36 +1,54 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Input Icon —
|
|
2
|
+
* Input Icon — decorative icon inside a relative wrapper, leading or trailing.
|
|
3
3
|
*
|
|
4
|
-
* Icon-library agnostic:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* the
|
|
8
|
-
*
|
|
4
|
+
* Icon-library agnostic: detects position by DOM order. First child that is
|
|
5
|
+
* not .input = leading icon; icon after .input = trailing. The icon keeps a
|
|
6
|
+
* fixed size; density contexts shrink the control geometry around it, not
|
|
7
|
+
* the pictogram.
|
|
8
|
+
*
|
|
9
|
+
* Public hooks:
|
|
10
|
+
* --input-icon-size
|
|
9
11
|
*/
|
|
10
12
|
.input-icon {
|
|
11
|
-
--input-icon-size:
|
|
13
|
+
--input-icon-size: 1.25rem;
|
|
12
14
|
|
|
13
15
|
display: block;
|
|
14
16
|
position: relative;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
/* Icon — shared by leading and trailing */
|
|
20
|
+
.input-icon > :where(:not(.input)) {
|
|
18
21
|
position: absolute;
|
|
19
22
|
inset-block-start: 50%;
|
|
20
|
-
inset-inline-start: var(--space-3);
|
|
21
23
|
transform: translateY(-50%);
|
|
22
24
|
font-size: var(--input-icon-size);
|
|
23
25
|
color: var(--text-muted);
|
|
24
26
|
pointer-events: none;
|
|
25
27
|
}
|
|
26
28
|
|
|
29
|
+
/* Leading: icon is the first child */
|
|
30
|
+
.input-icon > :first-child:where(:not(.input)) {
|
|
31
|
+
inset-inline-start: var(--space-30);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Trailing: input is the first child, icon follows */
|
|
35
|
+
.input-icon > .input:first-child ~ :where(:not(.input)) {
|
|
36
|
+
inset-inline-end: var(--space-30);
|
|
37
|
+
}
|
|
38
|
+
|
|
27
39
|
/* font-size only sizes text-metric icons (webfont <i>, <span>). An inline
|
|
28
40
|
<svg> with just a viewBox ignores it, so give it explicit dimensions. */
|
|
29
|
-
.input-icon > svg:
|
|
41
|
+
.input-icon > svg:where(:not(.input)) {
|
|
30
42
|
inline-size: var(--input-icon-size);
|
|
31
43
|
block-size: var(--input-icon-size);
|
|
32
44
|
}
|
|
33
45
|
|
|
34
|
-
|
|
35
|
-
|
|
46
|
+
/* Leading: input gets start padding to clear the icon */
|
|
47
|
+
.input-icon > :first-child:where(:not(.input)) ~ .input {
|
|
48
|
+
padding-inline-start: calc(var(--space-30) * 2 + var(--input-icon-size));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Trailing: input is first child and has a sibling → end padding */
|
|
52
|
+
.input-icon > .input:first-child:where(:not(:last-child)) {
|
|
53
|
+
padding-inline-end: calc(var(--space-30) * 2 + var(--input-icon-size));
|
|
36
54
|
}
|
package/src/css/forms/native.css
CHANGED
|
@@ -1,29 +1,19 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Native-only control polish: file and
|
|
3
|
-
* styling, so the framework stays out of
|
|
4
|
-
* and theme-aware.
|
|
2
|
+
* Native-only control polish: the file input and color picker mostly defeat
|
|
3
|
+
* custom styling, so the framework stays out of the way and just keeps them
|
|
4
|
+
* readable and theme-aware.
|
|
5
|
+
*
|
|
6
|
+
* Range has its own custom-styled file (range.css) so the focus ring can
|
|
7
|
+
* target the thumb pseudo-element rather than the rectangular CSS box.
|
|
5
8
|
*/
|
|
6
|
-
/* Intent boundary — inherited intents do not tint this component.
|
|
7
|
-
Local intent classes still win because :where() has zero specificity. */
|
|
8
|
-
:where(.range) {
|
|
9
|
-
/* @sync intent-boundary */
|
|
10
|
-
--intent: initial;
|
|
11
|
-
--intent-fg: initial;
|
|
12
|
-
--ui-bg: initial;
|
|
13
|
-
--ui-fg: initial;
|
|
14
|
-
--ui-border: initial;
|
|
15
|
-
--ui-hover-bg: initial;
|
|
16
|
-
/* @sync intent-boundary */
|
|
17
|
-
}
|
|
18
|
-
|
|
19
9
|
.file {
|
|
20
10
|
color: var(--text-muted);
|
|
21
11
|
}
|
|
22
12
|
|
|
23
13
|
.file::file-selector-button {
|
|
24
|
-
margin-inline-end: var(--space-
|
|
25
|
-
padding-block: var(--space-
|
|
26
|
-
padding-inline: var(--space-
|
|
14
|
+
margin-inline-end: var(--space-30);
|
|
15
|
+
padding-block: var(--space-10);
|
|
16
|
+
padding-inline: var(--space-20);
|
|
27
17
|
border: var(--border-width) solid var(--border);
|
|
28
18
|
border-radius: var(--radius-sm);
|
|
29
19
|
background: var(--surface-subtle);
|
|
@@ -42,27 +32,52 @@
|
|
|
42
32
|
cursor: not-allowed;
|
|
43
33
|
}
|
|
44
34
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
background: var(--border);
|
|
48
|
-
}
|
|
35
|
+
.file:not(:disabled)::file-selector-button:hover {
|
|
36
|
+
background: var(--border);
|
|
49
37
|
}
|
|
50
38
|
|
|
51
|
-
.
|
|
52
|
-
|
|
53
|
-
|
|
39
|
+
.file:focus-visible {
|
|
40
|
+
outline: 2px solid transparent;
|
|
41
|
+
box-shadow: var(--focus-ring-shadow);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* Native color picker — keep the browser's dialog and selected-color UI. */
|
|
45
|
+
.color {
|
|
46
|
+
inline-size: var(--control-size);
|
|
47
|
+
block-size: var(--control-size);
|
|
48
|
+
padding: var(--space-10);
|
|
49
|
+
border: var(--border-width) solid var(--border);
|
|
50
|
+
border-radius: var(--radius);
|
|
51
|
+
background: var(--surface);
|
|
52
|
+
cursor: pointer;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.color::-webkit-color-swatch-wrapper {
|
|
56
|
+
padding: 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.color::-webkit-color-swatch {
|
|
60
|
+
border: 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.color::-moz-color-swatch {
|
|
64
|
+
border: 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.color:disabled {
|
|
68
|
+
cursor: not-allowed;
|
|
69
|
+
opacity: var(--disabled-opacity);
|
|
54
70
|
}
|
|
55
71
|
|
|
56
|
-
.
|
|
57
|
-
|
|
58
|
-
outline: var(--focus-outline);
|
|
59
|
-
outline-offset: var(--focus-outline-offset);
|
|
72
|
+
.color:focus-visible {
|
|
73
|
+
outline: 2px solid transparent;
|
|
60
74
|
box-shadow: var(--focus-ring-shadow);
|
|
61
75
|
}
|
|
62
76
|
|
|
63
77
|
@media (forced-colors: active) {
|
|
64
|
-
.
|
|
65
|
-
|
|
66
|
-
|
|
78
|
+
.color {
|
|
79
|
+
border-color: ButtonText;
|
|
80
|
+
background: ButtonFace;
|
|
81
|
+
forced-color-adjust: none;
|
|
67
82
|
}
|
|
68
83
|
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Range slider — custom-styled so the focus ring always targets the
|
|
3
|
+
* user-manipulated surface (the thumb) rather than the CSS box.
|
|
4
|
+
*
|
|
5
|
+
* appearance: none gives us full control over track and thumb; without it,
|
|
6
|
+
* pseudo-element rules for ::-webkit-slider-thumb and ::-moz-range-thumb
|
|
7
|
+
* are not reliably applied across browsers.
|
|
8
|
+
*
|
|
9
|
+
* Public hooks:
|
|
10
|
+
* --range-thumb-size
|
|
11
|
+
* --range-track-height
|
|
12
|
+
* --range-thumb-bg
|
|
13
|
+
* --range-track-bg
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/* Intent boundary — inherited intents do not tint this component.
|
|
17
|
+
Local intent classes still win because :where() has zero specificity. */
|
|
18
|
+
:where(.range) {
|
|
19
|
+
--intent: initial;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.range {
|
|
23
|
+
--range-thumb-size: 1.25rem;
|
|
24
|
+
--range-track-height: 0.25rem;
|
|
25
|
+
--range-thumb-bg: var(--intent, var(--primary));
|
|
26
|
+
--range-track-bg: var(--border);
|
|
27
|
+
|
|
28
|
+
appearance: none;
|
|
29
|
+
inline-size: 100%;
|
|
30
|
+
block-size: var(--range-thumb-size);
|
|
31
|
+
background: transparent;
|
|
32
|
+
cursor: pointer;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.range::-webkit-slider-runnable-track {
|
|
36
|
+
block-size: var(--range-track-height);
|
|
37
|
+
border-radius: var(--radius-full);
|
|
38
|
+
background: var(--range-track-bg);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.range::-moz-range-track {
|
|
42
|
+
block-size: var(--range-track-height);
|
|
43
|
+
border-radius: var(--radius-full);
|
|
44
|
+
background: var(--range-track-bg);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.range::-webkit-slider-thumb {
|
|
48
|
+
appearance: none;
|
|
49
|
+
inline-size: var(--range-thumb-size);
|
|
50
|
+
block-size: var(--range-thumb-size);
|
|
51
|
+
border-radius: var(--radius-full);
|
|
52
|
+
border: 0;
|
|
53
|
+
background: var(--range-thumb-bg);
|
|
54
|
+
margin-block-start: calc((var(--range-track-height) - var(--range-thumb-size)) / 2);
|
|
55
|
+
cursor: inherit;
|
|
56
|
+
transition: box-shadow var(--duration);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.range::-moz-range-thumb {
|
|
60
|
+
inline-size: var(--range-thumb-size);
|
|
61
|
+
block-size: var(--range-thumb-size);
|
|
62
|
+
border-radius: var(--radius-full);
|
|
63
|
+
border: 0;
|
|
64
|
+
background: var(--range-thumb-bg);
|
|
65
|
+
cursor: inherit;
|
|
66
|
+
transition: box-shadow var(--duration);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.range:focus-visible {
|
|
70
|
+
outline: 2px solid transparent;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/*
|
|
74
|
+
* The 1px surface-coloured separator keeps the ring visually distinct from
|
|
75
|
+
* the small, coloured thumb — unlike a full-size input where the same gap
|
|
76
|
+
* would read as an unwanted void.
|
|
77
|
+
*/
|
|
78
|
+
.range:focus-visible::-webkit-slider-thumb {
|
|
79
|
+
box-shadow:
|
|
80
|
+
0 0 0 1px var(--surface),
|
|
81
|
+
0 0 0 calc(1px + var(--focus-ring-width)) var(--focus-ring);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.range:focus-visible::-moz-range-thumb {
|
|
85
|
+
box-shadow:
|
|
86
|
+
0 0 0 1px var(--surface),
|
|
87
|
+
0 0 0 calc(1px + var(--focus-ring-width)) var(--focus-ring);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.range:disabled {
|
|
91
|
+
--range-thumb-bg: var(--state-disabled);
|
|
92
|
+
cursor: not-allowed;
|
|
93
|
+
opacity: var(--disabled-opacity);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Forced colors: map the custom thumb/track to system colors. The focus
|
|
97
|
+
indicator stays visible through the transparent outline on :focus-visible. */
|
|
98
|
+
@media (forced-colors: active) {
|
|
99
|
+
.range {
|
|
100
|
+
--range-thumb-bg: ButtonText;
|
|
101
|
+
--range-track-bg: Canvas;
|
|
102
|
+
}
|
|
103
|
+
}
|
package/src/css/forms/select.css
CHANGED
|
@@ -8,19 +8,19 @@
|
|
|
8
8
|
* custom-select.css can be opted out independently. Add .native to a specific
|
|
9
9
|
* select to opt out of the enhancement.
|
|
10
10
|
*/
|
|
11
|
-
.select:
|
|
11
|
+
.select:not([multiple], [size]:not([size="1"])) {
|
|
12
12
|
appearance: none;
|
|
13
|
-
padding-inline-end: calc(var(--space-
|
|
14
|
-
background-image: var(--icon-chevron
|
|
15
|
-
background-position: center right var(--space-
|
|
13
|
+
padding-inline-end: calc(var(--space-30) + var(--space-60));
|
|
14
|
+
background-image: var(--icon-chevron);
|
|
15
|
+
background-position: center right var(--space-30);
|
|
16
16
|
background-size: 1rem auto;
|
|
17
17
|
background-repeat: no-repeat;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/* The chevron sits on the inline-end side; flip it in RTL so it follows
|
|
21
21
|
the padding-inline-end space reserved for it. */
|
|
22
|
-
.select:
|
|
23
|
-
background-position: center left var(--space-
|
|
22
|
+
.select:not([multiple], [size]:not([size="1"])):dir(rtl) {
|
|
23
|
+
background-position: center left var(--space-30);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
/* Both forced-colors and prefers-contrast hand the arrow back to the
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
simpler than maintaining contrast variants, and native rendering already
|
|
29
29
|
matches whatever contrast mode the user picked. */
|
|
30
30
|
@media (forced-colors: active), (prefers-contrast: more) {
|
|
31
|
-
.select:
|
|
31
|
+
.select:not([multiple], [size]:not([size="1"])) {
|
|
32
32
|
appearance: auto;
|
|
33
33
|
background-image: none;
|
|
34
|
-
padding-inline-end: var(--space-
|
|
34
|
+
padding-inline-end: var(--space-30);
|
|
35
35
|
}
|
|
36
36
|
}
|
package/src/css/forms/switch.css
CHANGED
|
@@ -3,18 +3,22 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The switch remains an input control, not a decorative toggle. Checked, hover,
|
|
5
5
|
* focus-visible, and disabled states stay clear.
|
|
6
|
+
*
|
|
7
|
+
* Public hooks:
|
|
8
|
+
* --switch-bg
|
|
9
|
+
* --switch-border
|
|
10
|
+
* --switch-knob
|
|
11
|
+
* --switch-width
|
|
12
|
+
* --switch-block-size
|
|
13
|
+
* --switch-knob-margin
|
|
14
|
+
* --switch-knob-size
|
|
15
|
+
* --switch-travel
|
|
6
16
|
*/
|
|
7
17
|
/* Intent boundary — inherited intents do not tint this component.
|
|
8
18
|
Local intent classes still win because :where() has zero specificity. */
|
|
9
19
|
:where(.switch) {
|
|
10
|
-
/* @sync intent-boundary */
|
|
11
20
|
--intent: initial;
|
|
12
21
|
--intent-fg: initial;
|
|
13
|
-
--ui-bg: initial;
|
|
14
|
-
--ui-fg: initial;
|
|
15
|
-
--ui-border: initial;
|
|
16
|
-
--ui-hover-bg: initial;
|
|
17
|
-
/* @sync intent-boundary */
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
.switch {
|
|
@@ -62,10 +66,8 @@
|
|
|
62
66
|
box-shadow var(--duration);
|
|
63
67
|
}
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
--switch-border: var(--intent, var(--primary));
|
|
68
|
-
}
|
|
69
|
+
.switch:hover:not(:disabled) {
|
|
70
|
+
--switch-border: var(--intent, var(--primary));
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
.switch:checked {
|
|
@@ -86,15 +88,14 @@
|
|
|
86
88
|
|
|
87
89
|
.switch:focus-visible {
|
|
88
90
|
--switch-border: var(--focus);
|
|
89
|
-
outline:
|
|
90
|
-
outline-offset: var(--focus-outline-offset);
|
|
91
|
+
outline: 2px solid transparent;
|
|
91
92
|
box-shadow: var(--focus-ring-shadow);
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
.switch:disabled {
|
|
95
96
|
--switch-bg: var(--surface-subtle);
|
|
96
97
|
--switch-border: var(--border);
|
|
97
|
-
--switch-knob: var(--
|
|
98
|
+
--switch-knob: var(--state-disabled);
|
|
98
99
|
opacity: 1;
|
|
99
100
|
cursor: not-allowed;
|
|
100
101
|
}
|
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Imported by default through forms/index.css. Custom builds may omit this
|
|
5
5
|
* file when an app wants to own validation styling completely.
|
|
6
|
+
*
|
|
7
|
+
* State coverage (which states each component ships) is audited in
|
|
8
|
+
* tests/validation.test.js and summarized in docs/pages/forms/validation.md —
|
|
9
|
+
* not kept as a source comment that can drift from the implementation.
|
|
10
|
+
*
|
|
11
|
+
* Public hooks:
|
|
12
|
+
* --form-invalid-border
|
|
13
|
+
*
|
|
14
|
+
* Internal:
|
|
15
|
+
* --choice-bg
|
|
16
|
+
* --switch-bg
|
|
6
17
|
*/
|
|
7
18
|
|
|
8
19
|
.field.danger {
|
|
@@ -25,17 +36,33 @@
|
|
|
25
36
|
--form-invalid-border: var(--danger);
|
|
26
37
|
}
|
|
27
38
|
|
|
28
|
-
/* Override checked/indeterminate
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
/* Override checked/indeterminate fill so both border and fill signal danger
|
|
40
|
+
instead of a red border around a primary-colored control. All three
|
|
41
|
+
invalid routes must converge: aria-invalid, was-validated :invalid, and
|
|
42
|
+
:user-invalid. */
|
|
43
|
+
.check:is(:checked, :indeterminate)[aria-invalid="true"],
|
|
44
|
+
.needs-validation.was-validated .check:is(:checked, :indeterminate):invalid,
|
|
45
|
+
.radio:checked[aria-invalid="true"],
|
|
46
|
+
.needs-validation.was-validated .radio:checked:invalid {
|
|
32
47
|
--choice-bg: var(--danger);
|
|
33
48
|
}
|
|
34
49
|
|
|
35
|
-
.switch[aria-invalid="true"]
|
|
50
|
+
.switch:checked[aria-invalid="true"],
|
|
51
|
+
.needs-validation.was-validated .switch:checked:invalid {
|
|
36
52
|
--switch-bg: var(--danger);
|
|
37
53
|
}
|
|
38
54
|
|
|
55
|
+
@supports selector(:user-invalid) {
|
|
56
|
+
.needs-validation .check:is(:checked, :indeterminate):user-invalid,
|
|
57
|
+
.needs-validation .radio:checked:user-invalid {
|
|
58
|
+
--choice-bg: var(--danger);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.needs-validation .switch:checked:user-invalid {
|
|
62
|
+
--switch-bg: var(--danger);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
39
66
|
/* Choice cards hide their native input, so invalid state must surface on the
|
|
40
67
|
card itself. Sets the same --form-invalid-border hook the card's border
|
|
41
68
|
consumes. */
|
package/src/css/grid.css
CHANGED
|
@@ -1,26 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Responsive equal-width grids, with fixed-column presets below.
|
|
3
|
+
*
|
|
4
|
+
* Public hooks:
|
|
5
|
+
* --grid-min
|
|
6
|
+
* --grid-columns
|
|
7
|
+
*/
|
|
1
8
|
.grid {
|
|
2
9
|
display: grid;
|
|
3
10
|
gap: var(--gap);
|
|
4
|
-
grid-template-columns:
|
|
11
|
+
grid-template-columns: var(
|
|
12
|
+
--grid-columns,
|
|
13
|
+
repeat(auto-fit, minmax(min(100%, var(--grid-min, 16rem)), 1fr))
|
|
14
|
+
);
|
|
5
15
|
}
|
|
6
16
|
|
|
7
|
-
/*
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* .grid-2: 2 cols
|
|
13
|
-
* .grid-3: 3 cols
|
|
14
|
-
* .grid-4: 4 cols
|
|
15
|
-
* .grid-6: 6 cols
|
|
16
|
-
* When placed inside .container-query, the column count collapses
|
|
17
|
-
* on narrow wrappers. The intermediate 2-col step applies to
|
|
18
|
-
* .grid-3/.grid-4; .grid-6 starts at 2 columns because it is meant
|
|
19
|
-
* for compact items that do not need a full single-column stack.
|
|
20
|
-
* .grid-2: 1 col -> 2 cols
|
|
21
|
-
* .grid-3: 1 col -> 2 cols -> 3 cols
|
|
22
|
-
* .grid-4: 1 col -> 2 cols -> 4 cols
|
|
23
|
-
* .grid-6: 2 cols -> 3 cols -> 6 cols */
|
|
17
|
+
/* Fixed-column grids at their full count by default: .grid-2/3/4/6 stay at
|
|
18
|
+
2/3/4/6 columns unless wrapped in .container-query, which collapses the
|
|
19
|
+
column count on narrow wrappers (a 2-col intermediate step for
|
|
20
|
+
.grid-3/.grid-4; .grid-6 starts at 2 columns because it is meant for
|
|
21
|
+
compact items that do not need a full single-column stack). */
|
|
24
22
|
.grid-2,
|
|
25
23
|
.grid-3,
|
|
26
24
|
.grid-4,
|
|
@@ -42,6 +40,9 @@
|
|
|
42
40
|
grid-template-columns: repeat(6, minmax(0, 1fr));
|
|
43
41
|
}
|
|
44
42
|
|
|
43
|
+
/* Container queries are the preferred responsive path; browsers without them
|
|
44
|
+
ignore these rules and get the equivalent @media rules from the
|
|
45
|
+
@supports not (container-type) block below — same columns, same breakpoints. */
|
|
45
46
|
@container (width < 28rem) {
|
|
46
47
|
.container-query .grid-2,
|
|
47
48
|
.container-query .grid-3,
|
package/src/css/intents.css
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Intent classes do not style elements directly.
|
|
3
3
|
* They only expose --intent and --intent-fg.
|
|
4
4
|
* Components and variants decide how to consume them. Each component that
|
|
5
|
-
* consumes --intent or --ui-*
|
|
6
|
-
* block that resets
|
|
5
|
+
* consumes --intent or --ui-* opens a local intent boundary in a :where()
|
|
6
|
+
* block that resets exactly the tokens it consumes to initial. Parent intents
|
|
7
7
|
* do not leak into nested components. Local intent classes (.primary, .danger,
|
|
8
8
|
* etc.) still apply because they have higher specificity than :where().
|
|
9
9
|
*/
|
package/src/css/layout.css
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Core layout primitives: stack, cluster, center, frame, app-shell, media,
|
|
3
|
+
* sidebar-layout. Each primitive owns a single, readable behavior; they are
|
|
4
|
+
* meant to compose in markup.
|
|
5
|
+
*
|
|
6
|
+
* Public hooks:
|
|
7
|
+
* --cluster-align
|
|
8
|
+
* --cluster-justify
|
|
9
|
+
* --cluster-wrap
|
|
10
|
+
* --center-size
|
|
11
|
+
* --center-pad
|
|
12
|
+
* --frame-ratio
|
|
13
|
+
* --sidebar-layout-size
|
|
14
|
+
*
|
|
15
|
+
* Child contracts:
|
|
16
|
+
* .stack owns vertical rhythm: direct-child margins are reset, spacing comes
|
|
17
|
+
* from --gap.
|
|
18
|
+
* .cluster owns inline rhythm: direct-child margins are reset, spacing comes
|
|
19
|
+
* from --gap.
|
|
20
|
+
* .frame sizes direct media children (img/video/iframe/canvas/svg) to fill the
|
|
21
|
+
* frame.
|
|
22
|
+
*/
|
|
1
23
|
.container-query {
|
|
2
24
|
container-type: inline-size;
|
|
3
25
|
}
|
|
@@ -14,12 +36,16 @@
|
|
|
14
36
|
|
|
15
37
|
.cluster {
|
|
16
38
|
display: flex;
|
|
17
|
-
flex-wrap: wrap;
|
|
39
|
+
flex-wrap: var(--cluster-wrap, wrap);
|
|
18
40
|
align-items: var(--cluster-align, center);
|
|
19
41
|
justify-content: var(--cluster-justify, flex-start);
|
|
20
42
|
gap: var(--gap);
|
|
21
43
|
}
|
|
22
44
|
|
|
45
|
+
.cluster > * {
|
|
46
|
+
margin: 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
23
49
|
.center {
|
|
24
50
|
box-sizing: content-box;
|
|
25
51
|
max-inline-size: var(--center-size, 72rem);
|
|
@@ -63,6 +89,8 @@
|
|
|
63
89
|
grid-template-columns: minmax(0, 1fr);
|
|
64
90
|
}
|
|
65
91
|
|
|
92
|
+
/* Container queries are the preferred path; without them the @supports not
|
|
93
|
+
block below restores the same columns at the same viewport breakpoint. */
|
|
66
94
|
@container (width >= 64rem) {
|
|
67
95
|
.container-query .sidebar-layout {
|
|
68
96
|
grid-template-columns: minmax(0, 1fr) var(--sidebar-layout-size, 18rem);
|