379 lines
11 KiB
Markdown
379 lines
11 KiB
Markdown
# xBid Design System & Style Guide
|
|
|
|
A reference for the visual language, component patterns, and design principles used in xBid so that future apps can maintain the same look and feel.
|
|
|
|
---
|
|
|
|
## 1. Core Principles
|
|
|
|
- **Dark-first**: The app is permanently dark mode (`className="dark"` on `<html>`). There is no light/dark toggle.
|
|
- **Minimal chrome**: Surfaces are dark zinc grays with amber as the single accent colour. No gradients, no heavy decoration.
|
|
- **Dense but readable**: Data-heavy UI uses compact sizing (`text-xs`, `text-sm`) with generous whitespace in page-level padding.
|
|
- **Amber accent, zinc foundation**: Brand colour is amber (`#f59e0b` / `amber-500`). All structural surfaces use the zinc palette.
|
|
|
|
---
|
|
|
|
## 2. Typography
|
|
|
|
### Font Family
|
|
- **Primary (body & UI)**: **Inter** — loaded via `next/font/google`, subset `latin`.
|
|
- **Mono**: Geist Mono (via `--font-geist-mono` CSS variable from shadcn).
|
|
- Applied globally: `font-sans` on `<html>`.
|
|
|
|
### Scale (Tailwind defaults, used in practice)
|
|
|
|
| Usage | Class(es) | Notes |
|
|
|---|---|---|
|
|
| Page title | `text-3xl font-bold text-white` | e.g. Dashboard h1 |
|
|
| Section title | `text-xl font-semibold text-white` | e.g. "Recent Jobs" h2 |
|
|
| Detail page title | `text-xl font-bold text-white` | Truncated with `truncate` |
|
|
| Card title | `text-base font-medium` (via `CardTitle`) | |
|
|
| Body / default | `text-sm` | |
|
|
| Captions / meta | `text-sm text-zinc-400` or `text-zinc-500` | |
|
|
| Table headers | `text-xs uppercase tracking-wider text-zinc-500` | |
|
|
| Table cells | `text-xs` or `text-sm` | |
|
|
| Error messages | `text-xs text-red-400` | Form validation |
|
|
| Stat value | `text-3xl font-bold text-white` | Dashboard stat cards |
|
|
|
|
---
|
|
|
|
## 3. Colour Palette
|
|
|
|
The app uses **Tailwind CSS v4 with OKLCH CSS variables**. All semantic tokens map to zinc grays in dark mode.
|
|
|
|
### Brand / Accent
|
|
|
|
| Token | Tailwind class | Hex approx | Usage |
|
|
|---|---|---|---|
|
|
| Brand amber | `amber-500` | `#f59e0b` | Logo bg, icon colour |
|
|
| Active nav highlight | `amber-500/10` bg + `amber-400` text | — | Active nav item |
|
|
| Amber text | `text-amber-400` | `#fbbf24` | Active nav, stat icons |
|
|
| Amber text bold | `text-amber-500` | `#f59e0b` | Logo "Bid" wordmark |
|
|
|
|
### Surface / Layout
|
|
|
|
| Purpose | Tailwind class | Notes |
|
|
|---|---|---|
|
|
| Page background | `bg-zinc-950` | Top bar, outermost bg |
|
|
| Sidebar & card bg | `bg-zinc-900` | Primary surface |
|
|
| Hover / secondary surface | `bg-zinc-800` | Hover states, secondary card |
|
|
| Input fields | `bg-zinc-900 border-zinc-700` | Form inputs override |
|
|
| Borders | `border-zinc-800` | Dividers, card outlines |
|
|
| Input borders | `border-zinc-700` | Slightly lighter than surface borders |
|
|
|
|
### Text
|
|
|
|
| Purpose | Tailwind class | Hex approx |
|
|
|---|---|---|
|
|
| Primary / headings | `text-white` | `#ffffff` |
|
|
| Secondary / labels | `text-zinc-400` | `#a1a1aa` |
|
|
| Tertiary / placeholders | `text-zinc-500` | `#71717a` |
|
|
| Disabled / subtle | `text-zinc-600` | `#52525b` |
|
|
| Bright secondary | `text-zinc-300` | `#d4d4d8` |
|
|
|
|
### Semantic / Status Badges
|
|
|
|
These appear on `Job` and `Invoice` status badges. Pattern: coloured bg at low opacity + matching light text.
|
|
|
|
| Status | Classes |
|
|
|---|---|
|
|
| DRAFT | `bg-zinc-700 text-zinc-300` |
|
|
| SENT | `bg-blue-900/60 text-blue-300` |
|
|
| APPROVED / PAID | `bg-green-900/60 text-green-300` |
|
|
| REJECTED / OVERDUE | `bg-red-900/60 text-red-300` |
|
|
| ARCHIVED | `bg-yellow-900/60 text-yellow-300` |
|
|
| VIEWED | `bg-purple-900/60 text-purple-300` |
|
|
| PARTIALLY_PAID | `bg-orange-900/60 text-orange-300` |
|
|
| CANCELLED | `bg-zinc-800 text-zinc-500` |
|
|
|
|
### Icon Colours (Dashboard Stats)
|
|
|
|
| Metric | Icon colour |
|
|
|---|---|
|
|
| Jobs / Total | `text-amber-400` |
|
|
| Clients | `text-blue-400` |
|
|
| Drafts | `text-zinc-400` |
|
|
| Sent / Pending | `text-yellow-400` |
|
|
| Approved | `text-green-400` |
|
|
|
|
---
|
|
|
|
## 4. Spacing & Layout
|
|
|
|
### App Shell
|
|
|
|
```
|
|
┌─────────────────────────────────────────┐
|
|
│ Sidebar (w-60 / w-16 collapsed) │ Main content (flex-1 overflow-y-auto)
|
|
│ bg-zinc-900 │ p-8 on page content
|
|
│ border-r border-zinc-800 │
|
|
└─────────────────────────────────────────┘
|
|
```
|
|
|
|
- Root: `flex h-full overflow-hidden`
|
|
- Sidebar: `flex flex-col bg-zinc-900 border-r border-zinc-800 transition-all duration-200`
|
|
- Main: `flex-1 overflow-y-auto`
|
|
- Page padding: `p-8`
|
|
|
|
### Page Header pattern
|
|
|
|
```tsx
|
|
<div className="mb-8">
|
|
<h1 className="text-3xl font-bold text-white">Page Title</h1>
|
|
<p className="text-zinc-400 mt-1">Subtitle / description.</p>
|
|
</div>
|
|
```
|
|
|
|
### Detail Page Top Bar
|
|
|
|
```tsx
|
|
<div className="border-b border-zinc-800 px-8 py-4 flex items-center gap-4 bg-zinc-950">
|
|
{/* back arrow + title + meta + actions */}
|
|
</div>
|
|
```
|
|
|
|
### Grid layouts
|
|
|
|
| Pattern | Class |
|
|
|---|---|
|
|
| Stat cards | `grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-5` |
|
|
| Form fields | `grid grid-cols-1 sm:grid-cols-2 gap-5` |
|
|
| Form field spacing | `space-y-1.5` (label + input) |
|
|
| Form section spacing | `space-y-6` |
|
|
|
|
---
|
|
|
|
## 5. Border Radius
|
|
|
|
Defined by CSS variable `--radius: 0.625rem` (10px).
|
|
|
|
| Token | Size | Tailwind class |
|
|
|---|---|---|
|
|
| sm | ~6px | `rounded-sm` / `rounded` |
|
|
| md | ~8px | `rounded-md` |
|
|
| lg | 10px (base) | `rounded-lg` |
|
|
| xl | ~14px | `rounded-xl` |
|
|
| 4xl (badges) | ~26px | `rounded-4xl` |
|
|
|
|
**In practice**: `rounded-lg` is the default for buttons, inputs, cards, nav items. Cards use `rounded-xl`. Badges use `rounded-4xl` (pill shape).
|
|
|
|
---
|
|
|
|
## 6. Component Patterns
|
|
|
|
### Sidebar Navigation
|
|
|
|
- Container: `bg-zinc-900 border-r border-zinc-800`
|
|
- Logo area: `px-4 py-5 border-b border-zinc-800`
|
|
- Icon: `w-8 h-8 rounded-lg bg-amber-500 flex items-center justify-center` with black icon inside
|
|
- Wordmark: `font-bold text-lg tracking-tight text-white` — "x" white, "Bid" amber
|
|
- Nav item inactive: `text-zinc-400 hover:text-white hover:bg-zinc-800 px-3 py-2.5 rounded-lg text-sm font-medium`
|
|
- Nav item active: `bg-amber-500/10 text-amber-400`
|
|
- Icon size: `h-4 w-4`
|
|
|
|
### Cards
|
|
|
|
```tsx
|
|
<Card className="bg-zinc-900 border-zinc-800">
|
|
<CardHeader>
|
|
<CardTitle>…</CardTitle>
|
|
<CardDescription>…</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>…</CardContent>
|
|
</Card>
|
|
```
|
|
|
|
- Card has `ring-1 ring-foreground/10` and `rounded-xl` by default via shadcn base-nova.
|
|
- Override bg/border on dark surfaces: `bg-zinc-900 border-zinc-800`.
|
|
|
|
### Buttons
|
|
|
|
Variants from shadcn `buttonVariants`:
|
|
|
|
| Variant | Appearance |
|
|
|---|---|
|
|
| `default` | Dark bg (`primary`), light text — effectively `bg-zinc-900 text-white` in dark mode |
|
|
| `outline` | Border, transparent bg, hover muted |
|
|
| `secondary` | Muted bg |
|
|
| `ghost` | No border, hover muted |
|
|
| `destructive` | Red tinted bg, red text |
|
|
| `link` | Underline on hover |
|
|
|
|
Sizes: `xs`, `sm`, `default` (h-8), `lg`, `icon`, `icon-xs`, `icon-sm`, `icon-lg`.
|
|
|
|
### Badges / Status Pills
|
|
|
|
```tsx
|
|
<Badge className="bg-green-900/60 text-green-300">Approved</Badge>
|
|
```
|
|
|
|
- Custom status colours applied via `className` override (see §3 Semantic/Status Badges).
|
|
- Default shape: pill (`rounded-4xl`), `h-5`, `text-xs`.
|
|
|
|
### Form Fields
|
|
|
|
```tsx
|
|
<div className="space-y-1.5">
|
|
<Label>Field Label *</Label>
|
|
<Input className="bg-zinc-900 border-zinc-700" placeholder="…" />
|
|
{error && <p className="text-red-400 text-xs">{error.message}</p>}
|
|
</div>
|
|
```
|
|
|
|
- Input base: `h-8 rounded-lg border border-input bg-transparent` — always override with `bg-zinc-900 border-zinc-700` for dark surface visibility.
|
|
- Textarea, Select follow the same pattern.
|
|
|
|
### Tables
|
|
|
|
Line-item tables are custom `<table>` elements styled with:
|
|
- `text-xs w-full`
|
|
- Header row: `border-b border-zinc-800 text-zinc-500 uppercase tracking-wider`
|
|
- Cells: `px-4 py-2 text-left`
|
|
- Alternating/hover rows: use `hover:bg-zinc-800/50`
|
|
|
|
shadcn `<Table>` component uses `text-sm` and standard `border-b` between rows.
|
|
|
|
### Tabs
|
|
|
|
```tsx
|
|
<Tabs defaultValue="…">
|
|
<TabsList>
|
|
<TabsTrigger value="…">Label</TabsTrigger>
|
|
</TabsList>
|
|
<TabsContent value="…">…</TabsContent>
|
|
</Tabs>
|
|
```
|
|
|
|
- `TabsList` bg: `bg-muted` (zinc-800 in dark), `rounded-lg`.
|
|
- `TabsTrigger` active: white text, slight bg lift.
|
|
|
|
### Dialogs / Sheets
|
|
|
|
Standard shadcn `Dialog` / `Sheet`. Content uses `bg-zinc-900` / card surface. Destructive actions use red confirm buttons.
|
|
|
|
### Toasts / Notifications
|
|
|
|
- Library: **Sonner** (`<Toaster richColors position="top-right" />`)
|
|
- `toast.success(…)` — green
|
|
- `toast.error(…)` — red
|
|
- Always called after async actions complete.
|
|
|
|
### Dropdown Menus
|
|
|
|
Standard shadcn `DropdownMenu`. Destructive actions get `text-destructive` styling on the `DropdownMenuItem`.
|
|
|
|
### Collapsible / Expandable Rows
|
|
|
|
Shot and asset cards use a chevron toggle (`ChevronDown` / `ChevronRight`) to expand inline line-item tables. Pattern:
|
|
|
|
```tsx
|
|
<button onClick={() => setExpanded(!expanded)}>
|
|
{expanded ? <ChevronDown /> : <ChevronRight />}
|
|
</button>
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Icons
|
|
|
|
Library: **Lucide React** (`lucide-react`)
|
|
|
|
Standard icon size: `h-4 w-4` (16px). Larger decorative: `h-5 w-5`.
|
|
|
|
Common icons used:
|
|
|
|
| Purpose | Icon |
|
|
|---|---|
|
|
| Dashboard | `LayoutDashboard` |
|
|
| Clients | `Users` |
|
|
| Jobs / Quotes | `Briefcase` |
|
|
| Invoices | `FileText` |
|
|
| Rate Cards | `CreditCard` |
|
|
| Settings | `Settings` |
|
|
| Back | `ArrowLeft` |
|
|
| Download PDF | `Download` |
|
|
| Duplicate | `Copy` |
|
|
| Delete | `Trash2` |
|
|
| Edit | `Pencil` |
|
|
| Add | `Plus` |
|
|
| More options | `MoreHorizontal` |
|
|
| Search | `Search` |
|
|
| Loading | `Loader2` (with `animate-spin`) |
|
|
| Drag handle | `GripVertical` |
|
|
| App logo | `Film` |
|
|
| Send | `Send` |
|
|
| Approve / Mark paid | `Check` / `CheckCircle` |
|
|
|
|
---
|
|
|
|
## 8. Motion & Transitions
|
|
|
|
- Sidebar collapse: `transition-all duration-200`
|
|
- Nav item colour: `transition-colors`
|
|
- Buttons: `transition-all`
|
|
- Drag-and-drop (DnD Kit): opacity `0.5` while dragging.
|
|
- Animation library: `tw-animate-css` (imported in globals.css).
|
|
|
|
---
|
|
|
|
## 9. Tech Stack
|
|
|
|
| Layer | Technology |
|
|
|---|---|
|
|
| Framework | Next.js 15 (App Router, React Server Components) |
|
|
| Styling | Tailwind CSS v4 |
|
|
| Component system | shadcn/ui (`base-nova` style) + `@base-ui/react` primitives |
|
|
| Icon library | Lucide React |
|
|
| Font | Inter (Google Fonts via `next/font`) |
|
|
| Notifications | Sonner |
|
|
| Forms | React Hook Form + Zod |
|
|
| Drag & Drop | `@dnd-kit/core` + `@dnd-kit/sortable` |
|
|
| ORM | Prisma |
|
|
| DB | PostgreSQL |
|
|
| Color space | OKLCH (CSS variables) |
|
|
|
|
---
|
|
|
|
## 10. Quick Reference — Recurring Class Combinations
|
|
|
|
```
|
|
# Page wrapper
|
|
p-8
|
|
|
|
# Page title
|
|
text-3xl font-bold text-white
|
|
|
|
# Page subtitle
|
|
text-zinc-400 mt-1
|
|
|
|
# Section heading
|
|
text-xl font-semibold text-white mb-4
|
|
|
|
# Top bar (detail pages)
|
|
border-b border-zinc-800 px-8 py-4 flex items-center gap-4 bg-zinc-950
|
|
|
|
# Card (dark surface)
|
|
bg-zinc-900 border-zinc-800
|
|
|
|
# Card text hierarchy
|
|
text-white (value) | text-zinc-400 (label) | text-zinc-500 (meta)
|
|
|
|
# Form input (dark)
|
|
bg-zinc-900 border-zinc-700
|
|
|
|
# Form error
|
|
text-red-400 text-xs
|
|
|
|
# Table header
|
|
border-b border-zinc-800 text-zinc-500 uppercase tracking-wider text-xs
|
|
|
|
# Empty state text
|
|
text-center text-sm text-zinc-500
|
|
|
|
# Active nav
|
|
bg-amber-500/10 text-amber-400
|
|
|
|
# Inactive nav
|
|
text-zinc-400 hover:text-white hover:bg-zinc-800
|
|
```
|