actual-css 0.1.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.
Files changed (129) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +166 -0
  4. package/dist/actual.css +4929 -0
  5. package/dist/actual.js +3 -0
  6. package/dist/actual.js.map +31 -0
  7. package/dist/actual.min.css +1 -0
  8. package/docs/README.md +26 -0
  9. package/docs/components.md +860 -0
  10. package/docs/design-notes/accessibility-decisions.md +32 -0
  11. package/docs/design-notes/browser-support.md +93 -0
  12. package/docs/design-notes/cascade-layer.md +52 -0
  13. package/docs/design-notes/css-anchor.md +33 -0
  14. package/docs/design-notes/naming.md +37 -0
  15. package/docs/design-notes/previous-work.md +5 -0
  16. package/docs/design-notes/references.md +324 -0
  17. package/docs/forms.md +1265 -0
  18. package/docs/javascript.md +426 -0
  19. package/docs/layout.md +480 -0
  20. package/docs/mental-model.md +73 -0
  21. package/docs/patterns.md +415 -0
  22. package/docs/reserved.md +7 -0
  23. package/docs/tokens.md +455 -0
  24. package/docs/typography.md +159 -0
  25. package/docs/ui.md +937 -0
  26. package/docs/utilities.md +384 -0
  27. package/llms.txt +17 -0
  28. package/package.json +101 -0
  29. package/src/css/actual.css +17 -0
  30. package/src/css/actual.full.css +2 -0
  31. package/src/css/actual.layer.css +3 -0
  32. package/src/css/base.css +70 -0
  33. package/src/css/components/accordion.css +97 -0
  34. package/src/css/components/actions.css +21 -0
  35. package/src/css/components/alert.css +137 -0
  36. package/src/css/components/avatar.css +199 -0
  37. package/src/css/components/badge.css +143 -0
  38. package/src/css/components/breadcrumb.css +56 -0
  39. package/src/css/components/busy.css +44 -0
  40. package/src/css/components/button.css +216 -0
  41. package/src/css/components/card.css +140 -0
  42. package/src/css/components/drawer.css +122 -0
  43. package/src/css/components/flyout.css +243 -0
  44. package/src/css/components/index.css +41 -0
  45. package/src/css/components/join.css +106 -0
  46. package/src/css/components/key.css +25 -0
  47. package/src/css/components/meter.css +81 -0
  48. package/src/css/components/modal.css +162 -0
  49. package/src/css/components/navbar.css +98 -0
  50. package/src/css/components/overline.css +49 -0
  51. package/src/css/components/pagination.css +30 -0
  52. package/src/css/components/progress.css +126 -0
  53. package/src/css/components/scrollspy.css +27 -0
  54. package/src/css/components/skeleton.css +67 -0
  55. package/src/css/components/spinner.css +60 -0
  56. package/src/css/components/status-bar.css +82 -0
  57. package/src/css/components/surface.css +48 -0
  58. package/src/css/components/tab.css +128 -0
  59. package/src/css/components/table.css +64 -0
  60. package/src/css/components/tooltip.css +107 -0
  61. package/src/css/dialog-fallback.css +56 -0
  62. package/src/css/focus.css +13 -0
  63. package/src/css/forms/choice-card.css +158 -0
  64. package/src/css/forms/choice.css +196 -0
  65. package/src/css/forms/control.css +106 -0
  66. package/src/css/forms/custom-select.css +93 -0
  67. package/src/css/forms/form-actions.css +43 -0
  68. package/src/css/forms/form.css +57 -0
  69. package/src/css/forms/index.css +15 -0
  70. package/src/css/forms/input-icon.css +36 -0
  71. package/src/css/forms/native.css +68 -0
  72. package/src/css/forms/select.css +36 -0
  73. package/src/css/forms/switch.css +127 -0
  74. package/src/css/forms/validation.css +63 -0
  75. package/src/css/grid.css +95 -0
  76. package/src/css/intents.css +33 -0
  77. package/src/css/layout.css +80 -0
  78. package/src/css/optional/index.css +5 -0
  79. package/src/css/optional/layout-extra.css +55 -0
  80. package/src/css/optional/scroll-snap.css +51 -0
  81. package/src/css/optional/scroller.css +51 -0
  82. package/src/css/optional/typography-fluid.css +48 -0
  83. package/src/css/optional/utilities-extra.css +289 -0
  84. package/src/css/print.css +47 -0
  85. package/src/css/prose.css +187 -0
  86. package/src/css/reset.css +56 -0
  87. package/src/css/theme.css +186 -0
  88. package/src/css/themes/corporate.css +60 -0
  89. package/src/css/themes/cyberpunk.css +60 -0
  90. package/src/css/themes/dark.css +32 -0
  91. package/src/css/themes/dim.css +33 -0
  92. package/src/css/themes/edge.css +143 -0
  93. package/src/css/themes/forest.css +65 -0
  94. package/src/css/themes/gradient.css +97 -0
  95. package/src/css/themes/index.css +43 -0
  96. package/src/css/themes/lavender.css +66 -0
  97. package/src/css/themes/mono.css +60 -0
  98. package/src/css/themes/neon.css +66 -0
  99. package/src/css/themes/ocean.css +60 -0
  100. package/src/css/themes/petrol.css +64 -0
  101. package/src/css/themes/spruce.css +64 -0
  102. package/src/css/themes/square.css +68 -0
  103. package/src/css/themes/sunset.css +66 -0
  104. package/src/css/tokens.css +122 -0
  105. package/src/css/utilities.css +162 -0
  106. package/src/css/variants.css +118 -0
  107. package/src/js/command.js +165 -0
  108. package/src/js/context-menu.js +291 -0
  109. package/src/js/dialog-fallback.js +163 -0
  110. package/src/js/dialog.js +503 -0
  111. package/src/js/enhance.js +208 -0
  112. package/src/js/events.js +22 -0
  113. package/src/js/filter.js +122 -0
  114. package/src/js/floating.js +462 -0
  115. package/src/js/flyout.js +187 -0
  116. package/src/js/focus.js +35 -0
  117. package/src/js/index.js +26 -0
  118. package/src/js/input.js +47 -0
  119. package/src/js/keys.js +30 -0
  120. package/src/js/mask.js +184 -0
  121. package/src/js/menu.js +88 -0
  122. package/src/js/password.js +98 -0
  123. package/src/js/scrollspy.js +121 -0
  124. package/src/js/selectors.js +59 -0
  125. package/src/js/status.js +158 -0
  126. package/src/js/surface.js +358 -0
  127. package/src/js/tab.js +173 -0
  128. package/src/js/tooltip.js +374 -0
  129. package/src/js/validation.js +419 -0
