# Flashquotes Design System

A design system for **Flashquotes** — an event service booking and quote management platform built for beverage caterers and event service vendors. Multi-tenant SaaS with quote templates, booking flows, invoicing (Stripe Connect), forms, workflows, and QuickBooks integration.

> **Source repo:** [Flashquotes/flashquotes](https://github.com/Flashquotes/flashquotes) (private). Stack: Next.js 14/15, T3, tRPC, Prisma, NextAuth, Tailwind + **shadcn/ui**, Radix primitives, lucide-react icons.

## Products represented

1. **Operator app** (authenticated dashboard) — internal CRM for caterers: leads, bookings, events, calendar, quotes, invoices, payments, contracts, workflows, forms, services, staff, resources, reports. Uses sidebar layout with breadcrumbs.
2. **Public booking flow** (customer-facing) — the page a lead lands on when sent a quote: themed by the operator's brand color and logo, multi-step (Quote → Form → Contract → Payment).

## Sources used

- `Flashquotes/flashquotes` — primary repo. Key files read:
  - `src/styles/globals.css` — sidebar/muted/border CSS variables, signature block styles
  - `tailwind.config.cjs` — design tokens, container, animations
  - `src/components/shadcn/ui/*` — button, badge, input, card, sidebar, dot-pill-select, etc.
  - `src/components/layouts/AppLayout.tsx`, `src/components/common/AppSidebar.tsx`
  - `src/components/quotes/common/QuotePageHeader.tsx`
  - `src/app/(public)/quotes/book/[quoteId]/_components/BookingHeader.tsx`
  - `src/app/(authenticated)/home/_components/EventsTodaySection.tsx`
  - `public/logo.png`, `public/flashquotes-lockup.svg`, `public/flashquotes-lockup-white.svg`, `public/DepartIcon.png`, `public/ReturnIcon.png`, `public/favicon.ico`

## Index

| File / Folder | What's in it |
|---|---|
| `colors_and_type.css` | All CSS variables — brand, neutrals, semantic colors, status dots, radii, shadows, spacing, type scale, semantic type classes (`.fq-h1`, `.fq-body`, etc.) |
| `assets/` | Logo (mark + lockup, dark + white), DepartIcon / ReturnIcon, favicon |
| `preview/` | Cards rendered in the Design System tab (one HTML per concept) |
| `ui_kits/operator-app/` | UI kit for the authenticated operator dashboard |
| `ui_kits/public-booking/` | UI kit for the public booking flow (brand-themed) |
| `SKILL.md` | Cross-compatible skill manifest for Claude Code |

---

## CONTENT FUNDAMENTALS

**Voice:** functional, professional, neutral. SaaS product copy — direct, terse, never cute. No exclamation points except in success toasts. Reads like Linear or Notion, not Mailchimp.

**Person:** mostly imperative ("Share via Email", "Mark as sent", "Copy Link") and noun-headers ("Events today", "Open Inquiries", "Link expiry"). User-facing app rarely uses "you"; the public booking flow does ("Get started", "Finish").

**Casing:** Title Case for nav items and primary buttons (`Bookings`, `Share via Email`, `Mark as sent`). Sentence case for descriptions, helper text, and tooltips ("This quote has been sent", "The date this link will expire and the quote can no longer be booked"). All-caps avoided.

**Punctuation:** No trailing periods on button labels, badges, or short headings. Periods on full sentences in tooltips and descriptions. No emoji. No em-dashes for stylistic emphasis.

**Numbers & time:** Hours formatted "h:mm a" (e.g. `11:00 AM`). Dates "MMM d, yyyy" or "EEE, MMM d, yyyy". Relative day labels: "Today", "Tomorrow", "Yesterday", then `EEE, MMM d`. Currency in cents internally, displayed with locale formatting.

**Status language:** Quote lifecycle = `Draft / Sent / Booked / Expired`. Pluralization handled ("1 hour of service" vs "3 hours of service"). Empty states are matter-of-fact: "No events today", "No items found." Loading states use skeletons over spinner text.

**Examples lifted from app:**
- Page title: `Quote for Sarah Chen`
- Button labels: `Share`, `View Quote`, `Mark as sent`, `Expire now`, `Copy Link`, `Download PDF`
- Section H2: `Events today`, `Open Inquiries`, `Tasks`, `Recent Bookings`
- Empty state: `No events on May 4`
- Tooltip: `This quote has not been sent yet`
- Toast: `Expiration date updated` / `Failed to update expiration date`
- Confirm: `Sign out`, `Email support`, `Privacy policy`

---

## VISUAL FOUNDATIONS

### Palette
**Brand:** Indigo-600 `#4F46E5` (the bolt logo color). Used sparingly — primary CTA button, primary badge, focus rings, active sidebar item text, "booked" status dot, link expiry highlight, signature-block accents. Never as a background fill on large surfaces.

**Neutrals are the system.** Slate scale carries 90% of the UI. Page background is `bg-muted/30` (slate-50 at low opacity); cards are white on top with a 1px slate-200 border; text is slate-900 on white, slate-600/500 for secondary. The dashboard reads almost grayscale — color is reserved for status meaning.

**Semantic colors (badge variants):** soft pastel bg + saturated 700-tone text:
- `success` emerald-50 / emerald-700
- `warning` amber-50 / amber-700
- `danger` rose-50 / rose-700
- `primary` indigo-50 / indigo-700
- `blue` blue-50 / blue-700
- `orange` orange-50 / orange-700
- `neutral` slate-50 / slate-700

**Status dots** (used in `Badge variant="dot"`): bg-white, slate-200 border, slate-600 text, with a 6px colored dot — booked indigo-500, sent emerald-500, draft amber-500, expired slate-400.

### Type
**Söhne** (Klim Type Foundry) is the canonical brand font — eight weights (`200–900`), upright + italic, self-hosted as static OTF files in `fonts/`. Klim weight names map as: Extraleicht 200 · Leicht 300 · **Buch 400** (book/regular) · Kräftig 500 · Dreiviertelfett 600 · Halbfett 700 · Fett 800 · Extrafett 900. There is no Thin (100) cut. Inter is retained in the stack as a fallback. Reference via `var(--fq-font-sans)`; the production codebase currently renders with Tailwind's default `font-sans` system stack — wire Söhne via `next/font/local` for production parity.

Scale: 12 (xs) / 14 (sm — body default) / 16 (base — mobile inputs) / 18 (lg — section H2) / 24 (2xl — page H1, CardTitle). Weights: 400 Buch (body) / 500 Kräftig (UI labels) / 600 Dreiviertelfett (headings). Line-height tight on headings, normal on body. Tracking is default with `-0.01em` on large headings (`tracking-tight`).

### Spacing & layout
8px base. Padding inside cards = `p-6` (24px). Sidebar width = `14rem` desktop / `18rem` mobile / `3rem` collapsed icon-only. Container max-width `1400px`, padding `2rem`. Top header bar fixed at `h-12` (48px). Buttons `h-9` (sm) / `h-10` (default) / `h-11` (lg).

### Backgrounds
**Flat surfaces, no gradients, no textures, no illustrations.** The app is dense, white-and-slate, business-tool minimal. The only "decorative" element is the dashed indigo-300 signature block in TipTap. The public booking flow is themed by the operator's brand color (CSS vars `--brand-color`, `--brand-color-hover`, `--brand-text-color`) and uses their uploaded logo at the top — otherwise still white surfaces with slate borders.

### Animation
Sparing. Tailwind defaults: `transition-colors`, `transition-[width,height,padding]` on sidebar (200ms ease-linear), accordion expand/collapse (200ms ease-out), `tailwindcss-animate` for Radix popover/dialog fade-zoom. No bounce, no spring, no scroll-triggered reveals. Loading uses `animate-pulse` skeletons (slate-200 blocks) and `animate-spin` on Loader2.

### Hover & press states
- **Buttons:** primary darkens to indigo-700; default (slate-900) goes to slate-900/90; outline gains slate-50 bg; ghost gains slate-100 bg.
- **Sidebar items:** custom hover `hsl(210 40% 96%)` (slate-100-ish) — set via `!important` in globals.css. Active state uses `bg-sidebar-accent` (slate-50) with `text-sidebar-accent-foreground` (indigo-700) and `font-medium`.
- **Links / list rows:** subtle slate-50 row hover (`hover:bg-slate-50`).
- **Press:** no shrink/scale transforms. Slight color darkening only.
- **Disabled:** `opacity-50` and `pointer-events-none`.

### Borders & dividers
Default border is slate-200 (`--border` CSS var = `214.3 31.8% 91.4%`). Strong outline = slate-300 (`ring-1 ring-inset ring-slate-300` on outline button). Dividers between list rows use `divide-y divide-slate-100` (slate-100, fainter than card borders). Dashed borders (indigo-300, 2px) reserved for signature blocks and "+" pill placeholders.

### Shadows
Light. `shadow-sm` (single 1px-blur shadow) on cards and inputs. `shadow` (sm + slight depth) on dropdowns and popovers via Radix defaults. Sidebar floating variant gets `shadow`. **No inner shadows.** No "elevation" stack — the system is flat with hairline borders doing most of the separation.

### Corners
Consistent rounding — `rounded-md` (6px) on buttons/inputs/menu items; `rounded-lg` (8px) on cards, signature blocks, skeletons, dialog content; `rounded-full` on badges, avatar, status dots, pagination. Avatars `rounded-lg` (not full-circle) — distinctive Flashquotes choice.

### Cards
White background, 1px slate-200 border, `rounded-lg`, `shadow-sm`, `p-6` body padding. CardTitle is 24px semibold; CardDescription is 14px slate-500. Dashed-border empty-state cards use slate-200 dashed.

### Transparency & blur
Used minimally. Header in app uses `bg-muted/25` (slate-50 at 25% opacity); main canvas uses `bg-muted/30`. No backdrop-blur. No glassmorphism.

### Imagery
The product itself ships **no stock imagery, no illustrations, no decorative photography.** Operator-uploaded logos appear in the public booking header. Avatars use AvatarFallback (initials in a slate-100 pill) when no image. Color vibe = neutral, screenshot-y, software-utility.

### Layout rules
Sidebar fixed left, content scrolls. Top app bar sticky inside the SidebarInset (header `h-12`, border-b, muted bg). Public booking flow has its own sticky header (`top-0 z-50 bg-white shadow-sm`). Page sections are stacked with `space-y-6` between major regions; `space-y-3` inside section headers and content.

---

## ICONOGRAPHY

**Single icon system: `lucide-react`** (v0.562.0). Used everywhere in app and booking flow. Stroke-based, 1.5–2px stroke, rounded line caps, 24×24 grid, sized via Tailwind (`h-4 w-4` for inline, `h-5 w-5` for sidebar triggers, `h-3.5 w-3.5` for tiny tooltips, `h-8 w-8` for empty-state placeholders).

Names lifted directly from imports: `Home`, `Inbox`, `Users`, `Bookmark`, `Sparkles`, `CalendarDays`, `Receipt`, `Package`, `Settings`, `HelpCircle`, `ChevronRight`, `LifeBuoy`, `Shield`, `LogOut`, `X`, `BarChart3`, `MessageCircle`, `LayoutTemplate`, `Form`, `Zap`, `UsersRound`, `Calendar`, `AlertCircle`, `Send`, `Clock`, `Hourglass`, `Mail`, `Link2`, `ChevronDown`, `Trash2`, `CheckCircle`, `Check`, `Download`, `Loader2`, `Plus`, `MoreVertical`, `Printer`, `PanelLeft`.

**Sourcing:** lucide is loaded from CDN in this design system (`https://unpkg.com/lucide@latest`) as a no-substitution match for the production set. No SVG hand-rolling, no emoji, no unicode glyphs in the UI.

**Custom raster icons:** two PNG glyphs in `public/` — `DepartIcon.png` and `ReturnIcon.png` — used for service-day depart/return time markers. Copied to `assets/`.

**Logos:**
- `assets/logo.png` (40×40-ish) — bolt mark only, indigo on transparent
- `assets/flashquotes-lockup.svg` — "flashquotes" wordmark + bolt, indigo on transparent
- `assets/flashquotes-lockup-white.svg` — same lockup, white (for dark/colored backgrounds)
- `assets/favicon.ico`

The bolt is the brand symbol — angular, lightning-strike geometry, in solid indigo `#4F46E5`. It's the only "illustration" in the system; everything else is interface.

**Emoji:** not used. Unicode glyphs not used. The `⌘` and `Ctrl` characters appear only inside the `<Kbd>` keyboard-shortcut component — that's it.
