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,21 @@
1
+ /*
2
+ * Actions — a horizontal action-button cluster.
3
+ *
4
+ * Composes with .btn (or any inline element) and feeds layout via the shared
5
+ * --gap token.
6
+ */
7
+ .actions {
8
+ --gap: var(--space-2);
9
+
10
+ display: flex;
11
+ flex-wrap: wrap;
12
+ align-items: center;
13
+ gap: var(--gap);
14
+ margin: 0;
15
+ padding: 0;
16
+ list-style: none;
17
+ }
18
+
19
+ .actions > li {
20
+ margin: 0;
21
+ }
@@ -0,0 +1,137 @@
1
+ /*
2
+ * Alerts for inline feedback and status messages.
3
+ *
4
+ * Keeps alert defaults local while consuming the shared --ui-* variant
5
+ * plumbing for emphasis.
6
+ *
7
+ * --alert-border-inline-start-color/-width are unset extension points, mirroring
8
+ * --btn-border-block-end-* (button.css), for themes that give alerts a
9
+ * callout-style colored flag on the leading edge instead of a uniform border.
10
+ */
11
+ /* Intent boundary — inherited intents do not tint this component.
12
+ Local intent classes still win because :where() has zero specificity. */
13
+ :where(.alert) {
14
+ /* @sync intent-boundary */
15
+ --intent: initial;
16
+ --intent-fg: initial;
17
+ --ui-bg: initial;
18
+ --ui-fg: initial;
19
+ --ui-border: initial;
20
+ --ui-hover-bg: initial;
21
+ /* @sync intent-boundary */
22
+ }
23
+
24
+ .alert {
25
+ --alert-pad-block: var(--variant-pad-block, var(--space-4));
26
+ --alert-pad-inline: var(--space-4);
27
+ --alert-font-size: var(--variant-font-size, var(--font-size-md));
28
+ --alert-icon-size: var(--variant-icon-size, 1.25rem);
29
+ --alert-bg: var(--surface-subtle);
30
+ --alert-border: var(--border);
31
+ --ui-bg: var(--alert-bg);
32
+ --ui-border: var(--alert-border);
33
+ --ui-fg: var(--intent, var(--text));
34
+
35
+ padding-block: var(--alert-pad-block);
36
+ padding-inline: var(--alert-pad-inline);
37
+ background: var(--ui-bg, transparent);
38
+ color: var(--ui-fg, var(--text));
39
+ border: var(--border-width) solid var(--ui-border, var(--border));
40
+ border-inline-start-color: var(
41
+ --alert-border-inline-start-color,
42
+ var(--ui-border, var(--border))
43
+ );
44
+ border-inline-start-width: var(--alert-border-inline-start-width, var(--border-width));
45
+ border-radius: var(--radius-lg);
46
+ font-size: var(--alert-font-size);
47
+ }
48
+
49
+ .alert > * {
50
+ min-inline-size: 0;
51
+ }
52
+
53
+ .alert > :first-child {
54
+ margin-block-start: 0;
55
+ }
56
+
57
+ .alert > :last-child {
58
+ margin-block-end: 0;
59
+ }
60
+
61
+ /* Alerts are soft by default. Use .solid or .outline for stronger emphasis. */
62
+ .alert.solid {
63
+ --ui-bg: var(--intent, var(--neutral));
64
+ --ui-fg: var(--intent-fg, var(--neutral-fg));
65
+ --ui-border: transparent;
66
+ }
67
+
68
+ .alert.outline {
69
+ --ui-bg: transparent;
70
+ --ui-fg: var(--intent, var(--text));
71
+ --ui-border: var(--intent, var(--border));
72
+ }
73
+
74
+ @supports (color: color-mix(in oklch, red, white)) {
75
+ .alert:where(:not(.solid, .outline)) {
76
+ /* @sync soft-recipe */
77
+ --ui-bg: color-mix(in oklch, var(--surface) var(--soft-bg-mix), var(--intent, var(--neutral)));
78
+ --ui-border: color-mix(
79
+ in oklch,
80
+ var(--surface) var(--soft-border-mix),
81
+ var(--intent, var(--neutral))
82
+ );
83
+ /* @sync soft-recipe */
84
+ }
85
+ }
86
+
87
+ /* Decorative leading icon */
88
+ .alert > :first-child[aria-hidden="true"] {
89
+ display: inline-block;
90
+ flex: none;
91
+ inline-size: var(--alert-icon-size);
92
+ block-size: var(--alert-icon-size);
93
+ font-size: var(--alert-icon-size);
94
+ line-height: 1;
95
+ vertical-align: -0.125em;
96
+ }
97
+
98
+ @supports selector(.alert:has(> *)) {
99
+ .alert:has(> :first-child[aria-hidden="true"]) {
100
+ display: grid;
101
+ grid-template-columns: auto minmax(0, 1fr);
102
+ align-items: var(--items-align, start);
103
+ column-gap: var(--space-3);
104
+ }
105
+
106
+ .alert:has(> :first-child[aria-hidden="true"]) > :first-child {
107
+ grid-column: 1;
108
+ grid-row: 1 / -1;
109
+ display: inline-grid;
110
+ place-items: center;
111
+ inline-size: var(--alert-icon-size);
112
+ block-size: var(--alert-icon-size);
113
+ margin-block-start: 0.125em;
114
+ font-size: var(--alert-icon-size);
115
+ line-height: 1;
116
+ }
117
+
118
+ .alert:has(> :first-child[aria-hidden="true"]) > :first-child:is(svg, i) {
119
+ inline-size: var(--alert-icon-size);
120
+ block-size: var(--alert-icon-size);
121
+ }
122
+
123
+ .alert:has(> :first-child[aria-hidden="true"]) > :not(:first-child) {
124
+ grid-column: 2;
125
+ }
126
+ }
127
+
128
+ /* Links */
129
+ .alert :where(a:not(.btn)) {
130
+ color: inherit;
131
+ font-weight: var(--font-weight-strong);
132
+ }
133
+
134
+ /* Forced colors: no override needed. The always-on border gets a forced
135
+ readable color, and text on a Highlight surface would trigger Chromium's
136
+ Canvas backplate (unreadable) — so .solid intentionally falls back to the
137
+ default look. */
@@ -0,0 +1,199 @@
1
+ /*
2
+ * Avatars for initials, images, links, and buttons.
3
+ *
4
+ * The visual box stays the same across semantic elements. Grouping and status
5
+ * dots (an empty .badge child) compose around the avatar without changing its
6
+ * meaning.
7
+ */
8
+ /* Intent boundary — inherited intents do not tint this component.
9
+ Local intent classes still win because :where() has zero specificity. */
10
+ :where(.avatar) {
11
+ /* @sync intent-boundary */
12
+ --intent: initial;
13
+ --intent-fg: initial;
14
+ --ui-bg: initial;
15
+ --ui-fg: initial;
16
+ --ui-border: initial;
17
+ --ui-hover-bg: initial;
18
+ /* @sync intent-boundary */
19
+ }
20
+
21
+ .avatar {
22
+ --avatar-size: var(--avatar-stack-size, 2.25rem);
23
+ --avatar-radius: var(--radius-full);
24
+ --avatar-font-size: var(--avatar-stack-font-size, var(--font-size-sm));
25
+ --avatar-bg: var(--intent, var(--surface-subtle));
26
+ --avatar-fg: var(--intent-fg, var(--tone-fg, var(--text-muted)));
27
+
28
+ position: relative;
29
+ display: inline-grid;
30
+ place-items: center;
31
+ inline-size: var(--avatar-size);
32
+ block-size: var(--avatar-size);
33
+ overflow: visible;
34
+ padding: 0;
35
+ border: 0;
36
+ border-radius: var(--avatar-radius);
37
+ background: var(--avatar-bg);
38
+ color: var(--avatar-fg);
39
+ font: inherit;
40
+ font-size: var(--avatar-font-size);
41
+ font-weight: var(--font-weight-strong);
42
+ line-height: 1;
43
+ text-decoration: none;
44
+ vertical-align: middle;
45
+ user-select: none;
46
+ transition:
47
+ box-shadow var(--duration),
48
+ opacity var(--duration),
49
+ transform var(--duration);
50
+ }
51
+
52
+ /* Forced colors wipe the background and the stack ring (box-shadow), so the
53
+ shape needs a real border to stay visible. */
54
+ @media (forced-colors: active) {
55
+ .avatar {
56
+ border: var(--border-width) solid CanvasText;
57
+ }
58
+
59
+ :where(a.avatar, button.avatar)::after {
60
+ display: none;
61
+ }
62
+ }
63
+
64
+ .avatar > :first-child:not(:where(img, picture, .badge)) {
65
+ display: inline-grid;
66
+ place-items: center;
67
+ inline-size: 100%;
68
+ block-size: 100%;
69
+ overflow: hidden;
70
+ border-radius: inherit;
71
+ }
72
+
73
+ .avatar > img,
74
+ .avatar > picture {
75
+ display: block;
76
+ inline-size: 100%;
77
+ block-size: 100%;
78
+ overflow: hidden;
79
+ border-radius: inherit;
80
+ }
81
+
82
+ .avatar > img,
83
+ .avatar > picture > img {
84
+ inline-size: 100%;
85
+ block-size: 100%;
86
+ object-fit: cover;
87
+ border-radius: inherit;
88
+ }
89
+
90
+ .avatar > picture > img {
91
+ display: block;
92
+ }
93
+
94
+ button.avatar {
95
+ appearance: none;
96
+ cursor: pointer;
97
+ }
98
+
99
+ :where(a.avatar, button.avatar)::after {
100
+ content: "";
101
+ position: absolute;
102
+ inset: 0;
103
+ border-radius: inherit;
104
+ background: var(--hover-overlay);
105
+ opacity: 0;
106
+ pointer-events: none;
107
+ transition: opacity var(--duration);
108
+ }
109
+
110
+ @media (hover: hover) {
111
+ :where(a.avatar, button.avatar):not(:where(:disabled, [aria-disabled="true"])):hover::after {
112
+ opacity: 1;
113
+ }
114
+ }
115
+
116
+ :where(a.avatar, button.avatar):not(:where(:disabled, [aria-disabled="true"])):active {
117
+ transform: translateY(1px);
118
+ }
119
+
120
+ button.avatar:disabled,
121
+ a.avatar[aria-disabled="true"] {
122
+ cursor: not-allowed;
123
+ opacity: var(--disabled-opacity);
124
+ }
125
+
126
+ a.avatar[aria-disabled="true"] {
127
+ pointer-events: none;
128
+ }
129
+
130
+ .avatar.sm {
131
+ --avatar-stack-size: 1.75rem;
132
+ --avatar-stack-font-size: var(--font-size-xs);
133
+ }
134
+
135
+ .avatar.lg {
136
+ --avatar-stack-size: 3rem;
137
+ --avatar-stack-font-size: var(--font-size-lg);
138
+ }
139
+
140
+ /* Status dot: an empty .badge positioned at the avatar's bottom-end corner.
141
+ The dot conveys no text — always pair it with an aria-label on the badge. */
142
+ .avatar > .badge:empty {
143
+ --avatar-status-translate-x: 50%;
144
+
145
+ position: absolute;
146
+ inset-block-end: var(--indicator-offset);
147
+ inset-inline-end: var(--indicator-offset);
148
+ z-index: 1;
149
+ box-shadow: 0 0 0 2px var(--indicator-ring);
150
+ transform: translate(var(--avatar-status-translate-x), 50%);
151
+ }
152
+
153
+ .avatar:dir(rtl) > .badge:empty {
154
+ --avatar-status-translate-x: -50%;
155
+ }
156
+
157
+ /* Avatar stack — explicit class for overlapping avatar groups.
158
+ Does not rely on role="group" auto-detection. */
159
+ .avatar-stack {
160
+ --avatar-stack-overlap: calc(var(--avatar-stack-size, 2.25rem) / -4);
161
+ --avatar-stack-ring: 2px;
162
+
163
+ display: inline-flex;
164
+ align-items: center;
165
+ }
166
+
167
+ .avatar-stack.sm {
168
+ --avatar-stack-size: 1.75rem;
169
+ --avatar-stack-font-size: var(--font-size-xs);
170
+ }
171
+
172
+ .avatar-stack.lg {
173
+ --avatar-stack-size: 3rem;
174
+ --avatar-stack-font-size: var(--font-size-lg);
175
+ }
176
+
177
+ @supports selector(:has(> .avatar)) {
178
+ .avatar-stack:has(> .avatar.sm) {
179
+ --avatar-stack-size: 1.75rem;
180
+ --avatar-stack-font-size: var(--font-size-xs);
181
+ }
182
+
183
+ .avatar-stack:has(> .avatar.lg) {
184
+ --avatar-stack-size: 3rem;
185
+ --avatar-stack-font-size: var(--font-size-lg);
186
+ }
187
+ }
188
+
189
+ .avatar-stack > .avatar {
190
+ box-shadow: 0 0 0 var(--avatar-stack-ring) var(--surface);
191
+ }
192
+
193
+ .avatar-stack > .avatar + .avatar {
194
+ margin-inline-start: var(--avatar-stack-overlap);
195
+ }
196
+
197
+ .avatar-stack > .avatar:is(:hover, :focus-visible) {
198
+ z-index: 1;
199
+ }
@@ -0,0 +1,143 @@
1
+ /*
2
+ * Badges for compact labels, counts, and state hints.
3
+ *
4
+ * Solid by default. Shared .soft/.outline variants stay available for quieter
5
+ * tags without adding a badge-specific color API.
6
+ */
7
+ /* Intent boundary — inherited intents do not tint this component.
8
+ Local intent classes still win because :where() has zero specificity. */
9
+ :where(.badge) {
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
+ .badge {
21
+ --badge-size: var(--variant-compact-size, 1.5rem);
22
+ --badge-pad-x: 0.55em;
23
+ --badge-font-size: var(--variant-compact-font-size, 0.8125rem);
24
+ --badge-dot-size: 0.5rem;
25
+ --badge-bg: var(--ui-bg, var(--intent, var(--neutral)));
26
+ --badge-fg: var(--ui-fg, var(--intent-fg, var(--neutral-fg)));
27
+ --badge-border: var(--ui-border, transparent);
28
+ --badge-radius: 999px;
29
+
30
+ display: inline-flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ gap: 0.35em;
34
+ min-block-size: var(--badge-size);
35
+ padding-inline: var(--badge-pad-x);
36
+ border: var(--border-width) solid var(--badge-border);
37
+ border-radius: var(--badge-radius);
38
+ background: var(--badge-bg);
39
+ color: var(--badge-fg);
40
+ font-size: var(--badge-font-size);
41
+ font-weight: var(--font-weight-strong);
42
+ line-height: 1;
43
+ text-decoration: none;
44
+ white-space: nowrap;
45
+ transition:
46
+ background-color var(--duration),
47
+ border-color var(--duration),
48
+ color var(--duration),
49
+ box-shadow var(--duration);
50
+ }
51
+
52
+ .badge:empty {
53
+ --badge-size: var(--badge-dot-size);
54
+ padding: 0;
55
+ border: 0;
56
+ inline-size: var(--badge-size);
57
+ block-size: var(--badge-size);
58
+ }
59
+
60
+ .badge > button {
61
+ display: inline-grid;
62
+ place-items: center;
63
+ flex: 0 0 auto;
64
+ min-inline-size: 1.5rem;
65
+ min-block-size: 1.5rem;
66
+ margin-block: -0.25rem;
67
+ margin-inline-start: -0.15em;
68
+ margin-inline-end: -0.45em;
69
+ padding: 0.25rem;
70
+ border: 0;
71
+ border-radius: var(--radius-full);
72
+ background: transparent;
73
+ color: inherit;
74
+ font: inherit;
75
+ line-height: 1;
76
+ cursor: pointer;
77
+ }
78
+
79
+ @media (hover: hover) {
80
+ .badge > button:not(:disabled):hover {
81
+ background: var(--hover-overlay);
82
+ }
83
+ }
84
+
85
+ /* .solid/.soft/.outline are shared via variants.css, including the
86
+ .soft/.outline hover recipe (--ui-hover-bg). The --badge-* fallbacks make
87
+ .badge solid by default. */
88
+ @media (hover: hover) {
89
+ :where(a.badge, button.badge):not(:where(:disabled, [aria-disabled="true"])):hover {
90
+ background: var(--ui-hover-bg, var(--badge-bg));
91
+ }
92
+
93
+ :where(a.badge, button.badge):not(:where(:disabled, [aria-disabled="true"], .outline)):hover {
94
+ box-shadow: inset 0 0 0 999px var(--hover-overlay);
95
+ }
96
+ }
97
+
98
+ button.badge {
99
+ cursor: pointer;
100
+ font: inherit;
101
+ }
102
+
103
+ button.badge:disabled,
104
+ .badge[aria-disabled="true"] {
105
+ opacity: var(--disabled-opacity);
106
+ cursor: not-allowed;
107
+ }
108
+
109
+ a.badge[aria-disabled="true"] {
110
+ pointer-events: none;
111
+ }
112
+
113
+ @media (forced-colors: active) {
114
+ .badge {
115
+ border-color: ButtonText;
116
+ background: Canvas;
117
+ color: CanvasText;
118
+ box-shadow: none;
119
+ }
120
+
121
+ .badge:empty {
122
+ background: ButtonText;
123
+ }
124
+
125
+ @media (hover: hover) {
126
+ :where(a.badge, button.badge):hover:not(:where(:disabled, [aria-disabled="true"])) {
127
+ outline: var(--border-width) solid ButtonText;
128
+ outline-offset: 2px;
129
+ }
130
+
131
+ .badge > button:not(:disabled):hover {
132
+ outline: var(--border-width) solid currentColor;
133
+ outline-offset: 1px;
134
+ }
135
+ }
136
+
137
+ button.badge:disabled,
138
+ .badge[aria-disabled="true"] {
139
+ border-color: GrayText;
140
+ background: Canvas;
141
+ color: GrayText;
142
+ }
143
+ }
@@ -0,0 +1,56 @@
1
+ /*
2
+ * Breadcrumb — current-location trail from the site root.
3
+ *
4
+ * Inline list separated by "/". Mark the current page with `aria-current="page"`
5
+ * either on the item or on the current link.
6
+ */
7
+ .breadcrumb {
8
+ display: flex;
9
+ flex-wrap: wrap;
10
+ align-items: center;
11
+ gap: var(--space-1);
12
+ margin: 0;
13
+ padding: 0;
14
+ color: var(--text-subtle);
15
+ font-size: var(--font-size-sm);
16
+ list-style: none;
17
+ }
18
+
19
+ .breadcrumb li {
20
+ display: inline-flex;
21
+ align-items: center;
22
+ gap: var(--space-1);
23
+ }
24
+
25
+ .breadcrumb li + li::before {
26
+ content: "/";
27
+ color: var(--text-subtle);
28
+ }
29
+
30
+ .breadcrumb a {
31
+ color: var(--text-muted);
32
+ text-decoration: none;
33
+ }
34
+
35
+ @media (hover: hover) {
36
+ .breadcrumb
37
+ li:not(:where([aria-current]:not([aria-current="false"])))
38
+ > a:hover:not(:where([aria-current]:not([aria-current="false"]))) {
39
+ color: var(--text);
40
+ text-decoration: underline;
41
+ }
42
+ }
43
+
44
+ .breadcrumb a:focus-visible {
45
+ border-radius: var(--radius-sm);
46
+ }
47
+
48
+ .breadcrumb a:where([aria-current]:not([aria-current="false"])) {
49
+ color: var(--text);
50
+ font-weight: var(--font-weight-medium);
51
+ }
52
+
53
+ .breadcrumb li:where([aria-current]:not([aria-current="false"])) > :where(a, span) {
54
+ color: var(--text);
55
+ font-weight: var(--font-weight-medium);
56
+ }
@@ -0,0 +1,44 @@
1
+ /*
2
+ * Busy overlay — non-button loading state.
3
+ *
4
+ * A non-button element with aria-busy="true" and a direct last-child .spinner
5
+ * shows that spinner as a centered overlay over a faded surface. Buttons keep
6
+ * their spinners inline (e.g. .btn[aria-busy] > .spinner stays inline-flex).
7
+ */
8
+ [aria-busy] > .spinner:last-child {
9
+ display: none;
10
+ }
11
+
12
+ [aria-busy="true"] > .spinner:last-child {
13
+ display: inline-block;
14
+ }
15
+
16
+ @supports selector([aria-busy="true"]:has(> .spinner:last-child)) {
17
+ [aria-busy="true"]:not(.btn):has(> .spinner:last-child) {
18
+ position: relative;
19
+ }
20
+
21
+ [aria-busy="true"]:not(.btn):has(> .spinner:last-child)::before {
22
+ content: "";
23
+ position: absolute;
24
+ inset: 0;
25
+ z-index: 1;
26
+ border-radius: inherit;
27
+ background: var(--busy-overlay-bg, var(--surface));
28
+ }
29
+
30
+ [aria-busy="true"]:not(.btn) > .spinner:last-child {
31
+ position: absolute;
32
+ inset: 0;
33
+ z-index: 2;
34
+ margin: auto;
35
+ display: block;
36
+ }
37
+ }
38
+
39
+ @supports (selector([aria-busy="true"]:has(> .spinner:last-child))) and
40
+ (color: color-mix(in oklch, red, white)) {
41
+ [aria-busy="true"]:not(.btn):has(> .spinner:last-child)::before {
42
+ background: color-mix(in oklch, var(--busy-overlay-bg, var(--surface)), transparent 20%);
43
+ }
44
+ }