Bedrock/Sources/Bedrock/Theme/TYPOGRAPHY_GUIDE.md

6.4 KiB

Typography System Guide

This guide documents the typography and text styling system in Bedrock.

Overview

The typography system consists of:

  1. Typography enum - All font definitions with explicit naming
  2. TextEmphasis enum - Semantic text colors
  3. StyledLabel - Simple text component combining typography + emphasis
  4. IconLabel - Icon + text component
  5. .typography() modifier - View extension for applying fonts

Typography Enum

All fonts are defined in the Typography enum. Each case name explicitly describes the font:

  • Base name = regular weight (e.g., .title2 = Font.title2)
  • *Bold = bold weight (e.g., .title2Bold = Font.title2.bold())
  • *Emphasis = semibold weight (e.g., .bodyEmphasis = Font.body.weight(.semibold))

Available Styles

Category Style Font
Hero/Titles .hero largeTitle
.heroBold largeTitle.bold()
.title title
.titleBold title.bold()
.title2 title2
.title2Bold title2.bold()
.title3 title3
.title3Bold title3.bold()
Headings .heading headline
.headingEmphasis headline.weight(.semibold)
.headingBold headline.bold()
.subheading subheadline
.subheadingEmphasis subheadline.weight(.semibold)
Body .body body
.bodyEmphasis body.weight(.semibold)
.callout callout
.calloutEmphasis callout.weight(.semibold)
Micro .footnote footnote
.footnoteEmphasis footnote.weight(.semibold)
.caption caption
.captionEmphasis caption.weight(.semibold)
.caption2 caption2
.caption2Emphasis caption2.weight(.semibold)

Usage

// Using the .typography() modifier (preferred)
Text("Hello")
    .typography(.heading)

// Using .font() directly
Text("Hello")
    .font(Typography.heading.font)

TextEmphasis Enum

Semantic text colors that work with any theme:

Emphasis Description
.primary Main text color
.secondary Supporting text
.tertiary Subtle/hint text
.disabled Disabled state
.inverse Contrasting backgrounds
.custom(Color) Custom color override

Usage

// With StyledLabel
StyledLabel("Title", .heading, emphasis: .primary)
StyledLabel("Subtitle", .subheading, emphasis: .secondary)
StyledLabel("Custom", .body, emphasis: .custom(.red))

// With custom theme colors
StyledLabel("Title", .heading, emphasis: .custom(AppTextColors.primary))

StyledLabel Component

A single component for all styled text. Replaces the old TitleLabel, BodyLabel, CaptionLabel.

StyledLabel(_ text: String, _ typography: Typography = .body, emphasis: TextEmphasis = .primary)

Examples

// Simple usage
StyledLabel("Morning Ritual", .heading)
StyledLabel("Day 6 of 28", .caption, emphasis: .secondary)

// With custom theme colors
StyledLabel("Title", .heading, emphasis: .custom(AppTextColors.primary))
StyledLabel("Subtitle", .subheading, emphasis: .custom(AppTextColors.secondary))

IconLabel Component

For icon + text combinations:

IconLabel(_ icon: String, _ text: String, _ typography: Typography = .body, emphasis: TextEmphasis = .primary)

Examples

IconLabel("bell.fill", "Notifications", .subheading)
IconLabel("star.fill", "Favorites", .body, emphasis: .secondary)

When to Use StyledLabel vs Text()

Use StyledLabel (Preferred)

Use StyledLabel for the vast majority of text:

// Simple text
StyledLabel("Settings", .subheadingEmphasis)
StyledLabel("Description", .subheading, emphasis: .secondary)

// With multiline alignment
StyledLabel("Centered text", .body, emphasis: .primary, alignment: .center)

// With line limit
StyledLabel("One line only", .caption, emphasis: .secondary, lineLimit: 1)

// In buttons (put frame modifiers outside)
Button(action: onContinue) {
    StyledLabel("Continue", .heading, emphasis: .custom(AppTextColors.inverse))
        .frame(maxWidth: .infinity)
        .frame(height: 50)
        .background(AppAccent.primary)
}

Use Text() with .typography() (Rare Exceptions)

Only use Text() when you genuinely need modifiers that StyledLabel doesn't support:

// Font design variants (rounded, etc.)
Text(formattedValue)
    .typography(.subheadingEmphasis)
    .fontDesign(.rounded)

// TextField styling
TextField("Placeholder", text: $value)
    .typography(.heading)

// Inside special view builders (like Charts AxisValueLabel)
AxisValueLabel {
    Text("\(value)%")
        .font(Typography.caption2.font)  // .typography() won't work here
        .foregroundStyle(color)
}

Never Use Raw .font() with System Fonts

Instead of:

// BAD
Text("Title").font(.headline)

Use:

// GOOD
StyledLabel("Title", .heading)

Migration from Old System

Old vs New

// OLD: Design.Typography
Text("Title").font(Design.Typography.headline)
Text("Body").font(Design.Typography.body)
Text("Caption").font(Design.Typography.caption)

// NEW: Typography enum
Text("Title").typography(.heading)
Text("Body").typography(.subheading)
Text("Caption").typography(.caption)

// OLD: Multiple Label components
TitleLabel("Title", style: .headline, color: .white)
BodyLabel("Body", emphasis: .secondary)
CaptionLabel("Caption")

// NEW: Single StyledLabel
StyledLabel("Title", .heading, emphasis: .custom(.white))
StyledLabel("Body", .subheading, emphasis: .secondary)
StyledLabel("Caption", .caption, emphasis: .secondary)

Mapping Table

Old (Design.Typography) New (Typography)
.displayLarge .heroBold
.displayMedium .titleBold
.displaySmall .title2Bold
.headline .heading
.headlineBold .headingBold
.body .subheading
.bodyMedium .subheadingEmphasis
.bodyLarge .body
.caption .caption
.captionMedium .captionEmphasis
.captionSmall .caption2
.settingsTitle .subheadingEmphasis
.sectionHeader .captionEmphasis

SymbolIcon (Unchanged)

The SymbolIcon component remains the same for consistent icon styling:

SymbolIcon("star.fill", size: .row, color: .accent)
SymbolIcon("sparkles", size: .hero, color: AppAccent.primary)
SymbolIcon.chevron()

See the existing documentation for SymbolIcon sizes and usage.