Philosophy
Design Philosophy
The principles that guide every design and engineering decision at Roam.
Clean, open, modern
Mobile first
Move fast without sacrificing quality
Neutral-first interface
Stack
Tech Stack
The technologies that power curbo-web. Know the stack before you ship.
| Layer | Technology |
|---|---|
| Framework | Next.js 15 (pages/ router) |
| Language | JavaScript / TypeScript (mixed) |
| Styling | Tailwind CSS v3 |
| Component library | shadcn/ui (primary default) |
| Primitives | Radix UI (via shadcn) |
| Legacy UI | MUI v5 — existing only, do not use for new work |
| CMS | Builder.io |
| State | Recoil |
| Forms | Formik |
| Data fetching | SWR + Axios |
| Auth | NextAuth.js |
| Testing | Jest + RTL (unit), Playwright (e2e) |
| Deployment | Netlify |
Components
Component Approach
How we choose, build, and organise components. shadcn/ui is the default — custom components are the exception.
Decision Flow
1. Use shadcn/ui
2. Extend shadcn
3. Build custom
Directory Structure
components/ ├── ui/ # shadcn/ui — do not modify unless necessary ├── Common/ # Shared utility components ├── Layouts/ # Page layout wrappers ├── Navbar/ # Site navigation ├── Footer/ # Site footer ├── CarCards/ # Vehicle listing cards ├── Checkout/ # Checkout flow components ├── Dashboard/ # Customer dashboard ├── Filters/ # Browse/filter UI ├── FormInput/ # Form field components └── ... # Other feature-specific components
The ui/ directory is managed by shadcn — avoid editing these files directly. Domain-specific components live in their own directories (CarCards, Checkout, etc.).
Styling
Styling Rules
The order of preference for styling and the hard rules that apply to all code.
Order of Preference
| # | Method | When to Use |
|---|---|---|
| 1 | shadcn/ui component | Always check here first |
| 2 | Tailwind utility classes | Layout, spacing, style overrides on shadcn components |
| 3 | CSS Modules | Only when Tailwind cannot express the style |
| 4 | Global CSS (globals.css) | Base resets and third-party library overrides only |
Hard Rules
No inline styles
Never use style={{}} props. All styles go through Tailwind or CSS Modules.
No raw hex values in JSX
Always use Tailwind tokens or CSS variables. Never write color: #F93771 in component code.
No new component libraries
Do not install a new component library without discussion. Use shadcn/ui first.
No new MUI components
MUI/Material UI is legacy. All new work uses shadcn + Tailwind. Existing MUI components may remain until migrated.
Correct Pattern
// Good — Tailwind tokens only
<Button className="bg-primary text-primary-foreground">
Subscribe
</Button>
// Bad — inline style with raw hex
<button style={{ backgroundColor: '#0A0A0A', color: '#fff' }}>
Subscribe
</button>
// Bad — new library import
import { Button } from '@chakra-ui/react';Colour
Colour in UI
How colour is applied in the interface. The UI is neutral-first — brand colours are accents, not foundations.
Primary CTA: Black
Use black (zinc-950 / #0A0A0A) as the primary CTA background. A neutral black CTA creates the clearest action hierarchy, works on any background, and avoids colour conflicts with brand accents or destructive states.
Fuchsia: Accent Only
Allowed
- Icon tints and decorative icon colour
- Small UI accents (underlines, indicators)
- Labels, badges, and promotional pills
- Brand moments within components
Never
- Buttons (any variant) — too close to destructive red
- Links — must use neutral colours
- Large component fills — overwhelming
Links
Links use neutral colours, not brand colours. They should feel part of the typographic system, not compete with accents.
Inline text links
text-foreground underline or text-muted-foreground underlineNavigation links
text-foreground or text-muted-foreground, no underline (hover underline OK)Functional / Utility Colours
For non-brand UI states, use Tailwind’s built-in palette:
| State | Tailwind Colours |
|---|---|
| Destructive / error | red-500, red-600, red-700 |
| Success | green-500, green-600 |
| Warning | amber-400, amber-500 |
| Info | blue-500, blue-600 |
| Muted text | slate-500, muted-foreground |
| Neutral labels | slate-100–300 (light), slate-700–800 (dark) |
Theming
Dark Mode
Dark mode is supported and must be considered for all new components.
How It Works
CSS variable system
Class-based toggle
dark class on <html> switches modes.Guidelines
- Do not hardcode light-mode colours. Always use shadcn tokens or Tailwind’s
dark:variant. - Light mode: White/near-white backgrounds, dark blue and fuchsia accents.
- Dark mode: Deep dark backgrounds (near #13013F), fuchsia and lighter purple as accents, white/light text.
- Test both modes before shipping every new component.
- shadcn components handle dark mode automatically when CSS variables are set correctly.
Pattern
// Good — uses shadcn token, adapts automatically <div className="bg-background text-foreground"> // Good — explicit dark variant <div className="bg-white dark:bg-zinc-900"> // Bad — hardcoded light colour, breaks in dark mode <div className="bg-white text-black">
Iconography
Icons
Consistent iconography across the site. One source, one style.
Sources
Primary: shadcn/ui defaults
Extended: shadcn Icons Library
shadcn.io/icons.Rules
- Consistency is mandatory — do not mix icon styles (line vs filled) or icons from incompatible libraries.
- Size with Tailwind:
w-4 h-4(16px),w-5 h-5(20px),w-6 h-6(24px). - Use currentColor so icons inherit text colour and adapt to dark mode automatically.
- Brand accent icons can use fuchsia or dark blue explicitly where appropriate.
- Do not use Heroicons or Lucide directly for new work unless the icon truly doesn’t exist in the shadcn set.
Sizing Reference
| Class | Size | Use Case |
|---|---|---|
w-4 h-4 | 16px | Inline with text, buttons, form fields |
w-5 h-5 | 20px | Navigation items, list icons |
w-6 h-6 | 24px | Standalone icons, feature highlights |