All skills

Comprehensive iOS development guidance including Swift best practices, SwiftUI patterns, UI/UX review against HIG, and app planning. Use for iOS code review, best practices, accessibility audits, or planning new iOS apps.

  • 38 files
  • 438.7 KB
  • Updated 3 months ago
  • GitHub

Use this Skill: https://skilld.dev/gh/rshankras/claude-code-apple-skills/ios

This session only. Nothing lands on disk.

ui-reviewfont-guidelines.md

≈2.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Font Usage Guidelines

Comprehensive guide for font usage, Dynamic Type, and typography best practices.

System Text Styles

iOS Text Style Hierarchy

Use the named system text styles (.largeTitle down through .caption2) rather than fixed point sizes — they carry the right weight/size for their role and scale automatically with Dynamic Type.

Usage Examples

// ✅ Page title
Text("Expenses")
    .font(.largeTitle)

// ✅ Section header
Text("Recent")
    .font(.headline)

// ✅ Body content
Text("This is the main content of the expense description...")
    .font(.body)

// ✅ Metadata
Text("Updated 2 hours ago")
    .font(.caption)
    .foregroundColor(.secondary)

Dynamic Type Support

Why Dynamic Type Matters

  • Accessibility: Users with vision impairments need larger text
  • User Preference: Some prefer smaller/larger text
  • App Store Requirement: Required for accessibility compliance
  • Better UX: Respects user system settings

Automatic Dynamic Type

// ✅ Automatically scales with Dynamic Type
Text("Title")
    .font(.headline)

Text("Body text here")
    .font(.body)

// All system text styles scale automatically

Custom Fonts with Dynamic Type

// ❌ Bad - Fixed size
Text("Custom Title")
    .font(.custom("SF Pro Display", size: 20))

// ✅ Good - Scales with Dynamic Type
Text("Custom Title")
    .font(.custom("SF Pro Display", size: 20, relativeTo: .headline))

// ✅ Alternative approach
Text("Custom Title")
    .font(.custom("SF Pro Display", fixedSize: 20))
    .dynamicTypeSize(.large...(.xxxLarge)) // Limit scaling range if needed

Testing Dynamic Type

// Test different sizes in preview
struct ExpenseView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            ExpenseView()
                .previewDisplayName("Default")

            ExpenseView()
                .environment(\.sizeCategory, .extraSmall)
                .previewDisplayName("Extra Small")

            ExpenseView()
                .environment(\.sizeCategory, .accessibilityExtraExtraExtraLarge)
                .previewDisplayName("XXXL")
        }
    }
}

Responsive Layouts for Large Text

// ❌ Bad - Layout breaks with large text
HStack {
    Text("Very long expense name here")
    Spacer()
    Text("$150.00")
}

// ✅ Good - Adaptive layout
ViewThatFits {
    HStack {
        Text(expense.name)
        Spacer()
        Text(expense.formattedAmount)
    }

    VStack(alignment: .leading) {
        Text(expense.name)
        Text(expense.formattedAmount)
    }
}

// ✅ Alternative - Manual check
@Environment(\.sizeCategory) var sizeCategory

var body: some View {
    if sizeCategory.isAccessibilityCategory {
        VStack(alignment: .leading, spacing: 4) {
            Text(expense.name)
            Text(expense.formattedAmount)
        }
    } else {
        HStack {
            Text(expense.name)
            Spacer()
            Text(expense.formattedAmount)
        }
    }
}

Dynamic Type Size Categories

extension DynamicTypeSize {
    var isAccessibilitySize: Bool {
        self >= .accessibility1
    }
}

// Size categories:
// .xSmall
// .small
// .medium (default)
// .large
// .xLarge
// .xxLarge
// .xxxLarge
// .accessibility1
// .accessibility2
// .accessibility3
// .accessibility4
// .accessibility5

Limiting Dynamic Type Range

// ✅ Limit scaling range when necessary
Text("Icon Label")
    .font(.caption)
    .dynamicTypeSize(.small ... .large)

// ✅ Good for fixed-size UI elements
Button {
    // action
} label: {
    Image(systemName: "plus")
    Text("Add")
        .dynamicTypeSize(.small ... .large) // Keep button compact
}

Font Weights & Styles

Font Weights

// Available weights
.font(.body.weight(.ultraLight))
.font(.body.weight(.thin))
.font(.body.weight(.light))
.font(.body.weight(.regular))
.font(.body.weight(.medium))
.font(.body.weight(.semibold))
.font(.body.weight(.bold))
.font(.body.weight(.heavy))
.font(.body.weight(.black))

// Shorthand
.font(.headline.bold())

Font Styles

// Italic
.font(.body.italic())

// Monospaced
.font(.body.monospaced())

// Monospaced digit (for aligned numbers)
.font(.body.monospacedDigit())

// Small caps
.font(.body.smallCaps())

// Leading (line spacing)
.font(.body.leading(.tight))
.font(.body.leading(.standard))
.font(.body.leading(.loose))

When to Use Font Weights

// ✅ Use semibold for emphasis
Text("Important Message")
    .font(.body.weight(.semibold))

// ✅ Use bold for strong emphasis
Text("Warning")
    .font(.body.bold())

// ❌ Avoid overuse of heavy weights
Text("Regular text")
    .font(.body.weight(.black)) // Too heavy for body text

Text Formatting

Line Limit

// ✅ Limit lines for truncation
Text("Long text here...")
    .lineLimit(2)

// ✅ Single line
Text("Title")
    .lineLimit(1)

// ✅ Unlimited lines (default)
Text("Description")
    .lineLimit(nil)

Truncation Mode

