Skip to content

Angular Setup Guide

Airframe is CSS-only. No Angular-specific package needed. Install the core CSS, import it, and use Airframe classes directly in your templates.

Works with Angular 17+ standalone components and NgModule applications.

npm install @airframeui/core

Using angular.json (recommended)

Add to angular.json under projects.<app>.architect.build.options:

"styles": [
  "node_modules/@airframeui/core/core.css",
  "src/styles.scss"
]

Using styles.scss

Alternatively, import directly in src/styles.scss:

@import "@airframeui/core/core.css";
@Component({
selector: 'app-root',
standalone: true,
template: `    <div class="af-container">
      <div class="af-stack af-gap-lg">
        <h1>
          Welcome
        </h1>
        <button class="af-btn">
          Get Started
        </button>
      </div>
</div>`
})
export class AppComponent {}

That’s it. No wrappers, no modules to import, no extra dependencies.

@Component({
selector: 'app-dashboard',
standalone: true,
template: `    <div class="af-container">
      <div class="af-stack af-gap-lg">
        <header class="af-inline af-justify-between">
          <h1>
            Dashboard
          </h1>
          <button class="af-btn">
            New Item
          </button>
        </header>
        <div class="af-grid af-grid-3@lg af-gap-lg">
          <div class="af-card">
            <h3 class="af-card__title">
              Revenue
            </h3>
            <p class="af-text-h2">
              $84,200
            </p>
          </div>
          <div class="af-card">
            <h3 class="af-card__title">
              Users
            </h3>
            <p class="af-text-h2">
              12,340
            </p>
          </div>
          <div class="af-card">
            <h3 class="af-card__title">
              Growth
            </h3>
            <p class="af-text-h2">
              +18%
            </p>
          </div>
        </div>
        <div class="af-grid">
          <div class="af-col-span-8@lg">
            <div class="af-card">
              <h2 class="af-card__title">
                Main Content
              </h2>
              <p class="af-card__body">
                Content goes here
              </p>
            </div>
          </div>
          <div class="af-col-span-4@lg">
            <div class="af-card">
              <h2 class="af-card__title">
                Sidebar
              </h2>
              <p class="af-card__body">
                Sidebar content
              </p>
            </div>
          </div>
        </div>
      </div>
</div>`
})
export class DashboardComponent {}
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms';

@Component({
selector: 'app-contact',
standalone: true,
imports: [ReactiveFormsModule],
template: `    <div class="af-container-sm">
      <form class="af-stack af-gap-lg" [formGroup]="form" (ngSubmit)="onSubmit()">
        <div class="af-field">
          <label for="name" class="af-field__label">
            Name
          </label>
          <input type="text" id="name" class="af-input" formControlName="name" />
        </div>
        <div class="af-field">
          <label for="email" class="af-field__label">
            Email
          </label>
          <input type="email" id="email" class="af-input" formControlName="email" />
          <p class="af-field__help">
            We'll never share your email.
          </p>
        </div>
        <div class="af-field">
          <label for="message" class="af-field__label">
            Message
          </label>
          <textarea id="message" class="af-textarea" rows="5" formControlName="message">
          </textarea>
        </div>
        <div class="af-inline af-gap-md">
          <button type="submit" class="af-btn" [disabled]="form.invalid">
            Send
          </button>
          <button type="button" class="af-btn af-is-secondary" (click)="form.reset()">
            Clear
          </button>
        </div>
      </form>
</div>`
})
export class ContactComponent {
form = inject(FormBuilder).group({
name: ['', Validators.required],
email: ['', [Validators.required, Validators.email]],
message: ['', Validators.required],
});

onSubmit(): void {
console.log(this.form.value);
}
}
import { Component } from '@angular/core';
import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';

