EXSA Cheatsheet
Complete reference for all tokens, utilities, element styles, and component classes. Cascade order: 1 tokens · 2 themes · 3 fluid · 4 reset · 5 utilities · 6 elements · 7 components · 8 layouts · 9 overrides.
Tokens
82 tokensDefined in @layer exsa.tokens. All components and element styles reference these. Override in your own stylesheet or swap the theme file. Export as JSON for Figma, JavaScript, or design tools.
Themes
20 themes + custom canvasThemes are optional — exsa.css ships a built-in default theme (light/dark). To customize, link one theme file after exsa.css: <link rel="stylesheet" href="dist/themes/abyss.css">. Every theme lives in @layer exsa.themes, auto-switches light/dark via prefers-color-scheme, and can be forced with <html data-theme-mode="light|dark">. custom.css is a blank canvas for your own palette.
Fluid Tokens & Profiles
Optional — link exsa.fluid.cssLink exsa.fluid.css after exsa.css to make spacing, typography, and shape scale smoothly with viewport width — no breakpoints, no responsive classes. Built-in density profiles via data-profile attribute.
Fluid Tokens (clamp-based)
| Group | Example |
|---|---|
--gap-xs → --gap-2xl | clamp(0.2rem, 0.15rem + 0.2vw, 0.35rem) → clamp(2rem, 1.5rem + 1.5vw, 3.5rem) |
--font-size-xs → --font-size-2xl | clamp(0.65rem, 0.6rem + 0.1vw, 0.75rem) → clamp(1.5rem, 1.3rem + 1.2vw, 2.4rem) |
--border-radius | clamp(4px, 0.3vw, 10px) |
--width-content | clamp(320px, 90%, 1080px) |
Behavioral Profiles
| Profile | Attribute | Factor Tokens |
|---|---|---|
| Compact | data-profile="compact" | --space-factor:0.8 --radius-factor:0.85 --font-factor:0.95 --motion-factor:0.6 |
| Comfortable | (default — no attribute) | --space-factor:1 --radius-factor:1 --font-factor:1 --motion-factor:1 |
| Spacious | data-profile="spacious" | --space-factor:1.3 --radius-factor:1.2 --font-factor:1.1 --motion-factor:1.4 |
Reset
@layer exsa.resetAlways-on base styles. No opt-in required. Applies globally.
| Rule | What It Does | Details |
|---|---|---|
*,*::before,*::after | Box-sizing border-box | All elements use border-box |
:focus-visible | Focus ring | 2px solid --color-link, offset 2px |
:focus:not(:focus-visible) | Suppress click focus | No outline on mouse click |
* { scrollbar-width } | Thin scrollbars | Firefox: thin. Webkit: 5px, rounded |
html | Smooth scrolling | scroll-behavior: smooth |
@media (prefers-reduced-motion) | Respects accessibility | Turns off smooth scroll & :target animations |
[dir=rtl] | RTL support | direction: rtl; slideshow track stays ltr |
body | Base styles | bg, color, font-family, line-height, no margin |
h1–h6 | Heading balance | text-wrap: balance |
img, svg, video | Responsive media | max-width: 100% |
figure | Clean figures | margin: 0; figcaption uses --color-text-secondary |
[hidden] | Hide elements | display: none |
.sr-only | Screen reader only | Visually hidden, accessible to SR |
Utilities
89 utilities + responsive variantsAlways-on utility classes. No prefix. Use directly in HTML.
Containers
| Class | Effect |
|---|---|
.container | max-width: 1080px, centered, padded |
.container-sm | max-width: 800px, centered, small pad |
.container-full | width: 100% |
Flex
| Class | Effect |
|---|---|
.flex | display: flex |
.flex-inline | display: inline-flex |
.flex-col / .flex-col-rev / .flex-row-rev | Column, column-reverse, row-reverse |
.flex-wrap / .flex-nowrap | Wrap / no-wrap |
.flex-1 / .flex-auto / .flex-none | flex: 1 / 0 1 auto / none |
.justify-start / .justify-end / .justify-center | flex-start / flex-end / center |
.justify-between / .justify-around / .justify-evenly | space-between / around / evenly |
.items-start / .items-end / .items-center | align-items values |
.items-baseline / .items-stretch | baseline / stretch |
.self-start / .self-end / .self-center / .self-stretch | align-self values |
.content-start / .content-end / .content-center | align-content values |
.content-between / .content-around | space-between / space-around |
Gap
| Class | Value |
|---|---|
.gap-0 | 0 |
.gap-xs / .gap-sm / .gap-md / .gap | 0.25rem / 0.5rem / 0.75rem / 1rem |
.gap-lg / .gap-xl / .gap-2xl | 1.5rem / 2rem / 3rem |
Grid
| Class | Effect |
|---|---|
.grid | display: grid |
.grid-cols-1 — .grid-cols-6 | 1 to 6 equal columns |
.grid-auto-fit | auto-fit, minmax(200px, 1fr) |
.fluid-type | Opt-in fluid typography. Add to <body> — scales --font-size-base, --font-size-xl, --font-size-2xl via clamp() |
Column Widths (Flex)
| Class | Width |
|---|---|
.col-1 | 100% |
.col-2 | 50% |
.col-3 | 33.333% |
.col-4 | 25% |
.col-5 | 20% |
.col-6 | 16.667% |
Bases are pure fractions — the parent row's gap-* utility supplies the gutters.
Responsive Helpers
| Breakpoint | Available Classes |
|---|---|
sm: (max 575px) | .sm\:flex-col, .sm\:flex-wrap, .sm\:grid-cols-1; cols stack to 100% |
md: (min 768px) | .md\:col-2/3/4/5/6, .md\:grid-cols-2/3/4/5/6 |
lg: (min 1024px) | .lg\:col-2/3/4/5/6, .lg\:grid-cols-3/4/5/6 |
xl: (min 1280px) | .xl\:col-2/3/4/5/6, .xl\:grid-cols-3/4/5/6 |
xxl: (min 1440px) | .xxl\:col-2/3/4/5/6, .xxl\:grid-cols-3/4/5/6 |
Breakpoint Tokens (JS)
All 5 breakpoints are CSS tokens (--bp-sm–--bp-xxl). Use EXSA.bp for JS-driven responsive logic:
| Method | Example |
|---|---|
EXSA.bp.up('md') | if(EXSA.bp.up('md').matches){/* tablet+ */} |
EXSA.bp.down('sm') | EXSA.bp.down('sm').addEventListener('change',fn) |
EXSA.bp.val('lg') | Returns raw value: "1024px" |
Typography
| Class | Effect |
|---|---|
.text-xs / .text-sm / .text-base / .text-md / .text-lg / .text-xl / .text-2xl | 7 sizes via --font-size-* tokens (11px–29px) |
.fw-normal / .fw-bold / .fw-heavy | 400 / 700 / 800 weight via --font-weight-* tokens |
.font-sans / .font-mono / .font-heading | System / monospace / heading font family |
.italic | font-style: italic |
.uppercase / .capitalize | text-transform |
.text-muted | color: var(--color-text-secondary) |
.text-start / .text-center / .text-end | Text alignment (logical + LTR/RTL) |
Elements
Classless, opt-in via body.exsaAdd class="exsa" to <body> to enable semantic HTML styling. All use :where() for zero specificity — your custom styles always win.
| Selector | Styling | Tokens Used |
|---|---|---|
:where(.exsa p) | 0.75rem vertical margin | — |
:where(.exsa a) | Bold, underlined, link color | --color-link |
:where(.exsa a:visited) | Secondary color for visited links | --color-secondary |
:where(.exsa a:hover) | Brightness boost on hover | --hover-brightness |
:where(.exsa a:active) | Dim on click | --active-brightness |
:where(.exsa small) | Muted color, guarded | --color-text-secondary |
:where(.exsa mark) | 0.1rem padding highlight, guarded | — |
:where(.exsa kbd) | Keyboard key — mono font, border, shadow, guarded | --color-bg-secondary, --color-border |
:where(.exsa abbr) | Dotted underline, help cursor, guarded | — |
:where(.exsa del) | Danger-color strikethrough, guarded | --color-danger |
:where(.exsa ins) | Success-color text (underline removed), guarded | --color-success |
:where(.exsa cite) | Italic, muted color, guarded | --color-text-secondary |
:where(.exsa sup) | Pill badge with secondary bg, guarded | --color-secondary, --color-bg |
:where(.exsa code) | Inline code with accent bg | --color-accent, --color-text |
:where(.exsa pre) | Code block, 1rem margin, max-width | --width-card-wide |
:where(.exsa hr) | 1px divider, 3rem vertical margin | --color-text |
:where(.exsa details) | 1rem margin, bold summary cursor | — |
:where(.exsa details details) | Nested indent + left border pipe for hierarchy | --color-border |
:where(.exsa header/main/footer) | Max-width 1080px, 3rem padding, centered | --width-content, --justify-heading |
:where(.exsa nav) | Basic flex row, bold, space-between. Mobile wrap ≤768px. Configurable via --justify-nav. | — |
:where(.exsa section) | Flex wrap, container queries. Structural detection via :has(). | --justify-heading |
:where(.exsa section aside) | Card with border, shadow, hover lift. --card-padding token for full-bleed sync. | --width-card, --box-shadow, --color-border |
:where(.exsa section aside:has(>img)) | Auto horizontal card — image left, text right. Collapses at 380px container width. Wrap text in <div>. | --width-card-medium |
:where(.exsa article>aside) | Callout box with left border accent | --color-secondary, --color-secondary-accent |
:where(.exsa table:not([class])) | Bordered, header bg (--color-link), cell padding, max-width: 100% | --color-link, --color-border, --border-radius |
:where(.exsa form:not([class])) | Card wrapper, styled inputs/selects/textareas. Fieldset bordered, legend bold. :invalid red, :valid green. | --box-shadow, --width-card-wide |
:where(.exsa progress / meter) | Native progress bars. Cross-browser styled (WebKit + Firefox). Meter: green→amber→red. | --color-link, --color-success, --color-warning, --color-danger |
:where(.exsa button:not([class])) | Neutral bordered button, hover bg change, click scale. :disabled: 50% opacity, not-allowed cursor. | --color-bg, --color-bg-secondary, --border-radius |
:where(.exsa blockquote:not([class])) | Large text, left border pipe, centered. Optional footer in muted color. | --width-card-medium, --color-link |
:where(.exsa dialog:not([class])) | Modal with backdrop, entry animation, responsive width | --color-bg-secondary, --border-radius |
Guarded Structural Detection (:has() — add any class to any child to opt out)
| Pattern | Selector | Effect |
|---|---|---|
| Card Grid | section:has(> article:nth-child(3)) | 3+ articles → auto-fit grid, 280px min |
| Gallery | section:has(> figure:nth-child(2)) | 2+ figures → auto-fit grid, 180px min |
| Stats Row | section:has(> p:nth-child(3)) | 3+ paragraphs → auto-fit stat cards |
| Hero Split | main header:has(> img, figure, svg, video) | Header with media → 2-col grid, full-width |
| Sidebar | main:has(> aside:first-child) | Aside first in main → sidebar layout |
| Full-Width Header | body.exsa > header | Header outside main → edge-to-edge |
| Nav Flush | nav:has(+ header) | Nav before header → zero bottom margin |
Components
68 components — 8 categoriesEach component is a single CSS file. Link only what you need. Components live in @layer exsa.components — they beat classless elements and lose to your own unlayered styles.
Layout
| Component | Core Class | Key Modifiers | Tokens |
|---|---|---|---|
| Accordion | .accordion | .accordion--sm | --color-link |
| Drawer | .drawer | .drawer--left | --color-bg, --box-shadow |
| Footer | .footer | .footer__brand, .footer__links, .footer__bottom | --color-bg-secondary, --color-link |
| Modal | .modal | .modal__dialog, .modal__header/body/footer | --box-shadow, --border-radius |
| Pricing Table | .pricing | .pricing__card--featured, .pricing__badge | --color-link, --box-shadow |
| Hero | body.exsa > header:has(> figure) | Video/image background overlay. --hero-height (60vh), --hero-overlay (gradient). Full-width, text centered, inverted button. | --hero-height, --hero-overlay |
| Kanban | .kanban | .kanban__col/cards/card, .kanban__add, drag-drop via js/kanban.js | --color-bg-secondary |
| Product Card | .shop__card | .shop__card-image/badge/body/title/price--old/rating | --color-link, --box-shadow |
Navigation
| Component | Core Class | Key Modifiers | Tokens |
|---|---|---|---|
| Topbar | .topbar | .topbar__brand, .topbar__nav, .topbar__btn--primary | --color-bg |
| Sidebar | .sidebar | .sidebar__link--active, .sidebar__sub-menu | --color-bg, --color-link |
| Back to Top | .back-top | Appears after 300px scroll | --color-link |
| Breadcrumbs | .breadcrumbs | .breadcrumbs__sep, .breadcrumbs__current | --color-link, --color-text-secondary |
| Pagination | .pagination | .pagination--sm, disabled state | --color-link, --color-bg-secondary |
| Stepper | .stepper | .stepper--vertical, .stepper__step--done/active | --color-link |
| Timeline | .timeline | .timeline--sm, .timeline__item--active | --color-link, --color-bg-secondary |
| Tooltip | .tooltip | data-tip attribute, bubble renders above | --color-text, --color-bg |
| Context Menu | .ctx-menu | .ctx-menu--soft, .ctx-menu--sm | --box-shadow, --border-radius |
| Command Palette | .command-palette | Ctrl+K search (reuses .modal__dialog shell); .command-palette__input/list/item, js/command-palette.js | --box-shadow |
| Notifications | .notifications | .notifications__trigger/list/item--unread; reuses js/dropdown.js | --color-link |
| Tree | .tree | .tree--lines, .tree__node/row/icon/label, native <details> | --color-bg-secondary |
Content
| Component | Core Class | Key Modifiers | Tokens |
|---|---|---|---|
| Avatar | .avatar | .avatar--xs/sm/lg/xl, .avatar--primary/success/danger/warning, .avatar__status--online/away/busy/offline, .avatar__fallback, .avatar-group | --color-text-secondary, --color-success/warning/danger |
| Badge | .badge | .badge--default/primary/success/warning/danger/dot/count | --color-link, --color-bg-secondary |
| Card | .card | .card--hoverable, .card--horizontal, .card--horizontal-reverse, .card__image, .card__body, .card__title, .card__text, .card__footer, .card__head-row, .card__rating, .card__divider | --box-shadow, --color-border |
| Table | .tbl | .tbl--striped, .tbl--bordered, .tbl--sm, .tbl--responsive | --color-bg-secondary |
| Data List | .data-list | .data-list--vertical, .data-list--bordered, .data-list--striped, --sm/--lg | --color-link |
| Separator | .sep | .sep--sm, .sep--lg, .sep--vertical | --color-bg-secondary |
| Code Block | .code-block | .code-block--no-header, .code-block--sm, .code-block__copy | --color-bg-secondary, --color-link, --color-success |
| Activity Feed | .activity | .activity__group/header/list/item/time, dot colors --danger/--warning/--muted | --color-success |
| Empty State | .empty-state | .empty-state--bordered/--sm, .empty-state__icon/title/text/actions | --color-text-secondary |
Media
| Component | Core Class | Key Modifiers | Tokens |
|---|---|---|---|
| Lightbox | .lightbox-gallery | .lightbox-gallery__thumb, prev/next arrows, keyboard nav | --color-link, --border-radius |
| Music Player | .media-display / .media-bar | .media-display__play, .media-bar__btn/time, data-duration attr | --color-link, --color-text |
| Video Gallery | .video-gallery | .video-gallery__card, data-video attr | --color-link, --color-bg-secondary |
| Slideshow | .slideshow | .slideshow__arrow--prev/next, .slideshow__dots, auto-advance | --color-link, --border-radius |
| Carousel | <exsa-carousel> | Web Component. pagination, navigation, autoplay, loop, mouse-dragging, slides-per-page, orientation=vertical. --aspect-ratio, --slide-gap, --scroll-hint. | --color-link, --border-radius |
Data
| Component | Core Class | Key Modifiers | Tokens |
|---|---|---|---|
| Donut Chart | .donut | .donut__slice--1/2/3/4, --s1…--s4 vars (degrees), .donut-legend | --color-link, --color-secondary |
| Bar Chart | .bar-row | .bar-row__fill, --bar-w (0—100%), background inline | --color-link, --color-bg-secondary |
| Progress Bars | .progress-item | .progress-item__head/label/pct, .progress-item__track, .progress-item__fill, --fill-pct, --fill-clr | --color-link, --color-secondary |
| Range Slider | .range | .range--sm, live value display | --color-link, --color-bg-secondary |
| Calendar | .calendar | data-calendar-date/events, js/calendar.js renders grid + nav | --color-link |
| Chart | .chart | .chart--line/area/bar/sparkline, data-chart-values, js/chart.js | --color-link, --color-secondary |
| KPI Card | .kpi | .kpi__value, .kpi__delta--up/--down, .kpi-grid, .kpi--sm | --color-success, --color-danger |
Actions
| Component | Core Class | Key Modifiers | Tokens |
|---|---|---|---|
| Buttons | .btn | .btn--primary/success/danger/warning/neutral/outline/ghost/soft/surface, .btn--xs/sm/lg, .btn--icon, .btn--block, .btn--loading, .btn--high-contrast, .btn--radius-full/none | --color-link, --color-success, --color-danger, --color-warning |
| Checkbox | .checkbox | :checked fills --color-link, disabled state | --color-link |
| Color Picker | .color-picker | .color-picker--sm, .color-picker--lg | — |
| Advanced Color Picker | .ex-cp | HSL+Alpha canvas. .ex-cp__trigger, .ex-cp__drop, .ex-cp__sliders, .is-open. Checkerboard transparency. | --color-link |
| Date Picker | .date-picker | Month nav, today highlight, click-to-select | --color-link, --color-bg-secondary |
| Dropdown | .dropdown | .dropdown--soft, .dropdown--sm, shortcuts, labels, danger items | --box-shadow, --border-radius |
| Form Validation | .form-helper | .input--error, .input--success, .form-helper--error | --color-danger, --color-success |
| Form Required | auto — classless | :user-invalid form border, required asterisk via :has() | --color-danger |
| Icon Button | .btn--icon | Combine with .btn--surface/ghost/outline | — |
| Input Group | .input-group | .input-group__icon, .input-group__addon | --color-link, --color-bg-secondary |
| Loading Button | .btn--loading | Works on all btn variants, spinner matches variant | --color-link |
| Password Input | .password-input | Show/hide toggle, JS ~24 lines | --color-link |
| Popover | .popover | .popover--sm/lg/xl, .popover--align-start/end, arrow | --box-shadow, --border-radius |
| Radio Button | .radio | name attr for groups, disabled state | --color-link, --color-bg-secondary |
| Rating | .rating | 5 stars, hover preview, CSS direction trick, JS ~27 lines | --color-link |
| Select | .select | .select--sm, .select--lg, error state, disabled | --color-link, --color-text-secondary |
| Tabs | .tabs | .tabs__tab--active, .tabs__panel--active, data-panel linking | --color-link |
| Toggle | .toggle | .toggle--sm, disabled state, :checked | --color-link |
| Tags Input | .tags-input | data-tags-suggestions/max/name, js/tags-input.js adds the field | --color-link |
| Transfer List | .transfer | .transfer__panel/list/item/actions, js/transfer.js | --color-link |
| Upload Dropzone | .upload | .upload__dropzone/list/item-track, data-upload-url, js/upload.js | --color-link, --color-success |
Feedback
| Component | Core Class | Key Modifiers | Tokens |
|---|---|---|---|
| Alert | .alert | .alert--info/success/warning/danger, .alert--outline/surface, .alert--sm/lg | --color-link, semantic colors |
| Cookie Bar | .cookie-bar | .cookie-bar--visible, .cookie-bar__btn--accept | --color-link, --color-bg |
| Skeleton | .skeleton | .skeleton__line--circle/h2/medium/short/btn | --color-bg-secondary |
| Spinner | .spinner | .spinner--sm, .spinner--lg | --color-link, --color-bg-secondary |
| Toast | .toast | .toast--success/error/info, #toast-container, auto-dismiss 3.5s | --border-radius, --box-shadow |
| Dashboard | .dash-card | .dash-card--sm/lg, .dash-card__head/title/sub, .dash-grid, .dash-stat (+__delta--up/down) | --dash-gap, --dash-col-min (optional) |
| Resizer | .resizer | ew-resize (horizontal), ns-resize (vertical) | --color-bg, --color-bg-secondary |
Effects
| Component | Core Class | Key Modifiers | Tokens |
|---|---|---|---|
| Background | .bg-page | .bg-cover/.bg-contain/.bg-auto, .bg-center/.bg-top/.bg-bottom, .bg-fixed, .bg-pattern, .bg-veil--dark, --bg-image/--bg-overlay/--bg-blend/--bg-filter | --bg-image, --color-bg |
| Page Transitions | vt-* | .vt-brand, .vt-title, .vt-card, .vt-panel, .vt-hero — cross-document View Transitions API, link once | — |
Icons
112 SVG icons (103 Feather Icons + 9 Simple Icons brands). Use with <span class="ic ic-<name>"></span>. Browse the full library: Icons →
| Category | Icon Classes |
|---|---|
| Navigation | ic-menu ic-home ic-search ic-chevup ic-chevdown ic-chevleft ic-chevright ic-ext-link ic-link ic-map-pin |
| Actions | ic-plus ic-minus ic-edit ic-copy ic-paste ic-trash ic-download ic-upload ic-share ic-settings ic-filter ic-refresh ic-sort |
| Text Editing | ic-type ic-bold ic-italic ic-underline ic-align-left ic-align-center ic-align-right ic-list |
| Media | ic-image ic-play ic-pause ic-video ic-camera ic-mic ic-volume ic-crop |
| Account & Security | ic-user ic-users ic-lock ic-unlock ic-key ic-shield ic-log-in ic-log-out |
| Commerce | ic-cart ic-credit-card ic-tag ic-gift ic-truck ic-package ic-briefcase |
| Developer | ic-terminal ic-database ic-cloud ic-code ic-layers ic-layout ic-clipboard |
| Status | ic-check ic-info ic-warn ic-error ic-close ic-eye ic-help |
Layouts
3 layouts + 2 starter templatesLink one layout file after your theme, then add body classes to compose the page shell — any zone can be toggled on/off. Layouts live in @layer exsa.layouts. Fullpage and Onepage are starter templates: copy the folder and open index.html.
Page Shells
| Layout | Body Classes | Structure |
|---|---|---|
general.css | layout--has-topbar layout--has-hero layout--aside-left layout--aside-right layout--has-sidebar layout--centered layout--sticky-footer layout--topbar-full/content layout--footer-full/content | .layout__shell .layout__aside-left .layout__aside-right .layout__content |
dashboard.css | dash--sidebar dash--sidebar-sm dash--sidebar-fixed dash--sidebar-dark dash--topbar dash--panel | .dash__shell .dash__sidebar .dash__content .dash__panel |
store.css | shop--announce shop--topbar shop--hero shop--sidebar shop--sidebar-right | .shop__shell .shop__aside .shop__aside-right .shop__content |
Starter Templates
| Template | Body Class | Structure |
|---|---|---|
fullpage | layout--fullpage — single-viewport landing with backdrop + social rings | .fullpage__backdrop .fullpage__content |
onepage | layout--onepage — multi-section marketing page | .onepage__header .onepage__footer |
Override Utilities
u-* — always wins over componentsUnprefixed utilities are structural and lose to components. u-* classes live in the top layer (exsa.overrides) — they beat components and layouts. Your own unlayered CSS still beats them.
| Class | Effect |
|---|---|
.u-text-start / .u-text-center / .u-text-end | Text alignment override |
.u-block / .u-flex / .u-inline-flex / .u-none | Display override |
.u-w-full / .u-w-auto | Width override |
.u-m-0 / .u-p-0 / .u-mx-auto | Spacing reset / centering |
.u-gap-0 / .u-gap-sm / .u-gap / .u-gap-lg | Gap override |
.u-radius-0 / .u-radius-full | Border-radius override |