Typography System Guide for iOS and Android

Comprehensive guide for implementing accessible typography in mobile apps – (V 2.1)

iOS Typography System

Apple's San Francisco font family with Dynamic Type support for accessibility. Automatically switches between SF Pro Text for smaller sizes (≤19pt) and SF Pro Display for larger sizes (≥20pt). As of iOS 18, Dynamic Type supports 12 sizes total — 7 standard sizes (xSmall through xxxLarge) plus 5 Larger Accessibility Sizes (AX1–AX5) — and body text can scale up to approximately 310% at AX5. Apple HIG — Typography

iOS Default Typography Styles

Large Title
Font SF Pro Display
Weight Bold
Size 34pt
Line Height 41pt
Title 1
Font SF Pro Display
Weight Regular
Size 28pt
Line Height 34pt
Title 2
Font SF Pro Display
Weight Regular
Size 22pt
Line Height 28pt
Title 3
Font SF Pro Display
Weight Regular
Size 20pt
Line Height 25pt
Headline
Font SF Pro Text
Weight Semibold
Size 17pt
Line Height 22pt
Body
Font SF Pro Text
Weight Regular
Size 17pt
Line Height 22pt
Callout
Font SF Pro Text
Weight Regular
Size 16pt
Line Height 21pt
Subhead
Font SF Pro Text
Weight Regular
Size 15pt
Line Height 20pt
Footnote
Font SF Pro Text
Weight Regular
Size 13pt
Line Height 18pt
Caption 1
Font SF Pro Text
Weight Regular
Size 12pt
Line Height 16pt
Caption 2
Font SF Pro Text
Weight Regular
Size 11pt
Line Height 13pt

Dynamic Type Demo

Large (Default)
Large Title The quick brown fox
Title 1 The quick brown fox
Title 2 The quick brown fox
Headline The quick brown fox
Body The quick brown fox
Callout The quick brown fox
Footnote The quick brown fox
Caption 1 The quick brown fox

Implementation Guidelines for iOS

Here are the best practices to properly implement typography in iOS applications:

  • Use San Francisco font for a native iOS look and feel
  • Implement Dynamic Type for accessibility compliance — iOS 18 supports 7 standard sizes (xSmall, Small, Medium, Large, xLarge, xxLarge, xxxLarge) plus 5 Larger Accessibility Sizes (AX1–AX5), 12 total
  • Set minimum font size for body text to 17pt for readability
  • For custom fonts, use UIFontMetrics for Dynamic Type scaling
  • Test your app across all 12 Dynamic Type sizes — 7 standard (xSmall → xxxLarge) and 5 Larger Accessibility Sizes (AX1–AX5). Body text reaches approximately 310% at AX5 — design layouts to handle this
  • Large Content Viewer (iOS 13+): For fixed-size UI elements that cannot grow with Dynamic Type — such as tab bars, toolbars, and navigation bar icons — implement the Large Content Viewer so users can still identify them at maximum text sizes. Use showsLargeContentViewer = true on UIKit elements or the .accessibilityShowsLargeContentViewer() modifier in SwiftUI

Apple Developer — Scaling Fonts Automatically

// Swift: Dynamic Type with system font
label.font = UIFont.preferredFont(forTextStyle: .body)
label.adjustsFontForContentSizeCategory = true

// Swift: Custom font with UIFontMetrics
let customFont = UIFont(name: "CustomFont-Regular", size: 16)!
label.font = UIFontMetrics.default.scaledFont(for: customFont)
label.adjustsFontForContentSizeCategory = true

// SwiftUI: Restrict Dynamic Type range when layout requires it
// (only use .accessibility1–.accessibility5 if you've tested all sizes)
Text("Sample Text")
    .font(.body)
    .dynamicTypeSize(.large ... .accessibility5)

// SwiftUI: Large Content Viewer for fixed-size elements
Image(systemName: "star.fill")
    .accessibilityShowsLargeContentViewer()

Android Typography System

Android uses the Roboto font family in Material Design 3. Text sizes in sp (scalable pixels) automatically scale with user preferences. As of Android 14, the maximum font scale increased from 130% to 200%, and the system now applies a non-linear scaling curve — larger text grows more slowly than smaller text to prevent oversized display text from breaking layouts. Android 14 — Font Scaling

Android Material Typography Styles

