Liking cljdoc? Tell your friends :D

BareDOM

Project Logo

Native web components. Zero runtime. No framework required.

npm version license ESM TypeScript Custom Elements v1


What is BareDOM?

BareDOM is a library of UI components built entirely on web standards — Custom Elements v1, Shadow DOM, and ES modules. There is no framework runtime, no virtual DOM, and no JavaScript framework peer dependency. Every component is a native HTML element that you register once and use anywhere.

The core rendering model is deliberately simple:

DOM = f(attributes, properties)

Components are stateless. Every visual state is derived directly from attributes and properties at render time. There are no hidden reactive systems, no internal signals, no component lifecycles to manage. Set an attribute, the DOM updates. Remove it, the DOM updates back.

BareDOM is authored in ClojureScript and compiled to optimised, minified ES modules using Google Closure's advanced compilation pass.

BareDOM has been created using Claude Code. The CLAUDE.md file is added to the repository for your convenience.

All web components, including new, modern, and exciting ones such as x-morph-stack, x-liquid-fill, x-liquid-glass, x=soft-body, x-ripple-effect, x-scroll-parallax, x-scroll-story, x-scroll-timeline, x-kinetic-font, x-kinetic-typography, x-organic-shape, x-gaussian-blur, x-neural-glow, x-metaball-cursor, x-organic-progress, and x-particle-button, can be seen here


Why BareDOM?

Like most Clojure/ClojureScript developers starting out with UIs, I went through the common phases of using Reagent and Re-frame—which are great utilities in their own right. However, as my UIs became larger and more complex, bundle sizes increased, and I found myself spending too much time rebuilding generic, reusable components from scratch.

I started looking for a different approach and discovered Web Components. I built a few, but didn't have the spare time to develop a comprehensive set that could be used in any project. Then AI arrived. While experimenting with Claude Code, I realised that 1 + 1 could be 3. That is how BareDOM, my first open-source project, was born.

I first built the usual suspects for web components, a basis to create a UI. I then thought about trying something more exciting, with animations, shapes, colours, and a whole range of web components that deal with morphing, kinetics and organic styles were born. I hope it brings you joy when using them in your web application!

Why web components?

Works in any stack. Because components are native HTML elements, they work wherever HTML works — vanilla JavaScript, React, Vue, Svelte, Angular, server-rendered HTML, or a static page. No adapter layer, no wrapper library.

No framework lock-in. Your components are not tied to the framework you are building with today. Migrate your app, keep your components.

Tree-shakeable by design. Each component is a separate ES module. Import only what you use; bundle tools eliminate the rest automatically.

Full theming with CSS custom properties. Every visual detail — colours, spacing, radius, shadows, typography — is exposed as a --x-<component>-<property> CSS custom property. Override at any scope: globally, per-page, per-instance. Use <x-theme> for centralised theming with built-in presets.

Light and dark mode included. All components adapt automatically to prefers-color-scheme. No JavaScript required, no class toggling.

Accessibility built in. ARIA roles, live regions, keyboard navigation, focus management, and prefers-reduced-motion support are part of the component, not an afterthought. You do not need to layer accessibility on top.

Mobile-ready. All components are tested on viewports from 320px up. Overlay panels cap their width to avoid overflow. Touch targets meet the 44px minimum on coarse-pointer devices. Pointer events are used throughout for unified mouse and touch input.

Open Shadow DOM. Shadow roots are mode: "open" — inspectable in DevTools, styleable via ::part(), and testable with standard DOM APIs.

First-class TypeScript support. Every component ships with auto-generated .d.ts type declarations and a Custom Elements Manifest. TypeScript consumers get typed element interfaces, typed custom events with detail payloads, and HTMLElementTagNameMap augmentation for querySelector type narrowing — all without installing a separate @types package.


TypeScript

BareDOM includes TypeScript declarations for every component. Types are auto-generated from component metadata and ship alongside the ESM files — no additional setup required.

What you get

  • Typed element interfaces extending HTMLElement with all properties and methods
  • Typed custom events with full detail payload types
  • HTMLElementTagNameMap augmentation so document.querySelector('x-button') returns XButton
  • Custom Elements Manifest (custom-elements.json) for IDE tooling and HTML intellisense

Usage

import '@vanelsas/baredom/x-button';
import '@vanelsas/baredom/x-alert';

