Typography guide
Airframe styles semantic elements by default and provides a small set of text roles for when semantics and visuals diverge.
Philosophy
Section titled “Philosophy”- Semantic first:
<h1>–<h6>,<p>,<small>look good by default. Use semantic elements by default. - Text roles only for visual hierarchy overrides: Use
af-text-h*oraf-text-bodyonly when semantics and visuals must diverge.- ✅
<h2 class="af-text-h1">— visually H1, semantically H2 - ✅
<p class="af-text-caption">— caption styling on paragraph - ❌
<h1 class="af-text-h1">— redundant, use<h1>instead
- ✅
- Token-driven: Typography is controlled by
--af-type-*tokens for easy brand overrides.
Semantic Defaults
Section titled “Semantic Defaults”These elements are styled automatically:
<h1>
Title
</h1>
<h2>
Section
</h2>
<p>
Body text
</p>
<small>
Caption
</small>Heading Groups
Section titled “Heading Groups”Use <hgroup> to visually group a heading with its subtitle or tagline. This is useful for hero sections, page headers, and card titles:
<hgroup>
<h1>
Page Title
</h1>
<p class="af-lead">
Subtitle or tagline text
</p>
</hgroup>Important: <hgroup> provides visual grouping only. Browsers don’t implement its semantic outline algorithm behavior, so ensure your heading hierarchy (<h1>–<h6>) remains correct for accessibility. The element is styled with appropriate spacing to group the heading and subtitle together.
Default Rhythm
Section titled “Default Rhythm”Headings, paragraphs, lists, blockquotes, and dividers include sensible spacing by default. You should rarely need to add manual spacing for typical long-form content.
Text Roles (Optional)
Section titled “Text Roles (Optional)”Use text roles when semantics and appearance need to diverge:
<h2 class="af-text-h1">
Visually H1, semantically H2
</h2>
<p class="af-text-h3">
Visually H3, semantically paragraph
</p>
<p class="af-text-body">
Explicit body role
</p>
<p class="af-text-caption">
Caption text
</p>
<span class="af-text-overline">
Overline label
</span>
<div class="af-text-display">
Hero headline
</div>Available roles:
af-text-displayaf-text-h1…af-text-h6af-text-bodyaf-text-captionaf-text-overline
Page Patterns (Hero, Stats, Prose)
Section titled “Page Patterns (Hero, Stats, Prose)”For marketing pages and dashboards:
af-eyebrow– Section label (uppercase, letter-spacing). Addaf-is-on-primaryfor hero/CTA on primary background.af-text-fluid-h1,af-text-fluid-h2,af-text-fluid-h3– Responsive hero headings (clamp-based).af-stat-display– Large hero numbers (e.g. “12.4k”, “0”, “1”).af-prose/af-measure– Readable line length (65ch). Variants:af-measure-narrow(45ch),af-measure-wide(80ch).
<p class="af-eyebrow">
Why Airframe
</p>
<h1 class="af-text-fluid-h1">
Hero heading
</h1>
<p class="af-stat-display">
12.4k
</p>
<div class="af-prose">
Long-form content with optimal line length
</div>Typography Helpers (Minimal)
Section titled “Typography Helpers (Minimal)”These are intentionally small and common:
af-text-muted– muted text coloraf-text-on-primary– text on primary background (hero/CTA)af-text-on-primary-muted– muted text on primary (opacity 0.7)af-text-mono– monospace fontaf-text-sm/af-text-md/af-text-lg– simple size helpersaf-title– quick title style
Text Alignment (Dir-Aware)
Section titled “Text Alignment (Dir-Aware)”Text alignment utilities that respect document direction (LTR/RTL):
<!-- Base alignment -->
<p class="af-text-center">
Centered text
</p>
<p class="af-text-start">
Start-aligned (left in LTR, right in RTL)
</p>
<p class="af-text-end">
End-aligned (right in LTR, left in RTL)
</p>
<!-- Responsive pattern: center on small, start on md+ -->
<h2 class="af-text-h2 af-text-center af-text-start@md">
Responsive, RTL-safe heading
</h2>Available classes:
af-text-center– Center alignmentaf-text-start– Start alignment (RTL-safe)af-text-end– End alignment (RTL-safe)
Responsive variants (v0.1):
af-text-center@{bp}– Center at breakpoint and upaf-text-start@{bp}– Start at breakpoint and up
Why start/end instead of left/right?
start= left in LTR, right in RTLend= right in LTR, left in RTL- Automatically respects
[dir="ltr"]and[dir="rtl"] - No JavaScript needed
- Standards-compliant and future-proof
Recommended Pattern:
<!-- Center on small screens, start-aligned from md up (RTL-safe) -->
<h2 class="af-text-h2 af-text-center af-text-start@md">
Responsive, RTL-safe heading
</h2>Behavior:
- xs / sm: Centered
- md+ (LTR): Left-aligned
- md+ (RTL): Right-aligned
Token Model
Section titled “Token Model”Typography uses semantic tokens so brands can override safely:
Font Families
Section titled “Font Families”:root {
/* Font primitives */
--af-base-font-sans: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji";
--af-base-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
/_ Semantic font tokens _/
--af-font-primary: var(--af-base-font-sans); /_ Primary font (used for body text by default) _/
--af-font-heading: var(--af-font-primary); /_ Heading font (applied to h1-h6, .af-text-h_, .af-text-display) _/
--af-font-body: var(--af-base-font-sans); /_ Body text font _/
--af-font-code: var(--af-base-font-mono); /_ Code font _/
--af-font-mono: var(--af-base-font-mono); /_ Monospace font */
}Customizing fonts:
:root {
/* Use custom web fonts */
--af-font-primary: 'Inter', 'Roboto', sans-serif;
--af-font-heading: 'Playfair Display', serif; /* Different font for headings */
--af-font-body: var(--af-font-primary); /* Body uses primary */
}Type Scale
Section titled “Type Scale”:root {
/* Sizes */
--af-text-xs: 0.75rem;
--af-text-sm: 0.875rem;
--af-text-md: 1rem;
--af-text-lg: 1.125rem;
--af-text-xl: 1.25rem;
--af-text-2xl: 1.5rem;
--af-text-3xl: 1.875rem;
/_ Leading (line-height) - typography term for vertical spacing between lines _/
/_ Use --af-leading-_ tokens; these map to CSS line-height property */
--af-leading-tight: 1.1;
--af-leading-normal: 1.5;
--af-leading-loose: 1.7;
/_ Weights _/
--af-weight-regular: 400;
--af-weight-medium: 500;
--af-weight-semibold: 600;
--af-weight-bold: 700;
/_ Roles _/
--af-type-h1-size: var(--af-text-3xl);
--af-type-h1-weight: var(--af-weight-semibold);
--af-type-h1-leading: var(--af-leading-tight);
--af-type-h1-tracking: -0.02em;
--af-type-h2-tracking: -0.01em;
--af-type-h3-tracking: 0em;
--af-type-h4-tracking: 0em;
--af-type-h5-tracking: 0em;
--af-type-h6-tracking: 0em;
}Example: Card Title with Visual Override
Section titled “Example: Card Title with Visual Override”<div class="af-card">
<h3 class="af-text-h2">
Card Title
</h3>
<p class="af-card__body">
Body text remains semantic and readable.
</p>
</div>Best Practices
Section titled “Best Practices”- Prefer semantic elements (
<h1>–<h6>,<p>,<small>) by default. - Use text roles only when you need visual hierarchy overrides.
- Override typography via tokens, not by increasing selector specificity.
For class-level utilities (af-text-*, alignment, wrap), see Typography utilities.