Display Large
Font Roboto
Weight Regular
Size 57sp
Line Height 64sp
Display Medium
Font Roboto
Weight Regular
Size 45sp
Line Height 52sp
Display Small
Font Roboto
Weight Regular
Size 36sp
Line Height 44sp
Headline Large
Font Roboto
Weight Regular
Size 32sp
Line Height 40sp
Headline Medium
Font Roboto
Weight Regular
Size 28sp
Line Height 36sp
Headline Small
Font Roboto
Weight Regular
Size 24sp
Line Height 32sp
Title Large
Font Roboto
Weight Regular
Size 22sp
Line Height 28sp
Title Medium
Font Roboto
Weight Medium
Size 16sp
Line Height 24sp
Title Small
Font Roboto
Weight Medium
Size 14sp
Line Height 20sp
Body Large
Font Roboto
Weight Regular
Size 16sp
Line Height 24sp
Body Medium
Font Roboto
Weight Regular
Size 14sp
Line Height 20sp
Body Small
Font Roboto
Weight Regular
Size 12sp
Line Height 16sp
Label Large
Font Roboto
Weight Medium
Size 14sp
Line Height 20sp
Label Medium
Font Roboto
Weight Medium
Size 12sp
Line Height 16sp
Label Small
Font Roboto
Weight Medium
Size 11sp
Line Height 16sp

Font Scaling Demo

1x
Display Large The quick brown fox
Headline Large The quick brown fox
Title Medium The quick brown fox
Body Large The quick brown fox
Label Small The quick brown fox

Implementation Guidelines for Android

Here are the best practices to properly implement typography in Android applications:

  • Always define text sizes in scalable pixels (sp) to respect user font size preferences — never use dp or px for text
  • Also use sp for line heights — if you define lineHeight in dp, it will not scale with the font, causing text to overlap at larger sizes on Android 14+
  • The minimum recommended size for body text is 16sp for readability (Material Design 3 minimum is 14sp)
  • For dense lists or information, you can use 14sp with a slightly heavier weight
  • Use dynamic layouts with wrap_content and ConstraintLayout to adapt to different text sizes
  • Android 14+: Test font scale from 0.85x up to 2.0x — the maximum increased from 130% to 200% in Android 14. The system applies non-linear scaling, so large Display text grows more slowly than Body text
  • Avoid setting fixed heights on containers that hold text — use wrap_content or minHeight instead

Android Developer — Screen Densities & sp units

<!-- XML: use sp for both textSize AND lineHeight -->
<TextView
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:textSize="16sp"
   android:lineHeight="24sp"
   android:textColor="@color/textPrimary"
   android:text="@string/sample_text" />

<!-- Compose: MaterialTheme styles handle sp scaling automatically -->
Text(
   text = "Sample Text",
   style = MaterialTheme.typography.bodyLarge,
   modifier = Modifier.fillMaxWidth()
)

<!-- Compose: Never hardcode sp values directly — use the type scale -->
<!-- MaterialTheme.typography.bodyLarge = 16sp / 24sp line height -->

Typography Accessibility Guidelines

Creating accessible typography following WCAG 2.2 AA standards for text scaling and readability. WCAG 2.2 was published October 2023 and is the current recommended standard. WCAG 2.2 Specification

WCAG 2.2 Success Criterion 1.4.3 — Contrast (Minimum)

Requirement

Level AA

Text and images of text must have a contrast ratio of at least 4.5:1 against their background. Large text (18pt / 14pt bold or larger) requires at least 3:1.

Typography-specific rules

  • Body text (≤17pt regular, ≤13pt bold): minimum 4.5:1 contrast ratio
  • Large text (≥18pt regular, or ≥14pt bold): minimum 3:1 contrast ratio
  • Placeholder text is real text — it must also meet 4.5:1, not just be "visible"
  • Disabled text is exempt, but use sparingly — if content is important enough to show, it should be readable
  • Dynamic Type interaction: as text scales up it may cross the large text threshold (18pt), meaning its required contrast ratio drops from 4.5:1 to 3:1 — your design must pass at both ends of the scale
  • WCAG 2.2 AAA raises the bar further to 7:1 (body) and 4.5:1 (large text) via SC 1.4.6 — worth targeting for high-stakes content

Mobile-specific considerations

  • On iOS, the system Bold Text setting increases font weight — a text style that was body-weight may become bold, potentially crossing the large text threshold
  • On Android, High Contrast Text mode forces system-level contrast adjustments — test that your custom text colors aren't overriding these
  • Layered elements (text over images, text over gradients) must meet contrast at the worst point of the gradient — not the average

WCAG 2.2 Success Criterion 1.4.4 — Resize Text

Requirement

Level AA

Text can be resized without assistive technology up to 200% without loss of content or functionality.

Mobile Implementation

  • Respect OS text size preferences
  • Use Dynamic Type (iOS) and sp units (Android)
  • Maintain functionality at all text sizes
  • Avoid horizontal scrolling

WCAG 2.2 Success Criterion 1.4.10 — Reflow

Requirement

Level AA

Content can be presented without loss of information or functionality, and without requiring scrolling in two dimensions at a viewport width equivalent to 320 CSS pixels.

