.claude/skills/swiftui-accessibility-expert/SKILL.md
Expert guidance on SwiftUI accessibility: labels, traits, actions, focus management, Dynamic Type, motion, color, grouping, rotors, and testing. Use when building or reviewing SwiftUI views, fixing VoiceOver issues, adding accessibility support, reviewing accessibility compliance, or improving assistive technology experience.
npx skillsauth add adamayoung/popcorn swiftui-accessibility-expertInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill provides comprehensive guidance for building SwiftUI apps accessible to everyone — including users of VoiceOver, Voice Control, Switch Control, and other assistive technologies across iOS, macOS, and visionOS.
Scope: SwiftUI accessibility modifiers, accessibility tree management, assistive technology support, Dynamic Type, motion/color/contrast preferences, accessibility testing.
Relationship to swiftui-expert-skill: The swiftui-expert-skill covers general SwiftUI best practices. This skill owns all accessibility concerns in depth — labels, traits, actions, grouping, focus management, motion, color, testing. Where topics overlap (e.g., Dynamic Type), this skill provides the authoritative accessibility perspective.
accessibilityLabel — "Delete" not "Button", "Close" not "X icon". Labels describe purpose, not appearance. Labels must not include the element type (VoiceOver adds it).Image(decorative:) or .accessibilityHidden(true) — prevent VoiceOver from reading filenames or placeholder text for purely visual elements.Button, Toggle, Picker, Slider instead of onTapGesture on Text/Image. Semantic controls provide correct traits, actions, and values automatically.accessibilityDifferentiateWithoutColor for additional non-color indicators..body, .headline) and @ScaledMetric for custom dimensions. Test at accessibility sizes.accessibilityReduceMotion — provide a static or fade alternative. Reduce means reduce, not remove — use subtle transitions instead of no animation..accessibilityElement(children: .combine) — reduce VoiceOver stops. A card with title, subtitle, and image should be one stop, not three.performAccessibilityAudit() in UI tests. Test at multiple Dynamic Type sizes, in dark mode, and with reduce motion enabled.When a developer needs accessibility guidance:
references/accessible-descriptions.md — labels, values, hints, headings, custom content, speechreferences/accessible-controls.md — traits, actions, adjustable, gestures, representationreferences/accessible-appearance.md — text styles, @ScaledMetric, contrast, reduce motionreferences/accessible-navigation.md — sort priority, rotors, focus state, notificationsreferences/accessible-grouping.md — combine, contain, ignore, conditional grouping, modalreferences/accessible-testing.md — Inspector, VoiceOver checklist, performAccessibilityAuditCommon VoiceOver/accessibility errors and the next best move:
references/accessible-descriptions.md — add accessibilityLabel describing the actionreferences/accessible-descriptions.md — use Image(decorative:) or .accessibilityHidden(true)references/accessible-grouping.md — use .accessibilityElement(children: .combine)references/accessible-appearance.md — pair with icon/text, check accessibilityDifferentiateWithoutColorreferences/accessible-controls.md — add correct traits and actionsreferences/accessible-navigation.md — use accessibilitySortPriorityreferences/accessible-descriptions.md — add accessibilityHint describing the navigation destination or resultreferences/accessible-appearance.md — check accessibilityReduceMotion, provide fade alternativereferences/accessible-navigation.md — use @AccessibilityFocusState or post AccessibilityNotificationButton(action: toggleFavorite) {
Image(systemName: isFavorite ? "heart.fill" : "heart")
}
.accessibilityLabel(isFavorite ? "Remove from favorites" : "Add to favorites")
HStack {
AsyncImage(url: movie.posterURL)
VStack(alignment: .leading) {
Text(movie.title)
Text(movie.year)
}
}
.accessibilityElement(children: .combine)
HStack {
if isEditing {
Button("Delete") { delete() }
}
Text(movie.title)
}
.accessibilityElement(children: isEditing ? .contain : .combine)
Use when each carousel item navigates to its own destination. Each item is an independent VoiceOver stop.
Carousel {
ForEach(Array(movies.enumerated()), id: \.offset) { offset, movie in
Button {
didSelectMovie(movie.id)
} label: {
MovieCard(movie: movie)
}
.accessibilityIdentifier("carousel.movie.\(offset)")
.accessibilityLabel(movie.title)
.accessibilityHint("View movie details")
.buttonStyle(.plain)
}
}
.accessibilityIdentifier("movies.carousel")
Warning: Do NOT use
.accessibilityElement()on the carousel container when items should be individually tappable — it collapses all children into one opaque element.
@Environment(\.accessibilityReduceMotion) private var reduceMotion
var body: some View {
ContentView()
.transition(reduceMotion ? .opacity : .move(edge: .trailing))
}
@ScaledMetric(relativeTo: .body) private var posterHeight: CGFloat = 120
AsyncImage(url: posterURL)
.frame(height: posterHeight)
accessibilityLabelaccessibilityHint describing the destinationImage(decorative:) or .accessibilityHidden(true)Button, Toggle, not onTapGesture on Text).isHeader or .h1–.h6.isSelected trait.isModal trait.combine to reduce VoiceOver stops.contain when editing)accessibilityChildren@ScaledMetricaccessibilityReduceMotionaccessibilityIgnoresInvertColors()performAccessibilityAudit() in UI tests| File | Description |
|------|-------------|
| references/_index.md | Navigation index with quick links by problem |
| references/accessible-descriptions.md | Labels, values, hints, headings, custom content, speech customization |
| references/accessible-controls.md | Traits (all 17), actions, adjustable, gestures, representation |
| references/accessible-appearance.md | Dynamic Type, color/contrast, motion, transparency, AT checks |
| references/accessible-navigation.md | Sort priority, rotors, linked groups, focus, notifications, charts |
| references/accessible-grouping.md | Children behavior, conditional grouping, hidden, modal, Canvas |
| references/accessible-testing.md | Inspector, VoiceOver checklist, audits, environment overrides, visionOS |
data-ai
Add properties to an existing domain model from TMDb
testing
Run all unit tests
testing
Run UI tests
testing
Run snapshot tests