exsa.css — Framework Source
Complete 9-layer cascade. Six layers carry code in exsa.css; themes, fluid, components, and layouts are separate files. Click any section to expand.
894 lines
9 layers
82 tokens
32.4 KB raw — ~7.6 KB gzip
Cascade order — unlayered always beats layered:
| Your CSS | unlayered | strongest |
| Layer 9 — Overrides | u-* escape hatch | |
| Layer 8 — Layouts | separate files | |
| Layer 7 — Components | separate files | |
| Layer 6 — Elements | ||
| Layer 5 — Utilities | ||
| Layer 4 — Reset | ||
| Layer 3 — Fluid | optional file | |
| Layer 2 — Themes | separate files | |
| Layer 1 — Tokens | weakest | |
Layer 1 — Tokens
Layer 2 — Themes
Layer 3 — Fluid
Layer 4 — Reset
Layer 5 — Utilities
Layer 6 — Elements
Layer 7 — Components
Layer 8 — Layouts
Layer 9 — 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
82 design tokens. Colors, spacing, typography, shadows, breakpoints, behavioral factors, z-index scale. — 94 lines
?
| 7 | @layer exsa.tokens { |
| 8 | |
| 9 | /* 94 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; |
| 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; |
| 64 | /* page canvas — the skin-preferred page backdrop (page chrome consumes it) */ |
| 65 | --surface-canvas:var(--color-bg); |
| 66 | } |
| 67 | |
| 68 | /* Dark/light — one definition per token via light-dark(); forced mode flips color-scheme */ |
| 69 | |
| 70 | /* Forced dark — <html data-theme-mode="dark">, ignores OS preference */ |
| 71 | :root[data-theme-mode="dark"] { color-scheme: dark; } |
| 72 | |
| 73 | /* Forced light — <html data-theme-mode="light">, ignores OS preference */ |
| 74 | :root[data-theme-mode="light"] { color-scheme: light; } |
| 75 | |
| 76 | /* Legacy fallback */ |
| 77 | @supports not (color: light-dark(#000, #fff)) { |
| 78 | :root { |
| 79 | --color-bg: Canvas; |
| 80 | --color-bg-secondary: ButtonFace; |
| 81 | --color-text: CanvasText; |
| 82 | --color-text-secondary: GrayText; |
| 83 | --color-link: LinkText; |
| 84 | --color-secondary: VisitedText; |
| 85 | --color-secondary-accent: ButtonFace; |
| 86 | --color-accent: ButtonFace; |
| 87 | --color-border: GrayText; |
| 88 | --color-overlay: rgba(0, 0, 0, .55); |
| 89 | --color-button-text-inverse: CanvasText; |
| 90 | --color-success: #16a34a; |
| 91 | --color-success-hover: #15803d; |
| 92 | --color-danger: #dc2626; |
| 93 | --color-danger-hover: #b91c1c; |
| 94 | --color-warning: #d97706; |
| 95 | --color-warning-hover: #b45309; |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | } /* tokens */ |
| 100 |
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 — 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 4 — Reset
Box model, focus rings, a11y, RTL, scrollbars, skip links. — 49 lines
?
| 101 | @layer exsa.reset { |
| 102 | |
| 103 | *,*::before,*::after{box-sizing:border-box} |
| 104 | :focus-visible{outline:2px solid var(--color-link);outline-offset:2px} |
| 105 | :focus:not(:focus-visible){outline:none} |
| 106 | |
| 107 | /* Universal a11y utility — intentionally unscoped, works without class="exsa" */ |
| 108 | .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} |
| 109 | |
| 110 | /* Windows High Contrast Mode */ |
| 111 | @media(forced-colors:active){ |
| 112 | :focus-visible{outline:2px solid Highlight;outline-offset:2px} |
| 113 | .sr-only{clip:auto;clip-path:none;position:static;width:auto;height:auto} |
| 114 | .skip-link{background:Highlight;color:HighlightText} |
| 115 | } |
| 116 | /* High contrast preference — boost borders and text weight */ |
| 117 | @media(prefers-contrast:high){ |
| 118 | body{text-shadow:none} |
| 119 | :where(.exsa a){text-decoration-thickness:2px} |
| 120 | *,*::before,*::after{border-color:currentColor} |
| 121 | } |
| 122 | @media(prefers-contrast:low){ |
| 123 | /* Low contrast = softer borders, not thinner text */ |
| 124 | *,*::before,*::after{border-color:color-mix(in srgb,currentColor 40%,transparent)} |
| 125 | } |
| 126 | *{scrollbar-width:thin;scrollbar-color:var(--color-scrollbar) transparent} |
| 127 | *::-webkit-scrollbar{width:5px;height:5px} |
| 128 | *::-webkit-scrollbar-track{background:transparent} |
| 129 | *::-webkit-scrollbar-thumb{background-color:var(--color-scrollbar);border-radius:10px} |
| 130 | html{scroll-behavior:smooth} |
| 131 | @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}} |
| 132 | @media(prefers-reduced-data:reduce){img,video{min-height:0;height:auto}} |
| 133 | [dir=rtl]{direction:rtl;unicode-bidi:isolate} |
| 134 | body{background:var(--color-bg);color:var(--color-text);font-family:var(--font-family);line-height:var(--line-height);margin:0;padding:0} |
| 135 | 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)} |
| 136 | img,svg,video{max-width:100%;height:auto} |
| 137 | figure{margin:0;padding:0} |
| 138 | [hidden]{display:none!important} |
| 139 | |
| 140 | /* Universal a11y utilities — intentionally unscoped, work without class="exsa" */ |
| 141 | .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} |
| 142 | .skip-link:focus{top:0} |
| 143 | [dir=rtl] .skip-link{border-radius:0 0 var(--border-radius) 0} |
| 144 | /* Intentionally unscoped — anchor-target flash highlight works globally */ |
| 145 | :target{scroll-margin-top:var(--scroll-margin-top);animation:exsa-target-flash 1.2s ease-out} |
| 146 | @keyframes exsa-target-flash{0%{background:var(--color-accent)}100%{background:transparent}} |
| 147 | |
| 148 | } /* reset */ |
| 149 |
Layer 5 — Utilities
89 layout & typography utilities. Flex, grid, gaps, containers, responsive columns. — 44 lines
?
| 150 | @layer exsa.utilities { |
| 151 | |
| 152 | /* Layout only — unprefixed utilities lose to elements/components/layouts; |
| 153 | use u-* (exsa.overrides) to force-win. */ |
| 154 | .container{max-width:var(--width-content);margin:0 auto;padding:0 var(--gap)} |
| 155 | .container-sm{max-width:var(--width-card-wide);margin:0 auto;padding:0 var(--gap-sm)} |
| 156 | .container-full{width:100%} |
| 157 | .flex{display:flex}.flex-inline{display:inline-flex} |
| 158 | .flex-col{flex-direction:column}.flex-col-rev{flex-direction:column-reverse}.flex-row-rev{flex-direction:row-reverse} |
| 159 | .flex-wrap{flex-wrap:wrap}.flex-nowrap{flex-wrap:nowrap} |
| 160 | .flex-1{flex:1}.flex-auto{flex:0 1 auto}.flex-none{flex:none} |
| 161 | .justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center} |
| 162 | .justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.justify-evenly{justify-content:space-evenly} |
| 163 | .items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center} |
| 164 | .items-baseline{align-items:baseline}.items-stretch{align-items:stretch} |
| 165 | .self-start{align-self:flex-start}.self-end{align-self:flex-end}.self-center{align-self:center}.self-stretch{align-self:stretch} |
| 166 | .content-start{align-content:flex-start}.content-end{align-content:flex-end}.content-center{align-content:center} |
| 167 | .content-between{align-content:space-between}.content-around{align-content:space-around} |
| 168 | .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)} |
| 169 | .gap-lg{gap:var(--gap-lg)}.gap-xl{gap:var(--gap-xl)}.gap-2xl{gap:var(--gap-2xl)} |
| 170 | .col-1{flex:0 0 100%}.col-2{flex:0 0 50%}.col-3{flex:0 0 33.333%} |
| 171 | .col-4{flex:0 0 25%}.col-5{flex:0 0 20%}.col-6{flex:0 0 16.667%} |
| 172 | .grid{display:grid} |
| 173 | .grid-cols-1{grid-template-columns:repeat(1,1fr)}.grid-cols-2{grid-template-columns:repeat(2,1fr)} |
| 174 | .grid-cols-3{grid-template-columns:repeat(3,1fr)}.grid-cols-4{grid-template-columns:repeat(4,1fr)} |
| 175 | .grid-cols-5{grid-template-columns:repeat(5,1fr)}.grid-cols-6{grid-template-columns:repeat(6,1fr)} |
| 176 | .grid-auto-fit{grid-template-columns:repeat(auto-fit,minmax(200px,1fr))} |
| 177 | /* Position */ |
| 178 | .relative{position:relative}.absolute{position:absolute}.fixed{position:fixed}.sticky{position:sticky} |
| 179 | .inset-0{inset:0} |
| 180 | /* Sizing */ |
| 181 | .w-full{width:100%}.h-full{height:100%} |
| 182 | /* Overflow */ |
| 183 | .overflow-hidden{overflow:hidden}.overflow-auto{overflow:auto} |
| 184 | /* Order (flex/grid children) */ |
| 185 | .order-first{order:-1}.order-last{order:999} |
| 186 | @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)}} |
| 187 | @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)}} |
| 188 | @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)}} |
| 189 | @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)}} |
| 190 | @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)}} |
| 191 | |
| 192 | } /* utilities */ |
| 193 |
Layer 6 — Elements
Guarded Classless™ — semantic HTML styled automatically via :where(). — 662 lines
?
| 194 | @layer exsa.elements { |
| 195 | |
| 196 | /* ── Inline text ── */ |
| 197 | :where(.exsa p) { |
| 198 | margin: var(--gap-md) 0; |
| 199 | padding: 0; |
| 200 | } |
| 201 | :where(.exsa a) { |
| 202 | color: var(--color-link); |
| 203 | font-weight: bold; |
| 204 | text-decoration: underline; |
| 205 | } |
| 206 | :where(.exsa a:visited) { |
| 207 | color: var(--color-secondary); |
| 208 | } |
| 209 | :where(.exsa a:hover) { |
| 210 | filter: brightness(var(--hover-brightness)); |
| 211 | } |
| 212 | :where(.exsa a:active) { |
| 213 | filter: brightness(var(--active-brightness)); |
| 214 | } |
| 215 | :where(.exsa small:not([class])) { |
| 216 | color: var(--color-text-secondary); |
| 217 | } |
| 218 | :where(.exsa mark:not([class])) { |
| 219 | padding: calc(0.1rem * var(--space-factor, 1)); |
| 220 | } |
| 221 | :where(.exsa kbd:not([class])) { |
| 222 | background: var(--color-bg-secondary); |
| 223 | border: 1px solid var(--color-border); |
| 224 | border-radius: 3px; |
| 225 | box-shadow: 0 1px 0 var(--color-border); |
| 226 | font-family: var(--font-family-mono); |
| 227 | font-size: .85em; |
| 228 | padding: .15em .4em; |
| 229 | } |
| 230 | :where(.exsa abbr:not([class])) { |
| 231 | cursor: help; |
| 232 | text-decoration: underline dotted; |
| 233 | } |
| 234 | :where(.exsa del:not([class])) { |
| 235 | color: var(--color-danger); |
| 236 | } |
| 237 | :where(.exsa ins:not([class])) { |
| 238 | color: var(--color-success); |
| 239 | text-decoration: none; |
| 240 | } |
| 241 | :where(.exsa cite:not([class])) { |
| 242 | font-style: italic; |
| 243 | color: var(--color-text-secondary); |
| 244 | } |
| 245 | :where(.exsa sup:not([class])) { |
| 246 | background-color: var(--color-secondary); |
| 247 | border-radius: var(--border-radius); |
| 248 | color: var(--color-bg); |
| 249 | font-size: xx-small; |
| 250 | font-weight: bold; |
| 251 | margin: calc(0.2rem * var(--space-factor, 1)); |
| 252 | padding: calc(0.2rem * var(--space-factor, 1)) calc(0.3rem * var(--space-factor, 1)); |
| 253 | vertical-align: super; |
| 254 | position: relative; |
| 255 | } |
| 256 | |
| 257 | /* ── Code ── */ |
| 258 | :where(.exsa code) { |
| 259 | background-color: var(--color-accent); |
| 260 | border-radius: var(--border-radius); |
| 261 | color: var(--color-text); |
| 262 | display: inline-block; |
| 263 | font-family: var(--font-family-mono); |
| 264 | margin: 0 calc(0.1rem * var(--space-factor, 1)); |
| 265 | padding: 0 var(--gap-sm); |
| 266 | } |
| 267 | :where(.exsa pre) { |
| 268 | font-family: var(--font-family-mono); |
| 269 | margin: var(--gap) 0; |
| 270 | max-width: var(--width-card-wide); |
| 271 | padding: var(--gap); |
| 272 | overflow: auto; |
| 273 | } |
| 274 | :where(.exsa pre code) { |
| 275 | display: block; |
| 276 | max-width: var(--width-card-wide); |
| 277 | padding: var(--gap-sm) var(--gap-xl); |
| 278 | white-space: pre-wrap; |
| 279 | } |
| 280 | |
| 281 | /* ── Lists & dividers ── |
| 282 | Guarded at the LIST level — a class on ul/ol opts the whole subtree out, |
| 283 | so component lists (tree, sidebar, breadcrumbs, …) stop matching. */ |
| 284 | :where(.exsa ol:not([class]) li), |
| 285 | :where(.exsa ul:not([class]) li) { |
| 286 | padding: 0; |
| 287 | padding-inline-start: 1.2em; |
| 288 | } |
| 289 | :where(.exsa hr) { |
| 290 | background: var(--color-text); |
| 291 | opacity: .12; |
| 292 | border: none; |
| 293 | height: 1px; |
| 294 | margin: var(--gap-2xl) 0; |
| 295 | width: 100%; |
| 296 | } |
| 297 | :where(.exsa details:not([class])) { |
| 298 | margin: var(--gap) 0; |
| 299 | } |
| 300 | :where(.exsa details:not([class]) summary) { |
| 301 | font-weight: bold; |
| 302 | cursor: pointer; |
| 303 | } |
| 304 | :where(.exsa details:not([class]) details) { |
| 305 | margin-inline-start: var(--gap-lg); |
| 306 | border-inline-start: 2px solid var(--color-border); |
| 307 | padding-inline-start: var(--gap); |
| 308 | } |
| 309 | |
| 310 | /* ── Figure & caption ── */ |
| 311 | :where(.exsa figure:not([class])) { |
| 312 | margin: var(--gap-xl) auto; |
| 313 | max-width: 100%; |
| 314 | text-align: center; |
| 315 | } |
| 316 | :where(.exsa figure:not([class]) img), |
| 317 | :where(.exsa figure:not([class]) svg), |
| 318 | :where(.exsa figure:not([class]) video) { |
| 319 | max-width: 100%; |
| 320 | height: auto; |
| 321 | display: block; |
| 322 | margin: 0 auto; |
| 323 | } |
| 324 | :where(.exsa figure:not([class]) figcaption) { |
| 325 | color: var(--color-text-secondary); |
| 326 | font-size: var(--font-size-sm); |
| 327 | margin-top: var(--gap-sm); |
| 328 | } |
| 329 | |
| 330 | /* ── Structural containers ── |
| 331 | Guarded — a class on header/main/footer opts the WHOLE subtree out. */ |
| 332 | :where(.exsa header:not([class])), |
| 333 | :where(.exsa main:not([class])), |
| 334 | :where(.exsa footer:not([class])) { |
| 335 | margin: 0 auto; |
| 336 | max-width: var(--width-content); |
| 337 | padding: var(--gap-2xl) var(--gap); |
| 338 | text-align: var(--justify-heading); |
| 339 | } |
| 340 | :where(.exsa main:not([class])) { |
| 341 | text-align: var(--justify-body); |
| 342 | } |
| 343 | :where(.exsa article header), |
| 344 | :where(.exsa div header), |
| 345 | :where(.exsa main header) { |
| 346 | padding-top: 0; |
| 347 | } |
| 348 | :where(.exsa header:not([class]) nav img) { |
| 349 | margin: var(--gap) 0; |
| 350 | } |
| 351 | |
| 352 | /* ── Section (container queries on classless sections only) ── */ |
| 353 | :where(.exsa section:not([class])) { |
| 354 | container-type: inline-size; |
| 355 | display: flex; |
| 356 | flex-wrap: wrap; |
| 357 | justify-content: var(--justify-heading); |
| 358 | } |
| 359 | :where(.exsa section header) { |
| 360 | padding-top: 0; |
| 361 | width: 100%; |
| 362 | } |
| 363 | :where(.exsa section img), |
| 364 | :where(.exsa article img) { |
| 365 | max-width: 100%; |
| 366 | } |
| 367 | :where(.exsa section pre) { |
| 368 | overflow: auto; |
| 369 | } |
| 370 | |
| 371 | /* ── Card (<section> <aside>) ── */ |
| 372 | :where(.exsa section:not([class]) aside) { |
| 373 | --card-padding: var(--gap-lg); |
| 374 | border: 1px solid var(--color-border); |
| 375 | border-radius: var(--border-radius); |
| 376 | box-shadow: var(--box-shadow) var(--color-shadow); |
| 377 | margin: var(--gap); |
| 378 | padding: var(--card-padding); |
| 379 | width: var(--width-card); |
| 380 | } |
| 381 | :where(.exsa section:not([class]) aside:hover) { |
| 382 | box-shadow: var(--box-shadow) var(--color-bg-secondary); |
| 383 | } |
| 384 | /* Full-bleed image at top of card — flush with edges */ |
| 385 | :where(.exsa section:not([class]) aside > svg:first-child), |
| 386 | :where(.exsa section:not([class]) aside > img:first-child):not(:only-child) { |
| 387 | display: block; |
| 388 | width: calc(100% + var(--card-padding) * 2); |
| 389 | max-width: calc(100% + var(--card-padding) * 2); |
| 390 | height: auto; |
| 391 | margin: calc(var(--card-padding) * -1) calc(var(--card-padding) * -1) var(--gap) calc(var(--card-padding) * -1); |
| 392 | border-radius: var(--border-radius) var(--border-radius) 0 0; |
| 393 | } |
| 394 | @container (max-width: 320px) { |
| 395 | :where(.exsa section:not([class]) aside) { |
| 396 | --card-padding: var(--gap-md); |
| 397 | margin: var(--gap-sm); |
| 398 | padding: var(--card-padding); |
| 399 | width: auto; |
| 400 | } |
| 401 | } |
| 402 | @container (min-width: 500px) { |
| 403 | :where(.exsa section:not([class]) aside) { |
| 404 | padding: var(--gap-lg); |
| 405 | } |
| 406 | } |
| 407 | |
| 408 | /* Classless horizontal card — :has() auto-detects <img>/<figure>/<video> */ |
| 409 | :where(.exsa section:not([class]) aside:has(> img, > figure, > video)) { |
| 410 | display: grid; |
| 411 | grid-template-columns: auto 1fr; |
| 412 | align-items: stretch; |
| 413 | width: auto; |
| 414 | padding: 0; |
| 415 | overflow: hidden; |
| 416 | } |
| 417 | :where(.exsa section:not([class]) aside:has(> img, > figure, > video)) > img, |
| 418 | :where(.exsa section:not([class]) aside:has(> img, > figure, > video)) > figure, |
| 419 | :where(.exsa section:not([class]) aside:has(> img, > figure, > video)) > video { |
| 420 | width: clamp(150px, 30vw, 260px); |
| 421 | height: calc(100% + 2px); |
| 422 | margin: calc(-1px * var(--space-factor, 1)) 0; |
| 423 | padding: 0; |
| 424 | align-self: stretch; |
| 425 | border-radius: var(--border-radius) 0 0 var(--border-radius); |
| 426 | overflow: hidden; |
| 427 | } |
| 428 | :where(.exsa section:not([class]) aside:has(> img, > figure, > video)) > img, |
| 429 | :where(.exsa section:not([class]) aside:has(> img, > figure, > video)) > video { |
| 430 | object-fit: cover; |
| 431 | } |
| 432 | :where(.exsa section:not([class]) aside:has(> img, > figure)) > figure img, |
| 433 | :where(.exsa section:not([class]) aside:has(> img, > figure)) > figure svg, |
| 434 | :where(.exsa section:not([class]) aside:has(> img, > figure)) > figure video { |
| 435 | width: 100%; |
| 436 | height: 100%; |
| 437 | object-fit: cover; |
| 438 | display: block; |
| 439 | } |
| 440 | /* Content area — wrapping <div> */ |
| 441 | :where(.exsa section:not([class]) aside:has(> img, > figure, > video)) > div { |
| 442 | padding: var(--gap-lg); |
| 443 | display: flex; |
| 444 | flex-direction: column; |
| 445 | justify-content: center; |
| 446 | } |
| 447 | /* Content card — aside with wrapper <div> but no media (shares row with siblings) */ |
| 448 | :where(.exsa section:not([class]) aside:has(> div)):not(:has(> img)):not(:has(> figure)):not(:has(> video)) { |
| 449 | flex: 1; |
| 450 | min-width: 260px; |
| 451 | padding: var(--gap-lg); |
| 452 | } |
| 453 | /* No <div> wrapper fallback — text children wrap and stack */ |
| 454 | :where(.exsa section:not([class]) aside:has(> img, > figure)):not(:has(> div)) { |
| 455 | grid-template-columns: auto 1fr; |
| 456 | overflow: hidden; |
| 457 | } |
| 458 | :where(.exsa section:not([class]) aside:has(> img, > figure)):not(:has(> div)) > :not(img):not(figure) { |
| 459 | margin: 0 var(--gap-lg); |
| 460 | } |
| 461 | :where(.exsa section:not([class]) aside:has(> img, > figure)):not(:has(> div)) > :is(h1,h2,h3,h4,h5,h6):first-of-type { |
| 462 | margin-top: var(--gap-lg); |
| 463 | } |
| 464 | :where(.exsa section:not([class]) aside:has(> img, > figure)):not(:has(> div)) > p:last-of-type { |
| 465 | margin-bottom: var(--gap-lg); |
| 466 | } |
| 467 | |
| 468 | /* Collapse to vertical on narrow containers */ |
| 469 | @container (max-width: 380px) { |
| 470 | :where(.exsa section:not([class]) aside:has(> img, > figure)) { |
| 471 | grid-template-columns: 1fr; |
| 472 | } |
| 473 | :where(.exsa section:not([class]) aside:has(> img, > figure)) > img, |
| 474 | :where(.exsa section:not([class]) aside:has(> img, > figure)) > figure { |
| 475 | width: 100%; |
| 476 | height: auto; |
| 477 | max-height: 220px; |
| 478 | } |
| 479 | } |
| 480 | |
| 481 | /* ── Callout (<article> <aside>) ── */ |
| 482 | :where(.exsa article:not([class]) > aside) { |
| 483 | background: var(--callout-bg, var(--color-secondary-accent)); |
| 484 | border-inline-start: 4px solid var(--callout-accent, var(--color-secondary)); |
| 485 | border-radius: var(--border-radius); |
| 486 | padding: var(--gap) var(--gap-lg); |
| 487 | margin: var(--gap) 0; |
| 488 | color: var(--color-text-secondary); |
| 489 | font-size: .9rem; |
| 490 | line-height: 1.6; |
| 491 | } |
| 492 | :where(.exsa article:not([class]) > aside p) { |
| 493 | margin: 0; |
| 494 | } |
| 495 | :where(.exsa article:not([class]) > aside p + p) { |
| 496 | margin-top: var(--gap-sm); |
| 497 | } |
| 498 | :where(.exsa article:not([class]) > aside strong) { |
| 499 | color: var(--callout-accent, var(--color-secondary)); |
| 500 | } |
| 501 | :where(.exsa article:not([class]) > aside code) { |
| 502 | font-family: var(--font-family-mono); |
| 503 | font-size: .85em; |
| 504 | background: var(--color-bg-secondary); |
| 505 | padding: calc(1px * var(--space-factor, 1)) calc(6px * var(--space-factor, 1)); |
| 506 | border-radius: 3px; |
| 507 | color: var(--color-text); |
| 508 | } |
| 509 | :where(.exsa article:not([class]) > aside a) { |
| 510 | color: var(--callout-accent, var(--color-secondary)); |
| 511 | text-decoration: underline; |
| 512 | text-underline-offset: 2px; |
| 513 | } |
| 514 | |
| 515 | /* ── Nav ── */ |
| 516 | :where(.exsa nav:not([class])) { |
| 517 | align-items: center; |
| 518 | display: flex; |
| 519 | font-weight: bold; |
| 520 | justify-content: var(--justify-nav, space-between); |
| 521 | margin-bottom: var(--gap-xl); |
| 522 | } |
| 523 | :where(.exsa nav:not([class]) ul:not([class])) { |
| 524 | list-style: none; |
| 525 | padding: 0; |
| 526 | } |
| 527 | :where(.exsa nav:not([class]) ul:not([class]) li) { |
| 528 | display: inline-block; |
| 529 | margin: 0 var(--gap-sm); |
| 530 | text-align: start; |
| 531 | } |
| 532 | @media (max-width: 768px) { |
| 533 | :where(.exsa nav:not([class])) { |
| 534 | flex-wrap: wrap; |
| 535 | } |
| 536 | :where(.exsa nav:not([class]) ul:not([class]) li) { |
| 537 | width: calc(100% - 1em); |
| 538 | } |
| 539 | } |
| 540 | |
| 541 | /* ── Table ── */ |
| 542 | :where(.exsa table:not([class])) { |
| 543 | border: 1px solid var(--color-border); |
| 544 | border-radius: var(--border-radius); |
| 545 | border-spacing: 0; |
| 546 | border-collapse: separate; |
| 547 | display: table; |
| 548 | width: 100%; |
| 549 | max-width: 100%; |
| 550 | padding: 0; |
| 551 | font-size: .84rem; /* deliberate — sits between --font-size-sm (.78rem) and --font-size-base (.88rem) */ |
| 552 | color: var(--color-text); |
| 553 | } |
| 554 | :where(.exsa table:not([class]) td), |
| 555 | :where(.exsa table:not([class]) th) { |
| 556 | padding: calc(0.4rem * var(--space-factor, 1)) calc(0.8rem * var(--space-factor, 1)); |
| 557 | text-align: var(--justify-heading); |
| 558 | } |
| 559 | :where(.exsa table:not([class]) thead) { |
| 560 | background-color: var(--color-link); |
| 561 | border-collapse: collapse; |
| 562 | border-radius: var(--border-radius); |
| 563 | color: var(--color-bg); |
| 564 | margin: 0; |
| 565 | padding: 0; |
| 566 | } |
| 567 | :where(.exsa table:not([class]) thead tr:first-child th:first-child) { |
| 568 | border-start-start-radius: var(--border-radius); |
| 569 | } |
| 570 | :where(.exsa table:not([class]) thead tr:first-child th:last-child) { |
| 571 | border-start-end-radius: var(--border-radius); |
| 572 | } |
| 573 | :where(.exsa table:not([class]) thead th:first-child), |
| 574 | :where(.exsa table:not([class]) tr td:first-child) { |
| 575 | text-align: var(--justify-body); |
| 576 | } |
| 577 | |
| 578 | /* ── Form ── */ |
| 579 | :where(.exsa form:not([class])) { |
| 580 | border: 1px solid var(--color-border); |
| 581 | border-radius: var(--border-radius); |
| 582 | box-shadow: var(--box-shadow) var(--color-shadow); |
| 583 | display: block; |
| 584 | max-width: var(--width-card-wide); |
| 585 | min-width: var(--width-card); |
| 586 | padding: calc(1.5rem * var(--space-factor, 1)); |
| 587 | text-align: var(--justify-body); |
| 588 | } |
| 589 | :where(.exsa form:not([class]) header) { |
| 590 | margin: calc(1.5rem * var(--space-factor, 1)) 0; |
| 591 | padding: calc(1.5rem * var(--space-factor, 1)) 0; |
| 592 | } |
| 593 | :where(.exsa form:not([class]) label) { |
| 594 | display: block; |
| 595 | font-weight: bold; |
| 596 | margin-bottom: calc(0.2rem * var(--space-factor, 1)); |
| 597 | } |
| 598 | :where(.exsa form:not([class]) input), |
| 599 | :where(.exsa form:not([class]) select), |
| 600 | :where(.exsa form:not([class]) textarea) { |
| 601 | background-color: var(--color-bg); |
| 602 | color: var(--color-text); |
| 603 | border: 1px solid var(--color-border); |
| 604 | border-radius: var(--border-radius); |
| 605 | display: block; |
| 606 | font-size: inherit; |
| 607 | margin-bottom: calc(1rem * var(--space-factor, 1)); |
| 608 | max-width: var(--width-card-wide); |
| 609 | padding: calc(0.4rem * var(--space-factor, 1)) calc(0.8rem * var(--space-factor, 1)); |
| 610 | } |
| 611 | :where(.exsa form:not([class]) input[type=checkbox]), |
| 612 | :where(.exsa form:not([class]) input[type=radio]) { |
| 613 | display: inline-block; |
| 614 | } |
| 615 | :where(.exsa form:not([class]) input[type=checkbox] + label), |
| 616 | :where(.exsa form:not([class]) input[type=radio] + label) { |
| 617 | display: inline-block; |
| 618 | font-weight: normal; |
| 619 | position: relative; |
| 620 | top: 1px; |
| 621 | } |
| 622 | :where(.exsa form:not([class]) input[type=range]) { |
| 623 | padding: calc(0.4rem * var(--space-factor, 1)) 0; |
| 624 | } |
| 625 | :where(.exsa form:not([class]) input[type=text]), |
| 626 | :where(.exsa form:not([class]) input:not([type])), |
| 627 | :where(.exsa form:not([class]) input[type=password]), |
| 628 | :where(.exsa form:not([class]) input[type=email]), |
| 629 | :where(.exsa form:not([class]) input[type=search]), |
| 630 | :where(.exsa form:not([class]) textarea) { |
| 631 | width: 100%; |
| 632 | } |
| 633 | :where(.exsa form:not([class]) input[readonly]), |
| 634 | :where(.exsa form:not([class]) textarea[readonly]) { |
| 635 | background-color: var(--color-bg-secondary); |
| 636 | } |
| 637 | :where(.exsa form:not([class]) input[type=file]) { |
| 638 | border: 1px solid var(--color-border); |
| 639 | border-radius: var(--border-radius); |
| 640 | font-size: inherit; |
| 641 | max-width: var(--width-card-wide); |
| 642 | padding: calc(0.3rem * var(--space-factor, 1)); |
| 643 | } |
| 644 | |
| 645 | /* Fieldset */ |
| 646 | :where(.exsa form:not([class]) fieldset) { |
| 647 | border: 1px solid var(--color-border); |
| 648 | border-radius: var(--border-radius); |
| 649 | margin: 0 0 calc(1rem * var(--space-factor, 1)); |
| 650 | padding: var(--gap-lg) var(--gap) var(--gap); |
| 651 | } |
| 652 | :where(.exsa form:not([class]) fieldset legend) { |
| 653 | font-weight: bold; |
| 654 | padding: 0 var(--gap-sm); |
| 655 | } |
| 656 | |
| 657 | /* Validation states */ |
| 658 | :where(.exsa form:not([class]) input:invalid:not(:placeholder-shown)), |
| 659 | :where(.exsa form:not([class]) textarea:invalid:not(:placeholder-shown)) { |
| 660 | border-color: var(--color-danger); |
| 661 | } |
| 662 | :where(.exsa form:not([class]) input:valid:not(:placeholder-shown)), |
| 663 | :where(.exsa form:not([class]) textarea:valid:not(:placeholder-shown)) { |
| 664 | border-color: var(--color-success); |
| 665 | } |
| 666 | |
| 667 | /* ── Progress & Meter ── */ |
| 668 | :where(.exsa progress), |
| 669 | :where(.exsa meter) { |
| 670 | width: 100%; |
| 671 | height: .8rem; |
| 672 | border-radius: var(--border-radius); |
| 673 | overflow: hidden; |
| 674 | } |
| 675 | :where(.exsa progress)::-webkit-progress-bar, |
| 676 | :where(.exsa meter)::-webkit-meter-bar { |
| 677 | background: var(--color-bg-secondary); |
| 678 | border-radius: var(--border-radius); |
| 679 | } |
| 680 | :where(.exsa progress)::-webkit-progress-value { |
| 681 | background: var(--color-link); |
| 682 | border-radius: var(--border-radius); |
| 683 | } |
| 684 | :where(.exsa meter)::-webkit-meter-optimum-value { |
| 685 | background: var(--color-success); |
| 686 | border-radius: var(--border-radius); |
| 687 | } |
| 688 | :where(.exsa meter)::-webkit-meter-suboptimum-value { |
| 689 | background: var(--color-warning); |
| 690 | border-radius: var(--border-radius); |
| 691 | } |
| 692 | :where(.exsa meter)::-webkit-meter-even-less-good-value { |
| 693 | background: var(--color-danger); |
| 694 | border-radius: var(--border-radius); |
| 695 | } |
| 696 | :where(.exsa progress)::-moz-progress-bar { |
| 697 | background: var(--color-link); |
| 698 | border-radius: var(--border-radius); |
| 699 | } |
| 700 | :where(.exsa meter)::-moz-meter-bar { |
| 701 | background: var(--color-success); |
| 702 | border-radius: var(--border-radius); |
| 703 | } |
| 704 | |
| 705 | /* ── Blockquote ── */ |
| 706 | :where(.exsa blockquote:not([class])) { |
| 707 | border-inline-start: var(--blockquote-border, 4px solid var(--color-secondary)); |
| 708 | display: block; |
| 709 | font-size: x-large; |
| 710 | line-height: var(--line-height); |
| 711 | margin: calc(1rem * var(--space-factor, 1)) auto; |
| 712 | max-width: var(--width-card-medium); |
| 713 | padding: calc(1rem * var(--space-factor, 1)) calc(1.5rem * var(--space-factor, 1)); |
| 714 | text-align: var(--justify-heading); |
| 715 | } |
| 716 | :where(.exsa blockquote:not([class]) footer) { |
| 717 | color: var(--color-text-secondary); |
| 718 | display: block; |
| 719 | font-size: small; |
| 720 | line-height: var(--line-height); |
| 721 | padding: calc(1.5rem * var(--space-factor, 1)) 0; |
| 722 | } |
| 723 | |
| 724 | /* ── Dialog ── */ |
| 725 | :where(.exsa dialog:not([class])) { |
| 726 | max-width: 90%; |
| 727 | max-height: 85dvh; |
| 728 | margin: auto; |
| 729 | padding-block: 0; |
| 730 | padding-inline: calc(20px * var(--space-factor, 1)); |
| 731 | border: 1px solid var(--color-border); |
| 732 | border-radius: .5rem; |
| 733 | overscroll-behavior: contain; |
| 734 | scroll-behavior: smooth; |
| 735 | animation: dialog-enter .25s ease-in-out forwards; |
| 736 | } |
| 737 | :where(.exsa dialog:not([class]))::backdrop { |
| 738 | background-color: var(--color-overlay); |
| 739 | } |
| 740 | @keyframes dialog-enter { |
| 741 | from { |
| 742 | opacity: 0; |
| 743 | transform: translateY(10%); |
| 744 | } |
| 745 | to { |
| 746 | opacity: 1; |
| 747 | transform: translateY(0); |
| 748 | } |
| 749 | } |
| 750 | :where(.exsa dialog:not([class]) hr) { |
| 751 | margin-block: calc(1rem * var(--space-factor, 1)); |
| 752 | } |
| 753 | |
| 754 | /* ── Button ── */ |
| 755 | :where(.exsa button:not([class])) { |
| 756 | display: inline-flex; |
| 757 | align-items: center; |
| 758 | justify-content: center; |
| 759 | gap: calc(6px * var(--space-factor, 1)); |
| 760 | font-family: inherit; |
| 761 | font-size: var(--font-size-base); |
| 762 | font-weight: 600; |
| 763 | line-height: 1; |
| 764 | padding: calc(10px * var(--space-factor, 1)) calc(20px * var(--space-factor, 1)); |
| 765 | border-radius: var(--border-radius); |
| 766 | border: 2px solid var(--color-border); |
| 767 | background: var(--color-bg); |
| 768 | color: var(--color-button-text-inverse); |
| 769 | cursor: pointer; |
| 770 | transition: background .15s, border-color .15s, transform .1s; |
| 771 | text-decoration: none; |
| 772 | white-space: nowrap; |
| 773 | user-select: none; |
| 774 | } |
| 775 | :where(.exsa button:not([class]):hover) { |
| 776 | background: var(--color-bg-secondary); |
| 777 | border-color: var(--color-text-secondary); |
| 778 | } |
| 779 | :where(.exsa button:not([class]):active) { |
| 780 | transform: scale(.97); |
| 781 | } |
| 782 | :where(.exsa button:not([class]):disabled) { |
| 783 | cursor: not-allowed; |
| 784 | opacity: .5; |
| 785 | transform: none; |
| 786 | } |
| 787 | |
| 788 | /* ── Guarded Structural Detection ── */ |
| 789 | /* Card grid — 3+ articles in a section */ |
| 790 | :where(.exsa section:not([class]):has(> article:not([class]):nth-child(3))) { |
| 791 | display: grid; |
| 792 | grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); |
| 793 | gap: var(--gap); |
| 794 | } |
| 795 | :where(.exsa section:not([class]):has(> article:not([class]):nth-child(3)) > article:not([class])) { |
| 796 | border: 1px solid var(--color-border); |
| 797 | border-radius: var(--border-radius); |
| 798 | box-shadow: var(--box-shadow) var(--color-shadow); |
| 799 | padding: var(--gap-lg); |
| 800 | margin: 0; |
| 801 | } |
| 802 | |
| 803 | /* Gallery — 2+ figures in a section (exclude sections with articles/asides) */ |
| 804 | :where(.exsa section:not([class]):has(> figure:nth-child(2))):not(:has(> aside)):not(:has(> article)) { |
| 805 | display: grid; |
| 806 | grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); |
| 807 | gap: var(--gap-sm); |
| 808 | } |
| 809 | :where(.exsa section:not([class]):has(> figure:nth-child(2))):not(:has(> aside)):not(:has(> article)) > figure { |
| 810 | margin: 0; |
| 811 | } |
| 812 | |
| 813 | /* Sidebar — aside as first child of main */ |
| 814 | :where(.exsa main:not([class]):has(> aside:first-child)) { |
| 815 | display: grid; |
| 816 | grid-template-columns: clamp(220px, 25%, 300px) 1fr; |
| 817 | gap: var(--gap-lg); |
| 818 | align-items: start; |
| 819 | } |
| 820 | |
| 821 | /* Hero split — header inside <main> that contains an image/figure/svg/video */ |
| 822 | :where(.exsa main header:not([class]):has(> img, > figure, > svg, > video)) { |
| 823 | display: grid; |
| 824 | grid-template-columns: 1fr 1fr; |
| 825 | gap: var(--gap-xl); |
| 826 | align-items: center; |
| 827 | max-width: none; |
| 828 | } |
| 829 | |
| 830 | /* Full-width header — header outside <main> (direct child of body) */ |
| 831 | :where(body.exsa > header:not([class])) { |
| 832 | max-width: none; |
| 833 | } |
| 834 | |
| 835 | /* Nav flush with hero — when nav is directly followed by header */ |
| 836 | :where(.exsa nav:has(+ header:not([class]))) { |
| 837 | margin-bottom: 0; |
| 838 | } |
| 839 | |
| 840 | /* Stats row — 3+ sibling paragraphs in a section */ |
| 841 | :where(.exsa section:not([class]):has(> p:nth-child(3))):not(:has(> aside)):not(:has(> article)) { |
| 842 | display: grid; |
| 843 | grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); |
| 844 | gap: var(--gap); |
| 845 | text-align: center; |
| 846 | } |
| 847 | :where(.exsa section:not([class]):has(> p:nth-child(3))):not(:has(> aside)):not(:has(> article)) > p { |
| 848 | border: 1px solid var(--color-border); |
| 849 | border-radius: var(--border-radius); |
| 850 | padding: var(--gap-lg) var(--gap); |
| 851 | margin: 0; |
| 852 | } |
| 853 | |
| 854 | } /* elements */ |
| 855 |
Layer 7 — 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 8 — Layouts
3 page shells — general, dashboard, store. — 4 lines
?
| — | /* Layer 8: exsa.layouts — drop-in page shells */ |
| — | dist/layouts/dashboard.css |
| — | dist/layouts/general.css |
| — | dist/layouts/store.css |
Layer 9 — Overrides
u-* escape-hatch utilities — always beat components and layouts; unlayered user CSS still wins. — 39 lines
?
| 856 | @layer exsa.overrides { |
| 857 | |
| 858 | /* ── Override utilities — force-win: u-* always beats components/layouts. |
| 859 | Unlayered user CSS still beats everything here. ── */ |
| 860 | |
| 861 | .u-text-start{text-align:start} |
| 862 | .u-text-center{text-align:center} |
| 863 | .u-text-end{text-align:end} |
| 864 | .u-block{display:block} |
| 865 | .u-flex{display:flex} |
| 866 | .u-inline-flex{display:inline-flex} |
| 867 | .u-none{display:none} |
| 868 | .u-w-full{width:100%} |
| 869 | .u-w-auto{width:auto} |
| 870 | .u-m-0{margin:0} |
| 871 | .u-p-0{padding:0} |
| 872 | .u-mx-auto{margin-inline:auto} |
| 873 | .u-gap-0{gap:0} |
| 874 | .u-gap-sm{gap:var(--gap-sm)} |
| 875 | .u-gap{gap:var(--gap)} |
| 876 | .u-gap-lg{gap:var(--gap-lg)} |
| 877 | .u-radius-0{border-radius:0} |
| 878 | .u-radius-full{border-radius:9999px} |
| 879 | /* Typography escape hatches (re-homed from exsa.utilities — the unprefixed |
| 880 | versions lost to elements/components, these always win) */ |
| 881 | .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)} |
| 882 | .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)} |
| 883 | .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)} |
| 884 | .u-italic{font-style:italic}.u-uppercase{text-transform:uppercase}.u-capitalize{text-transform:capitalize}.u-text-muted{color:var(--color-text-secondary)} |
| 885 | .u-text-left{text-align:left}.u-text-right{text-align:right} |
| 886 | .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)} |
| 887 | |
| 888 | /* Crash-prevention: reduced-motion users never get backdrop blur, whatever |
| 889 | skin is active (overrides beats skins layer 3). */ |
| 890 | @media (prefers-reduced-motion: reduce) { |
| 891 | :root { --surface-backdrop: none; } |
| 892 | } |
| 893 | |
| 894 | } /* overrides */ |