// Truncate at tail (default)
Text("Very long text...")
    .truncationMode(.tail)

// Truncate at head
Text("...end of text")
    .truncationMode(.head)

// Truncate in middle
Text("Beginning...end")
    .truncationMode(.middle)

Text Alignment

// Left aligned (default)
Text("Left")
    .multilineTextAlignment(.leading)

// Center aligned
Text("Center")
    .multilineTextAlignment(.center)

// Right aligned
Text("Right")
    .multilineTextAlignment(.trailing)

Text Case

// Uppercase
Text("title")
    .textCase(.uppercase) // "TITLE"

// Lowercase
Text("TITLE")
    .textCase(.lowercase) // "title"

// None (preserve original)
Text("Title")
    .textCase(nil)

Common Patterns

Expense Row Typography

struct ExpenseRow: View {
    let expense: Expense

    var body: some View {
        HStack(alignment: .firstTextBaseline) {
            VStack(alignment: .leading, spacing: 4) {
                Text(expense.name)
                    .font(.headline)
                    .lineLimit(1)

                Text(expense.category)
                    .font(.subheadline)
                    .foregroundColor(.secondary)
            }

            Spacer()

            VStack(alignment: .trailing, spacing: 4) {
                Text(expense.formattedAmount)
                    .font(.body.monospacedDigit())
                    .fontWeight(.semibold)

                Text(expense.date, style: .date)
                    .font(.caption)
                    .foregroundColor(.secondary)
            }
        }
        .padding(.vertical, 4)
    }
}

Section Header

struct SectionHeaderView: View {
    let title: String

    var body: some View {
        Text(title)
            .font(.headline)
            .foregroundColor(.primary)
            .frame(maxWidth: .infinity, alignment: .leading)
            .padding(.vertical, 8)
    }
}

Empty State Typography

struct EmptyStateView: View {
    var body: some View {
        VStack(spacing: 16) {
            Image(systemName: "tray.fill")
                .font(.system(size: 60))
                .foregroundColor(.secondary)

            Text("No Expenses")
                .font(.title2)
                .fontWeight(.semibold)

            Text("Add your first expense to get started")
                .font(.body)
                .foregroundColor(.secondary)
                .multilineTextAlignment(.center)
        }
        .padding()
    }
}

Form Label

struct FormRow: View {
    let label: String
    let value: String

    var body: some View {
        HStack {
            Text(label)
                .font(.body)
                .foregroundColor(.primary)

            Spacer()

            Text(value)
                .font(.body)
                .foregroundColor(.secondary)
        }
    }
}

Anti-Patterns

Fixed Font Sizes

// ❌ Bad - Doesn't scale
Text("Title")
    .font(.system(size: 18))

// ✅ Good - Scales with Dynamic Type
Text("Title")
    .font(.headline)

Mixing Styles Inconsistently

// ❌ Bad - Inconsistent hierarchy
VStack {
    Text("Header 1").font(.title)
    Text("Header 2").font(.headline)
    Text("Header 3").font(.title2) // Wrong order
}

// ✅ Good - Consistent hierarchy
VStack {
    Text("Header 1").font(.title)
    Text("Header 2").font(.title2)
    Text("Header 3").font(.headline)
}

Too Many Font Weights

// ❌ Bad - Visual noise
VStack {
    Text("Title").bold()
    Text("Subtitle").fontWeight(.semibold)
    Text("Description").fontWeight(.medium)
    Text("Footer").bold()
}

// ✅ Good - Clear hierarchy
VStack {
    Text("Title").bold()
    Text("Subtitle").font(.subheadline)
    Text("Description").font(.body)
    Text("Footer").font(.caption).foregroundColor(.secondary)
}

Ignoring Line Limits

// ❌ Bad - Can overflow
HStack {
    Text("Very long expense name that might wrap")
    Text("$150")
}

// ✅ Good - Controlled truncation
HStack {
    Text("Very long expense name that might wrap")
        .lineLimit(1)
    Spacer()
    Text("$150")
}

Accessibility Considerations

Text Scaling Checklist

  • All text uses system text styles or custom fonts with relativeTo:
  • Layouts adapt to larger text sizes
  • No clipped text at XXXL sizes
  • Critical information visible at all sizes
  • Buttons remain tappable at all sizes

Color & Contrast

  • Text has sufficient contrast with background
  • Secondary text still readable
  • Don't rely on color alone for meaning

Testing Tools

Settings → Accessibility → Display & Text Size:

  • Larger Text
  • Bold Text
  • Increase Contrast

Xcode Accessibility Inspector:

  • Text size preview
  • Contrast analyzer
  • Layout viewer

Font Naming Conventions

Variable Names

// ✅ Clear naming
let titleFont: Font = .headline
let bodyFont: Font = .body
let captionFont: Font = .caption

// ❌ Unclear naming
let font1: Font = .headline
let bigFont: Font = .title

Custom Text Styles

// ✅ Reusable text styles
struct TextStyles {
    static let pageTitle: Font = .largeTitle.bold()
    static let sectionHeader: Font = .headline
    static let emphasized: Font = .body.weight(.semibold)
    static let metadata: Font = .caption
}

// Usage
Text("Page Title")
    .font(TextStyles.pageTitle)

References

Source: SKILL.md on GitHub

No third-party reports yet.

Signed by skilld at 32566aa. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 3 months ago
What it can do
Reads files Network
last_verified
2026-07-16
review_by
2027-06-22
os_version
iOS 27 / macOS 27
All 4 allowed tools
ReadGlobGrepWebFetch

README badge

README badge for rshankras/claude-code-apple-skills/ios