EXSA
Generate custom CSS

EXSA Component Library — 68 Components, 20 Themes

Component Library

Browse 68 components with live demos across 7 categories. Every component is token-driven — swap the theme or skin and every demo restyles instantly. All components use --color-link, --color-bg, --surface-shadow-md and 89 other CSS custom properties. Try the live theme × skin lab on skins.php, open a demo, inspect the source — zero build step, just CSS.

Hero Banner

Full-bleed header with image/video background and centered text overlay. Detects <header> as a direct child of <body> (outside <main>) containing a <figure> with media. Requires dist/components/hero.css (included in exsa.bundle.css). Gradient overlay uses --hero-overlay; height via --hero-height (default 60vh). Classless <button> elements invert to white automatically.

Build Faster with EXSA

A token-driven CSS framework. No build step. Just CSS.

<!-- Link hero.css after exsa.css + your theme (included in exsa.bundle.css) --> <link rel="stylesheet" href="dist/components/hero.css"> <body class="exsa"> <header> <figure> <img src="hero-bg.jpg" alt="Background"> </figure> <h1>Build Faster with EXSA</h1> <p>A token-driven CSS framework. No build step. Just CSS.</p> <p> <button>Get Started</button> <button>Learn More</button> </p> </header> </body>

Accordion

Pure CSS expandable sections using hidden <input type="checkbox"> and the :checked pseudo-class. Zero JavaScript. Active label uses --color-link. Add .accordion--sm for compact variant.

82 design tokens from the active theme file drive all colors. --color-link for the active label, --color-bg-secondary for hover states and borders, --border-radius for the container corners.
Pure CSS. It uses a hidden checkbox input + the :checked pseudo-class to toggle the panel height. Same pattern as our Toggle Switch component.
Yes. Nest another .accordion block inside .accordion__panel-inner. Each nested accordion gets its own independent checkbox IDs. Works up to any depth.
<div class="accordion"> <div class="accordion__item"> <input type="checkbox" class="accordion__trigger" id="a1"> <label class="accordion__label" for="a1">Title</label> <div class="accordion__panel"> <div class="accordion__panel-inner">Content here</div> </div> </div> </div>

Kanban Board

Drag-and-drop board with columns and cards. Pointer-based dragging works with mouse and touch (touch-action: pan-y keeps vertical page scroll alive). Behavior: js/kanban.js. Fires exsa:kanban-drop / exsa:kanban-add events on the board.

To Do2
Design tokens audit
Sweep 82 tokens for drift
docsAug 24
Kanban demo
Aug 24
Doing1
Contracts probe
Aug 23
<div class="kanban"> <div class="kanban__col" data-col-id="todo"> <div class="kanban__col-head"> <span class="kanban__col-title">To Do</span> <span class="kanban__col-count badge badge--default">2</span> </div> <div class="kanban__cards"> <article class="kanban__card" data-card-id="c1" tabindex="0"> <div class="kanban__card-title">Design tokens audit</div> <div class="kanban__card-text">Sweep 82 tokens for drift</div> <div class="kanban__card-foot"> <span class="kanban__card-tags badge badge--primary">docs</span> <span class="kanban__card-date">Aug 24</span> </div> </article> </div> <button class="kanban__add" data-col-add="todo">+ Add card</button> </div> </div>

Product Card

Store product tile for layouts/store.css .shop__grid product grids. Pair with badge.css for the corner badge colors.

Product cover
-20%
Design System Starter
★★★★☆
$29 $39
Product cover
Token Explorer
★★★★★
$49
<div class="shop__card"> <div class="shop__card-image"><img src="product.jpg" alt=""></div> <span class="shop__card-badge badge badge--danger">-20%</span> <div class="shop__card-body"> <div class="shop__card-title">Design System Starter</div> <div class="shop__card-rating">★★★★☆</div> <div class="shop__card-price">$29 <span class="shop__card-price--old">$39</span></div> </div> </div>

Drawer

Slide-out panel triggered by a hidden checkbox. Includes backdrop overlay. Supports left variant with .drawer--left. Uses --color-bg and --color-overlay tokens. Pure CSS — no JavaScript required for the toggle.

<label class="btn btn--neutral" for="d1">Open Drawer</label> <input type="checkbox" class="drawer__toggle" id="d1"> <label class="drawer__backdrop" for="d1"></label> <div class="drawer__panel"> <div class="drawer__head"> <h3 class="drawer__title">Panel</h3> <label class="drawer__close" for="d1"><span class="ic ic-close"></span></label> </div> <div class="drawer__body">...</div> </div>

Pricing Table

Responsive pricing grid with featured plan highlight. Uses --color-link for the featured border and button, --box-shadow for hover lift, and --border-radius for cards and buttons.

Starter
$9
per month
  • 5 projects
  • 1 GB storage
  • Basic analytics
  • Priority support
  • Custom domain
Enterprise
$79
per month
  • Unlimited projects
  • 100 GB storage
  • Real-time analytics
  • 24/7 phone support
  • Custom domain + SSL
<div class="pricing"> <div class="pricing__card pricing__card--featured"> <div class="pricing__badge">Most Popular</div> <div class="pricing__name">Professional</div> <div class="pricing__price"><sup>$</sup>29</div> <div class="pricing__period">per month</div> <ul class="pricing__features"> <li>50 projects</li> <li class="pricing__feature--na">Custom domain</li> </ul> <button class="pricing__btn">Get Started</button> </div> </div>

RTL Direction

EXSA fully supports right-to-left (RTL) layouts. Simply set dir="rtl" on the <html> element — all components flip automatically using CSS logical properties (inline-start, inline-end). The button below is a convenience toggle to preview RTL on this page; it persists your choice in localStorage.

← Click to preview RTL layout
<!-- Enable RTL for the entire page --> <html dir="rtl" lang="ar"> ... </html>

Topbar / Navbar

