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>
This commit is contained in:
2026-06-22 11:51:24 +02:00
co-authored by Claude Haiku 4.5
parent 037332e52f
commit 6760257e49
5 changed files with 364 additions and 6 deletions
@@ -0,0 +1,74 @@
# 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
```ts
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:
```html
<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`)
```ts
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' })`.