Skip to content

Build Tool

The @airframeui/build package provides a PostCSS plugin and CLI for customizing breakpoints and typography, plus the af theme commands for generating theme CSS from design-token files.

Default responsive utilities already ship with @airframeui/core. Use this package when you need custom breakpoints, @af bp() / @af-apply in your CSS, generated typography overrides, or theme generation.

npm install -D @airframeui/build
  1. Initialize config (optional):
npx af init
  1. Build CSS:
npx af build

The PostCSS plugin can read configuration from airframe.config.js or accept options directly.

Option 1: Using airframe.config.js (Recommended)

Create airframe.config.js:

module.exports = {
breakpoints: {
  sm: '600px',
  md: '900px',
  lg: '1200px',
},
useDefaults: true,
outputDir: '.airframeui'
};

Then in postcss.config.js:

const airframeConfig = require('./airframe.config.js');

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

Option 2: Inline configuration

module.exports = {
plugins: {
  '@airframeui/build/postcss': {
    breakpoints: {
      sm: '600px',
      md: '900px',
      lg: '1200px',
    },
    useDefaults: true,
  },
},
};

Use the @af bp() directive in your CSS to apply styles at specific breakpoints:

.my-component {
  padding: 1rem;
}

@af bp(md) {
  .my-component {
    padding: 2rem;
  }
}

@af bp(lg) {
  .my-component {
    padding: 3rem;
  }
}

With the custom config above (md: 900px, lg: 1200px), this generates:

.my-component {
  padding: 1rem;
}

@media (min-width: 900px) {
  .my-component {
    padding: 2rem;
  }
}

@media (min-width: 1200px) {
  .my-component {
    padding: 3rem;
  }
}

Without a custom config, defaults are used (md768px, lg1024px).

The @af-apply directive allows you to apply utility classes inline within your CSS, similar to Tailwind’s @apply. This is useful when you want to reuse utility classes in custom CSS without duplicating their declarations.

Apply simple utility classes:

.my-component {
  @af-apply af-flex;
  @af-apply af-gap-md;
  @af-apply af-p-lg;
}

This inlines the declarations from those utility classes into .my-component.

The @af-apply directive also handles complex utilities that include pseudo-elements, such as af-scrollbar:

.sidebar-pane {
  @af-apply af-scrollbar;
}

This automatically includes all related rules, including:

  • Base scrollbar styles (Firefox scrollbar-width and scrollbar-color)
  • Webkit scrollbar pseudo-elements (::-webkit-scrollbar, ::-webkit-scrollbar-thumb, etc.)
  • Hover and active states

The plugin creates nested rules for pseudo-elements using the & selector, so the generated CSS looks like:

.sidebar-pane {
  /* Base styles from af-scrollbar */
  scrollbar-width: thin;
  scrollbar-color: var(--af-color-border-strong) transparent;
}

.sidebar-pane::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

.sidebar-pane::-webkit-scrollbar-thumb {
  background-color: var(--af-color-border);
  border-radius: var(--af-radius-full);
  border: 2px solid transparent;
  background-clip: padding-box;
  transition: background-color var(--af-duration-fast) var(--af-ease-standard);
}

.sidebar-pane::-webkit-scrollbar-thumb:hover {
  background-color: var(--af-color-border-strong);
}

.sidebar-pane::-webkit-scrollbar-track {
  background: transparent;
  border-radius: var(--af-radius-full);
}
  1. Plugin scans CSS: The PostCSS plugin scans all CSS rules to build a map of utility class definitions
  2. Finds utility class: When it encounters @af-apply utility-class-name, it looks up the utility class in the map
  3. Inlines declarations: For simple utilities, it inlines the declarations directly into the parent rule
  4. Handles pseudo-elements: For complex utilities with pseudo-elements, it creates nested rules using &::pseudo-element syntax
  • Utility classes must be defined: The utility class must be defined in your CSS before @af-apply can use it. Make sure to import @airframeui/core/core.css (or the utilities layer) before using @af-apply
  • PostCSS plugin must be configured: The @airframeui/build/postcss plugin must be included in your PostCSS configuration (see PostCSS Plugin section above)
@import "@airframeui/core/core.css";

.custom-card {
  @af-apply af-card;
  @af-apply af-p-xl;
  @af-apply af-shadow-lg;

  /_ Additional custom styles _/
  border: 2px solid var(--af-color-primary);
}

.scrollable-content {
  @af-apply af-scrollbar;
  max-height: 400px;
  overflow-y: auto;
}
  1. Install:
npm install -D @airframeui/build
  1. Create airframe.config.js (optional, for custom breakpoints):