Fixed top navigation bar with scroll shadow. Always solid --color-bg with a subtle shadow on scroll. Left section for brand + nav, right section for actions + avatar. Hamburger menu on mobile. The one at the very top of this page is a live example.

Scroll the page to see the topbar turn solid with a blur backdrop. Resize to mobile to try the hamburger menu.

<header class="topbar"> <div class="topbar__left"> <a href="#" class="topbar__brand"><span>CSS</span> Engine</a> <nav> <ul class="topbar__nav"> <li><a href="#" class="topbar__link"><span class="ic ic-folder"></span>Directory</a></li> <li><a href="#" class="topbar__link">Search</a></li> <li class="topbar__dropdown">...</li> </ul> </nav> </div> <div class="topbar__right"> <a href="#" class="topbar__link">Login</a> <a href="#" class="topbar__btn topbar__btn--primary">Join Us</a> <div class="avatar avatar--sm">...</div> </div> <button class="topbar__toggle"><span class="ic ic-menu"></span></button> </header>

Command Palette

Ctrl+K modal search built on modal.css. Behavior: js/command-palette.js — filters items live, arrow keys navigate, Enter selects. Press Ctrl+K anywhere on this page to open it.

<button class="btn btn--neutral" onclick="document.getElementById('demo-palette').classList.toggle('modal--open')">Open Command Palette</button> <div class="modal command-palette" role="dialog" aria-modal="true" aria-label="Command palette"> <div class="modal__dialog command-palette__dialog"> <div class="command-palette__input-wrap"> <input class="command-palette__input" type="text" placeholder="Type a command or search…"> <kbd class="command-palette__kbd">esc</kbd> </div> <div class="command-palette__list" role="listbox"> <div class="command-palette__group"> <div class="command-palette__group-title">Pages</div> <button class="command-palette__item" data-command="docs"> <span class="command-palette__item-body"> <span class="command-palette__item-label">Documentation</span> <span class="command-palette__item-desc">docs.php</span> </span> </button> </div> </div> </div> </div>

Notifications

Bell dropdown built on dropdown.css — the toggle reuses js/dropdown.js, so no extra behavior file. Unread items, timestamps, mark-all-read, and a footer link.

<div class="dropdown notifications"> <button class="dropdown__trigger notifications__trigger" aria-haspopup="true" aria-expanded="false" aria-label="Notifications"> <span class="ic ic-bell"></span><span class="badge badge--danger badge--count">3</span> </button> <div class="dropdown__menu notifications__menu" role="menu"> <div class="notifications__head"> <span class="notifications__title">Notifications</span> <button class="notifications__mark">Mark all read</button> </div> <div class="notifications__list"> <button class="notifications__item notifications__item--unread" role="menuitem"> <span class="notifications__body"> <span class="notifications__text">Release v1.0 is live</span> <span class="notifications__time notifications__time--unread">2m</span> </span> </button> </div> <div class="notifications__foot">View all</div> </div> </div>

Tree

Nested file tree — pure CSS, zero JS. Expand/collapse comes free from native <details>, so it's keyboard-accessible by default. .tree--lines adds the connecting lines.

  • src4
    • exsa.css
    • components
      • card.css
      • modal.css
<ul class="tree tree--lines"> <li><details class="tree__node" open> <summary class="tree__row"> <span class="tree__icon"><span class="ic ic-folder"></span></span> <span class="tree__label">src</span> <span class="tree__badge badge badge--default">4</span> </summary> <ul class="tree"> <li><span class="tree__row"> <span class="tree__icon"><span class="ic ic-file"></span></span> <span class="tree__label">exsa.css</span> </span></li> </ul> </details></li> </ul>

Back to Top

Floating button that appears after scrolling 300px. Smooth-scrolls to top on click. Uses --color-link for background. Fixed position, always visible in the bottom-right corner.

Scroll down to see the button appear in the bottom-right corner. Click it to scroll back up.

<button class="back-top back-top--visible" aria-label="Back to top"></button>

Pagination

Page navigation using a styled list. Active page uses --color-link. Disabled state uses reduced opacity. Add .pagination--sm for compact variant. Borders use --color-bg-secondary.

<nav class="pagination" aria-label="Page navigation"> <span class="pagination__item"> <a class="pagination__link pagination__link--disabled" href="#" aria-disabled="true"><span class="ic ic-chevleft"></span> Prev</a> </span> <span class="pagination__item"> <a class="pagination__link pagination__link--active" href="#">1</a> </span> <span class="pagination__item"> <a class="pagination__link" href="#">2</a> </span> <span class="pagination__item"><span class="pagination__ellipsis">…</span></span> <span class="pagination__item"> <a class="pagination__link" href="#">Next <span class="ic ic-chevright"></span></a> </span> </nav>

Stepper

Multi-step progress indicator with active and completed states. Connecting lines fill with --color-link when done. Supports horizontal and vertical (.stepper--vertical) layouts.

Cart
2
Shipping
3
Payment
4
Confirm
<div class="stepper"> <div class="stepper__step stepper__step--done"> <div class="stepper__dot"><span class="ic ic-check"></span></div> <span class="stepper__label">Cart</span> </div> <div class="stepper__step stepper__step--active">...</div> </div>

Timeline

Vertical timeline with dot markers and cards. Active item fills the dot with --color-link. The vertical line uses --color-bg-secondary. Add .timeline--sm for compact variant.

Jan 2026

Project Kickoff

Initial architecture defined. Token system designed with 82 tokens powering all 9 layers.

Mar 2026

First 8 Components

Slideshow, progress bars, toggle, tabs, charts, toast, tooltip, and breadcrumbs — all pure CSS.

Jun 2026

23 Components Complete

Extended to 23 components across 6 categories. Theme engine supports 5 presets with live switching.

Jul 2026

Export Engine v1

Selective CSS export: cherry-pick components, bundle with base framework + chosen theme. One download.

