Design parity 10: the Cards grid hides the unearned remainder - #439
Merged
Conversation
The Cards tab laid out the whole locked set, so a new learner met a wall of blanks. It now draws every earned card plus one locked teaser, and a dashed block underneath names what is left. What is drawn and what is counted are pure functions in `cards_grid.dart`, because both carry a trap a widget test sees badly: the teaser keeps its **authored position** rather than being appended, so it can sit between two earned cards; and the footer counts it, so a visible tile above "3 more to collect" is one of the three. Subtracting it would name only the cards the learner cannot see. Three more from the same checklist entry (#365 §5 items 23–25): the grid is flat rather than sectioned by module, the progress bar goes — the grid is the progress — and the header is the bare `1 OF 4` in mono the design gives it. That header also drops a second "Collection" title. The shared header already carries the tab's name, so the screen was saying it twice. The shell-level test asserted the grid drew every collectible, which is now the opposite of the rule; it asserts the teaser and the footer instead, and says what it used to check.
Design source, and the register that was supposed to catch it: - Three values were rounded onto the spacing and type scales rather than registered — the block's `20px 18px` padding, the 2px gap between its two lines, and the count's 0.08em tracking. `OffTokens.pickTilePadding` already settles the rule: "rounding it onto the scale is a design change, not a tidy-up". All three now sit in the register with their reasons. - The block's fill is mixed in sRGB where the design says oklab. Recorded; the ratio was transcribed exactly and the space it is mixed in was not. - The tile itself still draws `???` where the design draws the card's place in the set. That is #434, filed off this work and now named in the code — it matters more here, since the grid draws exactly one locked tile. Standards: - `IconBadge` gains an optional border, and the footer's lock well uses it. The well was hand-rolled because the badge could not outline itself, which argued for the parameter rather than the copy. - `_CollectionCount` takes the two numbers it renders instead of the whole list. - `testCardWithCollection` joins the fixtures, replacing a factory that had been copied into both new test files. Tests: - The assertion that the tab is not titled twice was vacuous — it pumped a bare `CardsScreen`, which never had a header to duplicate. It moves to the shell-level test, where it can fail, and asserts the title appears once. - The two files were both named `cards_screen_test.dart`; the isolated one is now `cards_collection_test.dart`. The changelog entry said the tab bar carries the title (the shared header does) and quoted `1 OF 30` for a learner who has earned nothing.
3 tasks
This was referenced Aug 29, 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.
Closes #396 — design parity 10, from the register tracked on #365 §5.
The grid laid out the whole locked set, so a learner who had earned nothing met a screen of blanks. It now draws every earned card plus one locked teaser, with a dashed block underneath naming the rest. Three more items from the same checklist entry (§5 23–25) come with it: the grid is flat rather than sectioned by module, the progress bar goes, and the header is the bare
0 OF 30in mono.Where to look hardest
Two things read as bugs and are not:
total − earned, as the design has it (screens.jsx:1653). Subtracting it would name only the cards the learner cannot see.Both are pure functions in
cards_grid.dartwith their own unit tests, because a widget test sees neither well.Two deliberate removals
CollectionCardreturns early atscreens.jsx:2398), and the app already agreed. The ticket records why it was dropped rather than carried, so it is not re-added — and this therefore does not wait on Card detail is a pushed screen where the design has a sheet — and the keepsake line never renders #385.Filed off this work
#434 — the tile itself. A locked one draws
???where the design draws its place in the set (03 / 30), and an earned one is missing its index line, its artwork and its per-kind tint. Untouched here, and named in the code so it is not lost; it matters more now, since exactly one locked tile is on screen and it is the next card the learner will earn.Review
/code-reviewon both axes; every finding fixed ine75f318. The one worth naming: three values had been rounded onto the spacing and type scales instead of registered, against the ruleOffTokens.pickTilePaddingalready states — "rounding it onto the scale is a design change, not a tidy-up". They are in the register now with their reasons.Gates clean: format, analyze, metrics, 1672 tests, changelog bullet.