Skip to content

Build Tool

Most apps do not need this package. Install @airframeui/core and import the CSS. Default breakpoints, typography, and af-grid-2@md-style utilities already ship in that stylesheet. Override --af-* in CSS at runtime — Theming.

Use @airframeui/build when you need a build step that core cannot do in the browser:

  • Custom breakpoint pixels (CSS media queries cannot read var(--af-bp-*))
  • @af bp() / @af-apply in your CSS
  • af theme to map a foreign token file into --af-*
  • af init --agents to drop agent bootstrap docs into a repo

Same version as core. Dev dependency. Do not install postcss-import — the plugin already inlines @import.

npm install -D @airframeui/build
@airframeui/core@airframeui/build
What it isRuntime CSSOptional Node toolchain
Who installs itEvery appApps that customize breakpoints, run af theme, or bootstrap agents
Default @sm / @md utilitiesAlready in responsive.cssDo not regenerate these unless you change the pixel values
Token overrides--af-* in your stylesheetPostCSS rewrites inlined --af-bp-* and @layer af.responsive from core.css

Two generators — one each, shared. The PostCSS plugin calls them in memory (preferred). They only write files when you set outputDir. buildResponsiveCss lives in the core repo so shipped responsive.css and custom-breakpoint CSS stay one implementation; it is not a published core export. @airframeui/build bundles it. Apps never import a generator from core.

@import "@airframeui/core/core.css"
      │
      └── PostCSS plugin
            ├── emitConfigTokenCss  → patch inlined --af-bp-*
            └── buildResponsiveCss  → replace inlined @layer af.responsive

outputDir: '.airframeui' (optional)
├── tokens.css
└── responsive.css

The PostCSS plugin and af build both call those functions. They do not emit a second copy of responsive.css or a second --af-* mapping. af theme is a CLI facade over @airframeui/theme; the library API stays in theme.

Create airframe.config.js in the project root. Only set values that differ from @airframeui/tokens.

module.exports = {
  breakpoints: {
    md: '900px',
  },
  useDefaultBreakpoints: true, // default; keep xs/sm/lg/… and override md
  // theme: {
  //   files: ['./src/tokens.css', './src/brands/*.css'],
  //   outputDir: '.airframeui', // af theme export (default); CLI -o overrides
  // },
  // outputDir: '.airframeui', // optional generated CSS; omit to keep @import core.css only
};

npx af init writes a starter file. The PostCSS plugin and af build / af theme all load this config from the project root.

Two different directories — do not reuse one key for both jobs:

KeyUsed byWhat it writes
theme.outputDiraf theme exportFigma pack + figma.variables.json (default .airframeui)
outputDirPostCSS / af buildtokens.css + responsive.css (optional; unset means no files)

useDefaultBreakpoints: true (the default) merges your breakpoints onto the token defaults. false uses only the breakpoints you listed — that list becomes the entire scale.

This is the usual way to run the toolchain inside Vite, Next.js, Angular, or Webpack. They all read postcss.config.js.

Install @airframeui/build only. It already depends on postcss-import — do not install that package or add it to the plugin list. One plugin:

module.exports = {
  plugins: {
    '@airframeui/build/postcss': {},
  },
};

The plugin inlines @import, then compiles the directives below and replaces core’s default @md media queries. Keep a single @import "@airframeui/core/core.css" — do not add generated CSS on top of core.css (that would leave core’s 768px @md in the tree).

import: false skips inlining if you already run postcss-import yourself. Pass import: { resolve } only when you need a custom resolver.

Empty options still load airframe.config.js. Pass options inline to override:

module.exports = {
  plugins: {
    '@airframeui/build/postcss': {
      breakpoints: { md: '900px' },
      useDefaultBreakpoints: true,
    },
  },
};

ESM equivalent:

import { postcssAirframe } from '@airframeui/build/postcss';

export default {
  plugins: [
    postcssAirframe({ useDefaultBreakpoints: true }),
  ],
};

Custom at-rules the PostCSS plugin compiles. They are not browser CSS — they do nothing without @airframeui/build/postcss in the pipeline.

Wrap styles in a min-width media query using your configured breakpoint pixels (or the token defaults). HTML still uses af-grid-2@md. Use this when a local class needs the same breakpoint.

With md: '900px' in airframe.config.js:

@af bp(md) {
  .hero {
    padding: var(--af-space-8);
  }
}

Compiles to:

@media (min-width: 900px) {
  .hero {
    padding: var(--af-space-8);
  }
}

The name must exist in config or in the token defaults (xs, sm, md, lg, xl, 2xl). No extra import — the at-rule is rewritten in place.

