diff --git a/docs/badge-wcag-phase1-analysis.md b/docs/badge-wcag-phase1-analysis.md index 5b6a834..911cfbd 100644 --- a/docs/badge-wcag-phase1-analysis.md +++ b/docs/badge-wcag-phase1-analysis.md @@ -1,88 +1,53 @@ -# Phase 1 — Badge WCAG Analysis & Migration +# Badge Component Design Notes (WCAG) -## 1) Repo-Analyse (Stand vor Änderungen) +Design rationale for the central `<.badge>` core component +(`MvWeb.CoreComponents`) and the WCAG-driven theme overrides in +`assets/css/app.css`. Before it, badges were raw `` +markup with no central component. -### Badge-Verwendungen (alle Fundstellen) +## `<.badge>` API contract -| Datei | Kontext | Markup | -|-------|---------|--------| -| `lib/mv_web/live/member_field_live/index_component.ex` | Tabelle (show_in_overview) | `` / `` | -| `lib/mv_web/live/components/member_filter_component.ex` | Filter-Chips (Anzahl) | `` (2×) | -| `lib/mv_web/live/role_live/index.html.heex` | Tabelle (System Role, Permission Set, Custom) | `badge-warning`, `permission_set_badge_class()`, `badge-ghost` (User Count) | -| `lib/mv_web/helpers/membership_fee_helpers.ex` | Helper | `status_color/1` → "badge-success" \| "badge-error" \| "badge-ghost" | -| `lib/mv_web/live/member_live/show.ex` | Mitgliedsdetail (Beiträge) | ``, `badge-ghost` (No cycles) | -| `lib/mv_web/live/membership_fee_settings_live.ex` | Settings (Fee Types) | `badge-outline`, `badge-ghost` (member count) | -| `lib/mv_web/live/membership_fee_type_live/index.ex` | Index (Fee Types) | `badge-outline`, `badge-ghost` (member count) | -| `lib/mv_web/live/role_live/index.ex` | (Helper-Import) | `permission_set_badge_class/1` | -| `lib/mv_web/live/member_live/show/membership_fees_component.ex` | Mitgliedsbeiträge | `badge-outline`, `["badge", status_color]` | -| `lib/mv_web/live/custom_field_live/index_component.ex` | Tabelle (show_in_overview) | `badge-success`, `badge-ghost` | -| `lib/mv_web/member_live/index/membership_fee_status.ex` | Helper | `format_cycle_status_badge/1` → map mit `color`, `icon`, `label` | -| `lib/mv_web/live/global_settings_live.ex` | Form (label-text-alt) | `badge badge-ghost` "(set)" (2×) | -| `lib/mv_web/live/member_live/index.html.heex` | Tabelle (Status) | `format_cycle_status_badge` + ``, `badge-ghost` (No cycle), `badge-outline badge-primary` (Filter-Chip) | -| `lib/mv_web/live/role_live/helpers.ex` | Helper | `permission_set_badge_class/1` → "badge badge-* badge-sm" | -| `lib/mv_web/live/group_live/show.ex` | Card | `badge badge-outline badge` | -| `lib/mv_web/live/role_live/show.ex` | Detail | `permission_set_badge_class`, `badge-warning` (System), `badge-ghost` (No) | +- `attr :variant` — `:neutral | :primary | :info | :success | :warning | :error` +- `attr :style` — `:soft | :solid | :outline` (default: `:soft`) +- `attr :size` — `:sm | :md` (default: `:md`) +- `attr :sr_label` — optional screen-reader label for icon-only badges +- `slot :icon` — optional +- `slot :inner_block` — badge text -### DaisyUI/Tailwind Config +## Design rules -- **Tailwind:** `assets/tailwind.config.js` — erweitert nur `theme.extend.colors.brand`; kein DaisyUI hier. -- **DaisyUI:** wird in `assets/css/app.css` per `@plugin "../vendor/daisyui"` mit `themes: false` geladen. -- **Themes:** Zwei Custom-Themes in `app.css`: - - `@plugin "../vendor/daisyui-theme"` mit `name: "dark"` (default: false) - - `@plugin "../vendor/daisyui-theme"` mit `name: "light"` (default: true) -- **Theme-Umschaltung:** `lib/mv_web/components/layouts/root.html.heex` — Inline-Script setzt `document.documentElement.setAttribute("data-theme", "light"|"dark")` aus `localStorage["phx:theme"]` oder `prefers-color-scheme`. Sidebar enthält Theme-Toggle (`<.theme_toggle />`). +- `:soft` and `:solid` use a visible background; `:soft` is the default. No + transparent ghost as a default. +- `:outline` **always** sets a background (e.g. `bg-base-100`) so the border + stays visible on grey (`base-200`/`base-300`) surfaces. +- Ghost only as an explicit opt-in, and then with `bg-base-100` for visibility + (plain DaisyUI `badge-ghost` is `base-200` on `base-200` — nearly invisible). +- Clickable chips keep `<.badge>` as a plain container with a button in the + `inner_block`. -### Core Components +## WCAG contrast overrides (`app.css`) -- **Modul:** `lib/mv_web/components/core_components.ex` (MvWeb.CoreComponents). -- **Vorhanden:** flash, button, dropdown_menu, form_section, input, header, table, icon, link, etc. -- **Badge:** Bisher keine zentrale `<.badge>`-Komponente. +DaisyUI defaults do not reach the WCAG 2.2 AA **4.5:1** text-contrast ratio for +badges, so `app.css` adds per-theme overrides on top of the custom `light` / +`dark` themes: -### DaisyUI Badge (Vendor) +- **Light theme:** darker `--badge-fg` for all solid variants; darker text on + `badge-soft`'s tinted background; uniform dark text for `badge-outline` on + `base-100`. +- **Dark theme:** slightly darkened solid-badge backgrounds so the light + `*-content` colors reach 4.5:1; lighter, readable variant tints for + `badge-soft`; light `--badge-fg` for `badge-outline` on `base-100`. -- **Default:** `--badge-bg: var(--badge-color, var(--color-base-100))`, `--badge-fg: var(--color-base-content)`. -- **badge-outline:** `--badge-bg: "#0000"` (transparent) → Kontrastproblem auf base-200/base-300. -- **badge-ghost:** `background-color: var(--color-base-200)`, `color: var(--color-base-content)` → auf base-200-Flächen kaum sichtbar. -- **badge-soft:** color-mix 8% Variante mit base-100 → sichtbar; Text ist Variantenfarbe (Kontrast prüfen). +Related: contrast overrides for the member-filter join buttons +(`.member-filter-dropdown .join .btn`) under the same 4.5:1 rule. ---- +## Variant helpers -## 2) Core Component <.badge> API (geplant) - -- **attr :variant** — `:neutral | :primary | :info | :success | :warning | :error` -- **attr :style** — `:soft | :solid | :outline` (Default: `:soft`) -- **attr :size** — `:sm | :md` (Default: `:md`) -- **slot :inner_block** — Badge-Text -- **attr :sr_label** — optional, für Icon-only (Screen Reader) -- **slot :icon** — optional - -Regeln: - -- `:soft` und `:solid` nutzen sichtbaren Hintergrund (kein transparenter Ghost als Default). -- `:outline` setzt immer einen Hintergrund (z. B. `bg-base-100`), damit der Rand auf grauen Flächen sichtbar bleibt. -- Ghost nur als explizites Opt-in; dann mit `bg-base-100` für Sichtbarkeit. - ---- - -## 3) Theme-Overrides (WCAG) - -- In `app.css` sind bereits Custom-Themes für `light` und `dark` mit eigenen Tokens. -- **Badge-Kontrast (WCAG 2.2 AA 4.5:1):** Zusätzliche Overrides in `app.css`: - - **Light theme:** Dunkle `--badge-fg` für alle Varianten (primary, success, error, warning, info, neutral); für `badge-soft` dunklere Textfarbe (`color`) auf getöntem Hintergrund; für `badge-outline` einheitlich dunkle Schrift auf base-100. - - **Dark theme:** Leicht abgedunkelte Badge-Hintergründe für Solid-Badges, damit die hellen *-content-Farben 4.5:1 erreichen; für `badge-soft` hellere, gut lesbare Variantentöne; für `badge-outline` heller Text (`--badge-fg`) auf base-100. - ---- - -## 4) Migration (erledigt) - -- Alle `` durch `<.badge variant="..." style="...">...` ersetzt. -- Klickbare Chips (z. B. Group Show „Remove“) bleiben als <.badge> mit Button im inner_block (Badge ist nur Container). -- **Neue Helper:** `MembershipFeeHelpers.status_variant/1` (→ :success | :error | :warning; suspended = :warning wie Edit-Button), `RoleLive.Helpers.permission_set_badge_variant/1` (→ :neutral | :info | :success | :error). -- **Angepasst:** `MembershipFeeStatus.format_cycle_status_badge/1` liefert zusätzlich `:variant` für <.badge>. -- **Migrierte Stellen:** member_field_live, member_filter_component, role_live (index + show), member_live (show, index, membership_fees_component), membership_fee_settings_live, membership_fee_type_live, custom_field_live, global_settings_live, group_live/show. - -## 5) Weitere Anpassungen (nach Phase 1) - -- **Filter Join-Buttons (WCAG):** In `app.css` Kontrast-Overrides für `.member-filter-dropdown .join .btn` (inaktiv: base-100/base-200 + dunkle/helle Schrift; aktiv: success/error mit 4.5:1). -- **Badge „Pausiert“ (suspended):** `status_variant(:suspended)` → `:warning` (gelb), damit Badge dieselbe Farbe wie der Edit-Button (btn-warning) hat. -- **Filter-Dropdown schließen:** `phx-click-away` vom inneren Panel auf den äußeren Wrapper (`member-filter-dropdown`) verschoben; Klick auf den Filter-Button schließt das Dropdown (konsistent mit Spalten/Ausblenden). +- `MembershipFeeHelpers.status_variant/1` → `:success | :error | :warning`. + `status_variant(:suspended) -> :warning` (yellow) deliberately matches the + Edit button (`btn-warning`), keeping the "suspended" badge the same color as + its action. +- `RoleLive.Helpers.permission_set_badge_variant/1` → + `:neutral | :info | :success | :error`. +- `MembershipFeeStatus.format_cycle_status_badge/1` additionally returns a + `:variant` for `<.badge>`. diff --git a/docs/daisyui-drawer-pattern.md b/docs/daisyui-drawer-pattern.md index dec599d..85690c9 100644 --- a/docs/daisyui-drawer-pattern.md +++ b/docs/daisyui-drawer-pattern.md @@ -1,533 +1,21 @@ -# DaisyUI Drawer Pattern - Standard Implementation - -This document describes the standard DaisyUI drawer pattern for implementing responsive sidebars. It covers mobile overlay drawers, desktop persistent sidebars, and their combination. - -## Core Concept - -DaisyUI's drawer component uses a **checkbox-based toggle mechanism** combined with CSS to create accessible, responsive sidebars without custom JavaScript. - -### Key Components - -1. **`drawer`** - Container element -2. **`drawer-toggle`** - Hidden checkbox that controls open/close state -3. **`drawer-content`** - Main content area -4. **`drawer-side`** - Sidebar content (menu, navigation) -5. **`drawer-overlay`** - Optional overlay for mobile (closes drawer on click) - -## HTML Structure - -```html -
- - - - -
- - -
- - - -
-``` - -## How drawer-toggle Works - -### Mechanism - -The `drawer-toggle` is a **hidden checkbox** that serves as the state controller: - -```html - -``` - -### Toggle Behavior - -1. **Label Connection**: Any `