module.exports = {
breakpoints: {
  sm: '640px',
  md: '768px',
  lg: '1024px',
},
useDefaults: true,
};
  1. Create postcss.config.js:
const airframeConfig = require('./airframe.config.js');

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

Or use defaults (no config file needed):

module.exports = {
plugins: {
  '@airframeui/build/postcss': {},
},
};
  1. Use in app/globals.css:
@import "@airframeui/core/core.css";

@af bp(md) {
  .container {
    max-width: 1200px;
  }
}
  1. Install:
npm install -D @airframeui/build
  1. Create airframe.config.js:
module.exports = {
breakpoints: {
  sm: '600px',
  md: '900px',
  lg: '1200px',
},
useDefaults: true,
};
  1. Create postcss.config.js:
const airframeConfig = require('./airframe.config.js');

module.exports = {
plugins: {
'@airframeui/build/postcss': airframeConfig,
},
};
  1. Use in CSS:
@import "@airframeui/core/core.css";

@af bp(md) {
  .grid {
    grid-template-columns: repeat(2, 1fr);
  }
}
  1. Install:
npm install -D @airframeui/build
  1. Create airframe.config.js (optional):
module.exports = {
breakpoints: {
  sm: '640px',
  md: '768px',
  lg: '1024px',
},
useDefaults: true,
};
  1. Create postcss.config.js:
const airframeConfig = require('./airframe.config.js');

module.exports = {
plugins: {
'@airframeui/build/postcss': airframeConfig,
},
};
  1. Use in styles.css:
@import "@airframeui/core/core.css";

@af bp(lg) {
  .sidebar {
    display: block;
  }
}
  1. Install:
npm install -D @airframeui/build
  1. Create airframe.config.js:
export default {
breakpoints: {
  sm: '640px',
  md: '768px',
  lg: '1024px',
},
useDefaults: true,
};
  1. Create postcss.config.js:
import airframeConfig from './airframe.config.js';

export default {
plugins: {
  '@airframeui/build/postcss': airframeConfig,
},
};
  1. Use in CSS:
@import "@airframeui/core/core.css";

@af bp(md) {
  .component {
    padding: 2rem;
  }
}

Create airframe.config.js:

module.exports = {
breakpoints: {
  sm: "640px",
  md: "768px",
  lg: "1024px",
  xl: "1280px",
  "2xl": "1536px"
},
useDefaults: true,
outputDir: ".airframeui",
typography: {
  fontSizes: {
    sm: "0.875rem",
    lg: "1.1875rem"
  },
  fonts: {
    sans: "ui-sans-serif, system-ui, sans-serif"
  }
}
};

Options:

  • breakpoints - Custom breakpoint values
  • useDefaults - Merge with default breakpoints (default: false)
  • outputDir - Directory for generated CSS files, resolved from the project root (default: .airframeui)
  • typography - Custom typography configuration (see Typography Customization section)

If no config is provided, these defaults are used:

  • xs: 360px
  • sm: 640px
  • md: 768px
  • lg: 1024px
  • xl: 1280px
  • 2xl: 1536px

The plugin writes generated CSS to the project-root outputDir (default: .airframeui/):

  • responsive.css - Responsive utility classes for your configured breakpoints (af-grid-2@md, af-gap-lg@lg, etc.)
  • typography.css - Custom typography tokens (if typography config is provided)

@airframeui/core already includes default responsive utilities. Import the generated responsive.css only when you customize breakpoints and want utilities regenerated for those values. @af bp() rules are inlined by PostCSS and do not require an extra import.

@import "@airframeui/core/core.css";
@import ".airframeui/responsive.css"; /* Generated */
@import ".airframeui/typography.css"; /* Generated (if typography config exists) */

You can add .airframeui/ to .gitignore if you prefer not to commit generated files.

Build CSS with custom breakpoints:

# Use default config
af build

# Custom config file

af build --config custom.config.json

# Custom input/output

af build --input src/app.css --output dist/app.css

Create default config file:

af init

Generate, lint, and validate Airframe theme CSS from design-token files (Tokens Studio, DTCG, CSS vars, Figma Variables, Style Dictionary). Powered by @airframeui/theme.

# Generate theme.css from a token file or HTTPS JSON/CSS URL
npx af theme generate --from ./tokens.json -o theme.css

# Lint an existing theme.css (unknown tokens, missing dark, contrast)

npx af theme lint theme.css --strict

# Validate a token file without writing CSS

npx af theme validate ./tokens.json --format auto

Supported --format values: auto, dtcg, tokens-studio, css, figma-variables, style-dictionary, airframe-spec.

For a browser UI with live preview, use Theme Studio. For AI agents, install @airframeui/mcp (generate_theme, lint_theme, validate_theme, map_tokens) and see @airframeui/theme/rules.

