Open, accessible, themable web components — vanilla HTML, CSS, and minimal JavaScript.
- Vanilla first. HTML, CSS, absolutely minimal JavaScript. No frameworks.
- Themable. Every color, font, spacing value, border radius, and shadow is a CSS custom property. Swap the theme file to restyle everything.
- Accessible. WCAG 2.2 AA as baseline. Keyboard navigation, focus-visible rings, screen-reader support,
prefers-reduced-motion, andprefers-color-schemeare built in. - Drop-in. Each component works independently. Copy the HTML, link the CSS, done.
<!-- Option A: Link the built single-file (recommended) -->
<link rel="stylesheet" href="dist/ui.min.css" />
<!-- Option B: Link the source entry (uses @import, fine for dev) -->
<link rel="stylesheet" href="base/main.css" />For dark/light mode toggling, add the script and a toggle button:
<script src="js/theme-ext-toggle.js"></script>
<button data-theme-toggle aria-label="Toggle dark mode">🌓</button>Lightning CSS bundles and minifies CSS; esbuild bundles and minifies JS.
npm install
npm run build # → dist/ui.min.css + dist/*.min.js (bundled + minified)
npm run dev # → CSS watch modethemes/
flexoki.css ← Flexoki 2.0 palette + semantic tokens + light/dark
base/
main.css ← CSS source entry point (imports everything)
reset.css ← modern CSS reset
typography.css ← headings, prose, lists, code, links
forms.css ← shared form styles, validation states
media.css ← images, details, progress, dialog
grid.css ← reusable grid utilities (.cols-2/3/4, .grid-auto)
components/
alert.css ← info, success, warning, danger, dismissible
avatar.css ← image and initials variants
badge.css ← color variants for all accent colors
button.css ← primary, secondary, outline, ghost, danger, sizes
card.css ← header/body/footer, image variant, compact
checkbox.css ← styled checkbox with indeterminate state
contents.css ← sticky sidebar table of contents
combobox.css ← searchable select extending listbox
description-list.css ← horizontal and vertical term/definition pairs
dialog.css ← modal and non-modal dialog
divider.css ← horizontal and vertical separators
dropdown.css ← trigger + floating menu
fieldset.css ← grouped form controls with legend
heading.css ← display and section headings
input.css ← text, number, date, and other input types
listbox.css ← inline/dropdown listbox with icons, avatars, flags
navbar.css ← responsive nav with logo, icons, indicators, dropdowns, vertical mode
pagination.css ← page navigation controls
radio.css ← radio button group
select.css ← native and custom select
switch.css ← toggle switch (checkbox variant)
table.css ← base styles, responsive wrapper, striped/grid/condensed/wide variants
text.css ← inline text elements
textarea.css ← auto-resizing textarea
user-menu.css ← avatar dropdown for user account actions
js/
alert-ext-dismiss.js ← dismiss .alert on .alert-dismiss click
checkbox-ext-select-all.js ← select-all with indeterminate state
contents-ext-bookmark.js ← scroll-spy, h3 injection, and section collapse for contents
dialog.js ← open/close native <dialog> via data attributes
dropdown.js ← toggle .dropdown menus; click-outside + Escape
input-ext-search.js ← expand-on-focus search with / shortcut
listbox.js ← panel and dropdown listbox; keyboard nav, multi-select, live region
listbox-ext-combobox.js ← filterable combobox extending listbox.js
textarea-ext-count.js ← character count for .textarea-field[maxlength]
navbar-ext-indicator.js ← animated sliding tab indicator, one active per navbar
navbar.js ← toggle, overlay, and Escape for .navbar-toggle
theme-ext-toggle.js ← dark/light toggle with localStorage persistence
user-menu.js ← user menu open/close behaviour
layouts/
auth.html / auth.css ← centred authentication layout
sidebar.html / sidebar.css ← sidebar + main content layout (uses navbar classes)
stacked.html / stacked.css ← stacked (app shell) layout (uses navbar classes)
tests/
elements.html ← living reference of all styled HTML elements
components.html ← preview of all components
dist/
ui.min.css ← built CSS output (gitignored)
*.min.js ← built JS output (gitignored)
The theme system has three layers:
- Raw palette (
--flexoki-red-600, etc.) — Flexoki 2.0's full extended palette. - Design tokens (
--space-4,--radius-md,--font-size-base) — spacing, typography, borders. - Semantic tokens (
--color-danger,--color-action,--color-surface) — what components actually use.
Light/dark mode is automatic via prefers-color-scheme. Override with .theme-light or .theme-dark on <html>.
To create a new theme, define the same semantic token variables in a new CSS file and swap the import.
- Focus rings via
:focus-visible(keyboard only, not mouse clicks) aria-invalid,aria-required,aria-describedbypatterns on formsrole="alert"andaria-livefor dynamic messagesprefers-reduced-motiondisables all animation/transitionscolor-schemedeclaration for native form control theming- Semantic HTML throughout:
<nav>,<main>,<article>,<dialog>,<details>
Color palette: Flexoki by Steph Ango (MIT License).