Inline an existing af-* utility into a local class. Import core CSS before the apply so the class is in the tree (the plugin inlines that @import first).

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

.scroll-demo {
  @af-apply af-scrollbar;
  max-height: 8rem;
  overflow-y: auto;
}

Compiles by copying .af-scrollbar declarations (including nested ::-webkit-scrollbar rules) into .scroll-demo. Also valid:

.scroll-demo { @af apply(af-scrollbar); }

Class names are exact (af-scrollbar, not af-scroll-bar). Prefer structure classes (af-stack, af-grid) in HTML. @af-apply is for the few cases you need a local class — for example a third-party surface you cannot put af-* on.

Working examples ship in the examples/build package of the Airframe workspace.

Prefer the PostCSS plugin and a single @import "@airframeui/core/core.css". The plugin replaces the inlined @layer af.responsive and patches --af-bp-*. Do not import generated files after core.css — core’s 768px @md would still apply from 768px up.

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

outputDir is optional. Unset means no files. Set it only when you want inspectable CSS or you are running af build without PostCSS. Token JSON from af theme export uses theme.outputDir, not this key.

module.exports = {
  breakpoints: { md: '900px' },
  outputDir: '.airframeui',
};

Then the plugin / af build write:

  • tokens.css — --af-bp-* from config (emitConfigTokenCss)
  • responsive.css — utilities for your breakpoint map (buildResponsiveCss)

Add .airframeui/ to .gitignore if you write those files.

npx af init
npx af doctor
npx af build
npx af build --input src/app.css --output dist/app.css
npx af theme generate --from ./tokens.json -o theme.css
npx af theme lint theme.css
npx af theme export
npx af theme export --no-figma
npx af figma sync
npx af figma check
npx af init --agents

af theme export writes the four-file pack (Theme Studio / manual drop) plus byo/<slug>/ when BYO Tokens is configured, and figma.variables.json for the Airframe Figma plugin — Airframe and BYO collection families. Use --no-figma for DTCG only. af figma export remains as an alias for the Variables payload alone. Enterprise teams can use af figma sync / check with a file key and token. How-to: Figma plugin. Product overview: Airframe → Figma. af build does not require an input CSS file. Without outputDir and without --input it does nothing — use the PostCSS plugin instead. Set outputDir to write generated CSS. Pass --input (and optionally --output) to also transform @af directives.

Writes a short, version-pinned agent file (AIRFRAME.md by default) and a package.json "af" script alias. Layout and pattern names in that file come from @airframeui/core/catalog — the same source of truth as IntelliSense, ESLint, and MCP.

They are complementary, not two versions of the same thing:

Agent bootstrap (af init --agents)Airframe MCP (@airframeui/mcp)
What it isA static instruction file in the repoA running stdio server the host talks to
What it doesStanding orders + "af" npm scriptLive catalog lookup (search_patterns, validate_markup, …)

Connect MCP separately — this command does not write .cursor/mcp.json.

After a package upgrade, npx af doctor checks that core / build / mcp share one version and that the agent file is pinned to the installed core. If it is stale, npx af init --agents refreshes the catalog snapshot. If AIRFRAME.md (or AGENTS.md) already exists without Airframe markers, that command refuses — --force will not replace a product file.

npx af init --agents
npx af init --agents --agent cursor
npx af init --agents --agent claude
npx af init --agents --force        # marked Airframe file only

See AI Rules → Bootstrap agent docs. Connect Airframe MCP separately — MCP.

CLI facade over @airframeui/theme. Generate, lint, validate, and export. You can call the library without this package.

npx af theme generate --from ./tokens.json -o theme.css
npx af theme lint theme.css --strict
npx af theme validate ./tokens.json --format auto
npx af theme export --css src/extra.css

Config (theme.files, theme.outputDir), DTCG layout, and the library API: Theme package. Working example: examples/theme in the Airframe workspace.

Browser UI: Theme Studio. Also Theming.

@af bp() not compiling — the PostCSS plugin must be in the pipeline that processes that file. The breakpoint name must exist in config or in the token defaults.

@af-apply “utility class not found” — import core CSS before the apply. Class names are exact (af-scrollbar, not af-scroll-bar).

postcss-import: Failed to find '@airframeui/core/core.css' — do not add a second postcss-import plugin. @airframeui/build/postcss already inlines @import and resolves package specifiers via Node. If you already run postcss-import, pass import: false.

Generated responsive.css missing — it is only written when outputDir is set and breakpoints is customized. Prefer the PostCSS plugin with @import "@airframeui/core/core.css" instead.

Breakpoints look like the defaults — useDefaultBreakpoints defaults to true (merge). Set false only when breakpoints is the entire scale. Do not import .airframeui/responsive.css after core.css.