https://github.com/avanelsas/baredom
BareDOM: Lightweight CLJS UI components built on web standards (Custom Elements, Shadow DOM, ES modules). No framework, just the DOM
https://github.com/avanelsas/baredom
baredom clojurescript full-theming javascript mobile-ready open-shadow-dom tree-shakeable webcomponents
Last synced: about 1 month ago
JSON representation
BareDOM: Lightweight CLJS UI components built on web standards (Custom Elements, Shadow DOM, ES modules). No framework, just the DOM
- Host: GitHub
- URL: https://github.com/avanelsas/baredom
- Owner: avanelsas
- License: mit
- Created: 2026-03-26T07:06:48.000Z (2 months ago)
- Default Branch: main
- Last Pushed: 2026-04-22T12:34:50.000Z (about 1 month ago)
- Last Synced: 2026-04-22T13:26:14.105Z (about 1 month ago)
- Topics: baredom, clojurescript, full-theming, javascript, mobile-ready, open-shadow-dom, tree-shakeable, webcomponents
- Language: Clojure
- Homepage: https://avanelsas.github.io/baredom/
- Size: 6.08 MB
- Stars: 12
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# BareDOM
**Native web components. Zero runtime. No framework required.**
[](https://www.npmjs.com/package/@vanelsas/baredom)
[](./LICENSE)
[](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)
[](https://www.typescriptlang.org/)
[](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements)
---
## 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](https://avanelsas.github.io/baredom/)
---
## 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--` CSS custom property. Override at any scope: globally, per-page, per-instance. Use [``](./docs/x-theme.md) 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](https://github.com/webcomponents/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](https://github.com/webcomponents/custom-elements-manifest)** (`custom-elements.json`) for IDE tooling and HTML intellisense
### Usage
```typescript
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](https://marketplace.visualstudio.com/items?itemName=nicktomlin.vscode-lit-html) or the [Custom Elements Language Server](https://marketplace.visualstudio.com/items?itemName=nicktomlin.vscode-lit-html) to get attribute autocomplete and validation in HTML templates.
---
## Theming
Wrap any subtree in `` to apply a consistent palette across all components:
```html
Themed button
```
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:
```js
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' }
});
```
```html
...
```
Or override individual tokens via CSS:
```html
...
```
See [docs/x-theme.md](./docs/x-theme.md) for the full token list, preset details, and API reference.
---
## Installation
```bash
npm install @vanelsas/baredom
```
```js
import { init } from '@vanelsas/baredom/x-button';
init();
```
Also available via [Clojars](./docs/installation.md#clojurescript-via-clojars) and [standalone ES modules](./docs/installation.md#vanilla-htmljs-via-es-modules). See the [full installation guide](./docs/installation.md).
---
## Usage
BareDOM components are native HTML elements. Import, register, and use them in any framework or vanilla HTML.
- **JavaScript / TypeScript** — see the [JavaScript Developer Guide](./docs/javascript-guide.md)
- **ClojureScript** — see the [ClojureScript Guide](./docs/clojurescript-guide.md)
---
## Components
### Form (17)
| Tag | Description |
|-----|-------------|
| [``](./docs/x-button.md) | Action control. Variants: `primary`, `secondary`, `tertiary`, `ghost`, `danger`. Sizes: `sm`, `md`, `lg`. States: `disabled`, `loading`, `pressed`. Icon slots. |
| [``](./docs/x-checkbox.md) | Boolean input. Reflects `checked` and `indeterminate` states to attributes. |
| [``](./docs/x-color-picker.md) | Colour picker with 2D saturation/brightness area, hue strip, optional alpha, preset swatches, eyedropper, and clipboard copy. Inline or popover mode. |
| [``](./docs/x-copy.md) | Copy-to-clipboard utility button with success feedback. |
| [``](./docs/x-currency-field.md) | Formatted currency input with locale-aware masking. |
| [``](./docs/x-date-picker.md) | Calendar-based date selection with keyboard navigation. |
| [``](./docs/x-fieldset.md) | Groups related form controls with a styled legend. |
| [``](./docs/x-file-download.md) | Download trigger that initiates a file transfer. |
| [``](./docs/x-form.md) | Form wrapper with coordinated validation state. |
| [``](./docs/x-form-field.md) | Label + input wrapper with error and hint text slots. |
| [``](./docs/x-radio.md) | Single-choice input within a radio group. |
| [``](./docs/x-search-field.md) | Search input with integrated clear button and search icon. |
| [``](./docs/x-select.md) | Dropdown select control with custom styling. |
| [``](./docs/x-slider.md) | Range slider with step, min/max, and value display. |
| [``](./docs/x-stepper.md) | Multi-step form progress indicator with navigation. |
| [``](./docs/x-switch.md) | Toggle switch for boolean settings. |
| [``](./docs/x-text-area.md) | Multi-line text input with auto-resize option. |
### Feedback (10)
| Tag | Description |
|-----|-------------|
| [``](./docs/x-alert.md) | Semantic alert banner. Types: `info`, `success`, `warning`, `error`. Auto-dismiss with `timeout-ms`. Fires `x-alert-dismiss`. |
| [``](./docs/x-badge.md) | Small inline label for counts, states, and categories. |
| [``](./docs/x-chip.md) | Compact tag component, optionally removable. |
| [``](./docs/x-notification-center.md) | Notification hub for aggregating and managing in-app notifications. |
| [``](./docs/x-progress.md) | Linear progress bar with determinate and indeterminate modes. |
| [``](./docs/x-progress-circle.md) | Circular progress indicator for compact spaces. |
| [``](./docs/x-skeleton.md) | Animated loading placeholder that mirrors content shape. |
| [``](./docs/x-spinner.md) | Inline loading spinner with size and colour variants. |
| [``](./docs/x-toast.md) | Single transient notification with enter/exit animations and auto-dismiss. |
| [``](./docs/x-toaster.md) | Toast manager. Positions a queue of `` elements, enforces `max-toasts`, and fires `x-toaster-dismiss`. |
### Navigation (8)
| Tag | Description |
|-----|-------------|
| [``](./docs/x-breadcrumbs.md) | Hierarchical path trail with separator customisation. |
| [``](./docs/x-menu.md) | Vertical menu container coordinating `` children. |
| [``](./docs/x-menu-item.md) | Individual menu entry with icon, label, description, and keyboard support. |
| [``](./docs/x-navbar.md) | Top navigation bar with responsive slot layout. |
| [``](./docs/x-pagination.md) | Page navigation controls with first/previous/next/last and page-size selection. |
| [``](./docs/x-sidebar.md) | Collapsible side navigation panel with collapse/expand animation. |
| [``](./docs/x-tab.md) | Individual tab within an `` container. |
| [``](./docs/x-tabs.md) | Tab container that coordinates `` children, manages active state, and fires change events. |
### Layout (6)
| Tag | Description |
|-----|-------------|
| [``](./docs/x-card.md) | Surface container. Variants: `elevated`, `outlined`, `filled`, `ghost`. Interactive mode available. |
| [``](./docs/x-collapse.md) | Expandable/collapsible section with animated height transition. |
| [``](./docs/x-container.md) | Responsive max-width container with configurable padding. |
| [``](./docs/x-divider.md) | Horizontal or vertical visual separator. |
| [``](./docs/x-grid.md) | CSS Grid layout component with responsive column configuration. |
| [``](./docs/x-spacer.md) | Flexible spacing element for flexbox and grid layouts. |
### Data (10)
| Tag | Description |
|-----|-------------|
| [``](./docs/x-avatar.md) | User photo or initials display. Shape, size, and status dot variants. |
| [``](./docs/x-avatar-group.md) | Overlapping avatar stack for representing multiple users. |
| [``](./docs/x-carousel.md) | Accessible carousel with swipe/drag, arrows, dot indicators, autoplay, slide/fade transitions, and horizontal/vertical orientation. |
| [``](./docs/x-chart.md) | Data visualisation component for common chart types. |
| [``](./docs/x-stat.md) | KPI / metric card with value, label, trend, and icon slots. |
| [``](./docs/x-table.md) | Data grid using CSS subgrid. Supports sorting, single/multi-select, striping, and accessible captions. |
| [``](./docs/x-table-cell.md) | Table cell for header and data modes, with sort indicator and alignment control. |
| [``](./docs/x-table-row.md) | Table row with interactive selection and `x-table-row-select` event. |
| [``](./docs/x-timeline.md) | Vertical timeline container that coordinates `` children. |
| [``](./docs/x-timeline-item.md) | Individual timeline event with time, icon, heading, and body slots. |
### Overlay (7)
| Tag | Description |
|-----|-------------|
| [``](./docs/x-cancel-dialogue.md) | Confirmation modal for destructive cancel actions. |
| [``](./docs/x-command-palette.md) | Keyboard-accessible global search and command interface. |
| [``](./docs/x-context-menu.md) | Right-click / long-press contextual action menu. |
| [``](./docs/x-drawer.md) | Off-canvas sliding panel, configurable from any edge. |
| [``](./docs/x-dropdown.md) | Positioned dropdown container for menus and selection. |
| [``](./docs/x-modal.md) | Centred dialog with backdrop, focus trap, and `Escape` to close. |
| [``](./docs/x-popover.md) | 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--`. 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:
| Module | Gzipped |
|--------|---------|
| `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:
| Browser | Minimum version |
|---------|----------------|
| Chrome / Edge | 67+ |
| Firefox | 63+ |
| Safari | 14+ |
No polyfills are included or required for these targets.
---
## Development
See the [development guide](./docs/development.md) for setting up the dev server, using [debug mode](./docs/development.md#debug-mode), and building from source.
---
## License
MIT