Reachable

The audit closed six polish stories last release. This one closes the sixth wave AND the last three findings we said would ship in the next release — the accessibility pass plus H3 (TipKit systematic fix), H4 (pausable elapsed timer), and H6 (Home vs preview name reconciliation). One release, four audit findings, first-run UX audit fully closed.

VoiceOver labels that stop lying

Six spots where VoiceOver either read the wrong thing or nothing at all:

  • Sign in with Google used to read as "Button" with no name — VoiceOver users couldn't tell it apart from Sign in with Apple. Now: "Sign in with Google, button."
  • The RPE effort chips in the workout summary read as generic buttons. Now each announces itself with its label ("Easy," "Hard," "Very hard") and the selected one announces "Selected."
  • The active-workout toolbar chevrons read as "Back" and "Forward" — technically correct but useless out of context. Now: "Previous exercise" and "Next exercise." The bottom-swipe dismiss now reads "Minimize workout" instead of "Go Down."
  • The diagnostic Back arrow read as an unlabeled system chevron. Now: "Previous question."
  • The set-log commit button used to be an unlabeled circle. Now it announces "Set 3, done" or "Set 3, not logged" so you know what state you're about to change.
  • The tier selection rows in onboarding gave no way to tell which one was picked over VoiceOver — every row read as "Button." Now the picked tier announces "Selected" and the row reads as one utterance instead of three tab-stops.

Four touch targets under 44pt

Apple's Human Interface Guidelines call for 44×44pt as the minimum tappable area. Four spots were under:

  • The +/− rep steppers on the set-log row had 20pt icon glyphs with only a few pt of surrounding padding. Now 44×44 with the icon centered visually.
  • The set-commit button — 32pt visible, hit area now 44pt.
  • The hold-timer Play/Stop button — same.
  • The "See how your data is managed…" privacy link on the sign-in screen was ~15pt tall. Kept the copy at footnote size (it's a secondary transparency link) but the hit area is now 44pt tall.

None of these changes moved anything visually — the visible controls stay exactly where they were. Just the tap area got padded out to something you can actually hit with a thumb.

Larger Text, everywhere it should be

The bigger structural addition: a new .appScaledFont helper that lets any text in the app grow with the iOS Larger Text setting, even when it's using a custom point size that doesn't match one of Apple's semantic styles (body, footnote, subheadline, etc.).

Before this release, the app had ~65 spots that used a hardcoded font size (.font(.system(size: 14)) and similar) that would NOT grow when a user bumped Larger Text. Users on accessibility text sizes saw the app render the same as everyone else at those spots — hero text on cards, section titles, badge copy.

The new helper is the fix. First three sites migrated as pilot examples on the Focus picker: the "Current Focus" eyebrow, the picked-skill name, and the "No focus active" row. Every future view we touch, when it needs a custom size, uses .appScaledFont instead of the pre-v1.46 hardcode — so Dynamic Type coverage grows opportunistically without a big-bang sweep.

Legitimate exceptions kept fixed: hero numbers (whose card layout depends on their exact size), TagPill capsules (whose capsule height is fixed), icon glyphs on SF Symbols (icons scale via variant, not point size).

Home stops disagreeing with the preview about session names

For Foundations users, the same session showed three different names across three screens: Home said "Upper body," the pre-workout preview said "Foundations · Upper body," and the save screen also said "Foundations · Upper body." Home was the outlier. Now all three surfaces route through the same helper and show "Foundations · Upper body" consistently. Home's "Today's workout" chrome above stays (it's the section framing, not part of session identity).

Same fix cleaned up a parallel drift on the "N exercises" count: Home was using an inline string-compare, preview and summary were using the shared enum-based helper. Both now go through the same helper so the count math can't drift again. Warmups still don't count — accessories do.

The tips systematic-fix, finally documented

The 5 first-workout tips were migrated to inline TipView (from .popoverTip) back in the v1.44 audit pass, but the pattern was documented in per-site comments only — the file header still told future contributors to reach for .popoverTip. That's now corrected: the "Adding a new first-workout tip" instructions call out the inline convention, explain the three reasons .popoverTip was rejected (covers its own anchor, absorbs close-X into the underlying interactive control, removes underlying view from VoiceOver hierarchy), and point at the canonical anchor modifiers. Preview also updated to demonstrate the shipping pattern rather than contradicting it.

The elapsed timer stops counting your reading time

When you tap the info icon on a warmup card to read the execution instructions for a movement you don't know, the workout elapsed timer now pauses — the seconds you spend reading no longer inflate your session duration. When you dismiss the sheet, the timer resumes automatically. Two guards on the auto-behavior: it only pauses if the session is running and not already paused (respecting a manual Pause tap), and it only resumes if it was the one that paused (so a manual Pause during reading isn't silently reversed on sheet close).

Scope note: the rest banner between sets does NOT pause the timer. Rest is a designed part of the session — strength training normally reports elapsed time inclusive of rest, and the rest banner is intentionally inline (not covering) so you can scroll and plan during it. Only the true "user is reading, not training" case (execution-instructions sheet) triggers the pause.

The first-run UX audit is now fully closed — all H-priority findings from the September pass shipped over v1.42-v1.46. The next release turns back to feature work. Backlog highlights: universal-baseline injection in skill calibrations, and Equipment Mode's muscle-transfer substitution engine. Feedback welcome at support@zenmotion.app.