Airframe and shadcn
shadcn solved a real problem: you should own the component source. Copy the file into the app. Change it. It is yours. No node_modules black box for the button.
That is not the same problem Airframe solves.
Airframe asks: what stays stable when the framework and the widget library change? Tokens, layout primitives, and a readable class contract. The grammar under the kit. Not the React file you copied.
They can sit together. They should not be confused.
Two different jobs
Section titled “Two different jobs”| shadcn | Airframe | |
|---|---|---|
| Owns | Component source in your repo | Structure: tokens, layouts, CSS patterns |
| Default engine | React (CLI copies TSX) | Any engine. The CSS does not care |
| Runtime | Whatever you copied (often Radix / Base UI / React Aria) | None. Native HTML for behaviour |
| Customisation | Edit the file | Override --af-*. Wrap af-btn |
| Failure mode | Every product forks a different button | Two looks: kit CSS beside af-* |
shadcn is a distribution model for React components. Airframe is a structural UI system. One is how you take delivery of widgets. The other is the language those widgets (or Angular ones, or plain HTML) are built in.
When to own the source
Section titled “When to own the source”Use a shadcn-style workflow when:
- The team is React-first and wants to edit dialogs, popovers, and menus as files.
- You need behaviour that native HTML does not cover, and you want that JS in-tree.
- You already chose primitives (Base UI, React Aria, Radix) and want to swap the foundation without losing the copy-paste habit.
That is a strong product. It still does not give Angular, Vue, or an AI agent a shared visual contract by itself. Each new engine is another copy of the idea.
When to own the grammar
Section titled “When to own the grammar”Use Airframe when:
- More than one engine must look like the same product.
- You want page layout (
af-stack,af-inline,af-grid) that reads as intent, not utility soup. - Tokens should live in CSS and work at runtime without a rebuild.
- Agents should look up coded rules instead of inventing class names.
- The kit already exists (
<my-button>,@acme/ui) and you need it to sit on one look.
Airframe does not replace “copy the component into components/ui.” It is what that component (or its Angular twin) should be made of.
Using both
Section titled “Using both”React kit in the shadcn shape. Airframe as the look.
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. Variants map to af-is-*. Layout on the page is af-stack, not a one-off flex flex-col gap-4 next to the kit. Tokens are --af-*, not a second colour file.
If Base UI or React Aria handles the dialog, keep that primitive. Style the surface with Airframe tokens and patterns. The JS layer is replaceable. The look is not a parallel system.
Same idea as Use as much as you need. Airframe is the grammar. A kit is the typed API on top, if you have one. shadcn is one way to deliver that kit in React.
Adjacent tools
Section titled “Adjacent tools”- Tailwind composes appearance as utilities. Airframe names structure. Comparison: Airframe vs Bootstrap vs Tailwind.
- Base UI / React Aria / Zag own accessible behaviour. Put them under widgets. Do not ask them to be the design system.
- Web Components own a framework-independent implementation. Still a widget layer. Sit them on the same tokens if you use them.
The question is not “which library has more buttons.” It is “what is invariant when we change engines.”
Next steps
Section titled “Next steps”- One UI language. Everywhere. — why the grammar should outlive the framework.
- Separate design from behaviour — primitives for JS, Airframe for look.
- Framework-agnostic UI is not a design system — widget portability vs a CSS contract.
- Use as much as you need — wrap
af-*if you have a kit. Or use Airframe as the system. - React Recipes — copy-paste wrappers in the same spirit as owning the file.
- Comparison — Airframe vs Bootstrap vs Tailwind v4.