Files
clienthub/design-reference/DESIGN-SYSTEM.md
T
simone 11870e15d3 feat: Client Portal redesign to Quiet Luxury + milestone stepper
- New MilestoneStepper primitive (horizontal per-phase progress)
- Token-migrate portal shell, phase cards, sidebar cards, kanban to dual-theme
- Soft-tint status pills (emerald/amber/muted) replacing solid badges
- embedded prop on ClientDashboard fixes double-header in multi-project tabs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 22:54:58 +02:00

9.6 KiB
Raw Blame History

Design System: iamcavalli Admin & Client Portal (v1.0)

"Minimalist Premium / Quiet Luxury" — the aesthetic that replaces the old mixed token/hardcoded styling across ClientHub's admin area. First applied to the Lead Pipeline page (Phase: Pipeline redesign); the tokens and primitives documented here are the base for restyling every other section.

Philosophy

  • Quiet, not loud. Elevation is nearly invisible (shadow opacity ≤ 0.02), borders are hairline, color is used sparingly and only to carry meaning (status, semantics) — never for decoration.
  • Density with air. Generous padding (py-4 px-6 in tables, p-4 in kanban columns) paired with small type (text-xs, text-[11px]) reads as premium rather than cramped.
  • Numbers are monospace. Prices, phone numbers, dates, counts — anything tabular/numeric — use font-mono and right-alignment so columns of digits line up.
  • Dual-theme by construction. Every surface is built from semantic tokens (bg-card, text-muted-foreground, border-border, …), never raw Tailwind palette classes (bg-white, text-slate-900, …) or hex literals. This is what makes dark mode "just work" without a parallel dark stylesheet.
  • Motion is functional. Transitions exist to explain state change (sidebar collapse, hover, drag) — duration-200duration-350, ease-[cubic-bezier(0.4,0,0.2,1)] for the sidebar specifically. No decorative animation.

Typography

  • Sans: Plus Jakarta Sans (weights 300700), loaded via next/font/google as --font-plus-jakarta-sans, mapped to Tailwind's --font-sans in @theme. Used for all UI text.
  • Mono: Geist Mono, --font-geist-mono--font-mono. Used for numeric data cells only (prices, phone numbers, counts, dates in tables).
  • Scale conventions:
    • Page title: text-2xl font-semibold tracking-tight
    • Page subtitle: text-xs text-muted-foreground
    • Table header cells: text-[11px] font-semibold uppercase tracking-wider text-muted-foreground
    • Table body: text-sm
    • Badge/pill label: text-[10px]text-xs font-semibold uppercase tracking-wide

Color Tokens

Raw values live in :root (light) / .dark (dark) in src/app/globals.css and are mapped to Tailwind utilities via @theme inline. Components must consume the mapped utility, never the raw hex or a Tailwind palette shade.

Utility Light Dark Usage
bg-background / text-foreground #ffffff / #1a1a1a #0e1512 / #f2f4f3 Page canvas
bg-card / text-card-foreground #ffffff #131a16 Cards, table, kanban cards, header bar
bg-muted / text-muted-foreground #f9f9f9 / #71717a #171f1b / #9aa39e Subtle backgrounds, secondary text, kanban columns
bg-primary / text-primary-foreground #1A463C #3FA88C Primary actions, sidebar, active nav state
bg-accent #DEF168 #DEF168 Rare highlight accent (lime)
border-border / border-input #e5e7eb #26302b All hairline borders
ring-ring #1A463C #3FA88C Focus rings
bg-destructive #ef4444 #f87171 Destructive actions