<div class="timeline"> <div class="timeline__item timeline__item--active"> <div class="timeline__dot"></div> <div class="timeline__content"> <div class="timeline__time">Jun 2026</div> <h4 class="timeline__title">Milestone</h4> <p class="timeline__desc">...</p> </div> </div> </div>

Tooltip

Pure CSS hover tooltip using ::after and ::before pseudo-elements. Arrow created with border trick. Text color uses --color-text, background inverts it to --color-bg.

Hover over this phrase to see the tooltip. Works on hover and keyboard focus.

<span class="tooltip" data-tip="Tooltip text here" tabindex="0"> hover me </span>

Context Menu

Right-click popup menu with items, separators, shortcuts, and danger actions. Auto-positions within the viewport. Closes on outside click, Escape, or item selection. Solid and soft variants via .ctx-menu--soft. Compact size via .ctx-menu--sm.

Right-click anywhere in this box
Actions


<div data-ctx-menu> Right-click here <div class="ctx-menu ctx-menu--soft"> <div class="ctx-menu__label">Actions</div> <button class="ctx-menu__item">Edit<span class="ctx-menu__shortcut">Ctrl+E</span></button> <hr class="ctx-menu__sep"> <button class="ctx-menu__item ctx-menu__item--danger">Delete</button> </div> </div>

Avatar

Squircle-masked user avatars with a proportional status dot (20% of size). Sizes (fluid — vw-clamped, scale down on small screens): --xs 24–40 · default 32–56 · --sm 40–80 · --lg 64–160 · --xl 96–320. Status variants: --online, --away, --busy, --offline. Fallback shows initials on a token-colored squircle when no image. Grouped avatars overlap with a background ring.

XS
Default — photo
SM
LG — photo
SM
AN
KB
JD
RT
1
2 — photo
3
+5
<div class="avatar avatar--sm"> <img src="user.jpg" alt=""> <span class="avatar__status avatar__status--online"></span> </div> <!-- sizes: --xs | default | --sm | --lg | --xl --> <div class="avatar avatar--xs"><img src="user.jpg" alt=""></div> <div class="avatar avatar--lg"><img src="user.jpg" alt=""><span class="avatar__status avatar__status--busy"></span></div> <!-- statuses: --online | --away | --busy | --offline; colors: --primary | --success | --danger | --warning --> <div class="avatar avatar--sm avatar--danger"> <span class="avatar__fallback">JD</span> <span class="avatar__status avatar__status--offline"></span> </div>

Badge

Small inline labels for status, counts, and categories. Five color variants, plus dot and count shapes. Primary variant uses --color-link; default uses --color-bg-secondary.

Default Primary Success Warning Danger 14
<span class="badge badge--default">Default</span> <span class="badge badge--primary">Primary</span> <span class="badge badge--success">Success</span> <span class="badge badge--warning">Warning</span> <span class="badge badge--danger">Danger</span> <span class="badge badge--primary badge--count">14</span> <span class="badge badge--dot badge--success" title="Online"></span>

Card

Versatile content container with image, body, and footer slots. .card--hoverable lifts on hover using --box-shadow. .card--horizontal places image beside content — collapses to vertical on narrow containers. Add .card--horizontal-reverse to flip the image to the right. All colors reference theme tokens.

Design tokens

Design Tokens

82 design tokens drive every component. Change one theme file to restyle the entire library.

Export engine

Export Engine

Select components, pick a theme, download a single CSS file. No build tools needed.

No Image Card

★★★★☆

Cards work without images too. Add a rating between the title and text for review-style cards. The body fills the full width.

Horizontal — image beside content

Token architecture

Horizontal Card

Image sits on the left, content flows on the right. Body is vertically centered for balanced reading. Collapses to stacked on narrow containers.

Reversed layout

Horizontal Reversed

Flexible ★★★★☆

Same layout, image on the right. Add .card--horizontal-reverse to flip the direction. Title, badge, and star rating share one row — divider separates metadata from body text. Works with all card variants.

<div class="grid grid-auto-fit gap"> <div class="card card--hoverable"> <img class="card__image" src="cover.jpg" alt=""> <div class="card__body"> <h4 class="card__title">Design Tokens</h4> <p class="card__text">82 design tokens drive every component.</p> </div> <div class="card__footer"> <span class="badge badge--primary">v2.4</span> </div> </div> <div class="card card--hoverable"> <div class="card__body"> <h4 class="card__title">No Image Card</h4> <span class="card__rating">★★★★☆</span> <p class="card__text">Cards work without images too.</p> </div> <div class="card__footer"> <span class="badge badge--default">Rating</span> </div> </div> </div>

Table

Responsive data table with striped rows, hover states, and status badges. Add .tbl--striped, .tbl--bordered, .tbl--sm, or .tbl--responsive for card fallback on mobile. Uses --color-bg-secondary for borders and stripes.

ProjectOwnerStatusUpdated
Token EngineAnaActive2h ago
Export CLIBenReview5h ago
Theme BuilderChenActive1d ago
Legacy DocsDanaArchived12d ago
CDN SetupEliActive3d ago
<div class="table-wrap"> <table class="tbl tbl--striped tbl--responsive"> <thead> <tr><th scope="col">Project</th><th scope="col">Owner</th><th scope="col">Status</th><th scope="col">Updated</th></tr> </thead> <tbody> <tr> <td data-label="Project">Token Engine</td> <td data-label="Owner">Ana</td> <td data-label="Status"><span class="tbl-badge tbl-badge--success">Active</span></td> <td data-label="Updated">2h ago</td> </tr> </tbody> </table> </div>

Data List

Key-value metadata display using semantic <dl>, <dt>, <dd> elements. Horizontal by default, stack vertically with .data-list--vertical. Add .data-list--bordered for a card wrapper or .data-list--striped for alternating row backgrounds. Three sizes: --sm, default, --lg.

