A visual landing-page builder for BareDOM. This project offers a drag and drop interface to build a web component based static page and using bindings, records and events can export to a fully interactive, functional extensible codebase in either ClojureScript or JavaScript.
Bareforge is built in ClojureScript on top of BareDOM's 90 stateless web components. The editor chrome itself is made from BareDOM elements — palette, inspector, toolbars, layers, modals — dogfooding the library inside a real end-user application. The canvas uses a hand-written DOM reconciler (no virtual DOM) to stay philosophically aligned with 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 then turned to another aspect of web development and thought about how I could make web components easier in use. I often found myself taking a Figma design and translating it into UI components and code.
I wondered if I could automate some of that and with that the idea for Bareforge was born. I wanted something that would not just deliver a static page. Instead it should allow exporting a complete and interactive CLJS or JS project that can be used for further development. It is still rough around the edges but I hope it brings you joy and usefulness when designing and developing web/landing pages.
- Drag-drop canvas with before / inside / after drop indicators
- Inspector with type-aware editors for every BareDOM component — enums as dropdowns, colors as pickers, booleans as switches, URLs, numbers, and a per-instance CSS-variable editor
- Free-form positioning for decoratives (absolute x/y/w/h) with an 8-handle resize overlay and keyboard nudging (arrow keys, 10 px with Shift, coalesced into a single undo step)
- Flow resize handles on non-free elements (E / S / SE only, updating
:width/:heightas CSS length strings) - Component-aware snap on drop — an
x-navbarlands at the top of the root at full width; anx-sidebardoes the same; easily extensible viameta/placement.cljs - Theme editor with 8 built-in presets and full per-token overrides
- Autosave to IndexedDB (debounced) plus explicit project files
(
.json) via File Download / File Upload - Project files are spec-validated on load — malformed payloads are refused rather than silently installed
- Four export modes:
- CDN export (static snapshot) — one HTML file, loads BareDOM from jsDelivr at runtime (requires internet). Markup only; no reactive state.
- Bundle export (static snapshot) — a
.zipwith the HTML + a localvendor/baredom/folder containing every module the document uses; serve from any static HTTP server offline. Markup only; same contract as CDN export. - ClojureScript export (interactive) — a full shadow-cljs
project that ships a minimal re-frame subset plus the
declarative data-binding layer. Buttons fire actions, fields
update state, computed subs recompute. See
docs/recipes.mdfor how to build one end-to-end. - Vanilla JavaScript export (interactive) — a
.zipwith a tiny reactive store, a hand-written reconciler, and per-group view modules. Same feature parity as the ClojureScript export (template groups, collection fields, the seven computed operations, bindings, triggers, raw-HTML icons) — no framework dependency, plain DOM + ES modules.
- Pluggable export system — the four targets above are
built-in plugins under
src/bareforge/export/<name>/; adding a new one (React, Svelte, your in-house framework) is a manifest- a single
generatefn. Seedocs/plugins.md.
- a single
- Undo / redo with 100-step history, including coalesced keyboard nudges (hold arrow → one undo step)
- Preview mode toggle — drops the editor chrome interactions so you can click through the page as a user
- Escape to deselect + full keyboard shortcuts (Cmd-Z / Cmd-Shift-Z / Delete / arrow-key nudge)
The deep-dive content lives in docs/ so this README
stays scannable:
docs/recipes.md— end-to-end walkthrough that builds a filterable product feed with an add-to-cart flow, plus a quick-reference index.docs/adding-components.md— scaffolder recipe for onboarding a new BareDOM component into the palette.docs/architecture.md— architecture, data model, rendering pipeline, project layout, and per-component notes for new contributors.docs/plugins.md— export plugin authoring guide.
A set of power-user gestures that make daily editing faster. Every
keyboard shortcut lives in a press-? cheat sheet; every action is
reachable through a Cmd-K command palette.
- Shift-click any node (canvas or Layers panel) to extend the selection. Drag from empty canvas for marquee select; hold Shift to extend.
- With multiple nodes selected, the Inspector shows the shared
attributes across them. Edit a row once, every selected node
updates in a single undo step. Mixed values render with a
Mixedplaceholder. - Cmd-D duplicates (deep-clone with fresh ids); Cmd-G wraps
the selection in an
x-container(Cmd-Shift-G prompts forx-grid/x-card/x-navbar); Delete removes the whole set in one commit. - Cmd-Opt-C / Cmd-Opt-V copy attributes from the selection and
paste them onto another node, filtered to the target tag's
supported attrs — a paste from
x-buttononto anx-cardsilently dropsvariantinstead of stamping an unknown attr.
- Drag-to-scrub numeric labels — the
min/max/steprows and the free-coord:layout :x / :y / :w / :hrows. Drag the label horizontally; hold Shift for ×10 steps. The whole drag is one undo entry. var(--x-…)autocomplete. Typevar(into a colour or length field and a native<datalist>surfaces every BareDOM theme token —--x-color-primary,--x-space-md,--x-radius-lg, and so on, sourced fromx-themeand resolved live by the active preset.
?opens a cheat sheet with every keyboard shortcut and gesture, grouped by category. Built onx-modal+x-typographyso it inherits the live theme preset.- Cmd-K opens a fuzzy command palette built on
x-command-palette. Insert any of the 90 BareDOM tags by typing a fragment of the name; toggle the theme editor, the templates panel, preview mode, or the cheat sheet itself — every toolbar action is one keystroke away. - Layers keyboard nav. Focus the Layers tree, then ↑ / ↓ walk siblings, ← / → step to parent / first child, Alt+↑ / Alt+↓ reorder within the parent slot.
- Palette pattern flyout. Components with curated variants —
x-button(primary / secondary / ghost / danger / loading),x-typography(h1–h3 / body / caption / code),x-alert,x-badge,x-card,x-grid(2-col / 3-col / 4-col / sidebar), and more — show a▾caret next to their palette tile. Expand, pick a chip, the component lands pre-styled. - Empty-slot hints. Empty containers in edit mode show a per-tag
prompt —
Drop nav links / actionsinside an emptyx-navbar,Drop tiles into the gridinside anx-grid,Drop x-tab hereinsidex-tabs— so the next move is always obvious.
Give a container a name and it becomes a component group with its
own reactive state. Declare typed fields on the group (e.g.
:cart-count → number, default 0); any attribute of any descendant
can then be bound to a field — a :read binding tracks it live
(the :text of an x-badge mirrors :cart-count), a :write
binding lets an event trigger update it, and event triggers pick an
operation from :increment, :decrement, :toggle, :set,
:clear. On export, fields become the group's default-db, reads
become rf/reg-sub + rf/query, and event triggers become
rf/reg-event + rf/dispatch — a minimal re-frame shape, no React,
no Reagent.
Bareforge ships with 8 starter templates that showcase BareDOM's component library and modern web design patterns:
| Template | Description |
|---|---|
| SaaS Hero | Gaussian blur background, navbar, kinetic typography headline, particle button CTAs, and a stats row |
| Bento Features | Section heading with a bento grid of feature cards, including a multi-span highlighted card |
| Our Story | Narrative cards separated by organic dividers — a vertical storytelling layout |
| Pricing Table | Three-tier pricing cards (Starter / Pro / Enterprise) with badges, dividers, and feature lists |
| Testimonials | Grid of customer quote cards with avatars and attributions |
| How It Works | Four-step timeline with labels, titles, and descriptive content |
| Contact | Two-column layout with a form (wrapped in x-form) and a contact info card |
| Full Landing Page | Complete multi-section page combining navbar, hero, stats, feature grid, testimonial, and CTA footer with a gaussian blur background |
Pick a template from the Templates panel to start with a pre-built structure, then customise content, theme, and layout in the editor.
Early alpha. Feature-complete: 90 BareDOM components in the
palette, four export plugins at full feature parity (HTML, bundle,
CLJS, vanilla-JS), nine starter templates, first-run welcome tour,
doc-level XSS sanitiser, CSP + SRI on every export. 571 tests / 0
release-build warnings under Closure Advanced. Expect rough edges
on less-common BareDOM components until their augment entries are
hand-tuned. See CHANGELOG.md for what's in
each release.
Bareforge is a consumer of BareDOM, pulled from Clojars as
com.github.avanelsas/baredom. This repository does not modify BareDOM
— version bumps happen via deps.edn.
# Install dev dependencies (jszip for bundle export)
npm install
# Start dev server on http://localhost:8765
npx shadow-cljs watch app
# Release build (Closure :advanced)
npx shadow-cljs release app
# Tests
npx shadow-cljs watch testRequirements: JDK 11+, Node 18+.
Every edit flows through a pure transform (doc.ops/*) before being
committed through the single atom (bareforge.state/app-state). The
reconciler is a one-way function DOM = f(state). Pure zone
(doc/, meta/, export/ serialization, storage/ serialization)
is side-effect-free and tested without a browser; effectful zone
(render/, dnd/, ui/, state.cljs) is the only place with DOM
access or atom writes. CLAUDE.md has the full rule list.
Read CLAUDE.md before writing code. It encodes the pure / effectful
zone boundary, the one-atom rule, spec usage at boundaries, Closure
Advanced safety, and the runtime-dependency policy (only BareDOM and
JSZip).
MIT. See LICENSE for the full text.