Basefox v0 · Mockup 1 completion · 26 Aug 2026
The three elements deferred from the Section A follow-up, now built. Two of them needed features rather than chrome.
The tab bar reads as chrome, but three of its five tabs had nothing behind them. Saved and Alerts needed a data model, persistence, and read state before a tab could point anywhere, and the drawer needed two destinations that did not exist. So this is mostly feature work with a tab bar on top.
NavigationStack, so leaving a board
and coming back lands where you were. Tapping the active tab pops it to root.TabView bar. A system bar
brings its own materials and tint rules, and the app runs one palette. This one uses
the Basefox tokens directly.Built to the split you chose: the drawer navigates, More configures. The drawer holds every category with its brand count, the occasions, the exchange-events board, the Foxhole, Saved, and Alerts. Nothing in it changes a setting, and nothing in More pushes a board, so no row appears in both places.
The header avatar still opens the profile controls as a quick-edit sheet. That sheet and
the More tab render the same ProfileFormSections view, so the two
entry points cannot drift apart.
It now pushes an exchange-events board. Home shows live events only, because Home is about right now; this board also carries what has been announced but has not opened, which is the reason for it to exist rather than just scrolling Home's row.
| Feature | Decision | Why |
|---|---|---|
| Saved | Saves a brand, never a route | A route is a door that can close between sweeps. The brand board always shows whichever doors are open today, so a saved brand cannot go stale into a dead link. |
| Alerts | Read state stamps the moment of the visit | Stamping the newest change date instead would hide anything published between a sweep and the next visit. |
| Alerts lens | “What I follow” = saved brands + watched categories | Reuses the watched-categories setting that already existed rather than inventing a second follow list. |
| More | Also carries dataset freshness and a manual refresh | The freshness posture applies to Basefox too. The footer line said it; now there is somewhere to act on it. |
Every tab stays mounted so its scroll position survives a switch, which means
onAppear fires at launch for tabs nobody has looked at.
AlertsView.onAppear was stamping the feed read on the first frame, so the
badge never appeared. Read state now keys off the selected tab instead. Caught it because
the first screenshot showed no badge with four unread changes sitting in the dataset.
The Foxhole board says these carry no verification badge, then rendered “✓ verified Jul 28” on the card below it. No sweep can confirm an in-store discount, so a field-tier route now reads “reported Jul 28” with the field marker. This changes the route card everywhere, including inside category boards, which is the correct blast radius: the green check should mean one thing.
| Area | Files | Change |
|---|---|---|
| Shell | AppShell.swift new, LarchApp.swift | Five tabs, hand-drawn bar with badge, shared AppHeader |
| Navigation | Navigation.swift | Tab; per-tab paths; push(_:on:), select(_:); two new destinations |
| Drawer | NavDrawer.swift new | Slide-over with scrim, swipe-to-close, jump-to rows |
| Tabs | CategoriesView, SavedView, AlertsView, MoreView new | The four non-Home roots |
| Boards | EventsBoardView, FoxholeView new | Destinations for “See all” and the drawer |
| State | Models.swift, ProfileStore.swift | savedBrands, lastSeenAlertsAt, hand-written Codable so old profiles still load |
| Fixes | Components.swift, ProfileSheet.swift, RouteBoardView.swift | Field-report footline; sections extracted for reuse; save affordance |
| Tests | ShellTests.swift new | 13 tests: per-tab stacks, saved brands, read state, legacy profile decoding |
| Spec | basefox-spec-v03.md | New §Shell paragraph; screens 6–9; Profile updated |
Codable would have thrown on the new keys and
silently reset all three. There is a test for exactly that payload.endsOn yet, so the Ending Soon card still
fires only from exchange events. Unchanged from last time, still worth naming.High on the mechanics: per-tab stacks, the drawer/More split you picked, brand-level saving, and visit-time read stamping all have a clear right answer and are tested. Lower on two things only usage settles — whether five tabs plus a drawer is one surface too many for an app this size, and whether “What I follow” is the lens people actually want versus filtering by change kind.
The queue from last time is unchanged: the 13 asset files for tiers 1 and 2, offer expiry for the Ending Soon card, and the “10% OFF” versus “−10%” discount vernacular. Two new candidates joined it — the route board and occasion board still hand-roll their rows instead of composing cards.