refactor: native-first UI sweep (Material 3 Expressive + SwiftUI) - #8
Merged
Conversation
Replace bespoke custom chrome with native platform components per the CLAUDE.md "Native-first UI" rule, applying Echo brand tokens to native components instead of rebuilding chrome from primitives. Android (Compose / Material 3 Expressive): - EchoPrimaryButton -> Button, EchoSecondaryButton -> FilledTonalButton, TagChip -> InputChip (same public APIs / brand tokens) - Screen headers -> TopAppBar + Icon/FilledIconButton (EditCard, DeckDetail, Settings, Profile, Paste, DeckEditor) - BasicTextField -> OutlinedTextField; custom pills/badges -> AssistChip - Custom progress bars -> LinearProgressIndicator; divider -> HorizontalDivider; Profile error banner -> Snackbar; inline CTAs -> native Button/OutlinedButton - Kept brand-custom: study card flip, DeckTile/AuthorRow/CardPreviewRow/StatsBar, hero/cover/avatar layouts iOS (SwiftUI): - New EchoButtonStyles.swift (reusable ButtonStyles mirroring the Android Echo buttons) replacing repeated Capsule().fill() chrome across the views - Custom progress bar -> ProgressView; kept brand-custom chips/hero cards Cross-cutting: - Extract shadow color literals to tokens (shadowAccent, shadowElevation*) on both EchoColors.kt and EchoColor.swift - CLAUDE.md: add rule to always import symbols, never fully-qualify inline Verified: ./gradlew detektAll :composeApp:assembleDebug (BUILD SUCCESSFUL), ./gradlew lintSwift (0 violations). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Aug 20, 2026
Closed
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 & why
Sweeps the remaining bespoke custom chrome across the app onto native platform components, per the CLAUDE.md "Native-first UI" rule (
design/DESIGN_GUIDELINE.md §4). Brand tokens (accent, type, radii) are applied to native components rather than rebuilding chrome fromBox/Row/Capsuleprimitives. Continues the pattern established by theEchoTabBar → ShortNavigationBarchange.Android (Compose / Material 3 Expressive)
EchoPrimaryButton→Button,EchoSecondaryButton→FilledTonalButton,TagChip→InputChip.TopAppBar+IconButton/FilledIconButton: EditCard, DeckDetail, Settings, Profile, Paste, DeckEditor.BasicTextField→OutlinedTextField; custom pills/badges →AssistChip.LinearProgressIndicator(Home, Study); divider →HorizontalDivider; Profile error banner →Snackbar; inline CTAs → nativeButton/OutlinedButton; SRS grade buttons rebuilt onButton; onboarding link →TextButton.DeckTile/AuthorRow/CardPreviewRow/StatsBar, hero/cover/avatar layouts.iOS (SwiftUI)
EchoButtonStyles.swift— reusableButtonStyles mirroring the Android Echo buttons, replacing repeatedCapsule().fill()chrome across 9 views.ProgressView. NativeTabView/.sheet/Divideralready in place; tag chips/hero cards kept brand-custom (no native iOS chip).Cross-cutting
shadowAccent,shadowElevation{Low,Medium,High,XHigh}) in bothEchoColors.ktandEchoColor.swift; 0 inline shadow literals remain.importsymbols rather than reference them fully-qualified inline (Kotlin + Swift).Verification
./gradlew detektAll :composeApp:assembleDebug→ BUILD SUCCESSFUL./gradlew lintSwift→ 0 violations🤖 Generated with Claude Code