Skip to content

Theme package

@airframeui/theme is the token pipeline. CSS is the spec. This package reads your --af-* overrides, checks them, and writes token JSON the rest of the toolchain can use.

Export to Figma is the main job. It is not the whole package. Inbound mapping (generate / lint) is here too. Git stays the source of truth.

Most apps never import the library. Override --af-* in CSS (Theming), then run the CLI from @airframeui/build.

npm install -D @airframeui/build
npx af theme export

Browser playground: Theme Studio. Product page: Airframe → Figma. Working repo: examples/theme.

  1. Brand the product in CSS (:root and [data-brand]).
  2. Point theme.files at those stylesheets in airframe.config.js.
  3. npx af theme export writes DTCG JSON into theme.outputDir.

Export layers, in order:

  1. Catalog defaults from @airframeui/tokens
  2. Breakpoints from airframe.config.js (if you set them)
  3. Each path in theme.files, in array order

Later files win when the same token is set twice. Earlier values stay if a later file does not repeat them. Brands are discovered from [data-brand]. Do not list brand ids in config. Colours and fonts stay in CSS.

Create airframe.config.js in the project root. npx af init writes a starter. af theme loads this file from cwd. --config picks another path.

/** @type {import('@airframeui/build').AirframeBuildOptions} */
export default {
  theme: {
    files: ['./src/tokens.css', './src/brands/*.css'],
    outputDir: '.airframeui',
  },
};
KeyWhat it does
theme.filesCSS/SCSS with --af-* overrides. Globs are allowed. Split colour across files is expected. A comma-separated [data-brand] list applies to every brand in the list.
theme.outputDirFolder tokens/ is written into. Default .airframeui if you omit it. CLI -o / --out overrides.

That is not top-level outputDir in the same file. That key is optional generated CSS for PostCSS / af build. See Build Tool.

Add .airframeui/ to .gitignore. Do not commit the export.

--css src/extra.css adds stylesheets for one export without editing config.

CommandDirectionWrites
af theme exportCSS → JSONtokens/<palette>/{light,dark,hc-*}.tokens.json
af theme generate --from …JSON/CSS → CSS--af-* stylesheet (best-effort inbound)
af theme lintcheck CSSdiagnostics only
af theme validatecheck a filethe same report, no CSS written
npx af theme export
npx af theme export -o ./figma-tokens
npx af theme export --css src/extra.css

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

Formats for generate / validate: auto, dtcg, tokens-studio, css, figma-variables, style-dictionary, airframe-spec.

--from accepts a local file or an HTTPS URL to JSON/CSS (not an HTML page). --strict fails on warnings.

Each af theme export replaces tokens/ so a brand you deleted in CSS does not linger. Other files in the output directory are left alone.

.airframeui/
tokens/
  default/          ← :root palette
    light.tokens.json
    dark.tokens.json
    hc-light.tokens.json
    hc-dark.tokens.json
  delta/            ← [data-brand='delta']
    light.tokens.json
    dark.tokens.json
    hc-light.tokens.json
    hc-dark.tokens.json
airframe.resolver.json

Same four filenames in every folder so modes line up. default is :root. Every other folder is a [data-brand] id. Do not name a brand base. That collides with --af-base-*.

This is why most teams export. Designers drop the four files in tokens/default onto one collection. Each file is a mode. Same names as the app.

A second brand is a second collection with the same four filenames, so light / dark / high contrast still line up.

Figma does not read airframe.resolver.json.

Theme Studio downloads the same pack in the browser: four JSON files, four modes, one collection. Product page: Airframe → Figma.

Do not import a Figma or Tokens Studio dump back into Airframe and expect a complete theme. Names rarely match. Override CSS yourself, then export.

Inbound is best-effort. The mapper is a small alias table (primary--af-base-primary, plus --af-* names). Proprietary paths (color.blue.500, brand/500) land in the unmapped report, not in CSS. Dark and high-contrast companions are often missing.

Keep generate for CSS that already uses --af-*, for a round-trip of our own DTCG, or when you have renamed tokens to match the catalog. Then lint.

Import the generated theme.css after @airframeui/core.

@airframeui/theme is the library behind the CLI. Dev dependency. Same version as core. It does not run in the browser. The app still needs @airframeui/core for runtime CSS.

npm install -D @airframeui/theme

Use the library when you are writing your own script. Most apps stop at af theme export.

import { emitDtcgExport, resolveTheme } from '@airframeui/theme';

const spec = resolveTheme({ config, css });
const { palettes } = emitDtcgExport({ spec });
// palettes.default.light … palettes.delta.dark