Files
slp-modular-cms/aidlc-docs/features/cms-frontend/construction/plans/unit-3-code-generation-plan.md
T
SluijsensandClaude Haiku 4.5 76b7bfc09b feat(unit-3): Layout & Navigation — sidebar, mobile overlay, theme toggle
- Remove Topbar; move UserMenu + LanguageSwitcher to sidebar footer
- Add ThemeToggle (dark/light) with localStorage persistence
- Add useTheme hook; no-flash inline script in index.html
- Add MobileBar (hamburger + app name, mobile-only)
- Add SidebarOverlay (slide-in from left, backdrop closes it)
- Sidebar: role-filtered nav (BR-U3-01–06), onClose prop for mobile
- AppLayout: desktop sidebar-only layout, mobile bar + overlay
- i18n: theme.* and nav.openMenu/closeMenu keys (NL + EN)
- Tests: 43/43 passing (14 new — useTheme, Sidebar roles, AppLayout)

Stories: US-08, US-18, US-19

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-22 12:03:10 +02:00

6.2 KiB

Code Generation Plan — Unit 3: Layout & Navigation

Status: 🚧 In Progress

Unit Context

Unit: Unit 3 — Layout & Navigation Type: Frontend (React/TypeScript with TanStack Router) Depends on: Unit 2 (AuthContext, useAuth, RoleGuard) Stories Covered: US-08, US-18, US-19

Key Deliverables:

  • AppLayout.tsx — modified: remove Topbar, add MobileBar + SidebarOverlay
  • Sidebar.tsx — modified: role-filtered nav, sidebar footer with UserMenu + LanguageSwitcher + ThemeToggle
  • MobileBar.tsx — new: slim mobile bar with hamburger button
  • SidebarOverlay.tsx — new: slide-over wrapper for Sidebar on mobile
  • ThemeToggle.tsx — new: dark/light toggle button
  • useTheme.ts — new: theme state + localStorage sync
  • Topbar.tsx — deleted
  • index.html — modified: no-flash inline script
  • i18n — additions: theme.*, nav.openMenu
  • Unit tests for all new/modified components

Answers

Q Answer Decision
Q1 Mobile animation A Slide in from left with CSS transition
Q2 Sidebar footer A UserMenu dropdown + LanguageSwitcher + ThemeToggle as icon buttons
Q3 Tests scope A All new and modified components

Code Generation Steps

Step 1: Add no-flash theme script to index.html

  • Add inline <script> in <head> before stylesheets
  • Reads localStorage['cms-theme'], falls back to prefers-color-scheme
  • Applies light or dark class to <html> synchronously

Step 2: Create useTheme.ts hook

  • Create src/hooks/useTheme.ts
  • Returns { theme, isDark, toggleTheme }
  • On mount: reads from localStorage (already set by inline script)
  • toggleTheme: flips value, writes to localStorage, updates <html> class

Step 3: Create ThemeToggle.tsx

  • Create src/components/layout/ThemeToggle.tsx
  • Uses useTheme() — renders <Sun> (dark mode) or <Moon> (light mode)
  • data-testid="theme-toggle"
  • i18n aria-label via theme.switchToLight / theme.switchToDark

Step 4: Create MobileBar.tsx

  • Create src/components/layout/MobileBar.tsx
  • Props: onMenuOpen: () => void
  • Visible only < md (md:hidden)
  • Contains <Menu> icon button + app name
  • data-testid="app-mobile-bar", data-testid="mobile-menu-button"

Step 5: Create SidebarOverlay.tsx

  • Create src/components/layout/SidebarOverlay.tsx
  • Props: onClose: () => void
  • Full-screen fixed overlay with semi-transparent backdrop
  • Sidebar panel slides in from left (translate-x-0 transition, duration-200)
  • Backdrop click calls onClose
  • data-testid="sidebar-overlay", data-testid="sidebar-backdrop"

Step 6: Modify Sidebar.tsx

  • Add roles?: Role[] to NavItem interface
  • Add nav items: Settings (Owner only), Profile (all) — Dashboard, Users, CMS already exist
  • Filter visible items: NAV_ITEMS.filter(item => !item.roles || item.roles.includes(user.role))
  • Add onClose?: () => void prop — render close button (<X>) at top of sidebar on mobile
  • Add SidebarFooter section pinned to bottom: UserMenu + LanguageSwitcher + ThemeToggle
  • Retain data-testid="app-sidebar"

Step 7: Modify AppLayout.tsx

  • Remove Topbar import and render
  • Add isMenuOpen state (useState(false))
  • Add MobileBar (triggers setIsMenuOpen(true))
  • Add SidebarOverlay (rendered when isMenuOpen, passes onClose)
  • Close overlay on route change via useEffect watching useLocation()
  • Desktop layout: Sidebar fills left, <main> fills right (no top bar)

Step 8: Delete Topbar.tsx

  • Remove src/components/layout/Topbar.tsx

Step 9: Extend i18n translations

  • Add to nl/translation.json: theme.switchToLight, theme.switchToDark, nav.openMenu
  • Add to en/translation.json: same keys in English

Step 10: Unit tests — useTheme

  • Create src/hooks/useTheme.test.ts
  • Initial value from localStorage / OS preference
  • Toggle updates localStorage and <html> class

Step 11: Unit tests — Sidebar role filtering

  • Create src/components/layout/Sidebar.test.tsx
  • Owner: all 5 items visible
  • Administrator: Dashboard, Users, Profile visible; Settings, CMS hidden
  • User: Dashboard, Profile visible; Users, Settings, CMS hidden

Step 12: Unit tests — AppLayout + MobileBar + SidebarOverlay

  • Create src/components/layout/AppLayout.test.tsx
  • Mobile bar renders on small screens
  • Hamburger opens overlay; backdrop click closes it
  • Overlay closes on navigation

Step 13: Final verification

  • pnpm build — no errors
  • pnpm lint — clean
  • pnpm test — all tests pass (existing 29 + new)
  • Dev server: verify sidebar on desktop, mobile overlay, theme toggle, role filtering

Step 14: Commit

  • Commit with message referencing US-08, US-18, US-19

Total Steps: 14

Please answer the following questions by filling in the letter after each [Answer]: tag.

Question 1: Mobile sidebar animation

When the slide-over sidebar opens on mobile, should it animate?

A) Slide in from the left with a CSS transition (recommended — feels native, Tailwind transition-transform) B) Fade in (opacity transition only) C) No animation — appear/disappear instantly


The sidebar footer contains UserMenu, LanguageSwitcher and ThemeToggle. How should these be presented?

A) UserMenu shows avatar/name button + dropdown (as it does now in the Topbar) — LanguageSwitcher and ThemeToggle as icon buttons next to it (recommended — compact, consistent with current UserMenu) B) Flat list: name + email displayed as text, separate logout button, LanguageSwitcher and ThemeToggle as icon buttons below C) Collapsed by default — only icons visible, expands on click to show labels


Question 3: Unit tests scope

Which components should have unit tests?

A) All new and modified components: AppLayout, Sidebar (role filtering), MobileBar, SidebarOverlay, ThemeToggle, useTheme (recommended) B) Only the role-filtering logic in Sidebar and the useTheme hook — skip layout wiring tests C) No new tests for this unit — layout is covered by existing RouteGuard tests