Skip to content

Design parity 10: the Cards grid hides the unearned remainder - #439

Merged
maximsan merged 3 commits into
mainfrom
fix/cards-grid-unearned-remainder
Aug 29, 2026
Merged

Design parity 10: the Cards grid hides the unearned remainder#439
maximsan merged 3 commits into
mainfrom
fix/cards-grid-unearned-remainder

Conversation

@maximsan

Copy link
Copy Markdown
Owner

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 30 in mono.

Where to look hardest

Two things read as bugs and are not:

  • The footer counts the teaser you can see. Three uncollected cards, one of them on screen, and the block says "3 more to collect" — total − earned, as the design has it (screens.jsx:1653). Subtracting it would name only the cards the learner cannot see.
  • The teaser keeps its authored position, so it can sit between two earned cards rather than after them. The design maps the whole set in order and drops the locked ones after the first.

Both are pure functions in cards_grid.dart with their own unit tests, because a widget test sees neither well.

Two deliberate removals

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-review on both axes; every finding fixed in e75f318. The one worth naming: three values had been rounded onto the spacing and type scales instead of registered, against the rule OffTokens.pickTilePadding already 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.

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.
@maximsan
maximsan merged commit d4e6748 into main Aug 29, 2026
6 checks passed
@maximsan
maximsan deleted the fix/cards-grid-unearned-remainder branch August 29, 2026 18:10
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.

Design parity 10: the Cards grid hides the unearned remainder

1 participant