What this means for mobile typography

  • At 400% browser zoom (equivalent to a 320px-wide viewport), all text content must reflow into a single column — no horizontal scrolling
  • Exception: content that requires two-dimensional layout for usage (data tables, maps, certain diagrams)
  • On native mobile: design single-column layouts that handle the largest text sizes without overflow
  • Avoid fixed-width containers with overflow: hidden — text will be clipped rather than reflowing

WCAG 2.2 Success Criterion 1.4.12 — Text Spacing

Requirement

Level AA

No loss of content or functionality occurs when users set all of the following: line height to at least 1.5× the font size, paragraph spacing to at least 2× the font size, letter spacing to at least 0.12× the font size, and word spacing to at least 0.16× the font size.

What this means for mobile typography

  • Your layouts must not break when users apply custom text spacing via accessibility settings or browser extensions
  • Avoid fixed-height containers that clip text when line height is increased
  • iOS Bold Text setting increases font weight without affecting spacing — test this separately
  • On Android, the system does not expose direct letter-spacing or word-spacing overrides, but your code should not hard-code tight spacing that would conflict with OS-level adjustments

WCAG 2.2 Success Criterion 2.5.8 — Target Size (Minimum)

Requirement

Level AA — New in WCAG 2.2

The size of the target for pointer inputs is at least 24×24 CSS pixels, except where spacing is sufficient or the target is inline text.

Why this matters for typography

  • At large text sizes, button labels wrap or shrink — the visual label may remain readable but the tap target can fall below 24×24px
  • iOS HIG recommends 44×44pt minimum touch targets — significantly stricter than WCAG's minimum
  • Android Material Design 3 recommends 48×48dp minimum touch targets
  • Use minHeight / minWidth constraints rather than wrapping content tightly
  • Test tap targets at AX5 (iOS) and 2.0x font scale (Android) — this is when they are most likely to fail

Assistive Technology vs. Accessibility Features

✅ Acceptable (Accessibility Features)

  • iOS: Dynamic Type size adjustments in Settings
  • Android: Font size scaling in Display settings
  • Both: Bold text system preferences
  • WebViews: Built-in browser zoom functionality

⚠️ Not Sufficient Alone (Assistive Technology)

  • iOS: Magnifier or 3-finger zoom
  • Android: Magnification gestures
  • Both: External screen reader zoom features

Note: While these are valuable, they should supplement, not replace, proper text scaling support.

Implementation Requirements for WCAG 1.4.4 Compliance

1. Implement Native Text Scaling

Use platform-specific APIs to ensure text scales with system settings:

// iOS - UIKit
label.font = UIFont.preferredFont(forTextStyle: .body)
label.adjustsFontForContentSizeCategory = true

// iOS - SwiftUI
Text("Sample Text")
    .font(.body)
    .dynamicTypeSize(.large ... .accessibility5)
<!-- Android XML -->
<TextView
    android:textSize="16sp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

// Android Compose
Text(
    text = "Sample Text",
    style = MaterialTheme.typography.bodyLarge
)
2. Responsive Layout Design

Ensure layouts adapt to larger text without breaking:

// iOS - Auto Layout
stackView.axis = .vertical
stackView.spacing = 8
label.numberOfLines = 0

// SwiftUI
VStack(spacing: 8) {
    Text("Title")
        .lineLimit(nil)
}
<!-- Android ConstraintLayout -->
<androidx.constraintlayout.widget.ConstraintLayout>
    <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintWidth_percent="0.8" />
</androidx.constraintlayout.widget.ConstraintLayout>
3. Handle Dynamic Changes

Respond to font size changes at runtime:

// iOS - Notification handling
NotificationCenter.default.addObserver(
    self,
    selector: #selector(contentSizeCategoryDidChange),
    name: UIContentSizeCategory.didChangeNotification,
    object: nil
)
// Android - Configuration changes
override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    // Refresh UI to reflect new font scale
    recreateViews()
}

Understanding Non-Linear Text Scaling

Both iOS and Android use non-linear scaling curves, meaning different text styles scale at different rates to maintain readability.

iOS Non-Linear Scaling — Approximate measured values at AX5 (Apple does not publish official percentages)
Text Style Default (Large) AX5 (approx.) Scale Factor (approx.) Reaches 200%?
Large Title 34pt ~76pt ~2.24x (~224%) ✅ Yes
Title 1 28pt ~60pt ~2.14x (~214%) ✅ Yes
Body 17pt ~53pt ~3.12x (~312%) ✅ Yes
Caption 1 12pt ~38pt ~3.17x (~317%) ✅ Yes
Android Non-Linear Scaling — Real measured values at 2.0x setting (Android 14+)
Base Size Default (1.0x) At 2.0x setting Actual Scale Factor Reaches 200%?
8sp (small label) 8sp ~17sp ~2.1x (210%) ✅ Yes
12sp (caption) 12sp ~21sp ~1.75x (175%) ❌ No
14sp (body medium) 14sp ~23sp ~1.64x (164%) ❌ No
16sp (body large) 16sp ~25sp ~1.56x (156%) ❌ No
24sp (headline) 24sp ~34sp ~1.42x (142%) ❌ No
57sp (display large) 57sp ~64sp ~1.12x (112%) ❌ No
Key Insight — Android 14 Non-Linear Curve

