Skip to content

fix(glass): iOS 26 Liquid Glass correctness + activity-screen UX + accessibility#89

Merged
saksham2001 merged 11 commits into
saksham2001:mainfrom
rgvxsthi:fix/glass-rendering
Jul 15, 2026
Merged

fix(glass): iOS 26 Liquid Glass correctness + activity-screen UX + accessibility#89
saksham2001 merged 11 commits into
saksham2001:mainfrom
rgvxsthi:fix/glass-rendering

Conversation

@rgvxsthi

Copy link
Copy Markdown
Contributor

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

  • Controls vanishing inside glass: Picker/DatePicker on the Edit-workout (RecordViews) and Log-past-activity screens were wrapped directly in pulseGlass, so they flickered/vanished when their popover opened. Moved glass to a background layer (the app's documented pattern).
  • Nested glass-on-glass rendering flat: Coach action-card CTAs, follow-up chips, chart cards, and the record-summary effort card were pulseGlass inside an outer glass surface. Swapped the inner ones for solid/gradient tiles.
  • Missing GlassEffectContainer: grouped the Coach composer/header, and workout-map overlays so sibling glass blends and renders efficiently.
  • Removed a glass container around ActivityView's whole content stack that was making adjacent workout cards bleed into each other.

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.

  • Whole card/pill is tappable (contentShape); the duration ± buttons too.
  • Selection haptics; a .success haptic when a workout starts / a past activity logs.
  • VoiceOver: cards/pills/chips are combined elements with labels + .isSelected; the stepper's ± are labelled and the minus disables at the 5-min floor.
  • Dynamic Type: the Record grid drops to one column at accessibility sizes.
  • PulseRadius tokens (card 20 / compact 16 / control 12) replace off-scale hardcoded radii; the "Started" row no longer wraps.

Perf

  • ActivityView now computes its Today summary in cached @State (refreshed on the data-change token) instead of on every body eval — removes a per-render buildTodaySummary and the glass tiles no longer re-materialise (white-flash) when the workout list changes.

Builds green. Note: MetricDetailView is also lightly touched here (a glass container + radius) and in #88 (a reactivity fix) — different regions, should merge cleanly.

rgvxsthi added 9 commits July 13, 2026 10:20
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.
@rgvxsthi
rgvxsthi requested a review from saksham2001 as a code owner July 13, 2026 04:28
@saksham2001
saksham2001 merged commit 0a8ab4e into saksham2001:main Jul 15, 2026
2 checks passed
@rgvxsthi
rgvxsthi deleted the fix/glass-rendering branch July 16, 2026 04:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants