All skills

Review SwiftUI code for iOS/watchOS Human Interface Guidelines compliance, font usage, Dynamic Type support, and accessibility. Use when user mentions UI review, HIG, accessibility audit, font checks, or wants to verify interface design against Apple standards.

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

This session only. Nothing lands on disk.

accessibility-quick-ref.md

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

Accessibility Quick Reference

Quick reference for SwiftUI accessibility implementation.

Essential Modifiers

Labels & Descriptions

// Label: What the element is
.accessibilityLabel("Add new expense")

// Value: Current state/value
.accessibilityValue("$150.00")

// Hint: What happens when activated
.accessibilityHint("Creates a new expense in this group")

Traits

// Add traits
.accessibilityAddTraits(.isButton)
.accessibilityAddTraits(.isHeader)
.accessibilityAddTraits(.isSelected)
.accessibilityAddTraits(.updatesFrequently)

// Remove traits
.accessibilityRemoveTraits(.isImage)

Grouping & Hiding

// Combine multiple elements into one
VStack {
    Text("Total")
    Text("$150")
}
.accessibilityElement(children: .combine)

// Hide decorative elements
Image("background-pattern")
    .accessibilityHidden(true)

Custom Actions

.accessibilityAction(named: "Delete") {
    deleteExpense()
}

.accessibilityAction(named: "Edit") {
    showEditSheet()
}

Common Patterns

Icon-Only Buttons

// ❌ Bad
Button {
    addExpense()
} label: {
    Image(systemName: "plus")
}

// ✅ Good
Button {
    addExpense()
} label: {
    Image(systemName: "plus")
}
.accessibilityLabel("Add expense")

Custom Controls

// ❌ Bad - VoiceOver doesn't know it's tappable
Image(systemName: "star")
    .onTapGesture { toggleFavorite() }

// ✅ Good
Image(systemName: "star")
    .onTapGesture { toggleFavorite() }
    .accessibilityAddTraits(.isButton)
    .accessibilityLabel("Favorite")
    .accessibilityValue(isFavorite ? "On" : "Off")

Lists with Actions

List {
    ForEach(expenses) { expense in
        ExpenseRow(expense: expense)
            .accessibilityAction(named: "Delete") {
                delete(expense)
            }
            .accessibilityAction(named: "Edit") {
                edit(expense)
            }
    }
}

Toggle States

Toggle("Enable notifications", isOn: $notificationsEnabled)
    .accessibilityValue(notificationsEnabled ? "Enabled" : "Disabled")

Progress & Status

ProgressView("Loading expenses", value: progress, total: 1.0)
    .accessibilityLabel("Loading")
    .accessibilityValue("\(Int(progress * 100)) percent complete")

Dynamic Type Support

Use Semantic Text Styles

// ✅ Good - Scales automatically
Text("Expense Title")
    .font(.headline)

Text("Description")
    .font(.body)

Text("Date")
    .font(.caption)

// ❌ Bad - Fixed size
Text("Expense Title")
    .font(.system(size: 18))

Custom Fonts with Scaling

// ✅ Good - Custom font that scales
Text("Title")
    .font(.custom("SF Pro Display", size: 17, relativeTo: .body))

// ❌ Bad - Fixed custom font
Text("Title")
    .font(.custom("SF Pro Display", fixedSize: 17))

Handle Large Text

// Use ViewThatFits for flexibility
ViewThatFits {
    HStack {
        Text("Long text here")
        Spacer()
        Text("$150")
    }

    VStack(alignment: .leading) {
        Text("Long text here")
        Text("$150")
    }
}

// Or use dynamic layout
@Environment(\.sizeCategory) var sizeCategory

var body: some View {
    if sizeCategory.isAccessibilityCategory {
        VStack { /* Vertical layout */ }
    } else {
        HStack { /* Horizontal layout */ }
    }
}