// querySelector returns typed XButton
const btn = document.querySelector('x-button')!;
btn.disabled = true;    // type-checked
btn.loading = true;     // autocomplete works

// Event listeners have typed detail payloads
btn.addEventListener('press', (e) => {
  console.log(e.detail.source);  // string — fully typed
});

// Custom events on other components
const alert = document.querySelector('x-alert')!;
alert.addEventListener('x-alert-dismiss', (e) => {
  console.log(e.detail.reason);  // string
  console.log(e.detail.type);    // string
});

// Components with methods
import '@vanelsas/baredom/x-modal';
const modal = document.querySelector('x-modal')!;
modal.show();   // typed method
modal.hide();   // typed method

IDE support

The custom-elements.json manifest enables HTML intellisense in editors that support it. For VS Code, install the Lit Plugin or the Custom Elements Language Server to get attribute autocomplete and validation in HTML templates.


Theming

Wrap any subtree in <x-theme> to apply a consistent palette across all components:

<x-theme preset="ocean">
  <x-button>Themed button</x-button>
  <x-alert type="info" text="Themed alert"></x-alert>
</x-theme>

Ships with 8 built-in presets: default, ocean, forest, sunset, neo-brutalist, aurora, mono-ai, warm-mineral. All presets include both light and dark mode values that work with prefers-color-scheme.

For custom themes, register your own preset via JavaScript:

import { registerPreset } from '@vanelsas/baredom/x-theme';

registerPreset('acme', {
  light: { '--x-color-primary': '#e11d48', '--x-color-surface': '#fff' },
  dark:  { '--x-color-primary': '#fb7185', '--x-color-surface': '#1a1a2e' }
});
<x-theme preset="acme">...</x-theme>

Or override individual tokens via CSS:

<x-theme preset="default" style="--x-color-primary: #e11d48;">
  ...
</x-theme>

See docs/x-theme.md for the full token list, preset details, and API reference.


Installation

npm install @vanelsas/baredom
import { init } from '@vanelsas/baredom/x-button';
init();

Also available via Clojars and standalone ES modules. See the full installation guide.


Usage

BareDOM components are native HTML elements. Import, register, and use them in any framework or vanilla HTML.


Components

Form (17)

TagDescription
<x-button>Action control. Variants: primary, secondary, tertiary, ghost, danger. Sizes: sm, md, lg. States: disabled, loading, pressed. Icon slots.
<x-checkbox>Boolean input. Reflects checked and indeterminate states to attributes.
<x-color-picker>Colour picker with 2D saturation/brightness area, hue strip, optional alpha, preset swatches, eyedropper, and clipboard copy. Inline or popover mode.
<x-copy>Copy-to-clipboard utility button with success feedback.
<x-currency-field>Formatted currency input with locale-aware masking.
<x-date-picker>Calendar-based date selection with keyboard navigation.
<x-fieldset>Groups related form controls with a styled legend.
<x-file-download>Download trigger that initiates a file transfer.
<x-form>Form wrapper with coordinated validation state.
<x-form-field>Label + input wrapper with error and hint text slots.
<x-radio>Single-choice input within a radio group.
<x-search-field>Search input with integrated clear button and search icon.
<x-select>Dropdown select control with custom styling.
<x-slider>Range slider with step, min/max, and value display.
<x-stepper>Multi-step form progress indicator with navigation.
<x-switch>Toggle switch for boolean settings.
<x-text-area>Multi-line text input with auto-resize option.

Feedback (10)

TagDescription
<x-alert>Semantic alert banner. Types: info, success, warning, error. Auto-dismiss with timeout-ms. Fires x-alert-dismiss.
<x-badge>Small inline label for counts, states, and categories.
<x-chip>Compact tag component, optionally removable.
<x-notification-center>Notification hub for aggregating and managing in-app notifications.
<x-progress>Linear progress bar with determinate and indeterminate modes.
<x-progress-circle>Circular progress indicator for compact spaces.
<x-skeleton>Animated loading placeholder that mirrors content shape.
<x-spinner>Inline loading spinner with size and colour variants.
<x-toast>Single transient notification with enter/exit animations and auto-dismiss.
<x-toaster>Toast manager. Positions a queue of <x-toast> elements, enforces max-toasts, and fires x-toaster-dismiss.

Navigation (8)