@@ -0,0 +1,57 @@
1
+ /*
2
+ * Form layout and supporting text.
3
+ *
4
+ * Individual controls keep their own files; .choice label layout lives in
5
+ * choice.css; .form-actions lives in form-actions.css.
6
+ *
7
+ * Public API: .field, .field-label, .field-help, .field-error, .field-group,
8
+ * .required-mark.
9
+ */
10
+ .field {
11
+ display: grid;
12
+ gap: var(--space-1);
13
+ color: var(--text);
14
+ }
15
+
16
+ .field-group {
17
+ min-inline-size: 0;
18
+ margin: 0;
19
+ padding: var(--space-3);
20
+ border: var(--border-width) solid var(--border);
21
+ border-radius: var(--radius);
22
+ }
23
+
24
+ .field-group + .field-group {
25
+ margin-block-start: var(--space-4);
26
+ }
27
+
28
+ .field-label,
29
+ .field-group > legend {
30
+ color: var(--text);
31
+ font-weight: var(--font-weight-strong);
32
+ }
33
+
34
+ .field-group > legend {
35
+ padding-block: 0;
36
+ padding-inline: var(--fieldset-legend-padding-inline, var(--space-1));
37
+ font-size: var(--font-size-xs);
38
+ }
39
+
40
+ .field-help {
41
+ color: var(--text-muted);
42
+ font-size: var(--font-size-sm);
43
+ }
44
+
45
+ .field-error {
46
+ display: none;
47
+ color: var(--danger);
48
+ font-size: var(--font-size-sm);
49
+ }
50
+
51
+ .required-mark {
52
+ color: var(--danger);
53
+ }
54
+
55
+ .field.danger .field-help {
56
+ color: var(--danger);
57
+ }
@@ -0,0 +1,15 @@
1
+ /* Forms — import order matters: switch.css must follow choice.css so its
2
+ :not([role="switch"]) exclusion in choice.css still applies.
3
+ validation.css is part of the default forms bundle, but custom builds may
4
+ omit it when validation styling is app-owned. */
5
+ @import "./form.css";
6
+ @import "./control.css";
7
+ @import "./input-icon.css";
8
+ @import "./choice.css";
9
+ @import "./choice-card.css";
10
+ @import "./select.css";
11
+ @import "./custom-select.css";
12
+ @import "./native.css";
13
+ @import "./switch.css";
14
+ @import "./validation.css";
15
+ @import "./form-actions.css";
@@ -0,0 +1,36 @@
1
+ /*
2
+ * Input Icon — leading decorative icon inside a relative wrapper.
3
+ *
4
+ * Icon-library agnostic: selects the wrapper's first child rather than a
5
+ * specific tag, so it works with an <i> webfont icon, an inline <svg>, or a
6
+ * <span>. Sizes with --variant-icon-size (variants.css) so .sm/.lg pick up
7
+ * the same icon scale used elsewhere (alert.css); the input's start padding
8
+ * grows to match so text never runs under the icon.
9
+ */
10
+ .input-icon {
11
+ --input-icon-size: var(--variant-icon-size, 1.25rem);
12
+
13
+ display: block;
14
+ position: relative;
15
+ }
16
+
17
+ .input-icon > :first-child {
18
+ position: absolute;
19
+ inset-block-start: 50%;
20
+ inset-inline-start: var(--space-3);
21
+ transform: translateY(-50%);
22
+ font-size: var(--input-icon-size);
23
+ color: var(--text-muted);
24
+ pointer-events: none;
25
+ }
26
+
27
+ /* font-size only sizes text-metric icons (webfont <i>, <span>). An inline
28
+ <svg> with just a viewBox ignores it, so give it explicit dimensions. */
29
+ .input-icon > svg:first-child {
30
+ inline-size: var(--input-icon-size);
31
+ block-size: var(--input-icon-size);
32
+ }
33
+
34
+ .input-icon > .input {
35
+ padding-inline-start: calc(var(--space-3) * 2 + var(--input-icon-size));
36
+ }
@@ -0,0 +1,68 @@
1
+ /*
2
+ * Native-only control polish: file and range inputs that defeat most custom
3
+ * styling, so the framework stays out of their way and just keeps them readable
4
+ * and theme-aware.
5
+ */
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
+ .file {
20
+ color: var(--text-muted);
21
+ }
22
+
23
+ .file::file-selector-button {
24
+ margin-inline-end: var(--space-3);
25
+ padding-block: var(--space-1);
26
+ padding-inline: var(--space-2);
27
+ border: var(--border-width) solid var(--border);
28
+ border-radius: var(--radius-sm);
29
+ background: var(--surface-subtle);
30
+ color: var(--text);
31
+ font: inherit;
32
+ font-size: var(--font-size-sm);
33
+ font-weight: var(--font-weight-medium);
34
+ cursor: pointer;
35
+ transition:
36
+ background-color var(--duration),
37
+ border-color var(--duration),
38
+ color var(--duration);
39
+ }
40
+
41
+ .file:disabled::file-selector-button {
42
+ cursor: not-allowed;
43
+ }
44
+
45
+ @media (hover: hover) {
46
+ .file:not(:disabled)::file-selector-button:hover {
47
+ background: var(--border);
48
+ }
49
+ }
50
+
51
+ .range {
52
+ inline-size: 100%;
53
+ accent-color: var(--intent, var(--primary));
54
+ }
55
+
56
+ .file:focus-visible,
57
+ .range:focus-visible {
58
+ outline: var(--focus-outline);
59
+ outline-offset: var(--focus-outline-offset);
60
+ box-shadow: var(--focus-ring-shadow);
61
+ }
62
+
63
+ @media (forced-colors: active) {
64
+ .file:focus-visible,
65
+ .range:focus-visible {
66
+ box-shadow: none;
67
+ }
68
+ }
@@ -0,0 +1,36 @@
1
+ /*
2
+ * Select — chevron and appearance layer on top of the shared text-control
3
+ * recipe.
4
+ *
5
+ * Base geometry and interaction states come from control.css (select shares
6
+ * the --control-* tokens). This file only removes the native arrow and draws
7
+ * the CSS chevron, so the picker (appearance: base-select) enhancement in
8
+ * custom-select.css can be opted out independently. Add .native to a specific
9
+ * select to opt out of the enhancement.
10
+ */
11
+ .select:is(:not([multiple], [size]), :not([multiple])[size="1"]) {
12
+ appearance: none;
13
+ padding-inline-end: calc(var(--space-3) + var(--space-6));
14
+ background-image: var(--icon-chevron-image);
15
+ background-position: center right var(--space-3);
16
+ background-size: 1rem auto;
17
+ background-repeat: no-repeat;
18
+ }
19
+
20
+ /* The chevron sits on the inline-end side; flip it in RTL so it follows
21
+ the padding-inline-end space reserved for it. */
22
+ .select:is(:not([multiple], [size]), :not([multiple])[size="1"]):dir(rtl) {
23
+ background-position: center left var(--space-3);
24
+ }
25
+
26
+ /* Both forced-colors and prefers-contrast hand the arrow back to the
27
+ browser instead of guessing a higher-contrast recolor of our own icon —
28
+ simpler than maintaining contrast variants, and native rendering already
29
+ matches whatever contrast mode the user picked. */
30
+ @media (forced-colors: active), (prefers-contrast: more) {
31
+ .select:is(:not([multiple], [size]), :not([multiple])[size="1"]) {
32
+ appearance: auto;
33
+ background-image: none;
34
+ padding-inline-end: var(--space-3);
35
+ }
36
+ }
@@ -0,0 +1,127 @@
1
+ /*
2
+ * Switch controls for checkbox inputs with switch semantics.
3
+ *
4
+ * The switch remains an input control, not a decorative toggle. Checked, hover,
5
+ * focus-visible, and disabled states stay clear.
6
+ */
7
+ /* Intent boundary — inherited intents do not tint this component.
8
+ Local intent classes still win because :where() has zero specificity. */
9
+ :where(.switch) {
10
+ /* @sync intent-boundary */
11
+ --intent: initial;
12
+ --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
+ }
19
+
20
+ .switch {
21
+ --switch-bg: var(--border);
22
+ --switch-border: var(--border);
23
+ --switch-knob: var(--surface-raised);
24
+ --switch-width: calc(var(--control-size) * 1.16);
25
+ --switch-block-size: calc(var(--control-size) * 0.63);
26
+ --switch-knob-margin: calc(var(--switch-block-size) * 0.12);
27
+ --switch-knob-size: calc(var(--switch-block-size) - 2 * var(--switch-knob-margin));
28
+ --switch-travel: calc(
29
+ var(--switch-width) -
30
+ var(--switch-knob-size) -
31
+ 2 *
32
+ var(--border-width) -
33
+ 2 *
34
+ var(--switch-knob-margin)
35
+ );
36
+
37
+ position: relative;
38
+ display: inline-grid;
39
+ align-items: center;
40
+ justify-content: start;
41
+ inline-size: var(--switch-width);
42
+ block-size: var(--switch-block-size);
43
+ margin: 0;
44
+ border: var(--border-width) solid var(--form-invalid-border, var(--switch-border));
45
+ border-radius: var(--radius-full);
46
+ background: var(--switch-bg);
47
+ appearance: none;
48
+ cursor: pointer;
49
+ }
50
+
51
+ .switch::before {
52
+ content: "";
53
+ inline-size: var(--switch-knob-size);
54
+ block-size: var(--switch-knob-size);
55
+ margin-inline: var(--switch-knob-margin);
56
+ border-radius: var(--radius-full);
57
+ background: var(--switch-knob);
58
+ box-shadow: var(--shadow);
59
+ transition:
60
+ transform var(--duration),
61
+ background-color var(--duration),
62
+ box-shadow var(--duration);
63
+ }
64
+
65
+ @media (hover: hover) {
66
+ .switch:hover:not(:disabled) {
67
+ --switch-border: var(--intent, var(--primary));
68
+ }
69
+ }
70
+
71
+ .switch:checked {
72
+ --switch-bg: var(--intent, var(--primary));
73
+ --switch-border: var(--intent, var(--primary));
74
+ --switch-knob: var(--intent-fg, var(--primary-fg));
75
+ }
76
+
77
+ .switch:checked::before {
78
+ transform: translateX(var(--switch-travel));
79
+ }
80
+
81
+ /* The knob travels toward inline-end; in RTL that is leftward, so the
82
+ translateX sign flips. translateX is physical, not logical. */
83
+ .switch:checked:dir(rtl)::before {
84
+ transform: translateX(calc(var(--switch-travel) * -1));
85
+ }
86
+
87
+ .switch:focus-visible {
88
+ --switch-border: var(--focus);
89
+ outline: var(--focus-outline);
90
+ outline-offset: var(--focus-outline-offset);
91
+ box-shadow: var(--focus-ring-shadow);
92
+ }
93
+
94
+ .switch:disabled {
95
+ --switch-bg: var(--surface-subtle);
96
+ --switch-border: var(--border);
97
+ --switch-knob: var(--text-subtle);
98
+ opacity: 1;
99
+ cursor: not-allowed;
100
+ }
101
+
102
+ @media (forced-colors: active) {
103
+ /* --switch-knob must be set on the element, not on ::before: a custom
104
+ property declared on the pseudo would shadow the :checked override. */
105
+ .switch {
106
+ --switch-border: ButtonText;
107
+ --switch-knob: ButtonText;
108
+ }
109
+
110
+ .switch::before {
111
+ box-shadow: none;
112
+ }
113
+
114
+ .switch:checked {
115
+ --switch-bg: Highlight;
116
+ --switch-border: Highlight;
117
+ --switch-knob: HighlightText;
118
+ forced-color-adjust: none;
119
+ }
120
+
121
+ .switch:disabled {
122
+ --switch-bg: Canvas;
123
+ --switch-border: GrayText;
124
+ --switch-knob: GrayText;
125
+ forced-color-adjust: auto;
126
+ }
127
+ }
@@ -0,0 +1,63 @@
1
+ /*
2
+ * Form validation states.
3
+ *
4
+ * Imported by default through forms/index.css. Custom builds may omit this
5
+ * file when an app wants to own validation styling completely.
6
+ */
7
+
8
+ .field.danger {
9
+ --form-invalid-border: var(--danger);
10
+ }
11
+
12
+ .field.danger > .field-error {
13
+ display: block;
14
+ }
15
+
16
+ @supports selector(:has(*)) {
17
+ .field:has([aria-invalid="true"]) > .field-error,
18
+ .needs-validation.was-validated .field:has(:invalid) > .field-error {
19
+ display: block;
20
+ }
21
+ }
22
+
23
+ :where(.input, .textarea, .select, .check, .radio, .switch)[aria-invalid="true"],
24
+ .needs-validation.was-validated :is(.input, .textarea, .select, .check, .radio, .switch):invalid {
25
+ --form-invalid-border: var(--danger);
26
+ }
27
+
28
+ /* Override checked/indeterminate background so both border and fill signal
29
+ danger instead of showing a red border around a primary-colored control. */
30
+ .check[aria-invalid="true"]:is(:checked, :indeterminate),
31
+ .radio[aria-invalid="true"]:checked {
32
+ --choice-bg: var(--danger);
33
+ }
34
+
35
+ .switch[aria-invalid="true"]:checked {
36
+ --switch-bg: var(--danger);
37
+ }
38
+
39
+ /* Choice cards hide their native input, so invalid state must surface on the
40
+ card itself. Sets the same --form-invalid-border hook the card's border
41
+ consumes. */
42
+ @supports selector(:has(*)) {
43
+ .choice-card:has([aria-invalid="true"]),
44
+ .needs-validation.was-validated .choice-card:has(:invalid) {
45
+ --form-invalid-border: var(--danger);
46
+ }
47
+ }
48
+
49
+ @supports selector(:user-invalid) {
50
+ .needs-validation :is(.input, .textarea, .select, .check, .radio, .switch):user-invalid {
51
+ --form-invalid-border: var(--danger);
52
+ }
53
+ }
54
+
55
+ @supports selector(:has(:user-invalid)) {
56
+ .needs-validation .field:has(:user-invalid) > .field-error {
57
+ display: block;
58
+ }
59
+
60
+ .needs-validation .choice-card:has(:user-invalid) {
61
+ --form-invalid-border: var(--danger);
62
+ }
63
+ }
@@ -0,0 +1,95 @@
1
+ .grid {
2
+ display: grid;
3
+ gap: var(--gap);
4
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--grid-min, 16rem)), 1fr));
5
+ }
6
+
7
+ /* Query-container wrapper that opts .grid-2/3/4/6 into responsive
8
+ * collapse. Wrap a .grid-2/3/4/6 inside .container-query to make the
9
+ * column count collapse on narrow wrappers. Without this wrapper,
10
+ * .grid-2/3/4/6 stay at their full column count. */
11
+ /* Fixed-column grids at their full count by default.
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 */
24
+ .grid-2,
25
+ .grid-3,
26
+ .grid-4,
27
+ .grid-6 {
28
+ display: grid;
29
+ gap: var(--gap);
30
+ }
31
+
32
+ .grid-2 {
33
+ grid-template-columns: repeat(2, minmax(0, 1fr));
34
+ }
35
+ .grid-3 {
36
+ grid-template-columns: repeat(3, minmax(0, 1fr));
37
+ }
38
+ .grid-4 {
39
+ grid-template-columns: repeat(4, minmax(0, 1fr));
40
+ }
41
+ .grid-6 {
42
+ grid-template-columns: repeat(6, minmax(0, 1fr));
43
+ }
44
+
45
+ @container (width < 28rem) {
46
+ .container-query .grid-2,
47
+ .container-query .grid-3,
48
+ .container-query .grid-4 {
49
+ grid-template-columns: 1fr;
50
+ }
51
+
52
+ .container-query .grid-6 {
53
+ grid-template-columns: repeat(2, minmax(0, 1fr));
54
+ }
55
+ }
56
+
57
+ @container (28rem <= width < 48rem) {
58
+ .container-query .grid-3,
59
+ .container-query .grid-4 {
60
+ grid-template-columns: repeat(2, minmax(0, 1fr));
61
+ }
62
+ }
63
+
64
+ @container (28rem <= width < 64rem) {
65
+ .container-query .grid-6 {
66
+ grid-template-columns: repeat(3, minmax(0, 1fr));
67
+ }
68
+ }
69
+
70
+ @supports not (container-type: inline-size) {
71
+ @media (max-width: 63.999rem) {
72
+ .container-query .grid-6 {
73
+ grid-template-columns: repeat(3, minmax(0, 1fr));
74
+ }
75
+ }
76
+
77
+ @media (max-width: 47.999rem) {
78
+ .container-query .grid-3,
79
+ .container-query .grid-4 {
80
+ grid-template-columns: repeat(2, minmax(0, 1fr));
81
+ }
82
+ }
83
+
84
+ @media (max-width: 27.999rem) {
85
+ .container-query .grid-2,
86
+ .container-query .grid-3,
87
+ .container-query .grid-4 {
88
+ grid-template-columns: 1fr;
89
+ }
90
+
91
+ .container-query .grid-6 {
92
+ grid-template-columns: repeat(2, minmax(0, 1fr));
93
+ }
94
+ }
95
+ }
@@ -0,0 +1,33 @@
1
+ /*
2
+ * Intent classes do not style elements directly.
3
+ * They only expose --intent and --intent-fg.
4
+ * Components and variants decide how to consume them. Each component that
5
+ * consumes --intent or --ui-* must open a local intent boundary in a :where()
6
+ * block that resets --intent / --intent-fg / --ui-* to initial. Parent intents
7
+ * do not leak into nested components. Local intent classes (.primary, .danger,
8
+ * etc.) still apply because they have higher specificity than :where().
9
+ */
10
+ .primary {
11
+ --intent: var(--primary);
12
+ --intent-fg: var(--primary-fg);
13
+ }
14
+ .secondary {
15
+ --intent: var(--secondary);
16
+ --intent-fg: var(--secondary-fg);
17
+ }
18
+ .success {
19
+ --intent: var(--success);
20
+ --intent-fg: var(--success-fg);
21
+ }
22
+ .warning {
23
+ --intent: var(--warning);
24
+ --intent-fg: var(--warning-fg);
25
+ }
26
+ .danger {
27
+ --intent: var(--danger);
28
+ --intent-fg: var(--danger-fg);
29
+ }
30
+ .neutral {
31
+ --intent: var(--neutral);
32
+ --intent-fg: var(--neutral-fg);
33
+ }
@@ -0,0 +1,80 @@
1
+ .container-query {
2
+ container-type: inline-size;
3
+ }
4
+
5
+ .stack {
6
+ display: flex;
7
+ flex-direction: column;
8
+ gap: var(--gap);
9
+ }
10
+
11
+ .stack > * {
12
+ margin: 0;
13
+ }
14
+
15
+ .cluster {
16
+ display: flex;
17
+ flex-wrap: wrap;
18
+ align-items: var(--cluster-align, center);
19
+ justify-content: var(--cluster-justify, flex-start);
20
+ gap: var(--gap);
21
+ }
22
+
23
+ .center {
24
+ box-sizing: content-box;
25
+ max-inline-size: var(--center-size, 72rem);
26
+ margin-inline: auto;
27
+ padding-inline: var(--center-pad, 1rem);
28
+ }
29
+
30
+ .frame {
31
+ overflow: hidden;
32
+ aspect-ratio: var(--frame-ratio, 16 / 9);
33
+ }
34
+
35
+ .frame > :where(img, video, iframe, canvas, svg) {
36
+ inline-size: 100%;
37
+ block-size: 100%;
38
+ object-fit: cover;
39
+ }
40
+
41
+ .app-shell {
42
+ min-block-size: var(--viewport-block);
43
+ display: flex;
44
+ flex-direction: column;
45
+ }
46
+
47
+ /* Media object: fixed-width leading element (avatar, icon, image) + flexible
48
+ trailing content. Replaces the repeated "auto minmax(0, 1fr)" pattern in
49
+ author cards, comments, meta rows, and similar compositions. */
50
+ .media {
51
+ display: grid;
52
+ grid-template-columns: auto minmax(0, 1fr);
53
+ gap: var(--gap);
54
+ align-items: start;
55
+ }
56
+
57
+ /* Sidebar layout: content + aside using grid. Use this when the aside is
58
+ sticky or the content is multi-section. The aside width is tunable with
59
+ --sidebar-layout-size. */
60
+ .sidebar-layout {
61
+ display: grid;
62
+ gap: var(--gap);
63
+ grid-template-columns: minmax(0, 1fr);
64
+ }
65
+
66
+ @container (width >= 64rem) {
67
+ .container-query .sidebar-layout {
68
+ grid-template-columns: minmax(0, 1fr) var(--sidebar-layout-size, 18rem);
69
+ align-items: start;
70
+ }
71
+ }
72
+
73
+ @supports not (container-type: inline-size) {
74
+ @media (min-width: 64rem) {
75
+ .container-query .sidebar-layout {
76
+ grid-template-columns: minmax(0, 1fr) var(--sidebar-layout-size, 18rem);
77
+ align-items: start;
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,5 @@
1
+ @import "scroller.css";
2
+ @import "scroll-snap.css";
3
+ @import "typography-fluid.css";
4
+ @import "utilities-extra.css";
5
+ @import "layout-extra.css";
@@ -0,0 +1,55 @@
1
+ /*
2
+ * Additional layout primitives not included in the core bundle.
3
+ *
4
+ * Import this file explicitly when a project needs these patterns.
5
+ */
6
+ .switcher {
7
+ display: flex;
8
+ flex-wrap: wrap;
9
+ gap: var(--gap);
10
+ }
11
+
12
+ .switcher > * {
13
+ flex-basis: calc((var(--switcher-threshold, 40rem) - 100%) * 999);
14
+ flex-grow: 1;
15
+ min-inline-size: 0;
16
+ }
17
+
18
+ /*
19
+ * Sticky, frosted top bar shell for app/dashboard shells (persistent
20
+ * sidebar + topbar layouts). Owns positioning, stacking, and the frosted
21
+ * background only — row content (search, breadcrumb, actions) stays local
22
+ * to the page.
23
+ *
24
+ * Not for marketing/editorial site headers — those are one-off visual
25
+ * identity per page; see docs/patterns.md's Header Navigation section.
26
+ * This is for the structural app-shell topbar, which recurs unchanged
27
+ * across app UIs regardless of brand.
28
+ */
29
+ .topbar {
30
+ position: sticky;
31
+ inset-block-start: 0;
32
+ z-index: var(--z-sticky);
33
+ isolation: isolate;
34
+ display: flex;
35
+ align-items: center;
36
+ gap: var(--gap);
37
+ padding-block: var(--space-3);
38
+ padding-inline: var(--space-4);
39
+ border-block-end: var(--border-width) solid var(--border);
40
+ background: var(--surface);
41
+ }
42
+
43
+ /* Both conditions are required: the translucent background contains var(),
44
+ so a browser with backdrop-filter but not color-mix (e.g. Safari < 16.2)
45
+ would accept the declaration at parse time, fail it at computed-value
46
+ time, and lose the background entirely instead of keeping the opaque
47
+ fallback above. */
48
+ @supports ((backdrop-filter: blur(1rem)) or (-webkit-backdrop-filter: blur(1rem))) and
49
+ (background: color-mix(in oklch, red 50%, transparent)) {
50
+ .topbar {
51
+ background: color-mix(in oklch, var(--surface) 88%, transparent);
52
+ backdrop-filter: blur(0.75rem);
53
+ -webkit-backdrop-filter: blur(0.75rem);
54
+ }
55
+ }