Skip to main content

Accessibility

LucidPal implements several iOS accessibility features. This page documents what is currently supported and what is not yet addressed.


Reduce Motion

LucidPal respects the iOS Reduce Motion system setting (Settings → Accessibility → Motion → Reduce Motion).

All animated components read @Environment(\.accessibilityReduceMotion) and adapt:

ComponentWith MotionReduce Motion
Orbital session ring (session list)Pulsing stroke + rotating ringRing hidden entirely (accessibilityHidden(true))
Session card slide-in8 px offset + spring animationInstant appear, no offset
Message bubble slide-in8–12 px offset + spring animationInstant appear, no offset

Relevant files:

  • SessionListView+Subviews.swift:76 — orbital ring reads reduceMotion, hides ring and stops all animations
  • MessageBubbleView.swift:33 — bubble entrance skips offset and animation when reduceMotion is true

VoiceOver

Labelled elements

ElementLabel
Pinned prompt chips"Pinned prompt: <text>" + hint: "Double tap to fill input. Long press for options."
Error dismiss button"Dismiss error"
Model loading banner"AI model loading, please wait" (children combined)
No-model banner"Your AI isn't set up yet. Go to Settings to download a model."
AirPods auto-listen banner"AirPods connected, auto-listening active"
Template start button"Start <name> chat"
Reminder card"Reminder: <title>, scheduled for <date>"
Voice overlay cancel"Cancel recording"
Voice overlay confirm"Confirm recording"
Temperature sliderlabel: "Temperature", value: numeric with 2 decimal places
Onboarding CTA buttonDynamic label from ctaLabel variable
Onboarding progress indicator"Step N of N" with .updatesFrequently trait
Model selection row"<name>[, Recommended][, Selected]" with .isSelected trait
Onboarding animated text.accessibilityElement(children: .ignore) + static label

Hidden elements (decorative/redundant)

The following are marked accessibilityHidden(true) because they are decorative or redundant with a parent label:

  • Orbital ring in session list
  • Decorative icons in ChatView+Banners (3 instances)
  • Decorative illustrations in UnsupportedDeviceView (4 instances)
  • Decorative images in OnboardingCarouselView (5 instances)

Dynamic Type

LucidPal does not currently implement dynamicTypeSize overrides. Text uses system defaults and will scale with the user's preferred text size via SwiftUI's automatic Dynamic Type support for standard Text views.

No components explicitly opt out of Dynamic Type scaling.


Known Gaps

AreaIssue
ChatView+Subviews.swiftNo explicit VoiceOver labels on typing indicator or streaming state
HabitDashboardView.swiftHabit progress rings have no accessibility labels
ThinkingDisclosure.swiftThinking disclosure triangle has no label
MessageBubbleView+ImageViewer.swiftImage viewer has no accessibility label on the image
Navigation orderFocus order in chat is not explicitly set; relies on SwiftUI default top-to-bottom
CalendarActionPill.swiftCalendar action pill has no explicit label

Testing VoiceOver

Enable VoiceOver on device or simulator:

Settings → Accessibility → VoiceOver → On

Or via Simulator: Hardware → Toggle Software Keyboard is not related — use Xcode → Open Simulator → I/O → VoiceOver.

Quick shortcut: triple-click the side button (if configured in Settings → Accessibility → Accessibility Shortcut).