fix(glass): iOS 26 Liquid Glass correctness + activity-screen UX + accessibility#89
Merged
Merged
Conversation
Two iOS-26 Liquid Glass rules the app already follows elsewhere were violated at six sites; fix them with the in-repo idioms (background-layer glass for controls; solid/gradient tile for nested glass). iOS 26 only — the pre-26 Material fallback was unaffected. Controls-inside-glass vanish (move glass to a .background layer): - RecordViews.fieldRow: Picker + two DatePickers on the Edit-workout sheet. - LogPastActivityView.PastActivityTimeCard: a compact DatePicker. Nested glass-on-glass rendered flat (replace inner glass with a tile): - CoachActionCardView: Confirm/Cancel CTAs inside the action card's glass (Confirm keeps a solid accent/danger fill for the destructive affordance). - CoachResponseView: follow-up chip inside the assistant bubble. - CoachChartView: chart card inside the assistant bubble. - RecordSummaryViews: effort pills + note field inside the effort card (selected effort keeps its tint as a solid fill; the field gets a visible input background).
On iOS 26 each pulseGlass surface samples the background independently, so adjacent glass siblings render without blending and cost more. Wrap the busiest clusters in pulseGlassContainer (mirrors DeviceHeroCard/Settings): Coach header + composer + prompt-chip rows, ActivityView Record/calendar pair + content stack, the two WorkoutMap overlays, and MetricDetailView's five stacked cards. Add a PulseRadius token (card 20 / compact 16 / control 12) and unify MetricDetailView's peer cards, which mixed 24 and 20 on one screen, onto PulseRadius.card. No repo-wide sweep.
…ainers - The selected activity pill/card tinted its glass with accent AND drew an accent glyph on an accentSoft circle, so on selection the icon vanished into the purple (Log Past Activity showed a selected pill with no icon). Use a white glyph on a white-translucent circle when selected. - Record "Choose activity" cards were a solid PulseColors.card fill; make them glass (accent-tinted when selected, accent hairline for definition) to match the pills. - Wrap both activity grids in pulseGlassContainer so the tiles blend and render efficiently on iOS 26.
The selected Record card is accent-tinted, but its helper subtext stayed PulseColors.textMuted (dark grey) — unreadable on the purple. Lighten the title and helper to white / white 0.75 when selected.
Converting the activity cards to glass dropped the full-card tap target — a glass background doesn't hit-test the empty (undrawn) card area like the old solid fill did, so only the opaque icon/text registered taps. Add an explicit .contentShape over the card and the Log-Past-Activity pill.
…pec) Definitive pass on the Record and Log Past Activity pickers, from a UX-research + UI-design review, so we stop patching contrast/tap issues: - Selected surface is now a dim accentSoft-tinted glass, not a saturated accent billboard — that over-prominent fill was the root cause of the vanishing-icon and unreadable-subtext patches. Keeps the white glyph/ subtext; adds a 1.5pt accent hairline and a corner checkmark so selection is signaled by shape, not colour alone (WCAG 1.4.1). - One grammar across the Record card, the Log-Past pill, and the quick- duration chips (chips drop their solid-accent fill for the same soft-tint + accent-border treatment). - VoiceOver: each card/pill/chip is a combined element with a label and an .isSelected trait; the duration stepper's ± buttons are labelled and the minus disables at the 5-min floor. - Selection haptics (.sensoryFeedback), PulseRadius tokens (card 20 / compact 16), and a single-column reflow for the card grid at accessibility Dynamic Type sizes.
…ding - "Started" label wrapped to a second line when the date/time chips were wide — pin it with .fixedSize. - The duration minus button only registered on the thin glyph (no contentShape), so taps on the rest of the 44pt circle did nothing — add .contentShape(Rectangle()). (Plus "worked" only because its glyph is bigger.) - Add a .success haptic when a workout starts (Record) or a past activity is logged, so the action is confirmed. - Drop the glass container around ActivityView's whole content stack — it made adjacent workout cards (Dance/Walk) blend/bleed into each other. Small button clusters keep their own containers.
…ash) ActivityView computed buildTodaySummary + recoverStaleSession inline in body, so every @query invalidation (e.g. logging the first activity) re-ran the full summary and re-created the glass tiles — on iOS 26 they flashed white before the material settled. Move them into cached @State refreshed via .task(id: dataChange.token) + onChange(sessions), so a data change updates the tiles in place. Also removes the per-body context fetches the audit flagged.
saksham2001
approved these changes
Jul 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
An iOS-26 Liquid Glass pass across the app (correctness + a UX/accessibility overhaul of the two activity pickers), from a UI/UX/a11y review. All changes are iOS-26-only in effect — the pre-26 Material fallback and Reduce-Transparency solid fallback are unaffected.
Glass correctness
Picker/DatePickeron the Edit-workout (RecordViews) and Log-past-activity screens were wrapped directly inpulseGlass, so they flickered/vanished when their popover opened. Moved glass to a background layer (the app's documented pattern).pulseGlassinside an outer glass surface. Swapped the inner ones for solid/gradient tiles.GlassEffectContainer: grouped the Coach composer/header, and workout-map overlays so sibling glass blends and renders efficiently.Activity pickers — one selection grammar
Both the Record cards and the Log-Past pills (and the quick-duration chips) now share one system: selected = dim accentSoft-tinted glass + a 1.5 pt accent hairline + a corner checkmark, not a saturated accent fill. Selection is signalled by shape, not colour alone (WCAG 1.4.1). Fixes a chain of contrast bugs (vanishing selected icon, unreadable subtext) at the root.
contentShape); the duration ± buttons too..successhaptic when a workout starts / a past activity logs..isSelected; the stepper's ± are labelled and the minus disables at the 5-min floor.PulseRadiustokens (card 20 / compact 16 / control 12) replace off-scale hardcoded radii; the "Started" row no longer wraps.Perf
ActivityViewnow computes its Today summary in cached@State(refreshed on the data-change token) instead of on every body eval — removes a per-renderbuildTodaySummaryand the glass tiles no longer re-materialise (white-flash) when the workout list changes.Builds green. Note:
MetricDetailViewis also lightly touched here (a glass container + radius) and in #88 (a reactivity fix) — different regions, should merge cleanly.