TagDescription
<x-breadcrumbs>Hierarchical path trail with separator customisation.
<x-menu>Vertical menu container coordinating <x-menu-item> children.
<x-menu-item>Individual menu entry with icon, label, description, and keyboard support.
<x-navbar>Top navigation bar with responsive slot layout.
<x-pagination>Page navigation controls with first/previous/next/last and page-size selection.
<x-sidebar>Collapsible side navigation panel with collapse/expand animation.
<x-tab>Individual tab within an <x-tabs> container.
<x-tabs>Tab container that coordinates <x-tab> children, manages active state, and fires change events.

Layout (6)

TagDescription
<x-card>Surface container. Variants: elevated, outlined, filled, ghost. Interactive mode available.
<x-collapse>Expandable/collapsible section with animated height transition.
<x-container>Responsive max-width container with configurable padding.
<x-divider>Horizontal or vertical visual separator.
<x-grid>CSS Grid layout component with responsive column configuration.
<x-spacer>Flexible spacing element for flexbox and grid layouts.

Data (10)

TagDescription
<x-avatar>User photo or initials display. Shape, size, and status dot variants.
<x-avatar-group>Overlapping avatar stack for representing multiple users.
<x-carousel>Accessible carousel with swipe/drag, arrows, dot indicators, autoplay, slide/fade transitions, and horizontal/vertical orientation.
<x-chart>Data visualisation component for common chart types.
<x-stat>KPI / metric card with value, label, trend, and icon slots.
<x-table>Data grid using CSS subgrid. Supports sorting, single/multi-select, striping, and accessible captions.
<x-table-cell>Table cell for header and data modes, with sort indicator and alignment control.
<x-table-row>Table row with interactive selection and x-table-row-select event.
<x-timeline>Vertical timeline container that coordinates <x-timeline-item> children.
<x-timeline-item>Individual timeline event with time, icon, heading, and body slots.

Overlay (7)

TagDescription
<x-cancel-dialogue>Confirmation modal for destructive cancel actions.
<x-command-palette>Keyboard-accessible global search and command interface.
<x-context-menu>Right-click / long-press contextual action menu.
<x-drawer>Off-canvas sliding panel, configurable from any edge.
<x-dropdown>Positioned dropdown container for menus and selection.
<x-modal>Centred dialog with backdrop, focus trap, and Escape to close.
<x-popover>Anchored popover for tooltips, help text, and contextual UI.

Design Principles

Stateless. No atom, no signal, no reactive state container lives inside a component. Every render is a pure function of the current attributes and properties. Debugging a component means inspecting attributes in DevTools — no hidden state to hunt for.

Standards-only. BareDOM relies on Custom Elements v1, Shadow DOM v1, and ES modules — all natively supported in modern browsers. There are no polyfills required and no proprietary APIs to learn.

Zero runtime dependency. Components are compiled to self-contained ES modules. The only JavaScript in your bundle is the component itself. No framework, no runtime library, no utility belt.

Accessible by default. ARIA roles, live regions, keyboard interaction patterns, focus indicators, and prefers-reduced-motion support are written into every component that needs them — not optional add-ons.

Predictable theming. CSS custom properties follow a single naming convention: --x-<component>-<property>. Tokens are set on :host and cascade normally. You override them the same way you override any CSS property.

Mobile-first. Components use dvh viewport units, calc(100vw - ...) width caps, and @media (pointer:coarse) rules for touch-friendly sizing. No component overflows on a 320px screen.


Bundle Size

BareDOM compiles to lightweight ES modules. Sizes below are gzipped:

ModuleGzipped
base.js (shared runtime)~35 KB
Median component~3 KB
Smallest (x-spacer)~1 KB
Largest (x-chart)~9 KB

Each component loads base.js once plus its own module. A typical page using 5-10 components weighs 40-65 KB gzipped total.

ESM only. BareDOM ships ES modules exclusively — there is no CommonJS or UMD build. This works natively in all modern browsers and with any bundler that supports ESM (webpack 5+, Vite, esbuild, Rollup, Parcel). TypeScript declarations (.d.ts) are included for every component. If you need server-side rendering, pre-render the HTML and hydrate with component registration on the client.


Browser Support

BareDOM targets browsers that support Custom Elements v1 and Shadow DOM v1 natively:

BrowserMinimum version
Chrome / Edge67+
Firefox63+
Safari14+

No polyfills are included or required for these targets.


Development

See the development guide for setting up the dev server, using debug mode, and building from source.


License

MIT

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close