feat: Quiet Luxury design system + Pipeline (ex-Lead) redesign
- Design system foundations: Plus Jakarta Sans font, shadow-card/radius tokens in @theme, design-reference/DESIGN-SYSTEM.md with component inventory - Collapsible admin shell (AdminShell): smooth w-64<->w-20 sidebar, new top header with "Admin" + avatar placeholder, localStorage-persisted state - Rename route /admin/leads -> /admin/pipeline (redirect stubs preserved), nav label Lead -> Pipeline; DB table unchanged - Reusable primitives: StatusBadge, SearchInput, SegmentedToggle (dual-theme) - Luxury restyle of lead table, kanban (6 stages + @dnd-kit intact), PageHeader - Tokenize editable-cell / option-multi-select for dark-mode legibility Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
# 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-200`–`duration-350`,
|
||||
`ease-[cubic-bezier(0.4,0,0.2,1)]` for the sidebar specifically. No
|
||||
decorative animation.
|
||||
|
||||
## Typography
|
||||
|
||||
- **Sans**: Plus Jakarta Sans (weights 300–700), 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-150`–`duration-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-64` ↔ `w-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. |
|
||||
Reference in New Issue
Block a user