feat(collection): a binder of your own, and the set picker's Binders tab (#1074)
Part of #1074 (PR 2 of 3: Steps 9 to 22 of the issue). Everything here sits behind VITE_SHOW_BINDERS, which stays off in Production until #1075's Step 23.
What it does
- A link opens one of your binders.
?binder=<scope>&custom=<id>narrows the pool to the binder's rules, heads the panel with its own numbers ("Complete binder"), and names it in the toolbar beside its cover's medal. A set picked inside it keeps the binder, read as "Amber · Set 12". - The set picker's Binders tab. My collection first, then your shelf in its order, each row with its cover, name and Complete percentage, the open one ringed in gold. Opening one clears the page's filters and search in the same write. Three tabs where promo sets exist, two where they don't.
- A binder that isn't on your shelf says so in the binder's place, with Open My collection. A failed read says "Couldn't load your binders." with Try again. A friend's binder shows none of yours: the param goes and the picker has no Binders tab.
- The toolbar's button grows for a binder's name (owner, 2026-11-03), taking the room from the search field and keeping its right edge on the panel's. Its 111px cut every name to "Amb…" once the medal took 18px. The words stop at 170px, so the longest names give the search field its room back.
- Privacy. No binder's name reaches an attribute or PostHog: each row and trigger is named by one screen-reader-only line and carries
ph-no-capture.
Off means not loaded
Every module under features/collection/binders/ is reached only through a dynamic import the bundler folds on SHOW_BINDERS, and binderCodeGuard (build-guards.ts) fails a flag-off build in which a page can load any of it. Checked with a flag-off production build: the binder chunks are emitted but nothing loads them.
Size budgets (owner's pick on the card, 2026-11-03)
- New group Binders JS (lazy), 8 kB: 5.27 kB.
- User-facing JS 460 → 462 kB: 461.44 kB. About 1.2 kB of the rise is
collectionStats, which was its own chunk on master and now joins Browse's. - Collection and reveals JS stays 67 kB: 66.82 kB.
Design check
Screenshots at 1920, 390 and 393, on the real page over the test stubs with the deployed card art: Binders Design Check (a private page on the owner's Claude account).
Tests
- Unit:
BinderTab,SetPicker(tabs, opening tab, arrows round three tabs, toolbar label and medal, no name in attributes),CollectionSetPanel(Complete binder),CollectionBinderSection(empty states name the binder),useBrowseCollection(params, friend guard, unknown and failed binder, import over the whole pool,pickerNamesBinder),BrowseToolbar(the group grows only for a binder). Each new assertion was checked to fail with its source line broken. - E2E: new
binders.spec.ts, 8 tests on every project, over a stubbedcollection_binders(e2e/helpers/binders.ts). Green on chromium and mobile-chrome, as iscollection-mode.spec.ts. - Stories for
BinderMedal,BinderTab,UnknownBinder, the picker's binder states and the panel's.check:storiesnow also scans a feature's other folders, such ascollection/binders. - Local gates: typecheck, lint,
check:design,check:stories, knip, the web, inkweave-ui and script suites, CodeScene (strict, passed).
After merge
The owner sets Preview VITE_SHOW_BINDERS=true, so staging and PR previews show binders. Production stays off.
Generated by Claude Code
Summary by cubic
Adds binders of your own: ?custom=<id> opens a binder, narrowing the pool to its rules and naming it in the toolbar beside its cover's medal, and the set picker gains a Binders tab. Everything sits behind VITE_SHOW_BINDERS, which stays off in Production.
New Features
- The set picker's Binders tab shows My collection, then your shelf in order, each row with its cover, name, and completion; opening one clears the page's filters and search.
- A binder not on your shelf says so with Open My collection, a failed read offers Try again (a chunk that resolves empty counts as one), and a friend's binder shows none of yours.
- No binder's name reaches an attribute or PostHog: rows and triggers are screen-reader-only and carry
ph-no-capture; Vercel's tools dropcustomtoo. - The toolbar's picker button grows for a binder's name, taking room from the search field.
- A failed shelf read retries on the next visit, and a binder's rules drop the dual-ink switch exactly where the page does.
Rollout
- Binder modules load only with the flag on; the bundler folds them away otherwise, and
binderCodeGuardfails a flag-off build that could load any. - After merge, set
VITE_SHOW_BINDERS=truein Preview; Production stays off until the follow-up.