Repository navigation
feat(ios): sync design system and app screens to code - #23
Merged
Merged
Conversation
Bring the iOS app in line with the BetterFit design system established in Figma, and ship the reference material alongside it. Design tokens - Replace the ember-orange / multi-accent palette with the two-colour identity: electric yellow (#FFD60A) as the only chromatic accent, with black ink and neutral structure. - Add a yellow ladder (bright/deep/dim) so status and recovery ride brightness plus words instead of competing status hues. - Add glass tokens for floating chrome only, plus borderStrong and accentText (deep yellow on light fields for AA). - Typography: BBH Hegarty display ramp, Inter UI ramp, JetBrains Mono tabular numerals. Ledger layout - Add BFLedger primitives (eyebrow, section rule, spec strip, slab, readout, bar, ledger row, quick actions) used across the rebuilt screens. Screens - WorkoutHome: Plan/ledger layout with plan exercise rows, inline weight/reps editing, superset pairing, and equipment swapping. - ActiveSession: live session with rest bar, quick actions, set logging, session ordering, and supersets. - Profile, Recovery, RootTab, SignIn, Settings, plus new Log and Targets tabs and a workout summary. Competitor-informed patterns (from Fitbod, Strong, Hevy, Freeletics) - Exercise thumbnails and completion state in the work list - Rest surfaced as an action bar rather than a separate screen - Single hero on home rather than a feed Reference material - design.md: the two-colour discipline and its rationale - design-system/: tokens, core/list/data/feedback/workout components, guidelines, and iOS/watchOS UI kits Code organisation - Split oversized views into extension files per AGENTS.md: WorkoutHomeView (+Sections/+Helpers), ActiveSessionView (+Sections/+Actions), ExerciseDetailSheet (+Sections). - Rename single-char identifiers and replace large tuples with named structs. All SwiftLint serious violations introduced by this work are cleared. Verified: swift build, 95/95 unit tests, ios:build:ci. Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
The betterfit-* skills read as a separate family next to the unprefixed project skills (adr-writing, tdd, swift-ios-teaching, qa). The prefix is redundant inside the BetterFit repo — every skill here is about BetterFit — so drop it and let them sort as one set. - betterfit-code-organization -> code-organization - betterfit-mise-workflows -> mise-workflows - betterfit-supabase-setup -> supabase-setup - betterfit-swift-file-splitting -> swift-file-splitting - betterfit-swiftui-reactivity -> swiftui-reactivity - betterfit-testing -> testing - betterfit-wide-events -> wide-events - betterfit-design -> design Update the cross-references in code-organization, mise-workflows, and remotion-app-showcase that pointed at the old names. Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
Two-axis review of the design sync (Standards + Spec) surfaced real bugs and drift. This fixes them. Correctness - WorkoutSummaryView showed fabricated recovery numbers: the summary data carried hardcoded Lats/Biceps/Rear-delts defaults that nothing ever overwrote. Recovery effects are now derived from the regions actually trained and the body map's real status; when there is no data the section is hidden rather than invented. - TargetsView displayed hardcoded Volume 48.2/80k and Time 2.4/4h as the user's own stats. Both are now computed from workout history (load volume from weight x reps, time from durations). Targets are derived from the plan and the trailing 4-week average, and a metric with no history renders without a target instead of a fake one. - ProfileView and AppSearchView were unreachable: the Ledger redesign orphaned onProfile/onSearch, which were wired to sheets but never invoked. The top bar now carries a Search button and a Profile avatar, so both are reachable again. Design system conformance - At most one yellow fill per screen (design.md:168): the docked primary now honours AppTheme.quietPrimaryAction and renders as neutral glass when the screen leads with a yellow field. - Quick-action strip dropped glass; glass is reserved for nav, dock and swipe actions. The superset badge is neutral so it no longer competes with the primary action. - Removed the chromatic .midnight/.forest/.sunset themes, which introduced blue/green/red against "do not introduce any additional accent hue". Stored values fall back to the default theme. - Work list rows gained the missing exercise thumbnails. Documentation and tokens - design.md still specified the old multi-hue status palette (green/amber/red/blue, blue-to-red recovery ramp) while the code shipped a yellow ladder. Rewrote both the palette table and the summary prompt; synced the design-system mirror. - Aligned the 15% action tint and the 11px bold eyebrow across design.md, the CSS tokens and BFTypography; added the seven type sizes typography.css claimed but omitted. - Documented the real type stack: BBH Hegarty for display when licensed, native system for UI copy, monospaced digits for numbers. Dead code - Removed no-op controls that lied to the user (Edit, Jump to date, Share summary x2), LogView's synthetic heatmap filler and fake ledger rows, the unused SupersetStore typealias, and the module-wide Double.rounded extension. Code organisation - Split BFLedger and ActiveSessionView into focused extension files and extracted shared number formatting into BFFormat. Tests - Added UI coverage for Log, Targets, Workout Summary and Workout Preview, including a guard that the summary hides recovery data when there is none. - The design sync changed the tab set to Workout/Body/Targets/Log; the suite still asserted "Plan"/"Me"/"Your Training Plan". Retargeted every test to current UI and restored the Profile tests the missing affordance had made untestable. Verified: swift build, 95/95 unit tests, ios:build:ci, build-for-testing for the UI test target, and zero SwiftLint serious violations in any file this work touches. Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
The keyboard toolbar was attached inside the exercise-row view, so every row registered its own Done button. Four exercises meant four buttons stacked into the keyboard accessory, rendering as overlapping yellow pills over the tab bar. Hoist the toolbar to the screen level so there is exactly one: - WorkoutHomeView commits whichever plan field has focus, via a new commitFocusedPlanField() over PlanField. - ActiveSessionView's commitFocusedField() now resolves the exercise from `current` instead of taking it per row. Verified in the simulator: tapping a weight field shows a single Done, and tapping it dismisses the keyboard and commits the draft. Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
Editing a weight dragged the floating Start workout pill to mid-screen, where it sat on top of the exercise rows directly above the keyboard. The dock is an overlay on the whole TabView, so keyboard avoidance moved it with the content — and a start-workout CTA is not the right affordance while typing anyway. Track keyboard visibility from the standard notifications and drop the dock while it is up, so only the keyboard accessory Done remains. Verified in the simulator: with a weight field focused the pill is gone and the rows stay readable; dismissing the keyboard restores it. Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
Two display bugs found while walking the app in the simulator. Body tab: the headline read 100% while every muscle row read 96%. The headline came from the model's getOverallRecoveryPercentage() and the rows from RecoveryStatus.bfReadiness (recovered = 0.96), two different mappings. Aggregate the same per-region readiness the rows render so the two can no longer disagree. Log tab: "Time training 1 hours". Pluralise the unit. Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
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.
What
Syncs the iOS app to the BetterFit design system established in Figma, and ships the reference material alongside it. This is the "app screens → code" pass.
Why
The Figma work (design system + competitor research + remixed app screens) was the source of truth for the new direction. The code still carried the old ember-orange / multi-accent palette and the pre-ledger layouts. This brings the two into agreement.
Changes
Design tokens
#FFD60Aas the only chromatic accent, black ink, neutral structure — replaces the ember-orange and multi-accent palette.borderStrongandaccentText(deep yellow on light fields for AA contrast).Ledger layout
New
BFLedgerprimitives used across the rebuilt screens: eyebrow, section rule, spec strip, slab, readout, bar, ledger row, quick actions.Screens
Competitor-informed patterns
Borrowed from the Fitbod / Strong / Hevy / Freeletics research on the Competitor Research page:
Reference material
design.md— the two-colour discipline and its rationaledesign-system/— tokens, core/list/data/feedback/workout components, guidelines, and iOS/watchOS UI kitsCode organisation
Split oversized views into extension files per
AGENTS.md:WorkoutHomeView→+Sections/+HelpersActiveSessionView→+Sections/+ActionsExerciseDetailSheet→+SectionsRenamed single-char identifiers and replaced large tuples with named structs.
Verification
swift buildmise run testmise run ios:build:ciNotes for reviewers
mainin files this PR does not touch (HealthKitComponents,AppSearchView,TrendsView,StreakSummarySheetView,SupabasePersistenceService,BetterFitWatchApp, two test files). CI runsbuild/test/ios:build:ciand does not gate on lint. Happy to clear those in a follow-up.fix/ci-workflows) was a rewritten duplicate history with no merge base tomain, so this is branched fresh offorigin/main— the diff here is exactly this work and nothing else.design-system/ui_kits/ios_app/v1/*.bakare leftover backup files and are intentionally not committed.