Airframe vs shadcn/ui
shadcn/ui and Airframe get compared a lot. They solve different problems. Many teams will want both.
shadcn/ui gives you component source. A CLI copies React files into your repo. You own them and edit them.
Airframe gives you a UI language. Design tokens, structure primitives, and accessible CSS patterns. One CSS import. Any framework.
Short version: shadcn/ui owns how components behave. Airframe owns how the interface looks and is arranged.
At a glance
Section titled “At a glance”| shadcn/ui | Airframe | |
|---|---|---|
| What you get | Component source files in your repo | Tokens, structure primitives, CSS patterns |
| Engine | React (community ports exist for other frameworks) | Any. HTML, React, Angular, Vue, server templates |
| Styling | Tailwind utilities inside each component | Named af-* classes and --af-* design tokens |
| Behaviour | Headless primitives (Radix UI, Base UI) handle focus and keyboard | Native HTML first (<dialog>, <details>, form controls) |
| Runtime | Whatever the copied components import | None. CSS only |
| Customisation | Edit the file | Override --af-*. Wrap af-* in your own components |
| Layout | Tailwind utilities (flex flex-col gap-4) | Structure primitives (af-stack, af-inline, af-grid) |
| Updates | You own the copy. Pull upstream changes by hand | npm update. Classes and tokens stay stable |
| For AI agents | Registry and MCP server for adding components | Rules, class reference, llms.txt, MCP server, markup validation |
Why use shadcn/ui
Section titled “Why use shadcn/ui”- You build in React and want working components today.
- You need behaviour-heavy widgets. Combobox, command menu, date picker, dropdown menu.
- You want the source in your repo, not behind a package version.
- Your team already writes Tailwind.
shadcn/ui got something important right. You should own your components. Copy the file. Change it. It is yours.
Why use Airframe
Section titled “Why use Airframe”- You want no runtime. Patterns are CSS on native HTML.
- You want markup that reads as structure.
af-stackinstead offlex flex-col gap-4. - You want one set of design tokens across products, Figma, and code.
- You want AI agents to produce consistent UI. Airframe gives them a small, named vocabulary they can look up and validate.
- You ship more than one framework, or expect to change framework later.
Same UI, two grammars
Section titled “Same UI, two grammars”A settings form row with actions.
<!-- shadcn/ui style: Tailwind utilities describe the implementation -->
<div class="flex flex-col gap-4 rounded-lg border bg-card p-6">
<label class="text-sm font-medium" for="name">Name</label>
<input id="name" type="text" class="h-9 rounded-md border px-3" />
<div class="flex justify-end gap-2">
<button class="h-9 rounded-md border px-4">Cancel</button>
<button class="h-9 rounded-md bg-primary px-4 text-primary-foreground">Save</button>
</div>
</div>
<!-- Airframe: classes describe the intent -->
<div class="af-card af-stack af-gap-md">
<div class="af-field">
<label class="af-field__label" for="name">Name</label>
<input id="name" type="text" class="af-field__control af-input" />
</div>
<div class="af-inline af-gap-sm af-justify-end">
<button type="button" class="af-btn af-is-outline">Cancel</button>
<button type="submit" class="af-btn">Save</button>
</div>
</div>The Airframe version is shorter. It is also easier for a person or a model to check. Every class is in the class reference. Unknown names fail ESLint and validate_markup.
Why they work together
Section titled “Why they work together”The two tools own different layers.
| Layer | Owner |
|---|---|
| Design tokens | Airframe (--af-*) |
| Layout | Airframe structure |
| Visual pattern | Airframe CSS |
| Behaviour | shadcn/ui and its primitives |
| Your app | You |
Airframe owns how it looks. Your kit owns how it behaves. shadcn/ui is a good kit.
Keep the shadcn/ui ownership model. Keep Radix or Base UI for focus, keyboard, and positioning. Swap the Tailwind styling inside each file for af-* classes. Now every component reads from one token layer and one class vocabulary.
1. Point shadcn/ui variables at Airframe tokens
Section titled “1. Point shadcn/ui variables at Airframe tokens”shadcn/ui themes through CSS variables. Alias them to --af-* once. Airframe owns the palette. Light, dark, and high contrast follow automatically.
:root {
--background: var(--af-color-background);
--foreground: var(--af-color-text);
--primary: var(--af-color-primary);
--muted-foreground: var(--af-color-text-muted);
--border: var(--af-color-border);
--radius: var(--af-radius-md);
}Components you have not converted yet now match the ones you have.
2. Render af-* inside the component file
Section titled “2. Render af-* inside the component file”Keep the file and its API. Map variants to Airframe modifiers.
import { clsx } from 'clsx';
import type { ButtonHTMLAttributes } from 'react';
const variants = {
default: '',
secondary: 'af-is-secondary',
outline: 'af-is-outline',
ghost: 'af-is-ghost',
destructive: 'af-is-danger',
link: 'af-is-link',
};
const sizes = { default: '', sm: 'af-is-sm', lg: 'af-is-lg' };
type Props = ButtonHTMLAttributes<HTMLButtonElement> & {
variant?: keyof typeof variants;
size?: keyof typeof sizes;
};
export function Button({ variant = 'default', size = 'default', className, ...props }: Props) {
return (
<button className={clsx('af-btn', variants[variant], sizes[size], className)} {...props} />
);
}Call sites do not change. <Button variant="outline"> still works.
3. Use structure for layout
Section titled “3. Use structure for layout”Arrange pages with af-stack, af-inline, and af-grid. Keep Tailwind out of layout and colour. Two grammars painting the same element drift apart.
Which should I pick?
Section titled “Which should I pick?”- AI agents write a lot of your UI: Airframe as the vocabulary. Agents look up patterns and validate markup against it.
- React only, need rich widgets now: shadcn/ui. Add Airframe tokens when you want one palette across products.
- Several frameworks, or server-rendered HTML: Airframe. Wrap
af-*in whatever components each app needs. - A shadcn/ui app that needs to scale into a design system: Both. Keep shadcn/ui for behaviour. Move tokens, layout, and visuals to Airframe one component at a time.
Next steps
Section titled “Next steps”- Use as much as you need — tokens, structure, patterns, or wrappers
- React recipes
- Airframe for AI agents
- Comparison — Bootstrap and Tailwind