Skip to content

Theme package

Most teams use this package for one job: get live brand colours into Figma.

CSS in the project is the source of truth. Brand with --af-* overrides (Theming), then export. Plugin how-to: Figma plugin. Product overview: Airframe → Figma.

  1. Preferred — npx af theme export → Airframe plugin → Update Figma (figma.variables.json)
  2. Alternate — same export’s DTCG pack or Theme Studio → drop four files by hand

Optional tools also map foreign files, lint CSS, and generate theme stylesheets. Most apps never import the library — they use the CLI from @airframeui/build.

npx af theme export

Working example: examples/theme in the Airframe workspace. BYO Tokens: examples/byotokens.

  1. Brand the product in CSS (:root and [data-brand]).
  2. Point theme.files at those stylesheets in airframe.config.js.
  3. Run npx af theme export — writes DTCG under tokens/ (and byo/ when BYO Tokens is set) plus figma.variables.json for the plugin. Use --no-figma for DTCG only.

Both layers of the export use the same inputs:

  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.

Extra --af-* names still export. Theming.

BYO Tokens: product-owned variables (theme.byoTokens.prefix) export in parallel under .airframeui/byo/<slug>/ and as separate Figma collections (Demo / default). Full guide: BYO Tokens.

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',
    byoTokens: { prefix: '--demo-' },
    existingUi: { excludePrefixes: ['--ion-'] },
  },
};
KeyWhat it does
theme.filesCSS/SCSS with --af-* overrides and BYO tokens. Globs are allowed. Split colour across files is expected. A comma-separated [data-brand] list applies to every brand in the list.
theme.byoTokens.prefixProduct namespace for BYO Tokens. Exports to byo/<slug>/ and a separate Figma collection. See BYO Tokens.
theme.existingUiAirframe → existing UI mapping (runtime only). excludePrefixes skips host vars from export.
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/… + byo/… (BYO Tokens) + figma.variables.json (omit Figma with --no-figma)
af figma exportCSS → JSONfigma.variables.json only (alias; prefer af theme export)
af figma syncrefresh + pushsame file; Enterprise REST when configured
af figma checkcheck FigmaEnterprise REST drift report
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 --no-figma
npx af theme export --css src/extra.css

npx af figma sync
npx af figma check

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/ (and byo/ when present) so a brand you deleted in CSS does not linger. It also rewrites figma.variables.json unless you pass --no-figma.

.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
byo/demo/default/light.tokens.json   ← when theme.byoTokens.prefix is set
figma.variables.json                 ← plugin payload (skip with --no-figma)

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-*.

After branding in CSS, sync the same colours into Figma Variables. CSS in the project is the source of truth — not Figma.

PathExportIn Figma
Preferred — pluginnpx af theme export → figma.variables.jsonOpen Airframe → drop file → Update Figma
Alternate — token packsame export’s tokens/ or Theme StudioDrop four DTCG files from tokens/default onto one collection

Step-by-step (plugin, drift, token pack, Enterprise): Figma plugin. Product overview: Airframe → Figma.

Export preserves aliases in the pack (for example button background → primary colour → brand base). Mode-specific and derived colour-mix values stay as resolved colours. Figma does not read airframe.resolver.json. How CSS becomes JSON: Tokens — source of truth.

Do not import a Figma or Tokens Studio dump back into Airframe and expect a complete theme. Names rarely match. Set colours in CSS (or Theme Studio), 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, emitFigmaVariablesModel, resolveTheme } from '@airframeui/theme';

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

const figma = emitFigmaVariablesModel({ spec });
// figma.collections[0].variables — native Variables payload (plugin / REST)