Skip to content

Comparison

How Airframe compares to two popular CSS systems.

This is a positioning guide, not a scorecard. Each tool wins for different teams.

AirframeBootstrapTailwind
Pre-built UIProduction-ready patterns (buttons, cards, forms, overlays…)Full component library (many need JS)None in core (ecosystem kits fill this)
Markup styleIntent classes (af-stack, af-card)Component classes (btn, card)Utility classes (flex, gap-4, rounded-lg)
SetupImport CSSImport CSS (or Sass)Build step (PostCSS / Vite plugin)
AccessibilityPatterns aim for solid defaults; you still own AAGenerally strong on componentsMostly manual — you compose a11y yourself
TokensCSS variables (--af-*)CSS variables (--bs-*) + SassCSS variables via @theme
ThemingRuntime token overrides, no rebuildRuntime for many vars; Sass for deeper workRuntime for existing theme vars; rebuild for new
Cascade layersYesNoYes
Layout modelStructural primitivesGrid/flex utilities + componentsUtility-based
Bundle approachFull import or selective pattern CSSFull or partial Sass/CSS buildsOn-demand utilities
AI / toolingRules, catalog, class reference, MCP, ESLintDocs + ecosystemDocs + strong ecosystem

Same dashboard idea in each system. Airframe and Bootstrap read as structure; Tailwind reads as implementation detail.

<div class="af-container">
  <div class="af-stack af-gap-lg">
    <header class="af-spread">
      <h1>Dashboard</h1>
      <div class="af-inline af-gap-sm">
        <button class="af-btn af-is-secondary">Export</button>
        <button class="af-btn">New</button>
      </div>
    </header>
    <div class="af-grid af-grid-1 af-grid-2@md af-grid-4@lg af-gap-lg">
      <div class="af-card">
        <h3 class="af-card__title">Users</h3>
        <p class="af-text-h2">1,234</p>
      </div>
    </div>
  </div>
</div>

Airframe — import CSS. No build step required. @airframeui/build is optional (custom breakpoints, @af directives).

npm install @airframeui/core
@import "@airframeui/core/core.css";

Tailwind — needs a build.

npm install tailwindcss @tailwindcss/vite

Add the Vite or PostCSS plugin, @import "tailwindcss", run your bundler.

Bootstrap

npm install bootstrap

Import CSS. Deeper customization often still uses Sass.


Airframe — override tokens at runtime, no rebuild.

:root {
  --af-base-primary: #0066cc;
}

Tailwind — @theme tokens become CSS variables. Runtime override of existing theme vars works; adding new tokens or utilities still needs a rebuild.

@import "tailwindcss";

@theme {
  --color-primary: oklch(0.55 0.2 250);
}

:root {
  --color-primary: #0066cc;
}

Bootstrap — many theme vars are runtime CSS variables. Deeper structural changes still often go through Sass.

:root {
  --bs-primary: #0066cc;
}

Airframe, Bootstrap, and Tailwind all expose theme values as CSS variables. Airframe’s difference is the whole system is built around that token layer with no required build step.


Airframe — You want readable structure-first markup, CSS-only patterns, layout primitives, and token theming without a build pipeline. Use it as the design system, or wrap a kit around it. As much or as little as you need.

Tailwind — You want maximum compositional freedom, utility-first design, and you’re fine with build tooling and denser markup.

Bootstrap — Your team already knows it, you want a mature component + JS ecosystem, and Bootstrap’s patterns fit the product.


shadcn solved a real problem: you should own the component source. Copy the file into the app. Change it. It is yours.

That is not the same problem Airframe solves. Airframe is the grammar under the kit: tokens, structure primitives, and a readable class contract.

shadcnAirframe
OwnsComponent source in your repoTokens, structure, CSS patterns
Default engineReact (CLI copies TSX)Any engine. The CSS does not care
RuntimeWhatever you copied (often Radix / Base UI / React Aria)None. Native HTML for behaviour
CustomisationEdit the fileOverride --af-*. Wrap af-btn

They can sit together. A shadcn-style file still renders af-btn. Variants map to af-is-*. Tokens stay --af-*. If Tailwind utilities keep painting layout and colour beside af-*, you have two grammars.

import { clsx } from 'clsx';
import type { ButtonHTMLAttributes } from 'react';

export function Button({
className,
...props
}: ButtonHTMLAttributes<HTMLButtonElement>) {
return <button className={clsx('af-btn', className)} {...props} />;
}

You still own the file. Full guide: Airframe vs shadcn/ui.