| status | done | ||
|---|---|---|---|
| depends |
|
||
| specs |
|
||
| issues | |||
| pr | 15 |
The Vite + React app's chrome: app shell (header, footer, breadcrumbs, search box, mobile sheet), routing scaffold, auth bootstrap, shadcn/ui setup, placeholder routes for /login and /account. No business screens beyond a stub home page — those land in public-screens.
Out of scope: any real data display (mocks until read-api lands), any write actions, the actual GitHub OAuth button wiring (the /login button hits a placeholder endpoint until github-oauth).
- behaviors/app-shell.md — header, primary nav, About dropdown, search box (typeahead UI; the API call gets wired in
public-screens), auth controls (Anonymous variant only at this stage), footer, breadcrumbs, loading/error banners, accessibility skip-link, mobile sheet, print stylesheet - screens/login.md — the GitHub-OAuth-button screen as a placeholder (button currently hits the stub endpoint that returns 501)
- From inside
apps/web/, run the shadcn init:npx shadcn@latest init -y(per thefrontend-shadcnskill). Commit the generated changes first per CLAUDE.md. - Add the components we'll use across the app:
button,card,dropdown-menu,sheet,dialog,input,tooltip,separator,tabs.npx shadcn@latest add .... Commit. - Install
react-router(notreact-router-dom). Wire<BrowserRouter>inmain.tsx. - Tailwind v4 should be configured by shadcn init.
apps/web/src/components/AppShell.tsx is the wrapper around every route. Renders header, optional breadcrumbs row, <main> slot, footer. Sticky header on scroll. Mobile sheet at < md.
apps/web/src/components/AppHeader.tsx:
- Logo + "Code for Philly"
- Primary nav: Projects, Help Wanted, Members, Volunteer (button), About (dropdown), Search
- Auth controls: "Sign in" button (anonymous) — replaced by avatar dropdown when signed in (post-
github-oauth) - Mobile: hamburger →
<Sheet>with stacked nav
apps/web/src/components/AppFooter.tsx:
- Three columns (Explore, About, Connect) per the spec
- Social icons: Instagram, LinkedIn, Facebook, Meetup, Mastodon, Bluesky (per deferred.md, no Twitter/X)
- Copyright + open-source-on-GitHub link
apps/web/src/components/Breadcrumbs.tsx:
- Each route declares its trail via a
breadcrumbsexport - The trail builder reads route metadata; screens without
breadcrumbsrender nothing
apps/web/src/App.tsx:
<Routes>
<Route element={<AppShell />}>
<Route path="/" element={<HomeStub />} />
<Route path="/projects" element={<ComingSoon />} />
<Route path="/projects/:slug" element={<ComingSoon />} />
<Route path="/help-wanted" element={<ComingSoon />} />
<Route path="/members" element={<ComingSoon />} />
<Route path="/members/:slug" element={<ComingSoon />} />
<Route path="/volunteer" element={<ComingSoon />} />
<Route path="/sponsor" element={<ComingSoon />} />
<Route path="/account" element={<ComingSoonRequiresAuth />} />
<Route path="/login" element={<LoginPlaceholder />} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>ComingSoon is a placeholder that says "Coming soon — see [related plan link]" plus a back link.
HomeStub renders a small hero with "Code for Philly is being rebuilt" — replaced fully in public-screens.
apps/web/src/hooks/useAuth.tsx calls GET /api/auth/me on mount + provides {person, accountLevel, signOut, reload} via context. Header consumes it for the auth controls.
The header search input is wired with a debounced state hook, but the actual GET /api/search (or per-entity calls) it'll trigger is left as a TODO that returns mock results. The dropdown UX, keyboard navigation, and "See all results for X" link layout all work; the data is wired in public-screens.
apps/web/vite.config.ts proxies /api/* to http://localhost:3001. The API is started concurrently via the root npm run dev.
A <TopProgressBar> component animates during navigation. A <NetworkErrorBanner> shows when an API call returns 5xx. An <OfflineBanner> shows when navigator.onLine === false.
- Skip link as the first focusable element
<main>hasid="main-content"matching the skip link- All dropdowns / sheets keyboard-navigable
- Mobile sheet traps focus while open
-
npm run devboots both apps; the web app athttp://localhost:5173/renders the shell with placeholders - Header nav links all route correctly (to placeholders)
- About dropdown opens, keyboard-navigable
- Mobile (< md) shows hamburger → sheet with stacked nav — validated via jsdom test (open/close via Escape); AXI browser automation runs at full viewport width only; not validated in headed Chrome at < md.
-
useAuthcalls/api/auth/meon mount; with no session, anonymous controls render - Skip link works (Tab from page load → "Skip to main content" → Enter focuses
<main>) —<main tabIndex={-1} id="main-content">verified programmatically focusable viadocument.getElementById('main-content').focus(). -
<NetworkErrorBanner>appears when the api is down (kill it and reload/) — NetworkErrorBanner context is implemented and tested; the "kill API and reload" manual step was not run (API was never started; proxy errors hit immediately anduseAuthhandled them as anonymous, which is the correct graceful behavior). - Search box accepts input but submits to a stub that's TODO'd
- Footer renders three columns + social icons + open-source link
- No Twitter/X icon in the footer (deferred.md compliance)
-
npm testpasses; jsdom-based tests cover the auth bootstrap + the mobile sheet open/close
- shadcn init disturbs files. Commit the generated changes first, then add manual edits. Use the
frontend-shadcnskill's recipe verbatim. - React Router v7 vs v6 imports. The skill says use
react-routernotreact-router-domfor v7. Stay disciplined. - Tailwind v4 + shadcn compatibility. Should be fine but watch for class-name drift; pin the shadcn version that matches Tailwind v4.
- Data router required.
useNavigation(forTopProgressBar) requires a data router —createBrowserRouter/RouterProvider, not<BrowserRouter>. Switched in implementation; the plan's Approach section still references<BrowserRouter>(that was the original sketch; the plan is now frozen).public-screensshould continue usingcreateBrowserRouter. - shadcn init is not fully headless. The
-yflag skips the confirmation prompt but not the component-library and preset prompts. Required--template vite --no-monorepo -p novaflags. Also required Tailwind v4 to be pre-installed before shadcn init could succeed; it doesn't install Tailwind for you. baseUrldeprecated in TS 6+. Added"ignoreDeprecations": "6.0"to tsconfig.json alongside"baseUrl"and"paths"to silence TS7's deprecation error. Consider migrating toimportsin package.json if TS 7 makesbaseUrlnon-functional.- Bundle size. Initial JS is 137 KB gzip (target 250 KB). Slightly above the spirit of the limit but well within the stated gate (250 KB). Lazy-loading in
public-screensandgithub-oauthwill bring per-route chunks down substantially. - API test pre-existing failures.
apps/apihas 2 timeout failures increateTestRepo-based tests; these are pre-existing (verified by stashing all changes and re-running). Not introduced by this plan. - vitest in worktree. The agent worktree has its own
node_modules. After shadcn added packages,npm installwas needed at the worktree root to populate vitest. CI should be unaffected (it installs fresh).
- Issue #16 — Validate mobile sheet < md in browser (hamburger → sheet stacked nav). Blocked by AXI viewport width limitation; add to
public-screensmanual QA checklist. - Deferred to
public-screens— Wire real API calls in SearchBox (replaces mockmockSearch); the TODO comment is already inuseSearch.ts. - Deferred to
public-screens— AddNetworkErrorBanner.showError()call sites on 5xx API responses (the context and component are wired; just needs call sites in data-fetching hooks). - Deferred to
github-oauth— Authenticated user avatar dropdown inAppHeader.AuthControls(renders correctly given aperson; no real sessions yet).