Name
Sam Intelco
Email
sam@intelco.com
Company
intelco
Status
Active
Repository
github.com/exsa/framework
Version
v1.0.0-rc.1
License
MIT
<dl class="data-list data-list--bordered"> <div class="data-list__item"> <dt class="data-list__label">Name</dt> <dd class="data-list__value">Sam Intelco</dd> </div> <div class="data-list__item"> <dt class="data-list__label data-list__label--success">Status</dt> <dd class="data-list__value"><span class="badge badge--success">Active</span></dd> </div> <div class="data-list__item"> <dt class="data-list__label data-list__label--primary">Repository</dt> <dd class="data-list__value">github.com/exsa/framework</dd> </div> </dl>

Separator

Horizontal (<hr>) or vertical (<span>) divider line. Three sizes: .sep--sm, default, .sep--lg. Vertical via .sep--vertical — use align-self:center in flex parents to keep it centered. Line color uses --color-text at low opacity — visible on any background. Common in navbars, toolbars, sidebar sections, and between content blocks.

HORIZONTAL

Tight spacing


Default spacing


Loose spacing


After lg separator

VERTICAL (in a toolbar)

EXSA Features Docs Themes v1.0.0-rc.1
<hr class="sep"> <!-- vertical in a toolbar --> <div style="display:flex;align-items:center;background:var(--color-bg-secondary);border-radius:var(--border-radius);padding:6px 14px;"> <span style="font-size:.8rem;font-weight:600;">EXSA</span> <span class="sep sep--vertical" style="height:18px;align-self:center;margin:0 10px;"></span> <span class="badge badge--primary" style="font-size:.7rem;">v1.0.0-rc.1</span> </div>

Code Block

Syntax-highlighted code block with a copy-to-clipboard button. The .code-block__header shows a language label; the copy button sits beside it. Clicking the button copies the <code> content and shows a green "Copied!" checkmark for 2 seconds. Omit the header with .code-block--no-header — the copy button overlays the top-right corner of the code area. Add .code-block--sm for a compact variant with smaller padding and font. Uses --color-bg-secondary for the background, --color-link for hover, and --color-success for the copied state. Requires ~86 lines of JavaScript for the clipboard API.

HTML
<div class="card card--hoverable">
  <img class="card__image" src="cover.jpg" alt="">
  <div class="card__body">
    <h4 class="card__title">Design Tokens</h4>
    <p class="card__text">82 design tokens drive every component.</p>
  </div>
  <div class="card__footer">
    <span class="badge badge--primary">v2.4</span>
  </div>