@Component({
selector: 'app-layout',
standalone: true,
imports: [RouterLink, RouterLinkActive, RouterOutlet],
template: `    <nav class="af-navbar">
      <div class="af-container">
        <a routerLink="/" class="af-navbar-brand">
          My App
        </a>
        <ul class="af-navbar-nav">
          <li>
            <a routerLink="/dashboard" routerLinkActive="af-is-active">
              Dashboard
            </a>
          </li>
          <li>
            <a routerLink="/settings" routerLinkActive="af-is-active">
              Settings
            </a>
          </li>
        </ul>
      </div>
    </nav>
    <main class="af-container af-section-lg">
      <router-outlet />
</main>`
})
export class LayoutComponent {}
import { Component, input } from '@angular/core';

@Component({
selector: 'app-user-table',
standalone: true,
template: `    <div class="af-card">
      <table class="af-table">
        <thead>
          <tr>
            <th>
              Name
            </th>
            <th>
              Email
            </th>
            <th>
              Role
            </th>
            <th>
              Actions
            </th>
          </tr>
        </thead>
        <tbody>
          @for (user of users(); track user.id) {
          <tr>
            <td>
              {{ user.name }}
            </td>
            <td>
              {{ user.email }}
            </td>
            <td>
              <span class="af-badge">
                {{ user.role }}
              </span>
            </td>
            <td>
              <div class="af-inline af-gap-sm">
                <button class="af-btn af-is-sm">
                  Edit
                </button>
                <button class="af-btn af-is-sm af-is-secondary">
                  Delete
                </button>
              </div>
            </td>
          </tr>
          }
        </tbody>
      </table>
</div>`
})
export class UserTableComponent {
users = input<{ id: number; name: string; email: string; role: string }[]>([]);
}

Override tokens in your styles.scss. No rebuild needed.

/* src/styles.scss */
@import "@airframeui/core/core.css";

:root {
--af-base-primary: #0066cc;
--af-font-body: 'Inter', sans-serif;
--af-radius-md: 0.5rem;
}

Dark mode follows system preference by default. Override per-element or globally:

<!-- Global override -->
<html lang="en" data-theme="dark">
  <!-- Scoped override -->
  <div class="af-card" data-theme="dark">
    Dark card on a light page
  </div>

Available themes: light, dark, high-contrast-light, high-contrast-dark

@Component({
selector: 'app-theme-toggle',
standalone: true,
template: `    <button class="af-btn af-is-sm" (click)="toggleTheme()">
      Toggle {{ isDark ? 'Light' : 'Dark' }}
</button>`
})
export class ThemeToggleComponent {
isDark = false;

toggleTheme(): void {
this.isDark = !this.isDark;
document.documentElement.setAttribute(
'data-theme',
this.isDark ? 'dark' : 'light'
);
}
}

Airframe classes work with Angular’s default view encapsulation. If you need to style Airframe elements from a parent component, use :host or set encapsulation to None:

@Component({
selector: 'app-custom',
standalone: true,
encapsulation: ViewEncapsulation.None,
styles: `
  .my-custom-card {
    --af-card-padding: var(--af-space-6);
    --af-card-radius: var(--af-radius-lg);
  }
`,
template: `    <div class="af-card my-custom-card">
      <h2 class="af-card__title">
        Custom Card
      </h2>
</div>`
})
export class CustomComponent {}

Airframe CSS works with Angular SSR out of the box. No special configuration needed. The CSS is loaded via angular.json styles array, which works in both client and server builds.

  • Check that @airframeui/core/core.css is in the styles array in angular.json
  • Or check that src/styles.scss imports it and is itself in the styles array
  1. Use class not className (Angular uses class)
  2. Check that no conflicting CSS is overriding Airframe styles
  3. If view encapsulation is blocking styles, override tokens via CSS variables instead of targeting Airframe class internals
  • Angular 17+ required
  • Node 20+ required by the Angular CLI toolchain (Airframe CSS itself has no Node runtime requirement)
  • Run npm install to ensure @airframeui/core is installed