Size Model

  • 12 sizes total: 7 default + 5 accessibility sizes. .body runs 17pt at the default size, 28pt at the first accessibility size, 53pt at the largest — roughly 3× (WWDC24 10074).
  • The App Store Accessibility Nutrition Label claim bar is 200% text scaling; the maximum accessibility size is ~310% — see ios/accessibility-audit for the label evaluation workflow.

Large Content Viewer

Scaling is always preferred — use the viewer only for bars that legitimately can't grow: tab bars, nav bars, toolbars, custom fixed-height bars (WWDC19 261). Rationale: a tab bar takes under 10% of screen height; scaled to accessibility sizes it would eat almost a quarter (WWDC24 10074). At accessibility text sizes, long-pressing a bar item shows a large HUD of its glyph + title; lifting the finger activates it. System bars are automatic; custom bars adopt it:

// SwiftUI
FigureButton(figure: figure)
    .accessibilityShowsLargeContentViewer {
        Label(figure.title, systemImage: figure.systemImage)
    }

// UIKit — UILargeContentViewerItem properties + the interaction (WWDC19 261)
button.showsLargeContentViewer = true
button.largeContentTitle = "Favorites"
button.largeContentImage = UIImage(systemName: "star.fill")
button.scalesLargeContentImage = true      // needs Preserve Vector Data on the asset
customBar.addInteraction(UILargeContentViewerInteraction())
// ❌ Body content that could scale, "fixed" with the viewer instead
label.font = UIFont.systemFont(ofSize: 13)

// ✅ Scale what can scale; the viewer is only for bars that can't
label.font = UIFont.preferredFont(forTextStyle: .body)
label.adjustsFontForContentSizeCategory = true

Color & Contrast

Semantic Colors

// ✅ Good - Adapts to light/dark mode
Text("Title")
    .foregroundColor(.primary)

Background()
    .fill(Color(.systemBackground))

// ❌ Bad - Fixed colors
Text("Title")
    .foregroundColor(.black)

Background()
    .fill(Color.white)

Contrast Requirements

Use Xcode's Accessibility Inspector to verify contrast ratios for text and UI components.

Testing Checklist

VoiceOver Testing

  • All interactive elements have labels
  • Navigation order is logical
  • Custom actions work correctly
  • Dynamic content is announced

Dynamic Type Testing

  • Test at largest size (Accessibility XXXL)
  • No clipped text
  • Layouts adapt appropriately
  • Buttons remain tappable

Visual Testing

  • Test in Dark Mode
  • Test with Increased Contrast
  • Test with Reduce Transparency
  • Test with Reduce Motion

Keyboard Testing (iPad/Mac)

  • All actions have keyboard shortcuts
  • Focus indicator is visible
  • Tab order is logical

Testing in Simulator

Enable Accessibility Features

Settings → Accessibility → VoiceOver → On
Settings → Accessibility → Display & Text Size → Larger Text
Settings → Accessibility → Display & Text Size → Increase Contrast
Settings → Accessibility → Motion → Reduce Motion

Xcode Accessibility Inspector

Xcode → Open Developer Tool → Accessibility Inspector

Features:

  • Inspect element accessibility properties
  • Audit for common issues
  • Check color contrast
  • Test with different settings

Full audit workflow — automated performAccessibilityAudit XCUITests, Inspector triage, and Accessibility Nutrition Label evaluation — lives in ios/accessibility-audit.

Resources

Source: SKILL.md on GitHub

No alerts2mo3 checks · Risk SAFE
  • Gen Agent Trust Hub2mo

    The skill is a specialized tool for reviewing SwiftUI code against Apple's Human Interface Guidelines and accessibility standards. It operates safely by reading project files and referencing official Apple documentation. No malicious patterns, obfuscation, or high-risk behaviors were detected.

  • Socket2mo

    No alerts

  • Snyk2mo

    Risk: LOW · No issues

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
All 4 allowed tools
ReadGlobGrepWebFetch

README badge

README badge for rshankras/claude-code-apple-skills/ui-review