flower
/
Docs Meta

Design review — July 2026

A full design-review pass over flower's Livewire UI against the bloom design system (Brief #38). Method: read every page's blade + the shared components + app.css tokens, and screenshot each page live (dashboard, briefs, feedback, roster, docs, analytics, prompts, config, inbox, nav/layout).

Verdict: the UI is in strong shape. The bloom system is cohesive and distinctive — one token vocabulary, a global :focus-visible baseline, centralized .nav-link / .pill / .chip / .data-table, good empty states, responsive grids, and the "warmth = liveness/recency" identity carried consistently. Findings are polish + accessibility micro-issues, not structural problems.

Severity is high (real a11y barrier or broken state), med (noticeable inconsistency), low (nit / subjective).

Applied in this pass (safe fixes)

Only safe, obviously-correct, non-structural fixes were applied.

Fix Files Severity Status
Accessible names (aria-label) for unlabeled <select> / <textarea> controls briefs index (project scope, project filter, merge-into), briefs detail (model, status, epic parent, add-dependency), roster (project filter), prompts (template body, sample brief/project) high ✅ fixed (Brief: #38)

Before, a screen reader announced these controls only by their first option (e.g. "all projects") with no idea what the control does. They're now named. No visual or behavior change.

Findings — accessibility

# Finding Where Severity Disposition
A1 Unlabeled form controls briefs / roster / prompts high Fixed above
A2 Same gap, other pages: <select>/<input> without an accessible name inbox/index.blade.php (setNoteStatus ~L109, reassign ~L116, project filter ~L61); config/index.blade.php (max-age ~L59, summaryModel ~L84, briefModel ~L111, pinModel ~L220, pinNewProvider ~L254) high Recommended — same one-line aria-label fix; not applied because these pages were outside the named review scope and a few controls are wrapped in a <label> already (verify before labeling to avoid a redundant name).
A3 Clickable table rows use <tr onclick="window.location=…"> dashboard.blade.php ~L173, feedback/index.blade.php ~L111 low Acceptable, leave as-is. Each row already contains a focusable inner <a wire:navigate> (the primary link, with event.stopPropagation()), so keyboard users have a path; the row onclick is a mouse convenience. Adding tabindex/role to the row would create a competing focus target — worse.
A4 Chart bars are hover-only, not keyboard-focusable analytics/index.blade.php ~L52 low Recommend, don't apply. These are decorative data-viz segments; the numbers are already in the legend/summary. Per-bar focus is noisy + opinionated. If desired, add a descriptive aria-label/summary to the chart container instead.

Findings — consistency & polish

# Finding Where Severity Disposition
P1 Dynamic status colors applied via inline style="background: {{ … }}" briefs index/show, roster rows low Note, don't apply. These correctly use bloom tokens (var(--…)), just inline because the value is per-row dynamic — legitimate. A shared helper (a <x-ui.dot :color> or a data-state → CSS map) would centralize them if maintainability becomes a concern.
P2 Two pill treatments for "kind" on /feedback — the composer toggle (Bug/Idea/Note) vs. the filter chips (All/Bug/Note/…) feedback/index.blade.php low Note. Minor; both read fine. Unifying them (or a shared segmented-control component) is a small consistency win.
P3 Roster row-action icons (poke · compaction · retire) are very low contrast roster/index.blade.php low Note. Poke/retire are intentionally-disabled stubs (faint is correct), but the enabled request-compaction action is hard to discover at rest. Consider a slightly higher rest contrast (or hover-reveal) for the enabled action only.

Recommendations — structural / opinionated (operator review, NOT applied)

These are genuine improvements but are structural or a matter of taste, so per the review's remit they are left for you to decide, not applied.

FluxUI Pro + slide-over Note/Report (your note on Brief #38). Moving the top-nav Note and Report actions from full-page navigations to a slide-over (keep the user on the page they're referencing) is a real UX win. It's structural, though — it means either adopting FluxUI Pro or building a reusable Alpine slide-over + extracting the two forms into embeddable partials. Worth its own brief. Note: I did not fetch the FluxUI license auth.json from the vodmanager project — installing a licensed dependency is outside a safe-polish pass and is your call.

  • R1 — Slide-over Note/Report (above). If you like it, a good first FluxUI fit; otherwise a ~1-file Alpine x-show slide-over + @include of each form.
  • R2 — Shared "segmented control" component for the repeated button-group pattern (analytics range 7d/30d/90d/All, feedback/brief filter toggles, dispatch fresh|resume). One <x-ui.segmented> would unify styling + keyboard support.
  • R3 — Inline-style → token-utility refactor (P1) if you want the dynamic color logic out of the markup.
  • R4 — Verified a11y label pass across inbox/config (A2) — safe, just needs each control checked for an existing wrapping <label> first.

What's strong (keep doing this)

  • Token discipline — no hardcoded hex / raw Tailwind palette colors found anywhere; everything routes through the bloom variables.
  • A global :focus-visible outline so keyboard focus is never invisible, and icon-only buttons already carry aria-labels.
  • Empty states everywhere (briefs, roster, feedback, inbox, prompts, config, search) and responsive grids that collapse cleanly to one column.
  • A distinctive, coherent identity — the "the light's still on" warmth model, the accent glow on genuinely-live things, mono for data, the left-rail active indicator. It doesn't read as a template.