Android 14's FontScaleConverter applies an intentionally non-linear curve: small text scales aggressively (8sp can exceed 200%) while large display text barely grows (57sp only reaches ~112% at the 2.0x setting). This prevents display-size text from becoming unusably large while ensuring small text remains readable. Only very small text reaches the 200% WCAG threshold — but this is still considered compliant because the platform provides the maximum available scaling, and the curve is designed to optimise legibility across the full type scale.

Testing for WCAG 1.4.4 Compliance

Complete Testing Process

Step 1: Platform Setup
📱 iOS Setup
  1. Open SettingsAccessibility
  2. Tap Display & Text Size
  3. Select Larger Text
  4. Enable Larger Accessibility Sizes
  5. Test all sizes from xSmall to AX5
🤖 Android Setup
  1. Open SettingsDisplay
  2. Tap Font size and style
  3. Test all sizes (Small to Huge)
  4. For extreme testing, use ADB:
    adb shell settings put system font_scale 2.0
Step 2: Validation Criteria
✅ Pass Requirements
  • All text scales using OS accessibility features
  • Content remains accessible at all sizes
  • No functionality lost when text enlarged
  • No horizontal scrolling required
  • Interactive elements remain usable
Step 3: Common Issues Checklist
🔍 Layout Issues
  • Fixed containers: Text overflow in fixed-height containers
  • Overlapping elements: Scaled text overlaps other UI elements
  • Cut-off text: Text clipped in constrained spaces
✂️ Truncation Issues

Most large text failures come from truncation that was never designed for scale. Watch for:

  • line-clamp / lineLimit: Hard caps that silently hide content at large sizes — use nil / 0 for unlimited lines unless layout genuinely requires it
  • ellipsize / truncationMode: Ellipsis hides content rather than reflowing — prefer scrollable containers or adaptive layouts
  • Fixed heights: frame(height:) in SwiftUI or layout_height="48dp" clips text — use wrap_content or no fixed height
  • Fallback rule: If truncation is unavoidable, put the most important words first — they survive the cut. Never truncate at the start or middle
🎯 Interaction Issues
  • Button labels: Action buttons become unusable if text doesn't fit
  • Navigation: Tab bars and menus lose functionality
  • Form inputs: Labels and inputs misalign or overlap
Step 4: Documentation
  • 📋 Record test results for each text size
  • 📸 Screenshot any layout issues
  • 📝 Document workarounds for identified problems
  • ✅ Create compliance checklist for future releases

Testing Checklist

iOS Testing
Android Testing

Best Practices

Design

  • Plan for 200%+ text scaling
  • Use minimum 16sp/17pt for body text
  • Ensure WCAG AA color contrast
  • Design flexible layouts

Development

  • Implement Dynamic Type/sp units
  • Use flexible layout systems
  • Test with max accessibility settings
  • Handle font size notifications

Testing

  • Test with real users
  • Use accessibility testing tools
  • Verify all functionality works
  • Document any limitations

W3C MATF Recommendations Implementation

The W3C Mobile Accessibility Task Force (MATF) has been working to clarify how WCAG applies to native mobile apps. WCAG 2.2 was published October 2023 and is the current recommended standard, superseding WCAG 2.1. The MATF publishes WCAG2Mobile (W3C Group Note, updated May 2025), which explains how WCAG 2.2 applies to native, web, and hybrid mobile apps. It is informative guidance only — not a normative W3C standard — but it is the most current official mobile accessibility reference from W3C:

  • Assistive Technology vs Accessibility Features: Distinguishes between zoom and magnification (assistive technologies) and system text scaling features.
  • Non-linear text scaling: Documents real scaling behavior on iOS and Android to help teams test typography under extreme settings.
  • Platform-specific implementation guidance: Explains how platform APIs such as Dynamic Type and sp units support accessible text scaling.
  • WebView considerations: Highlights that WCAG success criteria apply directly to web content rendered within native apps.
  • Testing methodologies: Provides practical testing steps for validating text scaling and layout resilience across accessibility settings.

Last reviewed against WCAG 2.2 (October 2023), iOS 18, and Android 14. WCAG2Mobile is a W3C Group Note — informative guidance only, not a normative standard.