Some checks failed
continuous-integration/drone/push Build is failing
Implement a new sidebar component based on DaisyUI Drawer pattern without custom CSS variants. The sidebar supports desktop (expanded/collapsed states) and mobile (overlay drawer) with full accessibility compliance. Sidebar Implementation: - Refactor sidebar component with sidebar_header, menu_item, menu_group, sidebar_footer sub-components - Add logo (mila.svg) with size-8 (32px) always visible - Implement toggle button with icon swap (chevron-left/right) for desktop - Add nested menu support with details/summary (expanded) and dropdown (collapsed) patterns - Implement footer with language selector (expanded-only), theme toggle, and user menu with avatar - Update layouts.ex to use drawer pattern with data-sidebar-expanded attribute for state management CSS & JavaScript: - Add CSS styles for sidebar state management via data-attribute selectors - Implement SidebarState JavaScript hook for localStorage persistence - Add smooth width transitions (w-64 ↔ w-16) for desktop collapsed state - Add CSS classes for expanded-only, menu-label, and icon visibility Documentation: - Add sidebar-analysis-current-state.md: Analysis of current implementation - Add sidebar-requirements-v2.md: Complete specification for new sidebar - Add daisyui-drawer-pattern.md: DaisyUI pattern documentation - Add umsetzung-sidebar.md: Step-by-step implementation guide Testing: - Add comprehensive component tests for all sidebar sub-components - Add integration tests for sidebar state management and mobile drawer - Extend accessibility tests (ARIA labels, roles, keyboard navigation) - Add regression tests for duplicate IDs, hover effects, and tooltips - Ensure full test coverage per specification requirements
1250 lines
34 KiB
Markdown
1250 lines
34 KiB
Markdown
# Sidebar Requirements Specification v2
|
||
|
||
**Erstellt:** 2025-12-16
|
||
**Version:** 2.0
|
||
**Status:** Spezifikation für Neuimplementierung
|
||
**Basierend auf:** `sidebar-analysis-current-state.md`, `daisyui-drawer-pattern.md`
|
||
|
||
---
|
||
|
||
## Executive Summary
|
||
|
||
Diese Spezifikation definiert die Anforderungen für eine **standardkonforme Sidebar-Implementierung** basierend auf **DaisyUI Drawer Pattern** ohne Custom CSS Variants. Die Sidebar unterstützt Desktop (expanded/collapsed States) und Mobile (Overlay Drawer).
|
||
|
||
**Kernprinzipien:**
|
||
- ✅ **100% DaisyUI Standard** - keine Custom Variants
|
||
- ✅ **Ein Layout** - keine Duplikate für Mobile/Desktop
|
||
- ✅ **State via data-attribute** - CSS reagiert auf `[data-sidebar-expanded]`
|
||
- ✅ **localStorage Persistence** - State bleibt über Seitenreloads erhalten
|
||
- ✅ **WCAG 2.1 Level AA** - vollständig accessible
|
||
|
||
---
|
||
|
||
## 1. Funktionale Anforderungen
|
||
|
||
### 1.1 Logo
|
||
|
||
| Eigenschaft | Wert | Begründung |
|
||
|-------------|------|------------|
|
||
| **Größe** | `size-8` (32px × 32px) | Konsistent, gut lesbar |
|
||
| **Sichtbarkeit** | Immer sichtbar | Branding, Orientierung |
|
||
| **States** | Gleich in expanded & collapsed | Keine unterschiedlichen Logo-Elemente |
|
||
| **Position** | Links im Header | Standard-Pattern |
|
||
| **Pfad** | `/images/mila.svg` | Projektspezifisch |
|
||
|
||
**Verhalten:**
|
||
- Expanded: Logo + Club-Name
|
||
- Collapsed: Logo zentriert (horizontal & vertikal)
|
||
|
||
### 1.2 Toggle-Button
|
||
|
||
| Eigenschaft | Wert | Begründung |
|
||
|-------------|------|------------|
|
||
| **Sichtbarkeit** | Nur Desktop (≥ lg) | Mobile nutzt Hamburger in Header |
|
||
| **Position** | Rechts im Sidebar-Header | Schnell erreichbar |
|
||
| **Icon (expanded)** | `hero-chevron-left` | Zeigt Collapse-Richtung |
|
||
| **Icon (collapsed)** | `hero-chevron-right` | Zeigt Expand-Richtung |
|
||
| **Größe** | `btn-sm btn-square` | Kompakt, icon-only |
|
||
| **Variante** | `btn-ghost` | Dezent, nicht dominant |
|
||
|
||
**Icon-Swap Strategie:**
|
||
- Zwei separate `<.icon>` Elemente
|
||
- CSS-Klassen: `.sidebar-expanded-icon` und `.sidebar-collapsed-icon`
|
||
- CSS zeigt/versteckt basierend auf `[data-sidebar-expanded]`
|
||
|
||
**ARIA:**
|
||
- `aria-label="Toggle sidebar"`
|
||
- `aria-expanded="true|false"` (via JavaScript)
|
||
- `aria-controls="main-sidebar"`
|
||
|
||
### 1.3 Menü-Items (Flat)
|
||
|
||
**Expanded State:**
|
||
- Icon (links) + Text-Label (rechts)
|
||
- Standard DaisyUI menu styling
|
||
- Gap: `gap-3` zwischen Icon und Text
|
||
|
||
**Collapsed State:**
|
||
- Nur Icon (zentriert)
|
||
- Tooltip erscheint rechts (`tooltip-right`)
|
||
- Tooltip-Text aus `data-tip` Attribut
|
||
|
||
**Hover-Effekt:**
|
||
- Einheitlich für expanded UND collapsed
|
||
- Standard DaisyUI: `hover:bg-base-300`
|
||
- Keine Custom-Styles
|
||
|
||
**Liste der Menü-Items:**
|
||
1. **Members** - `hero-users` - `/members`
|
||
2. **Users** - `hero-user-circle` - `/users`
|
||
3. **Custom Fields** - `hero-rectangle-group` - `/custom_fields`
|
||
4. **Settings** - `hero-cog-6-tooth` - `#` (Placeholder)
|
||
|
||
### 1.4 Nested Menu "Beiträge" (Contributions)
|
||
|
||
**Problem:** Standard Menu-Item Pattern funktioniert nicht für verschachtelte Menüs.
|
||
|
||
**Lösung:** Zwei unterschiedliche Darstellungen je nach State.
|
||
|
||
#### Expanded State:
|
||
```html
|
||
<details class="expanded-menu-group">
|
||
<summary>
|
||
Icon + "Beiträge"
|
||
</summary>
|
||
<ul>
|
||
<li>Beitragsarten</li>
|
||
<li>Einstellungen</li>
|
||
</ul>
|
||
</details>
|
||
```
|
||
|
||
**Eigenschaften:**
|
||
- Native `<details>/<summary>` für auf/zuklappbar
|
||
- Submenu eingerückt (`ml-4`)
|
||
- Chevron-Icon rotiert automatisch (Browser-Standard)
|
||
|
||
#### Collapsed State:
|
||
```html
|
||
<div class="collapsed-menu-group dropdown dropdown-right">
|
||
<button>Icon</button>
|
||
<ul class="dropdown-content">
|
||
<li class="menu-title">Beiträge</li>
|
||
<li>Beitragsarten</li>
|
||
<li>Einstellungen</li>
|
||
</ul>
|
||
</div>
|
||
```
|
||
|
||
**Eigenschaften:**
|
||
- DaisyUI `dropdown` mit `dropdown-right`
|
||
- Flyout erscheint RECHTS vom Icon
|
||
- Menu-Title zeigt "Beiträge"
|
||
- Tooltip auf Button (collapsed)
|
||
|
||
**Wichtig:**
|
||
- Nur EIN Hover-Effekt (nicht doppelt)
|
||
- CSS-Klassen `.expanded-menu-group` und `.collapsed-menu-group`
|
||
- CSS zeigt/versteckt basierend auf `[data-sidebar-expanded]`
|
||
|
||
**Submenu-Items:**
|
||
1. **Beitragsarten** - `/contribution_types`
|
||
2. **Einstellungen** - `/contribution_settings`
|
||
|
||
### 1.5 Footer
|
||
|
||
**Position:**
|
||
- IMMER am unteren Ende der Sidebar
|
||
- Flexbox: Navigation mit `flex-1`, Footer mit `mt-auto`
|
||
|
||
**Komponenten:**
|
||
|
||
#### 1.5.1 Language Selector
|
||
- **Sichtbarkeit:** Nur expanded (`.expanded-only`)
|
||
- **Typ:** `<select>` mit Form (POST zu `/locale`)
|
||
- **Variante:** `select select-sm w-full`
|
||
- **Optionen:** Deutsch (de), English (en)
|
||
- **Funktion:** Auto-submit on change
|
||
|
||
#### 1.5.2 Theme Toggle
|
||
- **Sichtbarkeit:** Immer (expanded & collapsed)
|
||
- **Layout:** Horizontal: Sun Icon + Toggle + Moon Icon
|
||
- **Variante:** `toggle toggle-sm theme-controller`
|
||
- **Funktion:** DaisyUI automatische Theme-Umschaltung
|
||
- **Collapsed:** Zentriert horizontal
|
||
|
||
#### 1.5.3 User Menu
|
||
- **Typ:** DaisyUI `dropdown dropdown-top dropdown-end`
|
||
- **Avatar:** Rund, erste Buchstabe der Email, zentriert
|
||
- **Größe:** `w-8 h-8` (collapsed), `w-10 h-10` (expanded)
|
||
- **Email:** Nur expanded sichtbar, `truncate`
|
||
- **Dropdown-Items:**
|
||
1. Profile (`/users/:id`)
|
||
2. Logout (`/sign-out`)
|
||
|
||
**Footer-Layout:**
|
||
```
|
||
┌─────────────────────────┐
|
||
│ [Language Selector] │ ← Nur expanded
|
||
│ ☀️ [Toggle] 🌙 │ ← Immer
|
||
│ [Avatar] [Email ▼] │ ← Avatar immer, Email nur expanded
|
||
└─────────────────────────┘
|
||
```
|
||
|
||
### 1.6 State Persistence
|
||
|
||
**localStorage Key:** `sidebar-expanded`
|
||
|
||
**Werte:**
|
||
- `"true"` - Sidebar ist expanded (default)
|
||
- `"false"` - Sidebar ist collapsed
|
||
|
||
**data-attribute auf Root:**
|
||
```html
|
||
<div data-sidebar-expanded="true">
|
||
```
|
||
|
||
**Verhalten:**
|
||
1. Beim Mount: Restore aus localStorage
|
||
2. Beim Toggle: Update localStorage + data-attribute
|
||
3. CSS reagiert auf data-attribute änderung
|
||
|
||
### 1.7 Responsive Verhalten
|
||
|
||
**Mobile (< lg / < 1024px):**
|
||
- Standard DaisyUI Drawer Overlay
|
||
- Hamburger-Icon in Mobile Header (außerhalb Sidebar)
|
||
- Overlay verdunkelt Hintergrund
|
||
- Click auf Overlay schließt Drawer
|
||
- Sidebar: volle Breite `w-64` (16rem)
|
||
- Toggle-Button in Sidebar: NICHT sichtbar
|
||
|
||
**Desktop (≥ lg / ≥ 1024px):**
|
||
- Fixed Sidebar (persistent)
|
||
- `lg:drawer-open` forciert Drawer als sichtbar
|
||
- Expanded: `w-64` (16rem)
|
||
- Collapsed: `w-16` (4rem)
|
||
- Smooth width transition: `300ms ease-in-out`
|
||
- Toggle-Button in Sidebar: sichtbar
|
||
- Mobile Header: NICHT sichtbar (`lg:hidden`)
|
||
|
||
---
|
||
|
||
## 2. Wireframes (ASCII-Art)
|
||
|
||
### 2.1 Desktop - Expanded State (w-64 / 16rem)
|
||
|
||
```
|
||
┌────────────────────────────────┐
|
||
│ 🏠 Club Name [◀] │ ← Header (Logo + Name + Toggle)
|
||
├────────────────────────────────┤
|
||
│ │
|
||
│ 👥 Members │
|
||
│ 👤 Users │
|
||
│ 💰 Beiträge ▼ │ ← Details/Summary (klappbar)
|
||
│ ├─ Beitragsarten │
|
||
│ └─ Einstellungen │
|
||
│ ⚙️ Settings │
|
||
│ │
|
||
│ │
|
||
│ (flex-1 - wächst) │
|
||
│ │
|
||
│ │
|
||
├────────────────────────────────┤
|
||
│ [Deutsch ▼] │ ← Language Selector
|
||
│ ☀️ [○] 🌙 │ ← Theme Toggle
|
||
│ (A) user@example.com [▼] │ ← User Menu (Avatar + Email)
|
||
└────────────────────────────────┘
|
||
16rem (256px)
|
||
```
|
||
|
||
### 2.2 Desktop - Collapsed State (w-16 / 4rem)
|
||
|
||
```
|
||
┌──────┐
|
||
│ 🏠 │ ← Logo zentriert
|
||
│ [▶] │ ← Toggle-Button
|
||
├──────┤
|
||
│ │
|
||
│ 👥 │ ← Tooltip: "Members"
|
||
│ 👤 │ ← Tooltip: "Users"
|
||
│ 💰 │ ← Dropdown öffnet rechts →
|
||
│ ⚙️ │ ← Tooltip: "Settings"
|
||
│ │
|
||
│ │
|
||
│ │ (flex-1)
|
||
│ │
|
||
│ │
|
||
├──────┤
|
||
│ │ ← Language Selector HIDDEN
|
||
│ ☀️○🌙│ ← Theme Toggle (icons kleiner)
|
||
│ (A) │ ← Avatar zentriert (kein Email)
|
||
└──────┘
|
||
4rem
|
||
```
|
||
|
||
**Dropdown-Verhalten (collapsed):**
|
||
```
|
||
┌──────┐ ┌─────────────────────┐
|
||
│ 👥 │ │ │
|
||
│ 👤 │ │ │
|
||
│ 💰 │────────────▶│ Beiträge │
|
||
│ ⚙️ │ │ ─────────────────── │
|
||
│ │ │ • Beitragsarten │
|
||
└──────┘ │ • Einstellungen │
|
||
└─────────────────────┘
|
||
```
|
||
|
||
### 2.3 Mobile mit Overlay (< lg / < 1024px)
|
||
|
||
**Geschlossen:**
|
||
```
|
||
┌─────────────────────────────┐
|
||
│ [☰] Club Name │ ← Mobile Header (außerhalb Sidebar)
|
||
├─────────────────────────────┤
|
||
│ │
|
||
│ Main Content │
|
||
│ │
|
||
└─────────────────────────────┘
|
||
```
|
||
|
||
**Geöffnet:**
|
||
```
|
||
┌────────────────────────────────┐
|
||
│ 🏠 Club Name │ ◀──────── Sidebar (w-64)
|
||
├────────────────────────────────┤ Overlay: bg-black/50
|
||
│ │
|
||
│ 👥 Members │
|
||
│ 👤 Users │
|
||
│ 💰 Beiträge ▼ │
|
||
│ ├─ Beitragsarten │
|
||
│ └─ Einstellungen │
|
||
│ ⚙️ Settings │
|
||
│ │
|
||
│ (flex-1) │
|
||
│ │
|
||
├────────────────────────────────┤
|
||
│ [Deutsch ▼] │
|
||
│ ☀️ [○] 🌙 │
|
||
│ (A) user@example.com [▼] │
|
||
└────────────────────────────────┘
|
||
│
|
||
└─ Kein Toggle-Button (nur expanded)
|
||
```
|
||
|
||
**Hinweis:** Mobile Sidebar ist IMMER expanded (kein collapsed state).
|
||
|
||
---
|
||
|
||
## 3. Benötigte DaisyUI-Komponenten
|
||
|
||
### 3.1 Layout-Komponenten
|
||
|
||
| Komponente | Verwendung | Klassen |
|
||
|------------|------------|---------|
|
||
| **drawer** | Basis-Container | `drawer`, `lg:drawer-open` |
|
||
| **drawer-toggle** | Hidden Checkbox (Mobile) | `drawer-toggle` |
|
||
| **drawer-content** | Main Content Area | `drawer-content`, `flex flex-col` |
|
||
| **drawer-side** | Sidebar Container | `drawer-side` |
|
||
| **drawer-overlay** | Mobile Overlay (Click to close) | `drawer-overlay`, `lg:hidden` |
|
||
|
||
### 3.2 Navigation-Komponenten
|
||
|
||
| Komponente | Verwendung | Klassen |
|
||
|------------|------------|---------|
|
||
| **menu** | Navigation Liste | `menu`, `flex-1`, `w-full`, `p-2` |
|
||
| **menu-title** | Abschnitts-Überschrift | `menu-title` |
|
||
| **tooltip** | Icon-Tooltips (collapsed) | `tooltip`, `tooltip-right` |
|
||
|
||
### 3.3 Interaktive Komponenten
|
||
|
||
| Komponente | Verwendung | Klassen |
|
||
|------------|------------|---------|
|
||
| **btn** | Toggle-Button | `btn`, `btn-ghost`, `btn-sm`, `btn-square` |
|
||
| **select** | Language Selector | `select`, `select-sm`, `w-full` |
|
||
| **toggle** | Theme Switch | `toggle`, `toggle-sm`, `theme-controller` |
|
||
| **dropdown** | User Menu & Nested Menu | `dropdown`, `dropdown-top`, `dropdown-end`, `dropdown-right` |
|
||
| **avatar** | User Avatar | `avatar`, `avatar-placeholder` |
|
||
| **navbar** | Mobile Header | `navbar`, `bg-base-100`, `shadow-sm`, `lg:hidden` |
|
||
|
||
### 3.4 Utility-Komponenten
|
||
|
||
| Komponente | Verwendung | Klassen |
|
||
|------------|------------|---------|
|
||
| **Flexbox** | Layout-Struktur | `flex`, `flex-col`, `flex-1`, `items-center`, `justify-center` |
|
||
| **Spacing** | Gaps & Padding | `gap-2`, `gap-3`, `gap-4`, `p-2`, `p-4`, `mt-auto` |
|
||
| **Sizing** | Widths & Heights | `w-64`, `w-16`, `w-8`, `h-8`, `size-5`, `size-8`, `min-h-screen` |
|
||
| **Colors** | Backgrounds | `bg-base-100`, `bg-base-200`, `bg-base-300`, `bg-neutral` |
|
||
| **Typography** | Text Styles | `text-lg`, `text-sm`, `font-bold`, `truncate` |
|
||
| **Borders** | Dividers | `border-t`, `border-b`, `border-base-300` |
|
||
| **Z-Index** | Layering | `z-10`, `z-20`, `z-50` |
|
||
|
||
### 3.5 Responsive Modifiers
|
||
|
||
| Modifier | Breakpoint | Verwendung |
|
||
|----------|------------|------------|
|
||
| **lg:** | ≥ 1024px | Desktop-spezifische Styles |
|
||
| **lg:hidden** | < 1024px | Mobile Header, Overlay |
|
||
| **lg:flex** | ≥ 1024px | Toggle-Button in Sidebar |
|
||
| **lg:drawer-open** | ≥ 1024px | Persistent Desktop Sidebar |
|
||
|
||
---
|
||
|
||
## 4. CSS-Strategie
|
||
|
||
### 4.1 Prinzipien
|
||
|
||
✅ **Erlaubt:**
|
||
- Tailwind `@apply` Direktiven
|
||
- Standard DaisyUI Komponenten-Klassen
|
||
- CSS attribute selectors (`[data-*]`)
|
||
- Tailwind responsive modifiers (`lg:`, `md:`)
|
||
- Standard pseudo-classes (`:hover`, `:focus`)
|
||
|
||
❌ **NICHT erlaubt:**
|
||
- Custom CSS Variants (`@custom-variant`)
|
||
- Custom Tailwind Plugin Variants
|
||
- Complex CSS Selectors (mehr als 3 Ebenen tief)
|
||
- `!important` (außer in extremen Edge-Cases)
|
||
|
||
### 4.2 State Management via Data-Attribute
|
||
|
||
**Selector Pattern:**
|
||
```css
|
||
[data-sidebar-expanded="false"] .sidebar .element {
|
||
/* Collapsed State Styles */
|
||
}
|
||
```
|
||
|
||
**Beispiel-Implementierung in `app.css`:**
|
||
|
||
```css
|
||
/* ============================================
|
||
Sidebar Base Styles
|
||
============================================ */
|
||
|
||
.sidebar {
|
||
@apply flex flex-col bg-base-200 min-h-screen;
|
||
@apply transition-[width] duration-300 ease-in-out;
|
||
width: 16rem; /* w-64 - Expanded */
|
||
}
|
||
|
||
/* Collapsed State */
|
||
[data-sidebar-expanded="false"] .sidebar {
|
||
width: 4rem; /* w-16 - Collapsed */
|
||
}
|
||
|
||
/* ============================================
|
||
Text Labels - Fade Out in Collapsed
|
||
============================================ */
|
||
|
||
.menu-label {
|
||
@apply transition-all duration-200 whitespace-nowrap;
|
||
@apply opacity-100;
|
||
}
|
||
|
||
[data-sidebar-expanded="false"] .sidebar .menu-label {
|
||
@apply opacity-0 w-0 overflow-hidden pointer-events-none;
|
||
}
|
||
|
||
/* ============================================
|
||
Toggle Button Icon Swap
|
||
============================================ */
|
||
|
||
.sidebar-expanded-icon {
|
||
@apply block;
|
||
}
|
||
|
||
.sidebar-collapsed-icon {
|
||
@apply hidden;
|
||
}
|
||
|
||
[data-sidebar-expanded="false"] .sidebar .sidebar-expanded-icon {
|
||
@apply hidden;
|
||
}
|
||
|
||
[data-sidebar-expanded="false"] .sidebar .sidebar-collapsed-icon {
|
||
@apply block;
|
||
}
|
||
|
||
/* ============================================
|
||
Nested Menu - Show/Hide Based on State
|
||
============================================ */
|
||
|
||
.expanded-menu-group {
|
||
@apply block;
|
||
}
|
||
|
||
.collapsed-menu-group {
|
||
@apply hidden;
|
||
}
|
||
|
||
[data-sidebar-expanded="false"] .sidebar .expanded-menu-group {
|
||
@apply hidden;
|
||
}
|
||
|
||
[data-sidebar-expanded="false"] .sidebar .collapsed-menu-group {
|
||
@apply block;
|
||
}
|
||
|
||
/* ============================================
|
||
Elements Only Visible in Expanded State
|
||
============================================ */
|
||
|
||
.expanded-only {
|
||
@apply block transition-opacity duration-200;
|
||
}
|
||
|
||
[data-sidebar-expanded="false"] .sidebar .expanded-only {
|
||
@apply hidden;
|
||
}
|
||
|
||
/* ============================================
|
||
Tooltips - Only Show in Collapsed State
|
||
============================================ */
|
||
|
||
.sidebar .tooltip::before,
|
||
.sidebar .tooltip::after {
|
||
@apply opacity-0 pointer-events-none;
|
||
}
|
||
|
||
[data-sidebar-expanded="false"] .sidebar .tooltip:hover::before,
|
||
[data-sidebar-expanded="false"] .sidebar .tooltip:hover::after {
|
||
@apply opacity-100;
|
||
}
|
||
|
||
/* ============================================
|
||
Menu Item Alignment - Center in Collapsed
|
||
============================================ */
|
||
|
||
[data-sidebar-expanded="false"] .sidebar .menu > li > a,
|
||
[data-sidebar-expanded="false"] .sidebar .menu > li > button {
|
||
@apply justify-center px-0;
|
||
}
|
||
```
|
||
|
||
### 4.3 Responsive Strategie
|
||
|
||
**Mobile (< lg):**
|
||
- Standard DaisyUI Drawer (checkbox-basiert)
|
||
- Keine custom State-Management
|
||
- Sidebar immer `w-64` wenn geöffnet
|
||
|
||
**Desktop (≥ lg):**
|
||
- `lg:drawer-open` für Persistence
|
||
- Custom State-Management via data-attribute
|
||
- Width-Transition: `w-64` ↔ `w-16`
|
||
|
||
**Keine Konflikte:**
|
||
- `lg:drawer-open` wird NICHT durch `data-sidebar-expanded` beeinflusst
|
||
- Collapsed State nur auf Desktop relevant
|
||
|
||
---
|
||
|
||
## 5. State-Management-Strategie
|
||
|
||
### 5.1 JavaScript Hook: `SidebarState`
|
||
|
||
**Datei:** `assets/js/app.js`
|
||
|
||
**Verantwortlichkeiten:**
|
||
1. **Mount:** Restore State aus localStorage
|
||
2. **Toggle:** Wechsle State, update localStorage + data-attribute
|
||
3. **ARIA:** Update `aria-expanded` auf Toggle-Button
|
||
4. **Global Function:** Expose `window.toggleSidebar()`
|
||
|
||
**Implementierung:**
|
||
|
||
```javascript
|
||
Hooks.SidebarState = {
|
||
mounted() {
|
||
// Restore state from localStorage
|
||
const expanded = localStorage.getItem('sidebar-expanded') !== 'false';
|
||
this.setSidebarState(expanded);
|
||
|
||
// Expose toggle function globally
|
||
window.toggleSidebar = () => {
|
||
const current = this.el.dataset.sidebarExpanded === 'true';
|
||
this.setSidebarState(!current);
|
||
};
|
||
},
|
||
|
||
setSidebarState(expanded) {
|
||
// Update data-attribute (CSS reacts to this)
|
||
this.el.dataset.sidebarExpanded = expanded;
|
||
|
||
// Persist to localStorage
|
||
localStorage.setItem('sidebar-expanded', expanded);
|
||
|
||
// Update ARIA for accessibility
|
||
const toggleBtn = document.getElementById('sidebar-toggle');
|
||
if (toggleBtn) {
|
||
toggleBtn.setAttribute('aria-expanded', expanded);
|
||
}
|
||
},
|
||
|
||
destroyed() {
|
||
// Cleanup
|
||
delete window.toggleSidebar;
|
||
}
|
||
};
|
||
```
|
||
|
||
### 5.2 localStorage Schema
|
||
|
||
**Key:** `sidebar-expanded`
|
||
|
||
**Values:**
|
||
- `"true"` (string) - Expanded
|
||
- `"false"` (string) - Collapsed
|
||
|
||
**Default:** `"true"` (wenn Key nicht existiert)
|
||
|
||
**Warum String?**
|
||
- localStorage speichert nur Strings
|
||
- Vermeidet Parsing-Fehler
|
||
- Einfache Vergleiche
|
||
|
||
### 5.3 CSS Reactions
|
||
|
||
**Automatisch via Attribute Selector:**
|
||
|
||
```html
|
||
<!-- Expanded -->
|
||
<div data-sidebar-expanded="true">
|
||
<aside class="sidebar"> <!-- width: 16rem -->
|
||
<span class="menu-label">Members</span> <!-- visible -->
|
||
</aside>
|
||
</div>
|
||
|
||
<!-- Collapsed -->
|
||
<div data-sidebar-expanded="false">
|
||
<aside class="sidebar"> <!-- width: 4rem -->
|
||
<span class="menu-label">Members</span> <!-- hidden -->
|
||
</aside>
|
||
</div>
|
||
```
|
||
|
||
**Vorteile:**
|
||
- Keine JavaScript DOM-Manipulation
|
||
- CSS Transitions funktionieren automatisch
|
||
- Performance: GPU-accelerated
|
||
- Wartbar: State ist zentral
|
||
|
||
### 5.4 Event Flow
|
||
|
||
```
|
||
User clicks Toggle-Button
|
||
│
|
||
▼
|
||
onclick="toggleSidebar()"
|
||
│
|
||
▼
|
||
JavaScript Hook: setSidebarState(!current)
|
||
│
|
||
├─▶ Update data-attribute
|
||
├─▶ Save to localStorage
|
||
└─▶ Update aria-expanded
|
||
│
|
||
▼
|
||
CSS Reactions
|
||
│
|
||
├─▶ Sidebar width transition
|
||
├─▶ Labels fade out/in
|
||
├─▶ Icons show/hide
|
||
└─▶ Tooltips enable/disable
|
||
│
|
||
▼
|
||
User sees smooth animation
|
||
```
|
||
|
||
**Keine Komplexität:**
|
||
- ❌ Kein tabindex management
|
||
- ❌ Kein focus management
|
||
- ❌ Keine event listener cleanup
|
||
- ❌ Keine checkbox manipulation
|
||
|
||
---
|
||
|
||
## 6. Komponentenstruktur
|
||
|
||
### 6.1 Hierarchie
|
||
|
||
```
|
||
app (layouts.ex)
|
||
├── Mobile Header (lg:hidden)
|
||
│ ├── Hamburger-Icon (drawer-toggle label)
|
||
│ └── Club Name
|
||
│
|
||
└── Sidebar (drawer-side)
|
||
├── sidebar_header
|
||
│ ├── Logo (size-8)
|
||
│ ├── Club Name (.menu-label)
|
||
│ └── Toggle-Button (hidden lg:flex)
|
||
│ ├── Icon: Chevron-Left (.sidebar-expanded-icon)
|
||
│ └── Icon: Chevron-Right (.sidebar-collapsed-icon)
|
||
│
|
||
├── sidebar_menu (flex-1)
|
||
│ ├── menu_item: Members
|
||
│ ├── menu_item: Users
|
||
│ ├── menu_group: Beiträge (.expanded-menu-group + .collapsed-menu-group)
|
||
│ │ ├── Expanded: <details><summary>
|
||
│ │ ├── Collapsed: <dropdown>
|
||
│ │ └── Submenu:
|
||
│ │ ├── menu_subitem: Beitragsarten
|
||
│ │ └── menu_subitem: Einstellungen
|
||
│ └── menu_item: Settings
|
||
│
|
||
└── sidebar_footer (mt-auto)
|
||
├── Language Selector (.expanded-only)
|
||
├── Theme Toggle (immer)
|
||
└── User Menu (dropdown-top dropdown-end)
|
||
├── Avatar + Email
|
||
└── Dropdown:
|
||
├── Profile
|
||
└── Logout
|
||
```
|
||
|
||
### 6.2 Elixir Komponenten
|
||
|
||
**Datei:** `lib/mv_web/components/layouts/sidebar.ex`
|
||
|
||
**Funktionen:**
|
||
1. `sidebar/1` - Root (deprecated, wird durch layouts.ex ersetzt)
|
||
2. `sidebar_content/1` - Wrapper für alle Sidebar-Inhalte
|
||
3. `sidebar_header/1` - Logo + Name + Toggle
|
||
4. `sidebar_menu/1` - Navigation Container
|
||
5. `menu_item/1` - Einfacher Menü-Eintrag
|
||
6. `menu_group/1` - Nested Menu Container
|
||
7. `menu_subitem/1` - Submenu-Eintrag
|
||
8. `sidebar_footer/1` - Footer Container
|
||
9. `theme_toggle/1` - Theme Switch
|
||
10. `user_menu/1` - User Dropdown
|
||
|
||
### 6.3 Attribute Definition
|
||
|
||
**Beispiel: `menu_item/1`**
|
||
|
||
```elixir
|
||
attr :href, :string, required: true, doc: "Navigation path"
|
||
attr :icon, :string, required: true, doc: "Heroicon name"
|
||
attr :label, :string, required: true, doc: "Menu item label"
|
||
attr :active, :boolean, default: false, doc: "Highlight as active"
|
||
|
||
defp menu_item(assigns) do
|
||
~H"""
|
||
<li role="none">
|
||
<.link
|
||
navigate={@href}
|
||
class={[
|
||
"flex items-center gap-3",
|
||
"tooltip tooltip-right",
|
||
@active && "active"
|
||
]}
|
||
data-tip={@label}
|
||
role="menuitem"
|
||
>
|
||
<.icon name={@icon} class="size-5 shrink-0" aria-hidden="true" />
|
||
<span class="menu-label">{@label}</span>
|
||
</.link>
|
||
</li>
|
||
"""
|
||
end
|
||
```
|
||
|
||
---
|
||
|
||
## 7. Accessibility (WCAG 2.1 Level AA)
|
||
|
||
### 7.1 Semantic HTML
|
||
|
||
| Element | Semantik | ARIA |
|
||
|---------|----------|------|
|
||
| `<nav>` | Hauptnavigation | `aria-label="Main navigation"` |
|
||
| `<ul role="menubar">` | Menu Container | `role="menubar"` |
|
||
| `<li role="none">` | List Item (deaktiviert) | `role="none"` |
|
||
| `<a role="menuitem">` | Menu Item | `role="menuitem"` |
|
||
| `<button aria-haspopup>` | Dropdown Trigger | `aria-haspopup="menu"` |
|
||
| `<details>` | Expandable Section | Native HTML (kein ARIA nötig) |
|
||
|
||
### 7.2 ARIA-Attribute
|
||
|
||
**Toggle-Button:**
|
||
```html
|
||
<button
|
||
aria-label="Toggle sidebar"
|
||
aria-expanded="true"
|
||
aria-controls="main-sidebar"
|
||
>
|
||
```
|
||
|
||
**User Menu:**
|
||
```html
|
||
<button
|
||
aria-label="User menu"
|
||
aria-haspopup="menu"
|
||
aria-expanded="false"
|
||
>
|
||
```
|
||
|
||
**Icons (dekorativ):**
|
||
```html
|
||
<.icon name="hero-users" aria-hidden="true" />
|
||
```
|
||
|
||
**Tooltips:**
|
||
```html
|
||
<a data-tip="Members" class="tooltip tooltip-right">
|
||
<!-- Tooltip ist visuell, nicht für Screen Reader -->
|
||
</a>
|
||
```
|
||
|
||
### 7.3 Keyboard Navigation
|
||
|
||
**Fokussierbare Elemente:**
|
||
- Toggle-Button (Space/Enter)
|
||
- Menu-Items (Enter)
|
||
- User-Menu-Button (Space/Enter, Arrows für Navigation)
|
||
- Dropdowns (Tab, Arrows, Escape)
|
||
- Theme-Toggle (Space)
|
||
- Language-Selector (Arrows, Enter)
|
||
|
||
**Focus-Indikatoren:**
|
||
```css
|
||
.focus:outline-none {
|
||
@apply ring-2 ring-primary ring-offset-2;
|
||
}
|
||
```
|
||
|
||
**Escape-Taste:**
|
||
- Schließt User-Menu-Dropdown
|
||
- Schließt Beiträge-Dropdown (collapsed)
|
||
- Schließt Mobile-Drawer
|
||
|
||
### 7.4 Color Contrast
|
||
|
||
**Mindestkontrast:** 4.5:1 (normal text), 3:1 (large text)
|
||
|
||
**DaisyUI Colors (geprüft):**
|
||
- `text-base-content` auf `bg-base-200`: ✅ 7.2:1
|
||
- `text-primary` auf `bg-base-100`: ✅ 5.1:1
|
||
- `text-neutral-content` auf `bg-neutral`: ✅ 8.5:1
|
||
|
||
### 7.5 Screen Reader Testing
|
||
|
||
**Tools:**
|
||
- NVDA (Windows)
|
||
- VoiceOver (Mac)
|
||
- JAWS (Windows)
|
||
|
||
**Erwartetes Verhalten:**
|
||
- "Main navigation, navigation landmark"
|
||
- "Members, link, menu item"
|
||
- "Toggle sidebar, button, expanded"
|
||
- "User menu, button, has popup"
|
||
|
||
---
|
||
|
||
## 8. Performance-Überlegungen
|
||
|
||
### 8.1 CSS Transitions
|
||
|
||
**Optimiert:**
|
||
- `transition-[width]` - GPU-accelerated
|
||
- `duration-300` - Wahrnehmbar, aber schnell
|
||
- `ease-in-out` - Smooth Start/Stop
|
||
|
||
**Vermeiden:**
|
||
- `transition-all` - Zu generisch, Performance-Impact
|
||
- Transitions auf `height: auto` - Nicht smooth
|
||
|
||
### 8.2 JavaScript
|
||
|
||
**Minimal:**
|
||
- Hook läuft nur beim Mount
|
||
- Toggle ist synchron (keine async Operations)
|
||
- Keine Event-Listener-Lawine
|
||
- Cleanup in `destroyed()`
|
||
|
||
**localStorage:**
|
||
- Synchron, aber schnell (Key/Value-Store)
|
||
- Nur bei Toggle geschrieben (nicht bei jedem Render)
|
||
|
||
### 8.3 CSS Paint
|
||
|
||
**Minimierte Repaints:**
|
||
- Width-Änderung triggert nur Reflow der Sidebar
|
||
- Content-Bereich passt sich automatisch an (Flexbox)
|
||
- Keine Layout-Thrashing
|
||
|
||
---
|
||
|
||
## 9. Browser-Kompatibilität
|
||
|
||
### 9.1 Unterstützte Browser
|
||
|
||
| Browser | Mindestversion | Notizen |
|
||
|---------|---------------|---------|
|
||
| **Chrome** | 90+ | Vollständig unterstützt |
|
||
| **Edge** | 90+ | Chromium-basiert |
|
||
| **Firefox** | 88+ | Vollständig unterstützt |
|
||
| **Safari** | 14+ | CSS-Transitions funktionieren |
|
||
|
||
### 9.2 Features mit Fallbacks
|
||
|
||
**CSS Grid/Flexbox:**
|
||
- Alle Browser unterstützen
|
||
|
||
**CSS Custom Properties:**
|
||
- Alle Browser unterstützen (DaisyUI Themes)
|
||
|
||
**localStorage:**
|
||
- Alle Browser unterstützen
|
||
- Fallback: State nur in Session
|
||
|
||
**data-attributes:**
|
||
- Alle Browser unterstützen
|
||
|
||
---
|
||
|
||
## 10. Testing-Strategie
|
||
|
||
### 10.1 Unit Tests
|
||
|
||
**Komponenten-Tests:**
|
||
```elixir
|
||
describe "sidebar_header/1" do
|
||
test "renders logo with correct size"
|
||
test "renders club name"
|
||
test "renders toggle button with both icons"
|
||
end
|
||
|
||
describe "menu_item/1" do
|
||
test "renders icon and label"
|
||
test "has tooltip with correct text"
|
||
test "has correct link"
|
||
end
|
||
|
||
describe "menu_group/1" do
|
||
test "renders expanded menu group"
|
||
test "renders collapsed menu group"
|
||
test "renders submenu items"
|
||
end
|
||
```
|
||
|
||
### 10.2 Integration Tests
|
||
|
||
**State-Management:**
|
||
```elixir
|
||
describe "sidebar state management" do
|
||
test "sidebar starts expanded by default"
|
||
test "toggle button changes state"
|
||
test "state persists across page reloads"
|
||
end
|
||
```
|
||
|
||
### 10.3 Visual Regression Tests
|
||
|
||
**Manuell (Checkliste):**
|
||
- [ ] Desktop Expanded: Screenshot
|
||
- [ ] Desktop Collapsed: Screenshot
|
||
- [ ] Mobile Closed: Screenshot
|
||
- [ ] Mobile Open: Screenshot
|
||
- [ ] Toggle Transition: Video
|
||
- [ ] Nested Menu (expanded): Screenshot
|
||
- [ ] Nested Menu (collapsed dropdown): Screenshot
|
||
- [ ] Footer Position: Screenshot
|
||
|
||
### 10.4 Accessibility Tests
|
||
|
||
**Tools:**
|
||
- Lighthouse (Chrome DevTools)
|
||
- axe DevTools Extension
|
||
- WAVE Extension
|
||
|
||
**Manuell:**
|
||
- Keyboard-Navigation (nur Tastatur)
|
||
- Screen-Reader (NVDA/VoiceOver)
|
||
- Zoom (200% / 400%)
|
||
- High-Contrast-Mode
|
||
|
||
---
|
||
|
||
## 11. Migrations-Plan (von aktueller Implementierung)
|
||
|
||
### 11.1 Zu entfernen
|
||
|
||
**CSS:**
|
||
- [ ] Alle `@custom-variant is-drawer-*` Definitionen
|
||
- [ ] Alle `.is-drawer-close:*` Klassen
|
||
- [ ] Alle `.is-drawer-open:*` Klassen
|
||
|
||
**HTML:**
|
||
- [ ] Inline `onclick` Handler (durch `toggleSidebar()` ersetzen)
|
||
- [ ] Magic IDs (durch konsistente Naming ersetzen)
|
||
|
||
**JavaScript:**
|
||
- [ ] Komplexe Tabindex-Management-Logik
|
||
- [ ] Event-Listener für Checkbox
|
||
- [ ] Focus-Management (außer ARIA)
|
||
|
||
### 11.2 Zu ergänzen
|
||
|
||
**CSS:**
|
||
- [ ] `[data-sidebar-expanded]` Selektoren
|
||
- [ ] `.menu-label` Klasse
|
||
- [ ] `.sidebar-expanded-icon` / `.sidebar-collapsed-icon`
|
||
- [ ] `.expanded-menu-group` / `.collapsed-menu-group`
|
||
- [ ] `.expanded-only`
|
||
|
||
**HTML:**
|
||
- [ ] Logo-Element
|
||
- [ ] Toggle-Button mit Icon-Swap
|
||
- [ ] `data-sidebar-expanded` auf root
|
||
- [ ] `phx-hook="SidebarState"` auf root
|
||
|
||
**JavaScript:**
|
||
- [ ] `Hooks.SidebarState` Hook
|
||
- [ ] `window.toggleSidebar()` Function
|
||
|
||
### 11.3 Zu prüfen/behalten
|
||
|
||
**DaisyUI:**
|
||
- [x] drawer Pattern (korrekt)
|
||
- [x] drawer-toggle (Mobile)
|
||
- [x] lg:drawer-open (Desktop)
|
||
- [x] drawer-overlay (Mobile)
|
||
|
||
**Accessibility:**
|
||
- [x] ARIA-Labels (größtenteils korrekt)
|
||
- [x] role Attributes (korrekt)
|
||
- [x] Focus-Indikatoren (korrekt)
|
||
|
||
---
|
||
|
||
## 12. Risiken und Mitigations
|
||
|
||
### 12.1 Identifizierte Risiken
|
||
|
||
| Risiko | Wahrscheinlichkeit | Impact | Mitigation |
|
||
|--------|-------------------|--------|------------|
|
||
| **CSS-Transitions flackern** | Mittel | Mittel | Pre-testing, GPU-Acceleration |
|
||
| **localStorage nicht verfügbar** | Niedrig | Niedrig | Try/Catch, Fallback auf Session |
|
||
| **Nested Menu doppelter Hover** | Mittel | Niedrig | CSS Specificity prüfen |
|
||
| **Mobile Drawer schließt nicht** | Niedrig | Hoch | DaisyUI-Standard verwenden |
|
||
| **Tooltip überlappen Content** | Mittel | Niedrig | z-index Management |
|
||
|
||
### 12.2 Browser-spezifische Risks
|
||
|
||
**Safari:**
|
||
- Flex-Gap ältere Versionen: Fallback mit margin
|
||
|
||
**Firefox:**
|
||
- Scrollbar-Styling: Akzeptieren (nicht kritisch)
|
||
|
||
---
|
||
|
||
## 13. Wartung und Erweiterbarkeit
|
||
|
||
### 13.1 Neues Menu-Item hinzufügen
|
||
|
||
```elixir
|
||
# In sidebar_menu/1 hinzufügen:
|
||
<.menu_item
|
||
href={~p"/new-feature"}
|
||
icon="hero-sparkles"
|
||
label={gettext("New Feature")}
|
||
/>
|
||
```
|
||
|
||
### 13.2 Neues Nested Menu hinzufügen
|
||
|
||
```elixir
|
||
<.menu_group
|
||
icon="hero-folder"
|
||
label={gettext("Documents")}
|
||
>
|
||
<.menu_subitem href={~p"/docs/contracts"} label={gettext("Contracts")} />
|
||
<.menu_subitem href={~p"/docs/invoices"} label={gettext("Invoices")} />
|
||
</.menu_group>
|
||
```
|
||
|
||
### 13.3 CSS-Anpassungen
|
||
|
||
**Sidebar-Breite ändern:**
|
||
```css
|
||
.sidebar {
|
||
width: 18rem; /* statt 16rem */
|
||
}
|
||
|
||
[data-sidebar-expanded="false"] .sidebar {
|
||
width: 5rem; /* statt 4rem */
|
||
}
|
||
```
|
||
|
||
**Transition-Dauer ändern:**
|
||
```css
|
||
.sidebar {
|
||
@apply transition-[width] duration-500; /* statt 300ms */
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 14. Abnahmekriterien
|
||
|
||
### 14.1 Funktional
|
||
|
||
- [ ] Logo ist immer 32px groß (expanded & collapsed)
|
||
- [ ] Toggle-Button wechselt Icon (Chevron-Left ↔ Right)
|
||
- [ ] Menü-Items zeigen Icons + Labels (expanded)
|
||
- [ ] Menü-Items zeigen nur Icons mit Tooltips (collapsed)
|
||
- [ ] Nested Menu: Details (expanded), Dropdown (collapsed)
|
||
- [ ] Footer am unteren Ende (Flexbox)
|
||
- [ ] Language-Selector nur expanded
|
||
- [ ] Theme-Toggle immer sichtbar
|
||
- [ ] User-Avatar zentriert, erste Buchstabe
|
||
- [ ] State in localStorage gespeichert
|
||
- [ ] State bleibt nach Reload erhalten
|
||
- [ ] Mobile: Hamburger öffnet Overlay-Drawer
|
||
- [ ] Mobile: Overlay schließt Drawer
|
||
|
||
### 14.2 Technisch
|
||
|
||
- [ ] Keine Custom CSS Variants verwendet
|
||
- [ ] Nur Tailwind + DaisyUI Klassen
|
||
- [ ] `data-sidebar-expanded` auf root
|
||
- [ ] CSS reagiert auf data-attribute
|
||
- [ ] JavaScript Hook implementiert
|
||
- [ ] localStorage funktioniert
|
||
- [ ] Keine Console-Errors
|
||
- [ ] Keine Console-Warnings
|
||
- [ ] Keine duplicate IDs
|
||
|
||
### 14.3 Visuell
|
||
|
||
- [ ] Smooth width transition (300ms)
|
||
- [ ] Labels fade smooth out/in
|
||
- [ ] Keine Layout-Shifts
|
||
- [ ] Keine Horizontal-Scrollbar
|
||
- [ ] Kein Flicker bei Transitions
|
||
- [ ] Hover-Effekte einheitlich
|
||
- [ ] Footer klebt am unteren Ende
|
||
- [ ] Responsive Breakpoints funktionieren
|
||
|
||
### 14.4 Accessibility
|
||
|
||
- [ ] Alle ARIA-Attribute korrekt
|
||
- [ ] Keyboard-Navigation funktioniert
|
||
- [ ] Screen-Reader-Test bestanden
|
||
- [ ] Color-Contrast ≥ 4.5:1
|
||
- [ ] Focus-Indikatoren sichtbar
|
||
- [ ] Lighthouse Score ≥ 95
|
||
|
||
### 14.5 Performance
|
||
|
||
- [ ] Keine Performance-Warnungen
|
||
- [ ] CSS-Transitions GPU-accelerated
|
||
- [ ] localStorage synchron
|
||
- [ ] Keine Memory-Leaks
|
||
|
||
---
|
||
|
||
## 15. Referenzen
|
||
|
||
### 15.1 Interne Dokumente
|
||
|
||
- `docs/sidebar-analysis-current-state.md` - Ist-Zustand Analyse
|
||
- `docs/daisyui-drawer-pattern.md` - DaisyUI Pattern Docs
|
||
- `docs/umsetzung-sidebar.md` - Implementierungs-Anleitung
|
||
- `CODE_GUIDELINES.md` - Projekt-Konventionen
|
||
|
||
### 15.2 Externe Dokumentation
|
||
|
||
- [DaisyUI Drawer](https://daisyui.com/components/drawer/)
|
||
- [DaisyUI Menu](https://daisyui.com/components/menu/)
|
||
- [DaisyUI Dropdown](https://daisyui.com/components/dropdown/)
|
||
- [Tailwind CSS Transitions](https://tailwindcss.com/docs/transition-property)
|
||
- [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/)
|
||
- [MDN: data-* attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*)
|
||
|
||
### 15.3 Design-Referenzen
|
||
|
||
- [Phoenix LiveView Docs](https://hexdocs.pm/phoenix_live_view/) - Navigation Pattern
|
||
- [GitHub Sidebar](https://github.com/) - Collapsed Icon Pattern
|
||
- [Discord Sidebar](https://discord.com/) - Tooltip Pattern
|
||
- [VS Code Sidebar](https://code.visualstudio.com/) - Toggle Icon Pattern
|
||
|
||
---
|
||
|
||
## 16. Change Log
|
||
|
||
| Version | Datum | Änderungen | Autor |
|
||
|---------|-------|------------|-------|
|
||
| 2.0 | 2025-12-16 | Initiale Version nach Analyse | Cursor AI |
|
||
|
||
---
|
||
|
||
## 17. Appendix
|
||
|
||
### A. DaisyUI Drawer Pattern (Kurzreferenz)
|
||
|
||
```html
|
||
<div class="drawer lg:drawer-open">
|
||
<input id="drawer" type="checkbox" class="drawer-toggle" />
|
||
|
||
<div class="drawer-content">
|
||
<!-- Mobile Header -->
|
||
<label for="drawer" class="lg:hidden">Open</label>
|
||
<!-- Main Content -->
|
||
</div>
|
||
|
||
<div class="drawer-side">
|
||
<label for="drawer" class="drawer-overlay lg:hidden"></label>
|
||
<aside class="sidebar">
|
||
<!-- Sidebar Content -->
|
||
</aside>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
### B. CSS Selector Beispiele
|
||
|
||
```css
|
||
/* Expanded (default) */
|
||
.sidebar { width: 16rem; }
|
||
.menu-label { opacity: 1; }
|
||
.expanded-only { display: block; }
|
||
|
||
/* Collapsed */
|
||
[data-sidebar-expanded="false"] .sidebar { width: 4rem; }
|
||
[data-sidebar-expanded="false"] .sidebar .menu-label { opacity: 0; }
|
||
[data-sidebar-expanded="false"] .sidebar .expanded-only { display: none; }
|
||
```
|
||
|
||
### C. localStorage Beispiel
|
||
|
||
```javascript
|
||
// Save
|
||
localStorage.setItem('sidebar-expanded', 'true');
|
||
|
||
// Load
|
||
const expanded = localStorage.getItem('sidebar-expanded') !== 'false';
|
||
|
||
// Check
|
||
if (localStorage.getItem('sidebar-expanded') === 'false') {
|
||
// Collapsed
|
||
}
|
||
```
|
||
|
||
### D. ARIA Beispiele
|
||
|
||
```html
|
||
<!-- Toggle-Button -->
|
||
<button
|
||
id="sidebar-toggle"
|
||
aria-label="Toggle sidebar"
|
||
aria-expanded="true"
|
||
aria-controls="main-sidebar"
|
||
>
|
||
|
||
<!-- Menu -->
|
||
<nav id="main-sidebar" aria-label="Main navigation">
|
||
<ul role="menubar">
|
||
<li role="none">
|
||
<a role="menuitem">Members</a>
|
||
</li>
|
||
</ul>
|
||
</nav>
|
||
|
||
<!-- Dropdown -->
|
||
<button aria-haspopup="menu" aria-expanded="false">
|
||
User Menu
|
||
</button>
|
||
```
|
||
|
||
---
|
||
|
||
**Ende der Spezifikation**
|
||
|
||
|