Files
SluijsensandClaude Haiku 4.5 6760257e49 docs(unit-3): Complete functional design based on user answers
All Q1-Q5 answered A:
- Topbar removed; LanguageSwitcher + UserMenu move to sidebar footer
- Mobile: slide-over overlay with hamburger button
- Theme toggle in sidebar footer
- No-flash init via inline script in index.html
- Role-filtered sidebar items client-side

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-22 11:51:24 +02:00

2.2 KiB
Raw Permalink Blame History

Business Logic Model — Unit 3: Layout & Navigation

Component Hierarchy

AppLayout
├── Sidebar                         desktop: always visible (md+)
│   ├── SidebarHeader (logo + app name)
│   ├── NavList
│   │   └── NavItem × N             filtered by user.role
│   └── SidebarFooter
│       ├── LanguageSwitcher        moved from Topbar
│       ├── ThemeToggle             new
│       └── UserMenu                moved from Topbar
├── MobileBar                       mobile only (< md)
│   └── hamburger button → opens SidebarOverlay
├── SidebarOverlay                  rendered only when mobile menu is open
│   ├── backdrop (closes on click)
│   └── Sidebar (with onClose prop)
└── <main>
    └── <Outlet />

NavItem Filtering

const visibleItems = NAV_ITEMS.filter(item =>
    !item.roles || item.roles.includes(user.role)
);

item.roles === undefined means visible to all authenticated users. Evaluated at render time.

Mobile Overlay State

isMenuOpen: boolean state lives in AppLayout. Passed as:

  • onMenuOpen to MobileBar
  • onClose to SidebarOverlay → forwarded as onClose to Sidebar

Closes automatically on route change via useEffect watching the current pathname.

Theme Initialisation (no flash)

A blocking inline <script> in index.html runs synchronously before React loads:

<script>
  (function() {
    var stored = localStorage.getItem('cms-theme');
    var theme = stored
      ? stored
      : (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
    document.documentElement.classList.add(theme);
  })();
</script>

The useTheme hook reads from localStorage on mount and keeps toggle in sync with the DOM class.

Theme Toggle Logic (useTheme)

function toggleTheme() {
    const next = theme === 'dark' ? 'light' : 'dark';
    setTheme(next);
    localStorage.setItem('cms-theme', next);
    document.documentElement.classList.remove('light', 'dark');
    document.documentElement.classList.add(next);
}

Logout Flow (unchanged)

UserMenu in sidebar footer calls useAuth().logout() then navigate({ to: '/login' }).