Skip to main content

Architecture

This document explains the architectural decisions behind Holt and why things work the way they do.

Why Behavior/Presentation Separation?

The behavior/presentation split comes from observing problems with traditional component libraries:

The coupling problem: When styles and interactions are intertwined, changing one risks breaking the other. A "simple" CSS change can inadvertently break keyboard navigation or accessibility.

The testing problem: Testing a button's click handler shouldn't require rendering visual styles, but coupled components make this difficult.

The reuse problem: You want a dropdown's keyboard navigation in a custom design, but the library's dropdown looks nothing like your mockups. With coupled components, you either accept the library's look or rewrite everything.

Separating behavior from presentation solves these:

  • Behaviors are stable contracts - They handle state, keyboard input, and ARIA attributes. These don't change when you want a different visual style.
  • Presentations are replaceable - Swap Tailwind classes, use a different design system, or create custom variants without touching behavior code.
  • Testing is focused - Test behaviors with simple assertions about state. Test presentations with visual snapshots.

Why Tailwind over CSS-in-Rust?

Rust has CSS-in-Rust solutions like Stylist and Leptos Style. Holt chose Tailwind for several reasons:

Ecosystem compatibility: Tailwind has massive adoption. Designers use it, tutorials assume it, and tooling supports it. Holt components work with your existing Tailwind setup.

No runtime cost: tailwind_fuse resolves class conflicts at the call site. There's no runtime CSS generation or injection.

Type safety where it matters: tailwind_fuse provides compile-time checks for variant combinations and class merging, catching common errors without the overhead of a full type system for CSS.

Familiar patterns: If you've used Shadcn UI or Radix in JavaScript, Holt's styling approach will feel natural.

Why Copy-Paste Components?

Holt follows the Shadcn model: components are meant to be copied into your project and customized, not consumed as an opaque library.

Your code, your control: When you copy a component, you own it. No fighting library opinions or waiting for upstream changes.

Learning tool: Reading and modifying component code teaches patterns you can apply elsewhere.

No version conflicts: Copied code doesn't have version compatibility issues with your other dependencies.

Incremental adoption: Copy the components you need. Skip the ones you don't. Mix Holt components with your own.

The holt-kit crate provides ready-to-use components if you prefer a traditional library approach, but the source is always available for customization.

Why a Storybook Framework?

Component development benefits from isolation. Holt Book provides:

Visual testing: See components in all their variants without building a full application.

Documentation: Stories serve as living documentation showing how components should be used.

Iteration speed: Hot reloading means changes appear instantly without full rebuilds.

The story macro system makes adding stories low-friction - a few lines of code creates a documented, interactive component example.