Also see Theming.

Custom Breakpoints Only (Default Behavior)

Section titled “Custom Breakpoints Only (Default Behavior)”

By default (useDefaults: false), only your custom breakpoints are used:

{
  "breakpoints": {
    "mobile": "480px",
    "tablet": "768px",
    "desktop": "1200px"
  }
}

Or explicitly:

{
  "breakpoints": {
    "mobile": "480px",
    "tablet": "768px",
    "desktop": "1200px"
  },
  "useDefaults": false
}

Then use in CSS:

@af bp(tablet) {
  .component {
    /* styles */
  }
}

Customize typography tokens by adding a typography section to your airframe.config.js:

module.exports = {
breakpoints: {
  sm: '640px',
  md: '768px',
},
typography: {
  // Font sizes (override any or all)
  fontSizes: {
    xs: '0.75rem',
    sm: '0.875rem',
    md: '1rem',
    lg: '1.1875rem',
    xl: '1.5rem',
    '2xl': '2rem',
    '3xl': '2.5rem',
    '4xl': '3.5rem',
    '5xl': '4.5rem',
  },
  // Font weights
  fontWeights: {
    regular: '400',
    medium: '500',
    semibold: '600',
    bold: '700',
    extrabold: '800',
  },
  // Line heights
  lineHeights: {
    tight: '1.15',
    normal: '1.55',
    loose: '1.75',
  },
  // Font families
  fonts: {
    sans: 'ui-sans-serif, system-ui, sans-serif',
    mono: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
  },
  // Type roles (override specific heading/body styles)
  typeRoles: {
    h1: {
      size: 'var(--af-text-3xl)',
      weight: 'var(--af-weight-bold)',
      leading: 'var(--af-leading-tight)',
      tracking: '-0.02em',
    },
    body: {
      size: 'var(--af-text-md)',
      weight: 'var(--af-weight-regular)',
      leading: 'var(--af-leading-normal)',
    },
  },
},
};

The plugin generates .airframeui/typography.css with your custom typography tokens. Import it after your core CSS:

@import "@airframeui/core/core.css";
@import ".airframeui/typography.css"; /* Generated file */

Available type roles:

  • h1, h2, h3, h4, h5, h6 - Heading styles
  • body - Body text
  • caption - Small text
  • display - Large display/hero text

Each type role can override:

  • size - Font size (e.g., "2rem" or "var(--af-text-2xl)")
  • weight - Font weight (e.g., "600" or "var(--af-weight-semibold)")
  • leading - Line height (e.g., "1.5" or "var(--af-leading-normal)")
  • tracking - Letter spacing (e.g., "-0.01em")

Default behavior (useDefaults: false):

  • Uses only your custom breakpoints (or defaults if none provided)
  • No merging with default breakpoints
  • Explicit control over which breakpoints are available

With useDefaults: true:

  • Merges your custom breakpoints with defaults
  • Extends the default set rather than replacing it
  • Useful when you want to add custom breakpoints while keeping defaults

Defaults:

  • If no config file is provided, defaults are used automatically
  • Sensible defaults apply without extra setup

If you were using npx --package @airframeui/core airframe-generate-responsive:

Before:

npx --package @airframeui/core airframe-generate-responsive

After (Recommended):

# Install the build package
npm install -D @airframeui/build

# Create airframe.config.js with your breakpoints

npx af build

Or continue using core directly:

npx --package @airframeui/core airframe-generate-responsive

The PostCSS plugin approach is more integrated and works seamlessly with your build process.

  • Make sure PostCSS plugin is configured correctly (see PostCSS Plugin section)
  • Check that breakpoint name exists in your config
  • Verify PostCSS is running in your build process
  • Make sure the PostCSS plugin is configured correctly (see PostCSS Plugin section)
  • Ensure utility classes are imported before using @af-apply (import @airframeui/core/core.css first)
  • Verify the utility class name is correct (e.g., af-scrollbar, not af-scroll-bar)
  • Check that PostCSS is processing your CSS files during build
  • The utility class must be defined in your CSS before @af-apply can reference it
  • Make sure you’ve imported the core CSS: @import "@airframeui/core/core.css";
  • Verify the class name matches exactly (case-sensitive, with correct hyphens)
  • Check outputDir in config (default: project-root .airframeui)
  • Ensure the build process has write permissions for that directory
  • Verify PostCSS/af build is processing your CSS files
  • Set useDefaults: true if you want to merge your breakpoints with defaults
  • If useDefaults: false, only your custom breakpoints will be used (or defaults if none provided)
  • Verify breakpoint values are valid (e.g., "640px" not 640)
  • Check that responsive.css is imported after core.css (if using generated utilities)