Fast, accessible UI primitives built on semantic HTML and modern CSS.
Documentation · Getting Started · Components · Examples · Contributing
Hydrateless is a lightweight component library that delivers accessible, themeable UI primitives using semantic HTML and modern CSS first, with no JavaScript by default. Overlays ride on the web platform's own building blocks: the Popover API, HTML Invoker Commands (command/commandfor), the native <dialog> element, and CSS anchor positioning, while <details> and the :has() selector drive disclosures and tabs. A button opens a modal or a popover with no script at all. That keeps runtime cost near zero, and tiny JavaScript enhancers auto-load only to add the accessibility the platform can't yet express on its own, such as roving focus, arrow-key navigation, and ARIA wiring.
- CSS-first components: Accordions, tabs, modals, drawers, tooltips, and more work out of the box with no JavaScript.
-
Built on the web platform: Overlays use the Popover API, Invoker Commands, the native
<dialog>, and CSS anchor positioning instead of reinventing them in script, so light-dismiss, the top layer, focus trapping, and positioning come from the browser. Targets the modern Baseline. - Tested in real browsers: Beyond unit tests, a Playwright and axe end-to-end suite exercises every component, with JavaScript both off and on, across Chromium, Firefox, and WebKit, so the no-JS baseline and the enhanced experience are both verified for accessibility.
- A full component set: Forms (button, input, textarea, select, checkbox, radio group, switch, slider, segmented control, combobox, field), actions & overlays (dropdown, menu, modal, drawer, popover, tooltip, command palette), feedback (alert, badge, progress, spinner, skeleton, toast), data display (card, avatar, sortable table, kbd), and navigation (breadcrumb, pagination, table of contents, separator). Every interactive component has an enhancer, including dismissible alerts, checkbox groups with select-all, sliders with a live readout, sortable tables, pagination, and button-based segmented controls.
-
Optional JS enhancers: Add keyboard navigation, focus return, and ARIA management only where needed. Every enhancer follows the same contract:
enhanceX(container?, options?)returns an imperative API (value/setValue,open/setOpen) and emitshl:change,hl:open-change,hl:select, orhl:commandDOM events. Enhancers are safe no-ops on the server. -
Configured from markup: Every non-function option has a
data-hl-*attribute (data-hl-activation="automatic",data-hl-allow-multiple,data-hl-placement="end"), so a server-rendered page or a CDN<script>tag gets the full range of behavior with no configuration code. Each enhancer exposes itsdefinition(selector, defaults, attribute schema) for tooling. - Live by default: Enhancers watch their root, so tabs, menu items, combobox options, table rows, and pagination controls added or removed after enhancement take part without re-running anything.
-
APG keyboard behavior: Menus, dropdowns, tabs, accordions, comboboxes, and the command palette follow the WAI-ARIA Authoring Practices: roving focus, arrow, Home/End, PageUp/PageDown, typeahead, disabled-item skipping,
menuitemcheckbox/menuitemradiostate, nested submenus to any depth, Escape-to-dismiss, and focus returned to the trigger when an overlay closes. Placement and arrow keys are logical, so everything mirrors underdir="rtl". -
Controlled or uncontrolled: Framework components support both modes:
defaultValue/defaultOpenfor hands-off use,value/openwith change callbacks (orv-model/bind:) for full control. -
Auto-initialization: The
@hydrateless/autopackage detectsdata-hl-*attributes, lazy-loads the right enhancers, keeps watching the DOM so dynamic content is enhanced automatically, and isolates failures so one broken component never takes down the rest. Opt out with<html data-hl-manual>to call it yourself. - Design tokens: Theme every component through CSS variables for colors, spacing, radii, typography, motion, focus rings, and overlay sizes.
-
Dark mode: Colors are defined once with
light-dark(); the system preference applies automatically, anddata-theme="light|dark"overrides it for the page or any subtree. -
CSS layers: All styles use
@layer, so your custom CSS can override defaults without specificity battles. - Accessible by default: ARIA roles, keyboard support, focus management, forced-colors support, reduced-motion handling, and skip links are built in.
-
RTL ready: Every stylesheet uses logical properties, so drawers, switches, breadcrumbs, and menus mirror correctly under
dir="rtl". - Modular imports: Import the full bundle or individual component stylesheets as needed.
-
Framework parity: First-class, fully typed component suites for React, Vue, and Svelte with identical component lists and prop contracts, a single
useEnhancerescape hatch per framework, and an Astro guide. - CDN ready: Drop in minified CSS and a self-contained auto-initializer from unpkg or jsDelivr. No build step required.
npm install hydrateless @hydrateless/auto<link rel="stylesheet" href="node_modules/hydrateless/dist/hydrateless.css" />
<script type="module" src="node_modules/@hydrateless/auto/dist/index.js"></script>
<!-- Options live in the markup: data-hl-allow-multiple lets both panels stay open. -->
<div data-hl-accordion data-hl-allow-multiple>
<details>
<summary>Section one</summary>
<div class="hl-accordion-panel">First panel content.</div>
</details>
<details>
<summary>Section two</summary>
<div class="hl-accordion-panel">Second panel content.</div>
</details>
</div># Pick your framework binding
npm install hydrateless @hydrateless/react
npm install hydrateless @hydrateless/vue
npm install hydrateless @hydrateless/svelteEvery binding ships the same first-class component suite with an identical, compound API; only the framework idioms differ:
// React
import 'hydrateless/hydrateless.css';
import { Tabs, TabList, Tab, TabPanel } from '@hydrateless/react';
<Tabs>
<TabList>
<Tab>One</Tab>
<Tab>Two</Tab>
</TabList>
<TabPanel>First</TabPanel>
<TabPanel>Second</TabPanel>
</Tabs>;<!-- Svelte 5 -->
<script>
import 'hydrateless/hydrateless.css';
import { Tabs, TabList, Tab, TabPanel } from '@hydrateless/svelte';
</script>
<Tabs>
<TabList>
<Tab>One</Tab>
<Tab>Two</Tab>
</TabList>
<TabPanel>First</TabPanel>
<TabPanel>Second</TabPanel>
</Tabs>See the framework guides for React, Vue, Svelte, and Astro.
No build step? Pull the minified CSS and the self-contained auto-initializer straight from a CDN:
<link rel="stylesheet" href="https://unpkg.com/hydrateless/dist/hydrateless.min.css" />
<script type="module" src="https://unpkg.com/@hydrateless/auto/dist/hydrateless.js"></script>Hydrateless targets the modern Baseline. Components build on the Popover API, the native <dialog> element, HTML Invoker Commands (command/commandfor), and CSS anchor positioning, which are interoperable across current Chrome, Edge, Firefox, and Safari. Where an engine hasn't shipped CSS anchor positioning yet, the enhancers fall back to a tiny JavaScript positioner so floating surfaces still land against their anchor. Behavior is verified continuously against Chromium, Firefox, and WebKit (see packages/e2e). The full feature-by-feature matrix is in the browser support guide.
To report a vulnerability, follow the process in SECURITY.md. Please don't open a public issue for security reports.
Full documentation (guides, framework integrations, per-component live demos, and the generated API reference) lives at hydrateless.com.
Contributions are welcome. Please see CONTRIBUTING.md for setup instructions, coding standards, and guidelines for submitting pull requests. ROADMAP.md lists what 1.0 means and what's planned on the way there.
