Use as much as you need
One UI language. Everywhere. Airframe is a structural UI design system for humans and AI. Your framework provides the engine. Airframe provides the structure.
Use af-* directly in templates. That is Airframe as the design system. If you already have a kit, wrap those patterns in <my-button> / <MyButton>. Both are valid. Do not run two looks.
The contract
Section titled “The contract”When there is a kit, it sits on Airframe, not beside it. If .ui-* classes keep painting their own buttons, cards, and spacing, you get two systems fighting in every template.
| Layer | Owns | Does not own |
|---|---|---|
Airframe (.af-*) | Reset, tokens, layout (af-stack / af-inline / af-grid), look of button / card / input | Domain UI, DI, routing, JS widgets |
Your UI kit (<my-*>, .ui-*) | Framework wrappers, toast / modal / sidebar, product chrome | A parallel palette or layout grammar |
| Product / apps | Compose af-* layouts + kit widgets | Ad-hoc page BEM that copies Airframe |
Airframe does not require a UI package. Teams without one use af-* directly in templates. Teams that already have @acme/ui (or similar) should treat that package as the only place @airframeui/core is imported. Same idea as keeping vendor SDKs in a platform layer.
Use as much as you need
Section titled “Use as much as you need”Pick a depth. Stay there until you need more.
| Depth | You take from Airframe | You keep |
|---|---|---|
| Tokens | --af-* mapped to your brand | Existing components and layout |
| Layouts | af-stack, af-inline, af-grid, and friends | Your buttons, cards, overlays |
| Patterns | All 57 CSS patterns (af-btn, af-card, af-input, …) as classes | Framework behaviour you already own |
| Wrappers | Patterns inside <my-button>, <my-card>, … | Typed inputs, slots, services |
You can mix depths on purpose. Layouts from Airframe plus a custom toast service is normal. Two palettes and two layout grammars is not.
How a wrapper should look
Section titled “How a wrapper should look”A kit button is an af-btn with framework inputs. Not a restyled cousin.
Angular
import { Component, input } from '@angular/core';
@Component({
selector: 'ui-button',
standalone: true,
template: `
<button
class="af-btn"
[class.af-is-primary]="variant() === 'primary'"
[class.af-is-outline]="variant() === 'outline'"
>
<ng-content />
</button>
`,
})
export class UiButton {
readonly variant = input<'primary' | 'outline' | 'secondary'>('primary');
}Host / focus / slot glue can stay in .ui-button. Keep real .ui-* CSS for things Airframe cannot do: ToastService, modal focus trap, sidebar collapse, marketing chrome.
React
import { clsx } from 'clsx';
import type { ButtonHTMLAttributes } from 'react';
export function Button({
variant = 'primary',
className,
...props
}: ButtonHTMLAttributes<HTMLButtonElement> & {
variant?: 'primary' | 'outline' | 'secondary';
}) {
return (
<button
className={clsx(
'af-btn',
variant === 'primary' && 'af-is-primary',
variant === 'outline' && 'af-is-outline',
className,
)}
{...props}
/>
);
}Copy-paste React wrappers: React Recipes.
Same rule for card, input, badge. If the kit component exists, product templates use the kit. If it does not, use the af-* pattern until you wrap it.
What pages should do
Section titled “What pages should do”Layout classes from Airframe. Interactive pieces from your kit.
<main class="af-stack af-gap-lg">
<header class="af-inline af-justify-between">
<h1>Team</h1>
<my-button>Invite</my-button>
</header>
<my-card>…</my-card>
</main>Kill page-local .page-container / .auth__card grids as you touch them. Do not invent a third layout language.
Teams without a kit use af-btn and af-card in the same slots. The page structure does not change.
Tokens: one source
Section titled “Tokens: one source”Map once. Then stop duplicating.
:root {
--af-base-primary: var(--color-primary);
/* or invert: --color-* aliases --af-* after the spike */
}Pick one canonical set. After adoption, --af-* should be canonical. Keep --color-* (or --bs-*, --mdc-*) as aliases until old SCSS dies.
Two token files forever is how the dual-system leak starts. Full override workflow: Theming. Mapping an existing token file: Theme Studio. Figma as a generated view: Design tokens as the source of truth.
Agent rules
Section titled “Agent rules”The payoff is a contract coding agents can follow. Add this next to npx af init --agents (or in your own AGENTS.md):
- Layout →
af-stack/af-inline/af-gridfrom the catalog. Do not invent layout utilities. - Widget →
<my-*>from your kit when a wrapper exists. - Never
af-btnin product templates if<my-button>exists. - Never new
.ui-*layout utilities that copy Airframe. - Never a second palette. Alias into
--af-*. - Do keep kit-only CSS for behaviour Airframe cannot do (toast, modal trap, sidebar).
Authoritative markup rules: AI Rules and @airframeui/core/rules.
What not to do
Section titled “What not to do”- Do not keep restyling
.ui-button/.ui-cardas the brand. Put the look onaf-btn/af-cardand wrap. - Do not let product mix Tailwind-style soup with both prefixes.
- Do not rewrite the whole app before you know the split works. Spike one surface (auth login or dashboard overview), then roll.
- Do not import
@airframeui/*from every feature folder if you have a platform UI package. Import it once, in the kit.
Incremental adoption
Section titled “Incremental adoption”- Import
@airframeui/core/core.cssin the app (or in the kit, if you have one). - Map tokens. One alias file. No new colours in feature SCSS.
- Replace one page’s layout with
af-stack/af-inline/af-grid. - Use
af-btn/af-cardin templates, or point one kit component at them. Leave any public API stable. - Repeat. Delete parallel CSS as you go.
Airframe is the structure. Use it as the design system, or put a typed kit on top.
Next steps
Section titled “Next steps”- One UI language. Everywhere. — the grammar across frameworks
- How to keep one design system across React and Angular — wrappers per engine
- Usage — patterns and primitives with no kit
- Theming — map
--af-*to your brand - Angular — CSS import and wrapper notes
- React Recipes — copy-paste wrappers
- AI Rules — the contract for humans and coding agents