EXSA
Generate custom CSS

exsa.css — Framework Source

Complete 10-layer cascade. Six layers carry code in exsa.css; themes, skins, fluid, components, and layouts are separate files. Click any section to expand.

942 lines 10 layers 82 tokens 32.4 KB raw — ~7.6 KB gzip
Cascade order — unlayered always beats layered:
Your CSSunlayeredstrongest

Layer 10 — Overridesu-* escape hatch
Layer 9 — Layoutsseparate files
Layer 8 — Componentsseparate files
Layer 7 — Elements
Layer 6 — Utilities
Layer 5 — Reset
Layer 4 — Fluidoptional file
Layer 3 — Skinsseparate files
Layer 2 — Themesseparate files
Layer 1 — Tokensweakest
Layer 1 — Tokens Layer 2 — Themes Layer 3 — Skins Layer 4 — Fluid Layer 5 — Reset Layer 6 — Utilities Layer 7 — Elements Layer 8 — Components Layer 9 — Layouts Layer 10 — Overrides
Preamble Copyright, @layer declaration, docs pointer — 6 lines ?
1 /* Copyright © 2026 Saif Almarri. MIT License. See LICENSE.
2 Cascade: tokens → themes → skins → (fluid — optional) → reset → utilities → elements → components → layouts → overrides.
3 */
4
5 @layer exsa.tokens, exsa.themes, exsa.skins, exsa.fluid, exsa.reset, exsa.utilities, exsa.elements, exsa.components, exsa.layouts, exsa.overrides;
6
Layer 1 — Tokens 95 design tokens. Colors, spacing, typography, shadows, breakpoints, behavioral factors, z-index scale. — 96 lines ?
7 @layer exsa.tokens {
8
9 /* 95 design tokens */
10
11 :root {
12 color-scheme: light dark;
13 --color-bg:light-dark(#fff,#333);--color-bg-secondary:light-dark(#e9e9e9,#555);--color-text:light-dark(#000,#f7f7f7);--color-text-secondary:light-dark(#757575,#aaa);
14 --color-link:light-dark(#118bee,#0097fc);--color-secondary:light-dark(#920de9,#e20de9);--color-secondary-accent:light-dark(#920de90b,#e20de94f);
15 --color-accent:light-dark(#118bee15,#0097fc4f);--color-shadow:light-dark(#f4f4f4,#bbbbbb20);--color-overlay:light-dark(rgba(0,0,0,.5),rgba(0,0,0,.65));--color-scrollbar:color-mix(in srgb,var(--color-text)25%,transparent);
16 --color-success:light-dark(#16a34a,#22c55e);--color-success-hover:light-dark(#15803d,#16a34a);--color-danger:light-dark(#dc2626,#ef4444);
17 --color-danger-hover:light-dark(#b91c1c,#dc2626);--color-warning:light-dark(#d97706,#f59e0b);--color-warning-hover:light-dark(#b45309,#d97706);
18 --color-button-text:#fff;--color-button-text-inverse:light-dark(#000,#eee);--color-border:light-dark(#e9e9e9,#555); /* hover variants & --color-button-text consumed by component CSS files */
19 --font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
20 --font-family-mono:'SF Mono','Cascadia Code','Fira Code',Consolas,monospace;
21 --font-family-heading:var(--font-family);
22 --font-weight-normal:400;--font-weight-bold:700;--font-weight-heavy:800;
23 --font-size-xs:.7rem;--font-size-sm:.78rem;--font-size-base:.88rem;--font-size-md:.95rem;--font-size-lg:1.08rem;--font-size-xl:1.3rem;--font-size-2xl:1.8rem;
24 --line-height:1.5;--border-radius:5px;--box-shadow:2px 2px 10px;
25 --hover-brightness:1.2;--active-brightness:0.85;
26 --justify-heading:center;--justify-body:start;--justify-nav:space-between;--blockquote-border:4px solid var(--color-secondary);
27 --width-card:285px;--width-card-medium:460px;--width-card-wide:800px;--width-content:1080px;--scroll-margin-top:80px;
28 --gap-xs:0.25rem;--gap-sm:0.5rem;--gap-md:0.75rem;--gap:1rem;--gap-lg:1.5rem;--gap-xl:2rem;--gap-2xl:3rem;
29 --bp-sm:575px;--bp-md:768px;--bp-lg:1024px;--bp-xl:1280px;--bp-xxl:1440px; /* informational — media queries can't consume vars; CSS hardcodes these values */
30 --space-factor:1;--radius-factor:1;--font-factor:1;--motion-factor:1; /* global scaling knobs — consumed by JS / components */
31 /* z-index scale — one stack for the whole framework. Override any layer in your theme. */
32 --z-floating:10; /* in-component floating controls (carousel arrows) */
33 --z-dropdown:100; /* dropdowns, tooltips, date/color picker popups */
34 --z-back-top:800; /* back-to-top button */
35 --z-drawer:900; /* drawer backdrop */
36 --z-drawer-panel:901; /* drawer panel */
37 --z-cookiebar:950; /* cookie consent bar */
38 --z-topbar:1000; /* fixed topbar */
39 --z-topbar-dropdown:1002; /* topbar dropdown menus (above topbar + announce) */
40 --z-modal:1050; /* modal dialogs + video modals (above topbar) */
41 --z-lightbox:1100; /* fullscreen lightbox */
42 --z-popover-backdrop:9995;
43 --z-popover:9996; /* popover content */
44 --z-context-backdrop:9997;
45 --z-context-menu:9998; /* context menu */
46 --z-toast:9999; /* toast notifications */
47 --z-skip:10000; /* skip link */
48 --z-layout-aside:90; /* fixed sidebars in layout shells (below bars/overlays) */
49 --z-layout-footer:95; /* footer above aside in aside-full mode */
50 --z-announce:1001; /* store announcement bar (above topbar) */
51 /* surface material — the skin axis (overridden by dist/skins/*.css).
52 Defaults mirror today's rendered pixels: zero visual change without a skin. */
53 --surface-bg:var(--color-bg);
54 --surface-bg-elevated:color-mix(in srgb,var(--color-bg) 97%,var(--color-text));
55 --surface-border:1px solid var(--color-border);
56 --surface-border-control:1px solid var(--color-border); /* control boundary — skins must keep it visible (never none) */
57 --surface-radius:var(--border-radius);
58 --surface-shadow-sm:2px 2px 10px; /* color omitted = currentColor — matches legacy --box-shadow hover */
59 --surface-shadow-md:0 4px 16px color-mix(in srgb,var(--color-text) 8%,transparent);
60 --surface-shadow-lg:0 8px 32px color-mix(in srgb,var(--color-text) 12%,transparent);
61 --surface-shadow-side:8px 0 32px color-mix(in srgb,var(--color-text) 14%,transparent);
62 --surface-inset:inset 0 0 0 0 transparent; /* no-op — composable in shadow lists; clay/skeuomorphic/glow replace it */
63 --surface-backdrop:none; /* box frost — frosted skins (glossy, glow) set blur(); default none = off */
64 --surface-backdrop-strong:none; /* elevated-surface frost (topbar, cards, tray) — frosted skins set a stronger blur */
65 /* page canvas — the skin-preferred page backdrop (paints the body background) */
66 --surface-canvas:var(--color-bg);
67 }
68
69 /* Dark/light — one definition per token via light-dark(); forced mode flips color-scheme */
70
71 /* Forced dark — <html data-theme-mode="dark">, ignores OS preference */
72 :root[data-theme-mode="dark"] { color-scheme: dark; }
73
74 /* Forced light — <html data-theme-mode="light">, ignores OS preference.
75 "only" = browsers must never auto/force-darken a page that pinned light. */
76 :root[data-theme-mode="light"] { color-scheme: only light; }
77
78 /* Legacy fallback */
79 @supports not (color: light-dark(#000, #fff)) {
80 :root {
81 --color-bg: Canvas;
82 --color-bg-secondary: ButtonFace;
83 --color-text: CanvasText;
84 --color-text-secondary: GrayText;
85 --color-link: LinkText;
86 --color-secondary: VisitedText;
87 --color-secondary-accent: ButtonFace;
88 --color-accent: ButtonFace;
89 --color-border: GrayText;
90 --color-overlay: rgba(0, 0, 0, .55);
91 --color-button-text-inverse: CanvasText;
92 --color-success: #16a34a;
93 --color-success-hover: #15803d;
94 --color-danger: #dc2626;
95 --color-danger-hover: #b91c1c;
96 --color-warning: #d97706;
97 --color-warning-hover: #b45309;
98 }
99 }
100
101 } /* tokens */
102
Layer 2 — Themes 20 theme files — token overrides only. Swap one <link> to re-skin everything. — 21 lines ?
/* Layer 2: exsa.themes — one file per theme, overrides exsa.tokens */
dist/themes/abyss.css
dist/themes/breeze.css
dist/themes/clinic.css
dist/themes/console.css
dist/themes/coral.css
dist/themes/ember.css
dist/themes/forest.css
dist/themes/ink.css
dist/themes/ledger.css
dist/themes/mono.css
dist/themes/night.css
dist/themes/nova.css
dist/themes/prism.css
dist/themes/sepia.css
dist/themes/shadow.css
dist/themes/sojourn.css
dist/themes/steel.css
dist/themes/travei.css
dist/themes/tropic.css
dist/themes/volt.css
Layer 3 — Skins 10 skin files — surface material recipes (shape, shadows, box treatment: solid / clear / frosted). — 11 lines ?
/* Layer 3: exsa.skins — one file per material, token-only :root rules */
dist/skins/brutalist.css
dist/skins/clay.css
dist/skins/flat.css
dist/skins/glass.css
dist/skins/glossy.css
dist/skins/glow.css
dist/skins/metallic.css
dist/skins/neomorphic.css
dist/skins/neon.css
dist/skins/skeuomorphic.css
Layer 4 — Fluid One optional file — clamp() scaling and density profiles. — 2 lines ?
/* Layer 3: exsa.fluid — link dist/exsa.fluid.css after exsa.css */
dist/exsa.fluid.css
Layer 5 — Reset Box model, focus rings, a11y, RTL, scrollbars, skip links. — 49 lines ?
103 @layer exsa.reset {
104
105 *,*::before,*::after{box-sizing:border-box}
106 :focus-visible{outline:2px solid var(--color-link);outline-offset:2px}
107 :focus:not(:focus-visible){outline:none}
108
109 /* Universal a11y utility — intentionally unscoped, works without class="exsa" */
110 .sr-only{position:absolute;width:1px;height:1px;padding:0;margin: calc(-1px * var(--space-factor, 1));overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
111
112 /* Windows High Contrast Mode */
113 @media(forced-colors:active){
114 :focus-visible{outline:2px solid Highlight;outline-offset:2px}
115 .sr-only{clip:auto;clip-path:none;position:static;width:auto;height:auto}
116 .skip-link{background:Highlight;color:HighlightText}
117 }
118 /* High contrast preference — boost borders and text weight */
119 @media(prefers-contrast:high){
120 body{text-shadow:none}
121 :where(.exsa a){text-decoration-thickness:2px}
122 *,*::before,*::after{border-color:currentColor}
123 }
124 @media(prefers-contrast:low){
125 /* Low contrast = softer borders, not thinner text */
126 *,*::before,*::after{border-color:color-mix(in srgb,currentColor 40%,transparent)}
127 }
128 *{scrollbar-width:thin;scrollbar-color:var(--color-scrollbar) transparent}
129 *::-webkit-scrollbar{width:5px;height:5px}
130 *::-webkit-scrollbar-track{background:transparent}
131 *::-webkit-scrollbar-thumb{background-color:var(--color-scrollbar);border-radius:10px}
132 html{scroll-behavior:smooth}
133 @media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}:target{animation:none}*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}
134 @media(prefers-reduced-data:reduce){img,video{min-height:0;height:auto}}
135 [dir=rtl]{direction:rtl;unicode-bidi:isolate}
136 body{background:var(--surface-canvas);background-attachment:fixed;color:var(--color-text);font-family:var(--font-family);line-height:var(--line-height);margin:0;padding:0}
137 h1,h2,h3,h4,h5,h6{font-family:var(--font-family-heading);line-height:var(--line-height);text-wrap:balance;margin:0 0 var(--gap-md)}
138 img,svg,video{max-width:100%;height:auto}
139 figure{margin:0;padding:0}
140 [hidden]{display:none!important}
141
142 /* Universal a11y utilities — intentionally unscoped, work without class="exsa" */
143 .skip-link{position:absolute;top:-100px;inset-inline-start:0;background:var(--color-link);color:#fff;padding:var(--gap-sm) var(--gap);z-index:var(--z-skip);font-weight:700;border-radius:0 0 var(--border-radius) var(--border-radius);text-decoration:none;transition:top .15s}
144 .skip-link:focus{top:0}
145 [dir=rtl] .skip-link{border-radius:0 0 var(--border-radius) 0}
146 /* Intentionally unscoped — anchor-target flash highlight works globally */
147 :target{scroll-margin-top:var(--scroll-margin-top);animation:exsa-target-flash 1.2s ease-out}
148 @keyframes exsa-target-flash{0%{background:var(--color-accent)}100%{background:transparent}}
149
150 } /* reset */
151
Layer 6 — Utilities 89 layout & typography utilities. Flex, grid, gaps, containers, responsive columns. — 44 lines ?
152 @layer exsa.utilities {
153
154 /* Layout only — unprefixed utilities lose to elements/components/layouts;
155 use u-* (exsa.overrides) to force-win. */
156 .container{max-width:var(--width-content);margin:0 auto;padding:0 var(--gap)}
157 .container-sm{max-width:var(--width-card-wide);margin:0 auto;padding:0 var(--gap-sm)}
158 .container-full{width:100%}
159 .flex{display:flex}.flex-inline{display:inline-flex}
160 .flex-col{flex-direction:column}.flex-col-rev{flex-direction:column-reverse}.flex-row-rev{flex-direction:row-reverse}
161 .flex-wrap{flex-wrap:wrap}.flex-nowrap{flex-wrap:nowrap}
162 .flex-1{flex:1}.flex-auto{flex:0 1 auto}.flex-none{flex:none}
163 .justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}
164 .justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.justify-evenly{justify-content:space-evenly}
165 .items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}
166 .items-baseline{align-items:baseline}.items-stretch{align-items:stretch}
167 .self-start{align-self:flex-start}.self-end{align-self:flex-end}.self-center{align-self:center}.self-stretch{align-self:stretch}
168 .content-start{align-content:flex-start}.content-end{align-content:flex-end}.content-center{align-content:center}
169 .content-between{align-content:space-between}.content-around{align-content:space-around}
170 .gap-0{gap:0}.gap-xs{gap:var(--gap-xs)}.gap-sm{gap:var(--gap-sm)}.gap-md{gap:var(--gap-md)}.gap{gap:var(--gap)}
171 .gap-lg{gap:var(--gap-lg)}.gap-xl{gap:var(--gap-xl)}.gap-2xl{gap:var(--gap-2xl)}
172 .col-1{flex:0 0 100%}.col-2{flex:0 0 50%}.col-3{flex:0 0 33.333%}
173 .col-4{flex:0 0 25%}.col-5{flex:0 0 20%}.col-6{flex:0 0 16.667%}
174 .grid{display:grid}
175 .grid-cols-1{grid-template-columns:repeat(1,1fr)}.grid-cols-2{grid-template-columns:repeat(2,1fr)}
176 .grid-cols-3{grid-template-columns:repeat(3,1fr)}.grid-cols-4{grid-template-columns:repeat(4,1fr)}
177 .grid-cols-5{grid-template-columns:repeat(5,1fr)}.grid-cols-6{grid-template-columns:repeat(6,1fr)}.grid-cols-7{grid-template-columns:repeat(7,1fr)}.grid-cols-8{grid-template-columns:repeat(8,1fr)}
178 .grid-auto-fit{grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}
179 /* Position */
180 .relative{position:relative}.absolute{position:absolute}.fixed{position:fixed}.sticky{position:sticky}
181 .inset-0{inset:0}
182 /* Sizing */
183 .w-full{width:100%}.h-full{height:100%}
184 /* Overflow */
185 .overflow-hidden{overflow:hidden}.overflow-auto{overflow:auto}
186 /* Order (flex/grid children) */
187 .order-first{order:-1}.order-last{order:999}
188 @media(max-width:575px){.col-2,.col-3,.col-4,.col-5,.col-6{flex:0 0 100%}.sm\:flex-col{flex-direction:column}.sm\:flex-wrap{flex-wrap:wrap}.sm\:grid-cols-1{grid-template-columns:repeat(1,1fr)}}
189 @media(min-width:768px){.md\:col-2{flex:0 0 50%}.md\:col-3{flex:0 0 33.333%}.md\:col-4{flex:0 0 25%}.md\:col-5{flex:0 0 20%}.md\:col-6{flex:0 0 16.667%}.md\:grid-cols-2{grid-template-columns:repeat(2,1fr)}.md\:grid-cols-3{grid-template-columns:repeat(3,1fr)}.md\:grid-cols-4{grid-template-columns:repeat(4,1fr)}.md\:grid-cols-5{grid-template-columns:repeat(5,1fr)}.md\:grid-cols-6{grid-template-columns:repeat(6,1fr)}}
190 @media(min-width:1024px){.lg\:col-2{flex:0 0 50%}.lg\:col-3{flex:0 0 33.333%}.lg\:col-4{flex:0 0 25%}.lg\:col-5{flex:0 0 20%}.lg\:col-6{flex:0 0 16.667%}.lg\:grid-cols-3{grid-template-columns:repeat(3,1fr)}.lg\:grid-cols-4{grid-template-columns:repeat(4,1fr)}.lg\:grid-cols-5{grid-template-columns:repeat(5,1fr)}.lg\:grid-cols-6{grid-template-columns:repeat(6,1fr)}}
191 @media(min-width:1280px){.xl\:col-2{flex:0 0 50%}.xl\:col-3{flex:0 0 33.333%}.xl\:col-4{flex:0 0 25%}.xl\:col-5{flex:0 0 20%}.xl\:col-6{flex:0 0 16.667%}.xl\:grid-cols-3{grid-template-columns:repeat(3,1fr)}.xl\:grid-cols-4{grid-template-columns:repeat(4,1fr)}.xl\:grid-cols-5{grid-template-columns:repeat(5,1fr)}.xl\:grid-cols-6{grid-template-columns:repeat(6,1fr)}}
192 @media(min-width:1440px){.xxl\:col-2{flex:0 0 50%}.xxl\:col-3{flex:0 0 33.333%}.xxl\:col-4{flex:0 0 25%}.xxl\:col-5{flex:0 0 20%}.xxl\:col-6{flex:0 0 16.667%}.xxl\:grid-cols-3{grid-template-columns:repeat(3,1fr)}.xxl\:grid-cols-4{grid-template-columns:repeat(4,1fr)}.xxl\:grid-cols-5{grid-template-columns:repeat(5,1fr)}.xxl\:grid-cols-6{grid-template-columns:repeat(6,1fr)}}
193
194 } /* utilities */
195
Layer 7 — Elements Guarded Classless™ — semantic HTML styled automatically via :where(). — 680 lines ?
196 @layer exsa.elements {
197
198 /* ── Inline text ── */
199 :where(.exsa p) {
200 margin: var(--gap-md) 0;
201 padding: 0;
202 }
203 :where(.exsa a) {
204 color: var(--color-link);
205 font-weight: bold;
206 text-decoration: underline;
207 }
208 :where(.exsa a:visited) {
209 color: var(--color-secondary);
210 }
211 :where(.exsa a:hover) {
212 filter: brightness(var(--hover-brightness));
213 }
214 :where(.exsa a:active) {
215 filter: brightness(var(--active-brightness));
216 }
217 :where(.exsa small:not([class])) {
218 color: var(--color-text-secondary);
219 }
220 :where(.exsa mark:not([class])) {
221 padding: calc(0.1rem * var(--space-factor, 1));
222 }
223 :where(.exsa kbd:not([class])) {
224 background: var(--color-bg-secondary);
225 border: 1px solid var(--color-border);
226 border-radius: 3px;
227 box-shadow: 0 1px 0 var(--color-border);
228 font-family: var(--font-family-mono);
229 font-size: .85em;
230 padding: .15em .4em;
231 }
232 :where(.exsa abbr:not([class])) {
233 cursor: help;
234 text-decoration: underline dotted;
235 }
236 :where(.exsa del:not([class])) {
237 color: var(--color-danger);
238 }
239 :where(.exsa ins:not([class])) {
240 color: var(--color-success);
241 text-decoration: none;
242 }
243 :where(.exsa cite:not([class])) {
244 font-style: italic;
245 color: var(--color-text-secondary);
246 }
247 :where(.exsa sup:not([class])) {
248 background-color: var(--color-secondary);
249 border-radius: var(--border-radius);
250 color: var(--color-bg);
251 font-size: xx-small;
252 font-weight: bold;
253 margin: calc(0.2rem * var(--space-factor, 1));
254 padding: calc(0.2rem * var(--space-factor, 1)) calc(0.3rem * var(--space-factor, 1));
255 vertical-align: super;
256 position: relative;
257 }
258
259 /* ── Code ── */
260 :where(.exsa code) {
261 background-color: var(--color-accent);
262 border-radius: var(--border-radius);
263 color: var(--color-text);
264 display: inline-block;
265 font-family: var(--font-family-mono);
266 margin: 0 calc(0.1rem * var(--space-factor, 1));
267 padding: 0 var(--gap-sm);
268 }
269 :where(.exsa pre) {
270 font-family: var(--font-family-mono);
271 margin: var(--gap) 0;
272 max-width: var(--width-card-wide);
273 padding: var(--gap);
274 overflow: auto;
275 }
276 :where(.exsa pre code) {
277 display: block;
278 max-width: var(--width-card-wide);
279 padding: var(--gap-sm) var(--gap-xl);
280 white-space: pre-wrap;
281 }
282
283 /* ── Lists & dividers ──
284 Guarded at the LIST level — a class on ul/ol opts the whole subtree out,
285 so component lists (tree, sidebar, breadcrumbs, …) stop matching. */
286 :where(.exsa ol:not([class]) li),
287 :where(.exsa ul:not([class]) li) {
288 padding: 0;
289 padding-inline-start: 1.2em;
290 }
291 :where(.exsa hr) {
292 background: var(--color-text);
293 opacity: .12;
294 border: none;
295 height: 1px;
296 margin: var(--gap-2xl) 0;
297 width: 100%;
298 }
299 :where(.exsa details:not([class])) {
300 margin: var(--gap) 0;
301 }
302 :where(.exsa details:not([class]) summary) {
303 font-weight: bold;
304 cursor: pointer;
305 }
306 :where(.exsa details:not([class]) details) {
307 margin-inline-start: var(--gap-lg);
308 border-inline-start: 2px solid var(--color-border);
309 padding-inline-start: var(--gap);
310 }
311
312 /* ── Figure & caption ── */
313 :where(.exsa figure:not([class])) {
314 margin: var(--gap-xl) auto;
315 max-width: 100%;
316 text-align: center;
317 }
318 :where(.exsa figure:not([class]) img),
319 :where(.exsa figure:not([class]) svg),
320 :where(.exsa figure:not([class]) video) {
321 max-width: 100%;
322 height: auto;
323 display: block;
324 margin: 0 auto;
325 }
326 :where(.exsa figure:not([class]) figcaption) {
327 color: var(--color-text-secondary);
328 font-size: var(--font-size-sm);
329 margin-top: var(--gap-sm);
330 }
331
332 /* ── Structural containers ──
333 Guarded — a class on header/main/footer opts the WHOLE subtree out. */
334 :where(.exsa header:not([class])),
335 :where(.exsa main:not([class])),
336 :where(.exsa footer:not([class])) {
337 margin: 0 auto;
338 max-width: var(--width-content);
339 padding: var(--gap-2xl) var(--gap);
340 text-align: var(--justify-heading);
341 }
342 :where(.exsa main:not([class])) {
343 text-align: var(--justify-body);
344 }
345 :where(.exsa article header),
346 :where(.exsa div header),
347 :where(.exsa main header) {
348 padding-top: 0;
349 }
350 :where(.exsa header:not([class]) nav img) {
351 margin: var(--gap) 0;
352 }
353
354 /* ── Section (container queries on classless sections only) ── */
355 :where(.exsa section:not([class])) {
356 container-type: inline-size;
357 display: flex;
358 flex-wrap: wrap;
359 justify-content: var(--justify-heading);
360 }
361 :where(.exsa section header) {
362 padding-top: 0;
363 width: 100%;
364 }
365 :where(.exsa section img),
366 :where(.exsa article img) {
367 max-width: 100%;
368 }
369 :where(.exsa section pre) {
370 overflow: auto;
371 }
372
373 /* ── Card (<section> <aside>) ── */
374 :where(.exsa section:not([class]) aside) {
375 --card-padding: var(--gap-lg);
376 border: 1px solid var(--color-border);
377 border-radius: var(--border-radius);
378 box-shadow: var(--box-shadow) var(--color-shadow);
379 margin: var(--gap);
380 padding: var(--card-padding);
381 width: var(--width-card);
382 }
383 :where(.exsa section:not([class]) aside:hover) {
384 box-shadow: var(--box-shadow) var(--color-bg-secondary);
385 }
386 /* Full-bleed image at top of card — flush with edges */
387 :where(.exsa section:not([class]) aside > svg:first-child),
388 :where(.exsa section:not([class]) aside > img:first-child):not(:only-child) {
389 display: block;
390 width: calc(100% + var(--card-padding) * 2);
391 max-width: calc(100% + var(--card-padding) * 2);
392 height: auto;
393 margin: calc(var(--card-padding) * -1) calc(var(--card-padding) * -1) var(--gap) calc(var(--card-padding) * -1);
394 border-radius: var(--border-radius) var(--border-radius) 0 0;
395 }
396 @container (max-width: 320px) {
397 :where(.exsa section:not([class]) aside) {
398 --card-padding: var(--gap-md);
399 margin: var(--gap-sm);
400 padding: var(--card-padding);
401 width: auto;
402 }
403 }
404 @container (min-width: 500px) {
405 :where(.exsa section:not([class]) aside) {
406 padding: var(--gap-lg);
407 }
408 }
409
410 /* Classless horizontal card — :has() auto-detects <img>/<figure>/<video> */
411 :where(.exsa section:not([class]) aside:has(> img, > figure, > video)) {
412 display: grid;
413 grid-template-columns: auto 1fr;
414 align-items: stretch;
415 width: auto;
416 padding: 0;
417 overflow: hidden;
418 }
419 :where(.exsa section:not([class]) aside:has(> img, > figure, > video)) > img,
420 :where(.exsa section:not([class]) aside:has(> img, > figure, > video)) > figure,
421 :where(.exsa section:not([class]) aside:has(> img, > figure, > video)) > video {
422 width: clamp(150px, 30vw, 260px);
423 height: calc(100% + 2px);
424 margin: calc(-1px * var(--space-factor, 1)) 0;
425 padding: 0;
426 align-self: stretch;
427 border-radius: var(--border-radius) 0 0 var(--border-radius);
428 overflow: hidden;
429 }
430 :where(.exsa section:not([class]) aside:has(> img, > figure, > video)) > img,
431 :where(.exsa section:not([class]) aside:has(> img, > figure, > video)) > video {
432 object-fit: cover;
433 }
434 :where(.exsa section:not([class]) aside:has(> img, > figure)) > figure img,
435 :where(.exsa section:not([class]) aside:has(> img, > figure)) > figure svg,
436 :where(.exsa section:not([class]) aside:has(> img, > figure)) > figure video {
437 width: 100%;
438 height: 100%;
439 object-fit: cover;
440 display: block;
441 }
442 /* Content area — wrapping <div> */
443 :where(.exsa section:not([class]) aside:has(> img, > figure, > video)) > div {
444 padding: var(--gap-lg);
445 display: flex;
446 flex-direction: column;
447 justify-content: center;
448 }
449 /* Content card — aside with wrapper <div> but no media (shares row with siblings) */
450 :where(.exsa section:not([class]) aside:has(> div)):not(:has(> img)):not(:has(> figure)):not(:has(> video)) {
451 flex: 1;
452 min-width: 260px;
453 padding: var(--gap-lg);
454 }
455 /* No <div> wrapper fallback — text children wrap and stack */
456 :where(.exsa section:not([class]) aside:has(> img, > figure)):not(:has(> div)) {
457 grid-template-columns: auto 1fr;
458 overflow: hidden;
459 }
460 :where(.exsa section:not([class]) aside:has(> img, > figure)):not(:has(> div)) > :not(img):not(figure) {
461 margin: 0 var(--gap-lg);
462 }
463 :where(.exsa section:not([class]) aside:has(> img, > figure)):not(:has(> div)) > :is(h1,h2,h3,h4,h5,h6):first-of-type {
464 margin-top: var(--gap-lg);
465 }
466 :where(.exsa section:not([class]) aside:has(> img, > figure)):not(:has(> div)) > p:last-of-type {
467 margin-bottom: var(--gap-lg);
468 }
469
470 /* Collapse to vertical on narrow containers */
471 @container (max-width: 380px) {
472 :where(.exsa section:not([class]) aside:has(> img, > figure)) {
473 grid-template-columns: 1fr;
474 }
475 :where(.exsa section:not([class]) aside:has(> img, > figure)) > img,
476 :where(.exsa section:not([class]) aside:has(> img, > figure)) > figure {
477 width: 100%;
478 height: auto;
479 max-height: 220px;
480 }
481 }
482
483 /* ── Callout (<article> <aside>) ── */
484 :where(.exsa article:not([class]) > aside) {
485 background: var(--callout-bg, var(--color-secondary-accent));
486 border-inline-start: 4px solid var(--callout-accent, var(--color-secondary));
487 border-radius: var(--border-radius);
488 padding: var(--gap) var(--gap-lg);
489 margin: var(--gap) 0;
490 color: var(--color-text-secondary);
491 font-size: .9rem;
492 line-height: 1.6;
493 }
494 :where(.exsa article:not([class]) > aside p) {
495 margin: 0;
496 }
497 :where(.exsa article:not([class]) > aside p + p) {
498 margin-top: var(--gap-sm);
499 }
500 :where(.exsa article:not([class]) > aside strong) {
501 color: var(--callout-accent, var(--color-secondary));
502 }
503 :where(.exsa article:not([class]) > aside code) {
504 font-family: var(--font-family-mono);
505 font-size: .85em;
506 background: var(--color-bg-secondary);
507 padding: calc(1px * var(--space-factor, 1)) calc(6px * var(--space-factor, 1));
508 border-radius: 3px;
509 color: var(--color-text);
510 }
511 :where(.exsa article:not([class]) > aside a) {
512 color: var(--callout-accent, var(--color-secondary));
513 text-decoration: underline;
514 text-underline-offset: 2px;
515 }
516
517 /* ── Nav ── */
518 :where(.exsa nav:not([class])) {
519 align-items: center;
520 display: flex;
521 font-weight: bold;
522 justify-content: var(--justify-nav, space-between);
523 margin-bottom: var(--gap-xl);
524 }
525 :where(.exsa nav:not([class]) ul:not([class])) {
526 list-style: none;
527 padding: 0;
528 }
529 :where(.exsa nav:not([class]) ul:not([class]) li) {
530 display: inline-block;
531 margin: 0 var(--gap-sm);
532 text-align: start;
533 }
534 @media (max-width: 768px) {
535 :where(.exsa nav:not([class])) {
536 flex-wrap: wrap;
537 }
538 :where(.exsa nav:not([class]) ul:not([class]) li) {
539 width: calc(100% - 1em);
540 }
541 }
542
543 /* ── Table ── */
544 :where(.exsa table:not([class])) {
545 border: 1px solid var(--color-border);
546 border-radius: var(--border-radius);
547 border-spacing: 0;
548 border-collapse: separate;
549 /* A table must stay display:table for its columns to fill width:100% —
550 display:block here shrink-wraps the header/rows to content. Wide
551 tables wrap; wrap one in .table-wrap (the table component) to scroll. */
552 width: 100%;
553 max-width: 100%;
554 padding: 0;
555 font-size: .84rem; /* deliberate — sits between --font-size-sm (.78rem) and --font-size-base (.88rem) */
556 color: var(--color-text);
557 }
558 :where(.exsa table:not([class]) td),
559 :where(.exsa table:not([class]) th) {
560 padding: calc(0.4rem * var(--space-factor, 1)) calc(0.8rem * var(--space-factor, 1));
561 text-align: var(--justify-heading);
562 }
563 :where(.exsa table:not([class]) thead) {
564 background-color: var(--color-link);
565 border-collapse: collapse;
566 border-radius: var(--border-radius);
567 color: var(--color-bg);
568 margin: 0;
569 padding: 0;
570 }
571 :where(.exsa table:not([class]) thead tr:first-child th:first-child) {
572 border-start-start-radius: var(--border-radius);
573 }
574 :where(.exsa table:not([class]) thead tr:first-child th:last-child) {
575 border-start-end-radius: var(--border-radius);
576 }
577 :where(.exsa table:not([class]) thead th:first-child),
578 :where(.exsa table:not([class]) tr td:first-child) {
579 text-align: var(--justify-body);
580 }
581
582 /* ── Form ── */
583 :where(.exsa form:not([class])) {
584 border: 1px solid var(--color-border);
585 border-radius: var(--border-radius);
586 box-shadow: var(--box-shadow) var(--color-shadow);
587 display: block;
588 max-width: var(--width-card-wide);
589 min-width: min(var(--width-card), 100%);
590 padding: calc(1.5rem * var(--space-factor, 1));
591 text-align: var(--justify-body);
592 }
593 :where(.exsa form:not([class]) header) {
594 margin: calc(1.5rem * var(--space-factor, 1)) 0;
595 padding: calc(1.5rem * var(--space-factor, 1)) 0;
596 }
597 :where(.exsa form:not([class]) label) {
598 display: block;
599 font-weight: bold;
600 margin-bottom: calc(0.2rem * var(--space-factor, 1));
601 }
602 :where(.exsa form:not([class]) input),
603 :where(.exsa form:not([class]) select),
604 :where(.exsa form:not([class]) textarea) {
605 background-color: var(--color-bg);
606 color: var(--color-text);
607 border: 1px solid var(--color-border);
608 border-radius: var(--border-radius);
609 display: block;
610 font-size: inherit;
611 margin-bottom: calc(1rem * var(--space-factor, 1));
612 max-width: var(--width-card-wide);
613 padding: calc(0.4rem * var(--space-factor, 1)) calc(0.8rem * var(--space-factor, 1));
614 }
615 :where(.exsa form:not([class]) input[type=checkbox]),
616 :where(.exsa form:not([class]) input[type=radio]) {
617 display: inline-block;
618 }
619 :where(.exsa form:not([class]) input[type=checkbox] + label),
620 :where(.exsa form:not([class]) input[type=radio] + label) {
621 display: inline-block;
622 font-weight: normal;
623 position: relative;
624 top: 1px;
625 }
626 :where(.exsa form:not([class]) input[type=range]) {
627 padding: calc(0.4rem * var(--space-factor, 1)) 0;
628 }
629 :where(.exsa form:not([class]) input[type=text]),
630 :where(.exsa form:not([class]) input:not([type])),
631 :where(.exsa form:not([class]) input[type=password]),
632 :where(.exsa form:not([class]) input[type=email]),
633 :where(.exsa form:not([class]) input[type=search]),
634 :where(.exsa form:not([class]) textarea) {
635 width: 100%;
636 }
637 :where(.exsa form:not([class]) input[readonly]),
638 :where(.exsa form:not([class]) textarea[readonly]) {
639 background-color: var(--color-bg-secondary);
640 }
641 :where(.exsa form:not([class]) input[type=file]) {
642 border: 1px solid var(--color-border);
643 border-radius: var(--border-radius);
644 font-size: inherit;
645 max-width: var(--width-card-wide);
646 padding: calc(0.3rem * var(--space-factor, 1));
647 }
648
649 /* Fieldset */
650 :where(.exsa form:not([class]) fieldset) {
651 border: 1px solid var(--color-border);
652 border-radius: var(--border-radius);
653 margin: 0 0 calc(1rem * var(--space-factor, 1));
654 padding: var(--gap-lg) var(--gap) var(--gap);
655 }
656 :where(.exsa form:not([class]) fieldset legend) {
657 font-weight: bold;
658 padding: 0 var(--gap-sm);
659 }
660
661 /* Validation states */
662 :where(.exsa form:not([class]) input:invalid:not(:placeholder-shown)),
663 :where(.exsa form:not([class]) textarea:invalid:not(:placeholder-shown)) {
664 border-color: var(--color-danger);
665 }
666 :where(.exsa form:not([class]) input:valid:not(:placeholder-shown)),
667 :where(.exsa form:not([class]) textarea:valid:not(:placeholder-shown)) {
668 border-color: var(--color-success);
669 }
670
671 /* ── Progress & Meter ── */
672 :where(.exsa progress),
673 :where(.exsa meter) {
674 width: 100%;
675 height: .8rem;
676 border-radius: var(--border-radius);
677 overflow: hidden;
678 }
679 :where(.exsa progress)::-webkit-progress-bar,
680 :where(.exsa meter)::-webkit-meter-bar {
681 background: var(--color-bg-secondary);
682 border-radius: var(--border-radius);
683 }
684 :where(.exsa progress)::-webkit-progress-value {
685 background: var(--color-link);
686 border-radius: var(--border-radius);
687 }
688 :where(.exsa meter)::-webkit-meter-optimum-value {
689 background: var(--color-success);
690 border-radius: var(--border-radius);
691 }
692 :where(.exsa meter)::-webkit-meter-suboptimum-value {
693 background: var(--color-warning);
694 border-radius: var(--border-radius);
695 }
696 :where(.exsa meter)::-webkit-meter-even-less-good-value {
697 background: var(--color-danger);
698 border-radius: var(--border-radius);
699 }
700 :where(.exsa progress)::-moz-progress-bar {
701 background: var(--color-link);
702 border-radius: var(--border-radius);
703 }
704 :where(.exsa meter)::-moz-meter-bar {
705 background: var(--color-success);
706 border-radius: var(--border-radius);
707 }
708
709 /* ── Blockquote ── */
710 :where(.exsa blockquote:not([class])) {
711 border-inline-start: var(--blockquote-border, 4px solid var(--color-secondary));
712 display: block;
713 font-size: x-large;
714 line-height: var(--line-height);
715 margin: calc(1rem * var(--space-factor, 1)) 0;
716 padding: calc(1rem * var(--space-factor, 1)) calc(1.5rem * var(--space-factor, 1));
717 text-align: var(--justify-heading);
718 }
719 :where(.exsa blockquote:not([class]) footer) {
720 color: var(--color-text-secondary);
721 display: block;
722 font-size: small;
723 line-height: var(--line-height);
724 padding: calc(1.5rem * var(--space-factor, 1)) 0;
725 }
726
727 /* ── Dialog ── */
728 :where(.exsa dialog:not([class])) {
729 max-width: 90%;
730 max-height: 85dvh;
731 margin: auto;
732 padding-block: 0;
733 padding-inline: calc(20px * var(--space-factor, 1));
734 border: 1px solid var(--color-border);
735 border-radius: .5rem;
736 overscroll-behavior: contain;
737 scroll-behavior: smooth;
738 animation: dialog-enter .25s ease-in-out forwards;
739 }
740 :where(.exsa dialog:not([class]))::backdrop {
741 background-color: var(--color-overlay);
742 }
743 @keyframes dialog-enter {
744 from {
745 opacity: 0;
746 transform: translateY(10%);
747 }
748 to {
749 opacity: 1;
750 transform: translateY(0);
751 }
752 }
753 :where(.exsa dialog:not([class]) hr) {
754 margin-block: calc(1rem * var(--space-factor, 1));
755 }
756
757 /* ── Button ── */
758 :where(.exsa button:not([class])) {
759 display: inline-flex;
760 align-items: center;
761 justify-content: center;
762 gap: calc(6px * var(--space-factor, 1));
763 font-family: inherit;
764 font-size: var(--font-size-base);
765 font-weight: 600;
766 line-height: 1;
767 padding: calc(10px * var(--space-factor, 1)) calc(20px * var(--space-factor, 1));
768 border-radius: var(--border-radius);
769 border: 2px solid var(--color-border);
770 background: var(--color-bg);
771 color: var(--color-button-text-inverse);
772 cursor: pointer;
773 transition: background .15s, border-color .15s, transform .1s;
774 text-decoration: none;
775 white-space: nowrap;
776 user-select: none;
777 }
778 :where(.exsa button:not([class]):hover) {
779 background: var(--color-bg-secondary);
780 border-color: var(--color-text-secondary);
781 }
782 :where(.exsa button:not([class]):active) {
783 transform: scale(.97);
784 }
785 :where(.exsa button:not([class]):disabled) {
786 cursor: not-allowed;
787 opacity: .5;
788 transform: none;
789 }
790
791 /* ── Guarded Structural Detection ── */
792 /* Card grid — 3+ articles in a section */
793 :where(.exsa section:not([class]):has(> article:not([class]):nth-child(3))) {
794 display: grid;
795 grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
796 gap: var(--gap);
797 }
798 :where(.exsa section:not([class]):has(> article:not([class]):nth-child(3)) > article:not([class])) {
799 border: 1px solid var(--color-border);
800 border-radius: var(--border-radius);
801 box-shadow: var(--box-shadow) var(--color-shadow);
802 padding: var(--gap-lg);
803 margin: 0;
804 }
805
806 /* Gallery — 2+ figures in a section (exclude sections with articles/asides) */
807 :where(.exsa section:not([class]):has(> figure:nth-child(2))):not(:has(> aside)):not(:has(> article)) {
808 display: grid;
809 grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
810 gap: var(--gap-sm);
811 }
812 :where(.exsa section:not([class]):has(> figure:nth-child(2))):not(:has(> aside)):not(:has(> article)) > figure {
813 margin: 0;
814 }
815
816 /* Sidebar — aside as first child of main */
817 :where(.exsa main:not([class]):has(> aside:first-child)) {
818 display: grid;
819 grid-template-columns: clamp(220px, 25%, 300px) 1fr;
820 gap: var(--gap-lg);
821 align-items: start;
822 }
823 /* Keep the content track shrinkable — wide tables/pre can't blow out the 1fr column */
824 :where(.exsa main:not([class]):has(> aside:first-child)) > * { min-width: 0; }
825
826 /* Hero split — header inside <main> that contains an image/figure/svg/video */
827 :where(.exsa main header:not([class]):has(> img, > figure, > svg, > video)) {
828 display: grid;
829 grid-template-columns: 1fr 1fr;
830 gap: var(--gap-xl);
831 align-items: center;
832 max-width: none;
833 }
834
835 /* Small screens — stack the split hero and sidebar layouts into one column */
836 @media (max-width: 768px) {
837 :where(.exsa main header:not([class]):has(> img, > figure, > svg, > video)) {
838 grid-template-columns: 1fr;
839 gap: var(--gap);
840 }
841 :where(.exsa main:not([class]):has(> aside:first-child)) {
842 grid-template-columns: 1fr;
843 }
844 :where(.exsa main:not([class]):has(> aside:first-child)) > aside:first-child {
845 max-height: 45vh;
846 overflow-y: auto;
847 }
848 }
849
850 /* Full-width header — header outside <main> (direct child of body) */
851 :where(body.exsa > header:not([class])) {
852 max-width: none;
853 }
854
855 /* Nav flush with hero — when nav is directly followed by header */
856 :where(.exsa nav:has(+ header:not([class]))) {
857 margin-bottom: 0;
858 }
859
860 /* Stats row — 3+ sibling paragraphs in a section */
861 :where(.exsa section:not([class]):has(> p:nth-child(3))):not(:has(> aside)):not(:has(> article)) {
862 display: grid;
863 grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
864 gap: var(--gap);
865 text-align: center;
866 }
867 :where(.exsa section:not([class]):has(> p:nth-child(3))):not(:has(> aside)):not(:has(> article)) > p {
868 border: 1px solid var(--color-border);
869 border-radius: var(--border-radius);
870 padding: var(--gap-lg) var(--gap);
871 margin: 0;
872 }
873
874 } /* elements */
875
Layer 8 — Components 68 plug-and-play BEM components. One file each. Link only what you need. — 72 lines ?
/* Layer 7: exsa.components — 68 separate CSS files */
/* Link what you need. The Generator bundles only selected components. */
dist/components/accordion.css
dist/components/activity.css
dist/components/advanced-color-picker.css
dist/components/alert.css
dist/components/avatar.css
dist/components/back-top.css
dist/components/background.css
dist/components/badge.css
dist/components/breadcrumbs.css
dist/components/buttons.css
dist/components/calendar.css
dist/components/card.css
dist/components/carousel.css
dist/components/chart.css
dist/components/checkbox.css
dist/components/code-block.css
dist/components/color-picker.css
dist/components/command-palette.css
dist/components/consent-bar.css
dist/components/context-menu.css
dist/components/dashboard.css
dist/components/data-list.css
dist/components/date-picker.css
dist/components/donut.css
dist/components/drawer.css
dist/components/dropdown.css
dist/components/empty-state.css
dist/components/footer.css
dist/components/form-required.css
dist/components/form-validation.css
dist/components/hero.css
dist/components/icons.css
dist/components/input-group.css
dist/components/kanban.css
dist/components/kpi.css
dist/components/lightbox.css
dist/components/modal.css
dist/components/music.css
dist/components/notifications.css
dist/components/pagination.css
dist/components/password-input.css
dist/components/popover.css
dist/components/pricing-table.css
dist/components/product-card.css
dist/components/progress.css
dist/components/radio.css
dist/components/range-slider.css
dist/components/rating.css
dist/components/resizer.css
dist/components/select.css
dist/components/separator.css
dist/components/sidebar.css
dist/components/skeleton.css
dist/components/slideshow.css
dist/components/spinner.css
dist/components/stepper.css
dist/components/table.css
dist/components/tabs.css
dist/components/tags-input.css
dist/components/timeline.css
dist/components/toast.css
dist/components/toggle.css
dist/components/tooltip.css
dist/components/topbar.css
dist/components/transfer.css
dist/components/transitions.css
dist/components/tree.css
dist/components/upload.css
dist/components/video-gallery.css
Layer 9 — Layouts 2 layout files — general (the paper base) + dashboard (app-frame focus layer). — 3 lines ?
/* Layer 8: exsa.layouts — base shell + app-frame focus layer */
dist/layouts/dashboard.css
dist/layouts/general.css
Layer 10 — Overrides u-* escape-hatch utilities — always beat components and layouts; unlayered user CSS still wins. — 67 lines ?
876 @layer exsa.overrides {
877
878 /* ── Override utilities — force-win: u-* always beats components/layouts.
879 Unlayered user CSS still beats everything here. ── */
880
881 .u-text-start{text-align:start}
882 .u-text-center{text-align:center}
883 .u-text-end{text-align:end}
884 .u-block{display:block}
885 .u-flex{display:flex}
886 .u-inline-flex{display:inline-flex}
887 .u-none{display:none}
888 .u-w-full{width:100%}
889 .u-w-auto{width:auto}
890 .u-m-0{margin:0}
891 .u-p-0{padding:0}
892 .u-mx-auto{margin-inline:auto}
893 .u-gap-0{gap:0}
894 .u-gap-sm{gap:var(--gap-sm)}
895 .u-gap{gap:var(--gap)}
896 .u-gap-lg{gap:var(--gap-lg)}
897 .u-radius-0{border-radius:0}
898 .u-radius-full{border-radius:9999px}
899 /* Typography escape hatches (re-homed from exsa.utilities — the unprefixed
900 versions lost to elements/components, these always win) */
901 .u-text-xs{font-size:var(--font-size-xs)}.u-text-sm{font-size:var(--font-size-sm)}.u-text-base{font-size:var(--font-size-base)}.u-text-md{font-size:var(--font-size-md)}.u-text-lg{font-size:var(--font-size-lg)}.u-text-xl{font-size:var(--font-size-xl)}.u-text-2xl{font-size:var(--font-size-2xl)}
902 .u-fw-normal{font-weight:var(--font-weight-normal)}.u-fw-bold{font-weight:var(--font-weight-bold)}.u-fw-heavy{font-weight:var(--font-weight-heavy)}
903 .u-font-sans{font-family:var(--font-family)}.u-font-mono{font-family:var(--font-family-mono)}.u-font-heading{font-family:var(--font-family-heading)}
904 .u-italic{font-style:italic}.u-uppercase{text-transform:uppercase}.u-capitalize{text-transform:capitalize}.u-text-muted{color:var(--color-text-secondary)}
905 .u-text-left{text-align:left}.u-text-right{text-align:right}
906 .u-fluid-type{--font-size-base:clamp(.85rem,.8rem + .25vw,1rem);--font-size-xl:clamp(1.2rem,1.1rem + .5vw,1.5rem);--font-size-2xl:clamp(1.6rem,1.4rem + 1vw,2.2rem)}
907
908 /* Crash-prevention: reduced-motion users never get backdrop blur, whatever
909 skin is active (overrides beats skins layer 3). */
910 @media (prefers-reduced-motion: reduce) {
911 :root { --surface-backdrop: none; --surface-backdrop-strong: none; }
912 }
913
914 /* Legacy color-mix() fallback — engines with @layer but without color-mix()
915 (Chrome <111, Firefox <113, Safari <16.2). Core and skin surface tokens are
916 invalid at computed-value time there, so floating surfaces would render
917 transparent and shadows would vanish. Static values keep every surface flat
918 but opaque — the same "system colors, readable" philosophy as the
919 light-dark() fallback in exsa.tokens. Lives in overrides so it wins over
920 skin/layout color-mix() definitions. */
921 @supports not (background: color-mix(in srgb, red 50%, blue)) {
922 :root {
923 --surface-bg: var(--color-bg);
924 --surface-bg-elevated: var(--color-bg);
925 --surface-canvas: var(--color-bg);
926 --surface-border: 1px solid var(--color-border);
927 --surface-border-control: 1px solid var(--color-border);
928 --surface-shadow-sm: 2px 2px 10px;
929 --surface-shadow-md: 0 4px 16px rgb(0 0 0 / .08);
930 --surface-shadow-lg: 0 8px 32px rgb(0 0 0 / .12);
931 --surface-shadow-side: 8px 0 32px rgb(0 0 0 / .14);
932 --color-scrollbar: var(--color-text-secondary);
933 }
934 /* Component-level color-mix(): the transparent topbar is the one place a
935 bare color-mix() removes a structural surface. */
936 .topbar--transparent,
937 .exsa .topbar--transparent {
938 background: var(--surface-bg-elevated);
939 }
940 }
941
942 } /* overrides */