</div>
/* EXSA theme token reference */
:root {
  --color-bg:           #fff;
  --color-bg-secondary: #e9e9e9;
  --color-text:         #000;
  --color-link:         #118bee;
  --color-success:      #16a34a;
  --color-danger:       #dc2626;
  --border-radius:      5px;
  --box-shadow:         2px 2px 10px;
}
CSS
.btn { display: inline-flex; align-items: center; gap: .4em; }
.btn--primary { background: var(--color-link); color: #fff; }
<div class="code-block"> <div class="code-block__header"> <span class="code-block__lang">HTML</span> <button class="code-block__copy" aria-label="Copy code"><span class="ic ic-copy"></span> Copy</button> </div> <pre class="code-block__body"><code>...</code></pre> </div>

Activity Feed

Grouped timeline feed (today / yesterday) with dot colors: --danger, --warning, --muted (default success). Pure CSS; pair with avatar.css for richer rows.

Today
Invoice #1042 paid
by Acme Corp · $1,240
09:41
Deploy delayed
CI queue full
08:12
Yesterday
Payment failed
Card expired
17:03
Backup completed
Nightly job
03:00
<div class="activity"> <div class="activity__group"> <div class="activity__header">Today</div> <div class="activity__list"> <div class="activity__item"> <div class="activity__body"> <div class="activity__title">Invoice #1042 paid</div> <div class="activity__text">by Acme Corp · $1,240</div> </div> <span class="activity__time">09:41</span> </div> <div class="activity__item activity__item--warning"> <div class="activity__body"> <div class="activity__title">Deploy delayed</div> <div class="activity__text">CI queue full</div> </div> <span class="activity__time">08:12</span> </div> <div class="activity__item activity__item--danger"> <div class="activity__body"> <div class="activity__title">Payment failed</div> <div class="activity__text">Card expired</div> </div> <span class="activity__time">17:03</span> </div> <div class="activity__item activity__item--muted"> <div class="activity__body"> <div class="activity__title">Backup completed</div> <div class="activity__text">Nightly job</div> </div> <span class="activity__time">03:00</span> </div> </div> </div> </div>

Empty State

Icon + title + CTA placeholder for blank lists and tables. .empty-state--bordered adds the card border; .empty-state--sm is the compact variant.

No projects yet

Create your first project to get started.

No results

Try a different search term.

<div class="empty-state empty-state--bordered"> <span class="empty-state__icon"><span class="ic ic-inbox"></span></span> <h3 class="empty-state__title">No projects yet</h3> <p class="empty-state__text">Create your first project to get started.</p> <div class="empty-state__actions"> <button class="btn btn--primary">New project</button> <button class="btn btn--outline">Import</button> </div> </div> <!-- compact: .empty-state--sm --> <div class="empty-state empty-state--sm"> <span class="empty-state__icon"><span class="ic ic-search"></span></span> <h3 class="empty-state__title">No results</h3> <p class="empty-state__text">Try a different search term.</p> </div>

Music Player

Music playback UI with two-part structure: a .media-display for album art + play overlay, and a .media-bar for transport controls (prev/play/next), time readout, and volume. Set data-duration (in seconds) on the .music-player wrapper to display total time. Requires ~34 lines of JavaScript for play/pause toggle and time tracking. Colors use --color-link for the play button and --color-text for time labels.

1:26 / 4:02
<div class="block music-player" data-duration="242"> <div class="media-display"> <button class="media-display__play music-player__overlay-play" aria-label="Play"><span class="ic ic-play"></span></button> </div> <div class="media-bar"> <button class="media-bar__btn" aria-label="Previous"><span class="ic ic-chevleft"></span></button> <button class="media-bar__btn music-player__play" aria-label="Play"><span class="ic ic-play"></span></button> <button class="media-bar__btn" aria-label="Next"><span class="ic ic-chevright"></span></button> <div class="media-bar__spacer"></div> <span class="media-bar__time music-player__current">1:26</span> <span class="media-bar__sep">/</span> <span class="media-bar__time">4:02</span> <div class="media-bar__spacer"></div> <button class="media-bar__btn" aria-label="Volume"><span class="ic ic-volume"></span></button> </div> </div>

Slideshow

Accessible image carousel with ARIA role="region" and aria-roledescription="carousel". Auto-advances every 4.2 seconds (5s after manual navigation). Each slide supports an optional <figcaption>. Prev/next arrow buttons and dot navigation are generated by JavaScript (~39 lines). Active dot uses --color-link; container corners use --border-radius. Wrap multiple slideshows by giving each a unique id.

Modular component architecture
Token-driven component architecture
Theme color palettes
Twenty themes from 82 tokens
CSS bundle package
Download full CSS — own it
Fast performance
Zero runtime dependencies
<div class="slideshow"> <div class="slideshow__track"> <div class="slideshow__slide">...</div> </div> <button class="slideshow__arrow slideshow__arrow--prev"><span class="ic ic-chevleft"></span></button> <button class="slideshow__arrow slideshow__arrow--next"><span class="ic ic-chevright"></span></button> <div class="slideshow__dots">...</div> </div>

Donut Chart

Pure CSS donut chart using the clip-rect technique. Up to 4 slices, each controlled by a CSS variable: --s1 through --s4 (set as degrees, e.g. 90deg). Slice colors use --color-link, --color-secondary, and --color-bg-secondary. Center displays a value and label via .donut__center-val and .donut__center-label. Pair with .donut-legend for labeled items with percentages. Zero JavaScript.

JUNE
2026

iOS

21%

Mac

48%

Linux

9%

<div class="donut"> <div class="donut__slice donut__slice--1"><div class="fill"></div></div> <div class="donut__slice donut__slice--2"><div class="fill"></div></div> <div class="donut__slice donut__slice--3"><div class="fill"></div></div> <div class="donut__slice donut__slice--4"><div class="fill"></div></div> <div class="donut__center"> <div class="donut__center-val">JUNE</div> <div class="donut__center-label">2026</div> </div> </div> <div class="donut-legend"> <div class="donut-legend__item"> <p class="donut-legend__label">iOS</p> <p class="donut-legend__pct">21%</p> </div> </div>

Bar Chart

Horizontal bar chart built from stacked .bar-row elements. Each row has a .bar-row__label (left), a .bar-row__track with a .bar-row__fill bar, and a .bar-row__val (right). Set bar width via the CSS variable --bar-w (0–100%) on the fill element. Bar color is set via inline background — use --color-link, --color-secondary, or any token. Track background uses --color-bg-secondary. Pure CSS, no JavaScript.

Apr
24k
May
31k
Jun
38k
Jul
28k
Aug
44k
<div class="bar-row"> <span class="bar-row__label">Apr</span> <div class="bar-row__track"> <div class="bar-row__fill" style="--bar-w:62%"></div> </div> <span class="bar-row__val">24k</span> </div>

Progress Bars

Progress indicator with icon, label, percentage readout, and gradient-filled track. Set fill width with --fill-pct (0–100%) and color with --fill-clr on the .progress-item__fill element. The fill renders as a gradient from the chosen color to a lighter tint. Each .progress-item contains a head row (.progress-item__head) with optional icon, label, and percentage, plus a track. Uses --color-bg-secondary for the empty track. Pure CSS — no JavaScript needed.

Downloading assets...81%
Uploading build...43%
CSS compiling...96%
<div class="progress-item"> <div class="progress-item__head"> <span class="progress-item__icon"><span class="ic ic-download"></span></span> <span class="progress-item__label">Downloading assets...</span> <span class="progress-item__pct">81%</span> </div> <div class="progress-item__track"> <div class="progress-item__fill" style="--fill-pct:81%;--fill-clr:var(--color-link)"></div> </div> </div>

Range Slider

Custom styled <input type="range"> with live value display. Thumb uses --color-link fill with white border. Track uses --color-bg-secondary. Add .range--sm for compact variant.

65
32
88
<div class="range"> <input type="range" class="range__input" id="rng1" min="0" max="100" value="65" aria-label="Volume level"> <span class="range__val" id="rng1-val">65</span> </div> <!-- compact: .range--sm --> <div class="range range--sm"> <input type="range" class="range__input" id="rng3" min="0" max="100" value="88" aria-label="Zoom level"> <span class="range__val" id="rng3-val">88</span> </div>

Calendar

Month grid + event chips rendered from data attributes. Behavior: js/calendar.js — builds the grid, month navigation, today highlight, and fires exsa:day-select / exsa:event-click / exsa:month-change.

<div class="calendar" data-calendar-date="2026-08-24" data-calendar-week-start="0" data-calendar-events='[{"date":"2026-08-21","title":"Release v1.0","type":"primary"}]'></div>

Chart

SVG line / area / bar / sparkline from data attributes — zero third-party dependencies. Behavior: js/chart.js generates the SVG; CSS styles it. Types via class (.chart--line/area/bar/sparkline) or data-chart-type.

<div class="chart chart--line" data-chart-labels="Mon,Tue,Wed,Thu,Fri" data-chart-values="12,28,19,33,24" aria-label="Weekly sales"></div> <!-- also: chart--area | chart--bar | chart--sparkline -->

KPI Card

Big number + delta (+ sparkline/progress) for dashboards. .kpi__delta--up/--down color the trend; .kpi-grid lays cards out responsively. Standalone — works with or without card.css.

Revenue
$48,290
12.4%vs last month
Signups
1,204
3.1%this week
Churn
2.8%
0.4%watch this
<div class="kpi-grid"> <div class="kpi"> <div class="kpi__head"> <span class="kpi__label">Revenue</span> <span class="kpi__icon"><span class="ic ic-chart"></span></span> </div> <div class="kpi__value">$48,290</div> <div class="kpi__foot"> <span class="kpi__delta kpi__delta--up">12.4%</span> <span class="kpi__delta-note">vs last month</span> </div> </div> </div>

Checkbox

Custom checkbox with a CSS-drawn checkmark. Checked state fills the box with --color-link; the check draws in with a scale animation (a rotated border ::after — no SVG, no JavaScript). Add the disabled attribute to the hidden <input> for a muted, non-interactive state. Label goes after .checkbox__box inside the <label> wrapper. Pure CSS using :checked on the hidden input. Group checkboxes by sharing a common context in a <fieldset>.

<label class="checkbox"> <input type="checkbox" checked> <span class="checkbox__box"></span> <span class="checkbox__label">Accept terms</span> </label>

Color Picker

Styled wrapper around the native <input type="color">. Click the swatch to open the OS color picker. Hex value displays below. Three sizes: default, .color-picker--sm, .color-picker--lg.

Theme Color
<div class="color-picker"> <span class="color-picker__label">Theme Color</span> <div class="color-picker__swatch"> <input type="color" class="color-picker__input" value="#3b82f6" aria-label="Pick a theme color"> </div> <span class="color-picker__value"></span> </div> <!-- sizes: --sm | --lg --> <div class="color-picker color-picker--sm"> <div class="color-picker__swatch"><input type="color" class="color-picker__input" value="#ef4444"></div> <span class="color-picker__value"></span> </div> <div class="color-picker color-picker--lg"> <div class="color-picker__swatch"><input type="color" class="color-picker__input" value="#22c55e"></div> <span class="color-picker__value"></span> </div>

Advanced Color Picker

HSL + Alpha color picker with canvas saturation/lightness field, hue/light/alpha sliders, and hex input. Click the swatch trigger to open the dropdown panel. Uses --color-link, --color-bg, --border-radius, and --box-shadow tokens. Requires ~133 lines of JavaScript.

<div class="ex-cp"> <div class="ex-cp__trigger"></div> <div class="ex-cp__drop"> <div class="ex-cp__canvas-wrap"> <canvas class="ex-cp__canvas"></canvas> <div class="ex-cp__dot"></div> </div> <div class="ex-cp__sliders"> <label>Hue</label> <input type="range" class="ex-cp__slider--hue"> <label>Light</label> <input type="range" class="ex-cp__slider--light"> <label>Alpha</label> <input type="range" class="ex-cp__slider--alpha"> </div> <div class="ex-cp__hex"> <button class="ex-cp__eyedrop"></button> <!-- pick color from screen --> <div class="ex-cp__hex-swatch"></div> <input class="ex-cp__hex-input"> </div> </div> </div>

Date Picker

Interactive calendar popup with month navigation, today highlight, and click-to-select dates. Click the input to open. Adapted from GeeksForGeeks. Uses --color-link, --color-bg-secondary, and --border-radius tokens.

July 2026
SunMonTueWedThuFriSat
<div class="date-picker__wrapper"> <div style="position:relative;display:flex;align-items:center;"> <span class="ic ic-calendar" style="position:absolute;left:10px;z-index:1;width:16px;height:40px;pointer-events:none;"></span> <input class="date-picker__input" placeholder="Pick a date..." readonly style="padding-left:34px;" aria-label="Pick a date"> </div> <div class="date-picker"> <div class="date-picker__header"> <button class="date-picker__nav date-picker__nav--prev" aria-label="Previous month"><span class="ic ic-chevleft"></span></button> <span class="date-picker__header-date">July 2026</span> <button class="date-picker__nav date-picker__nav--next" aria-label="Next month"><span class="ic ic-chevright"></span></button> </div> <div class="date-picker__weekdays"><span>Sun</span><span>Mon</span>…</div> <div class="date-picker__dates"></div> </div> </div>

Form Validation

Input error and success states with icon indicators and helper text. Uses semantic --color-danger and --color-success tokens with focus-ring shadows. Add .input--error, .input--success, or .form-helper--error to any input.

Please enter a valid email address. Username is available. Must be at least 8 characters with one number.
<input class="input--error" value="user@com"> <span class="form-helper form-helper--error">Invalid email</span> <input class="input--success" value="john_doe"> <span class="form-helper form-helper--success">Available</span>

Form Required

Automatic required-field feedback — zero JavaScript. Required labels get a danger-colored asterisk via label:has(+ :required)::after, and any form containing a :user-invalid field gets a danger border via :has(). Works on any page with the exsa body class. Pair with form-validation.css for per-field error states.

The red asterisks next to Full name and Email are added automatically from the required attribute — no extra markup. Submit empty, or type an invalid email and blur, and the form border turns danger red automatically.

<form> <label>Full name</label> <input type="text" required> <label>Email</label> <input type="email" required> <button type="submit" class="btn btn--primary">Submit</button> </form> <!-- Label gets a red * automatically; form border turns red when a field becomes :user-invalid -->

Icon Button

Now part of buttons.css. Use .btn .btn--icon for circular icon-only buttons, or .btn .btn--radius-full for pill-shaped icon+label combos. Combine with .btn--surface, .btn--ghost, or .btn--outline for styling.

<button class="btn btn--icon btn--primary" aria-label="Search"><span class="ic ic-search"></span></button>

Input Group

Label + icon/addon + input combined into one visual unit with a shared focus ring. Two adornment types: .input-group__icon for leading SVG icons (mail, search, user) and .input-group__addon for text prefixes like "https://". The focus ring uses --color-link blended semi-transparently. The wrap border uses --color-bg-secondary. Optional .input-group__label sits above the wrap. Works with type="email", type="text", type="search", and type="password".

https://
<div class="input-group"> <label class="input-group__label">Email Address</label> <div class="input-group__wrap"> <span class="input-group__icon"><span class="ic ic-mail"></span></span> <input class="input-group__input" type="email" placeholder="you@example.com"> </div> </div> <!-- text addon --> <div class="input-group"> <label class="input-group__label">Website</label> <div class="input-group__wrap"> <span class="input-group__addon">https://</span> <input class="input-group__input" type="text" placeholder="yoursite.com"> </div> </div> <!-- search icon --> <div class="input-group"> <div class="input-group__wrap"> <span class="input-group__icon"><span class="ic ic-search"></span></span> <input class="input-group__input" type="text" placeholder="Search components..."> </div> </div>

Loading Button

Built-in loading state via .btn--loading. Text hides, spinner appears centered — works on all variants. Outline/ghost/soft/surface variants get a link-colored spinner. No extra markup needed.

<button class="btn btn--loading btn--primary">Submit</button> <button class="btn btn--loading btn--neutral">Upload</button>

Password Input

Password field with inline show/hide toggle. Focus ring uses --color-link. Toggle button reveals the plain-text value by swapping input type. Requires ~24 lines of JavaScript.

<div class="password-input"> <input type="password" class="password-input__field"> <button class="password-input__toggle"><svg>...</svg></button> </div>

Popover

Click-triggered floating card for rich content — forms, text, media, or actions. Arrow points to the trigger. Four sizes: --sm, default, --lg, --xl. Align with .popover--align-start or .popover--align-end. Closes on outside click or Escape.

User

Notifications

You have 3 unread messages and 1 pending invite.

<div class="popover"> <button class="btn btn--soft popover__trigger">💬 Comment</button> <div class="popover__content"> <div class="popover__arrow"></div> <textarea placeholder="Write a comment…" aria-label="Comment"></textarea> <button class="btn btn--primary btn--sm">Comment</button> </div> </div>

Radio Button

Styled radio button group. Each <label class="radio"> wraps a hidden <input type="radio">, a .radio__circle (outer ring + inner dot), and a .radio__label. All inputs in a group share the same name attribute for mutual exclusion. Checked state fills the inner dot with --color-link; the outer ring uses --color-bg-secondary. Add the disabled attribute to the input for a muted, non-interactive option. Pure CSS — no JavaScript required.

<label class="radio"> <input type="radio" name="theme" checked> <span class="radio__circle"></span> <span class="radio__label">Light theme</span> </label>

Rating Stars

Interactive 5-star rating. Click to set, hover to preview. Uses CSS direction trick for right-to-left fill. Requires ~27 lines of JavaScript for click handling.

3 / 5
<div class="rating"> <input type="radio" ...><label class="rating__star">★</label> <!-- repeat 5 times --> </div> <span class="rating__value">3 / 5</span>

Styled Select

Custom <select> wrapper with chevron icon, focus ring, and size variants. Uses --color-link for focus and --color-text-secondary for the chevron. Add .select--sm or .select--lg for compact/large variants.

<div class="select"> <select class="select__field"> <option>Choose a theme...</option> <option>Breeze</option> </select> </div> <!-- error state --> <div class="select select--error"> <select class="select__field"> <option>Error state</option> </select> </div>

Tabs

Tabbed interface for switching between content panels. Click a .tabs__tab to show its matching panel — linked by matching the tab's data-panel value to a panel's id prefixed with panel-. Active tab gets a bottom border in --color-link. Requires ~41 lines of JavaScript to toggle panels. Optional modifiers: .tabs--vertical for side tabs, .tabs--full for equal-width tabs filling the container. Panels use .tabs__panel--active to control visibility.

Tab components use --color-link for the active indicator line. The border and text colors follow the active theme from the CSS custom properties token file.

82 design tokens drive every visual aspect. Change one theme file and all components — tabs, toggles, spinners, everything — update simultaneously.

Add exsa.css alongside a theme. Pick your components. Zero configuration. All 68 Components ready to use.

<div class="tabs"> <button class="tabs__tab tabs__tab--active" data-panel="dt1">Overview</button> <button class="tabs__tab" data-panel="dt2">Tokens</button> </div> <div class="tabs__panel tabs__panel--active" id="panel-dt1">...</div> <div class="tabs__panel" id="panel-dt2">...</div>

Toggle Switch

Pure CSS toggle switch built on a hidden <input type="checkbox"> with the :checked pseudo-class. Active (on) state fills the track with --color-link and slides the thumb knob to the right. Add .toggle--sm for a compact 32px-wide variant. Add the disabled attribute to the input for a muted, non-interactive state. Label text goes after the track inside the <label> wrapper. Zero JavaScript.

<label class="toggle"> <input type="checkbox" checked> <span class="toggle__track"></span> <span class="toggle__label">Email notifications</span> </label> <label class="toggle toggle--sm"> <!-- compact variant --> <input type="checkbox" checked> ... </label>

Buttons

Button system with 9 variants, 4 sizes, and built-in loading. Theme-aware: uses --color-link, --color-success, --color-danger, --color-warning tokens. Works on both <button> and <a> elements. Mods: .btn--xs, .btn--sm, .btn--lg, .btn--icon, .btn--block, .btn--loading, .btn--high-contrast, .btn--radius-full, .btn--radius-none, and disabled.

Solid
Subtle
HC
Sizes
Mods
States
<button class="btn btn--primary btn--radius-full">Pill</button>

Panel Resizer

Drag handle for resizable panels. Horizontal (ew-resize) and vertical (ns-resize) variants. Drag the grip to resize the parent panel. Uses --color-bg and --color-bg-secondary tokens.

Drag the handle →
<div class="resizer__target" style="width:320px"> <div class="resizer"></div> ...panel content... </div> <!-- also: .resizer--left | .resizer--bottom -->

Tags Input

Chips + type-to-add with suggestions. Behavior: js/tags-input.js — creates the text field, filters suggestions, caps at data-tags-max, and writes the hidden input (data-tags-name). Fires exsa:tags-change.

design docs
<div class="tags-input" data-tags-suggestions='["design","a11y","docs"]' data-tags-max="8" data-tags-name="tags"> <span class="tags-input__chip" data-value="design"> <span class="tags-input__chip-label">design</span> <button class="tags-input__remove">×</button> </span> <span class="tags-input__chip tags-input__chip--success" data-value="docs"> <span class="tags-input__chip-label">docs</span> <button class="tags-input__remove">×</button> </span> <!-- the text field is added by js/tags-input.js --> </div>

Transfer List

Two-panel list picker with search and move controls. Behavior: js/transfer.js — click selects, double-click / Enter moves, action buttons data-action add/remove/add-all/remove-all. Fires exsa:transfer-change.

Available2
  • Editor
  • Viewer
Selected1
  • Admin
<div class="transfer" data-transfer-name="roles"> <div class="transfer__panel"> <div class="transfer__head"> <span class="transfer__title">Available</span> <span class="transfer__count badge badge--default">2</span> </div> <input class="transfer__search" type="search" placeholder="Filter…" aria-label="Filter available"> <ul class="transfer__list"> <li class="transfer__item" data-value="editor" tabindex="0">Editor</li> </ul> </div> <div class="transfer__actions"> <button type="button" class="transfer__btn" data-action="add" aria-label="Add selected">→</button> <button type="button" class="transfer__btn" data-action="remove" aria-label="Remove selected">←</button> </div> <div class="transfer__panel"> <div class="transfer__head"> <span class="transfer__title">Selected</span> <span class="transfer__count badge badge--default">1</span> </div> <ul class="transfer__list"> <li class="transfer__item" data-value="admin" tabindex="0">Admin</li> </ul> </div> </div>

Upload Dropzone

Dropzone + file list with progress bars. Behavior: js/upload.js — with data-upload-url it uploads via XHR with live progress; without it, progress is simulated (demo mode). Fires exsa:upload-add / -done / -error / -remove.

Drop files here or click to browse
PDF, DOC up to 10 MB
    <div class="upload"> <div class="upload__dropzone" tabindex="0" role="button" aria-label="Upload files"> <span class="upload__icon"><span class="ic ic-upload"></span></span> <div class="upload__title">Drop files here or click to browse</div> <div class="upload__hint">PDF, DOC up to 10 MB</div> <input class="upload__input" type="file" multiple hidden> </div> <ul class="upload__list"></ul> </div> <!-- real uploads: add data-upload-url="/api/upload" -->

    Alert / Notice

    Contextual feedback messages with four color variants, three styles (soft, outline, surface), and three sizes. Soft is the default tinted-background style. Outline uses a colored border with transparent background. Surface is card-like. Info variant uses --color-link; others use fixed semantic colors.

    Soft Info

    Default tinted style. Uses --color-link at 10% opacity for background.

    Outline Success

    Transparent background, colored border. Good for minimal UIs.

    Surface Style

    Card-like with subtle shadow. Neutral, works with any theme.

    Small Warning

    Compact alert--sm size for tight spaces.

    Large Danger

    Large alert--lg size. Build failed — check your theme file for missing variables.

    <div class="alert alert--danger alert--lg"> <span class="alert__icon"><span class="ic ic-error"></span></span> <div class="alert__body"> <div class="alert__title">Large Danger</div> <p class="alert__msg">Build failed — check your theme file for missing variables.</p> </div> <button class="alert__close" data-dismiss="alert" aria-label="Dismiss"><span class="ic ic-close"></span></button> </div>

    Skeleton Loader

    Shimmer placeholder for loading states. Pure CSS animation sliding a gradient across the element via background-position. The shimmer shifts between --color-bg-secondary and a lighter tint. Shape modifiers on .skeleton__line: --circle (round avatar, 40px), --h2 (thick heading, 60% height), --medium (half-width line), --short (quarter-width line), --btn (pill-shaped button). Compose realistic page skeletons by combining shapes inside a .skeleton wrapper. Zero JavaScript — pure CSS animation.

    <div class="skeleton"> <div class="skeleton__row"> <div class="skeleton__line skeleton__line--circle"></div> <div class="skeleton__line skeleton__line--h2"></div> </div> <div class="skeleton__line"></div> <div class="skeleton__line skeleton__line--medium"></div> <div class="skeleton__line skeleton__line--short"></div> </div>

    Spinner

    CSS-only loading spinner using the border technique: a full circle with a transparent portion creates the spinning arc. The colored arc uses --color-link, the inactive track uses --color-bg-secondary. Three sizes: .spinner--sm (20px), default (36px), .spinner--lg (52px). Use inline within buttons or text, or center it in a container with flex/grid. To override the arc color, set border-top-color inline. Pure CSS animation via @keyframes spin — zero JavaScript.

    <div class="spinner spinner--sm"></div> <div class="spinner"></div> <div class="spinner spinner--lg"></div>

    Toast Notifications

    Temporary notification that appears in the bottom-right corner and auto-dismisses after 3.5 seconds. Three semantic variants: .toast--success (green), .toast--error (red), .toast--info (neutral). Each toast contains an icon, message, and optional close button. Toasts stack vertically and slide in/out with a CSS transition. Requires a #toast-container element on the page and ~31 lines of JavaScript to create and remove toasts. Uses --border-radius and --box-shadow from the theme.

    <div class="toast toast--success"> <span class="toast__icon"><span class="ic ic-check"></span></span> <span class="toast__msg">File saved!</span> <button class="toast__close"><span class="ic ic-close"></span></button> </div>

    Settings Panel

    This panel slides in from the right using pure CSS. The toggle is a hidden checkbox — click the backdrop or close button to dismiss.

    It reads --color-bg for its background, --color-bg-secondary for borders, and uses box-shadow for depth.