Sidebar exception: the sidebar stays brand green bg-[#1A463C] in both themes (it is not a themed surface — it's the constant brand anchor).

Status/semantic colors (lead stages, badges) use Tailwind's default palette (blue/purple/amber/orange/emerald/red) directly, each paired with an explicit dark: variant for legibility on dark surfaces — see StatusBadge.

Elevation & Radius

  • --radius: 0.5rem (base) is wired into @theme as --radius-lg (= --radius) and --radius-xl (= --radius + 0.25rem), so rounded-lg / rounded-xl utilities resolve consistently off one token.
  • --shadow-card: 0 4px 20px rgba(0,0,0,0.01)shadow-card utility. Default resting elevation for cards, table containers, kanban cards.
  • --shadow-card-hover: 0 8px 30px rgba(0,0,0,0.02)shadow-card-hover utility. Applied on hover for interactive cards.

Layout Conventions

  • Table container: bg-card rounded-xl border border-border shadow-card overflow-hidden.
  • Table header row: bg-muted/50 border-b border-border text-[11px] uppercase tracking-wider text-muted-foreground.
  • Table body rows: py-4 px-6, divide-y divide-border, hover:bg-muted/40 transition-colors.
  • Kanban column: bg-muted/60 rounded-xl border border-border p-4.
  • Kanban card: bg-card rounded-lg border border-border shadow-sm hover:border-primary/30 hover:shadow-card-hover transition-all.
  • Page header: title text-2xl font-semibold tracking-tight text-foreground, subtitle text-xs text-muted-foreground mt-1, primary action button bg-primary text-primary-foreground hover:bg-primary/90.

Color/Class Migration Map (dual-theme principle)

When restyling any surface, replace hardcoded classes with token utilities:

Old (hardcoded) New (token)
bg-white bg-card
bg-slate-50 / bg-gray-50 bg-muted
border-slate-100 / border-slate-200 border-border
text-slate-900 / text-[#1a1a1a] text-foreground
text-slate-500 / text-slate-400 / text-[#71717a] text-muted-foreground
bg-brand-dark / text-[#1A463C] (as action color) bg-primary / text-primary

Motion

  • Sidebar collapse: transition-[width] duration-350 ease-[cubic-bezier(0.4,0,0.2,1)] on the <aside>; label/logo text use whitespace-nowrap transition-opacity duration-200 and fade to opacity-0 immediately on collapse, but only fade back in ~150ms after the width transition starts on expand (avoids text reflow/wrap during the animation).
  • Hover states: transition-colors duration-150duration-200 on rows, buttons, nav links.
  • Kanban drag: dragged card renders in a DragOverlay with rotate-1 shadow-xl; drop target column highlights via border-primary bg-primary/5.

UX Rules

  1. Numeric/tabular data is always monospace and right-aligned in tables.
  2. Every interactive control has a visible focus state (focus:ring-1 focus:ring-ring focus:border-primary pattern) for keyboard accessibility.
  3. Status is always communicated redundantly — color + text label — never color alone.
  4. Destructive actions use destructive tokens, never ad-hoc red.
  5. Empty states are muted, centered, and brief ("Nessun lead trovato").
  6. Sidebar collapse state persists across page loads via localStorage (iamcavalli:sidebar) and is restored before paint where possible to avoid layout jump.

Component Inventory

Primitives extracted from the Lead Pipeline redesign, intended for reuse across all future admin pages.

Component Path Purpose
AdminShell src/components/admin/AdminShell.tsx Client shell holding sidebar-collapsed state (persisted to localStorage), renders sidebar + top header + <main>. Wraps all /admin/* pages.
AdminSidebar src/components/admin/AdminSidebar.tsx Collapsible brand sidebar (w-64w-20), controlled by AdminShell. Nav items, theme toggle, sign-out.
StatusBadge src/components/ui/StatusBadge.tsx Rounded-full status pill with a color map per lead stage, each with a dark: variant. Replaces the old scattered STAGE_COLOR maps.
SearchInput src/components/ui/SearchInput.tsx Search-icon input, rounded-lg border-border focus:ring-primary.
SegmentedToggle src/components/ui/SegmentedToggle.tsx Generic Lista/Kanban-style segmented control (bg-muted p-1 rounded-lg, active option bg-card shadow-sm).
PageHeader src/components/admin/PageHeader.tsx Page title (text-2xl tracking-tight) + subtitle + action slot, token-based.
LeadTable src/components/admin/leads/LeadTable.tsx Luxury table restyle: bg-card rounded-xl shadow-card, uppercase muted headers, hover:bg-muted/40 rows, uses StatusBadge.
LeadsKanbanBoard src/components/admin/leads/LeadsKanbanBoard.tsx Kanban restyle: bg-muted/60 columns with count pills, bg-card cards with hover:border-primary/30. Uses @dnd-kit, keeps all 6 lead stages.
LeadsViewToggle src/components/admin/leads/LeadsViewToggle.tsx Wires SearchInput + SegmentedToggle together with the table/kanban views.
MilestoneStepper src/components/client/MilestoneStepper.tsx Client-portal horizontal milestone stepper: one node per real phase, connecting line filled by global_progress_pct. Done = filled bg-primary ✓, active = border-primary outline, upcoming = bg-muted.

Client Portal restyle notes

The /client/[token] portal was migrated to the same "Quiet Luxury" tokens (applied 2026-07-10). Key conventions specific to the client-facing surface:

  • Header: bg-card sticky bar, three zones — iamcavalli | Client Portal (left), brand name centered, "Area Riservata Protetta" emerald pill (right).
  • Status pills (phases, payments) use soft tints, not solid fills: emerald-50/700 (done/saldato), amber-50/700 (in corso/da saldare), bg-muted/text-muted-foreground (upcoming) — each with a dark: variant.
  • Phase cards (PhaseCard): rounded-xl border-border-light bg-card shadow-card, progress bar colored per status (emerald/amber/bg-border).
  • Sidebar cards (OffersSection, PaymentStatus, DocumentsSection, NotesSection, TranscriptsSection) all follow rounded-xl border-border-light bg-card shadow-card.
  • ClientDashboard takes an embedded prop: when rendered inside the multi-project tabs wrapper (page.tsx), it drops its own header/footer/canvas so the page-level chrome isn't duplicated.