Vilo Platform / Design System

A calm, trustworthy system for hosts & their guests.

Wielo's design system is built for a real booking business — direct, professional, mobile-first. Modern and flat, one emerald palette pulled from the logo. This page is the canonical reference for every token, component and pattern. Copy the Tailwind config from the bottom and you're set.

Primary
#10B981
Deep
#064E3B
Tint
#D1FAE5
Ink
#0A1510
Foundations

Design principles

Trustworthy by default

Hosts share their Wielo URL with guests. Every screen must read as a real, professional business — not a hobby project.

Mobile is the default

Most hosts run their inbox from their phone. Design mobile-first, then progressively enhance for desktop.

Subtract, don't add

No gradients, no decoration, no shadow stacks. If a pixel doesn't earn its place, remove it.

One source of truth

Brand colour means brand colour. Status green means a confirmed booking. Never reuse a token for an unrelated job.

Foundations

Color

Wielo's palette is the emerald pulled directly from the logo. Bright emerald carries the brand and structure; deep emerald is reserved for emphasis — dark surfaces, featured badges, the second-stop of the brand gradient.

Brand palette

brand.primary
#10B981
oklch(73% .17 162)
brand.deep
#064E3B
aka secondary
brand.accent
#D1FAE5
tint surface
brand.dark
#0A1510
hero / footer
brand.light
#F0FDF4
page bg

Brand gradient

The 135° linear gradient from #10B981 → #064E3B is the logo's primary surface. Use it on the logo itself, app icon backgrounds, and one hero element per screen — nowhere else.

Light gradient
linear-gradient(135deg, #10B981, #064E3B)
Dark gradient
linear-gradient(145deg, #030806, #0a1510, #051209)

Booking status

These five tones map 1:1 to booking states from the spec. Never use status colours outside their booking context.

confirmed
#10B981
pending
#F59E0B
cancelled
#EF4444
completed
#6366F1
draft
#94A3B8

Neutrals & surfaces

A slightly emerald-tinted neutral ramp — not pure grey — keeps everything sitting in the same family as the brand greens.

surface
#F0FDF4
raised
#FFFFFF
sunken
#E7FAEE
line
#DCEAE0
mute
#4A7C6A
ink
#052E1F
Foundations

Typography

Two families. Plus Jakarta Sans for display + headings; Inter for everything else. Both load through next/font/google so there's no flash.

Display
Aa
Plus Jakarta Sans
500 · 600 · 700 · 800
For headlines and marketing surfaces. Pair with tight letter-spacing on h1/h2.
UI / Body
Aa
Inter
400 · 500 · 600 · 700
Every UI label, paragraph, form field, table cell, badge. Defaults to font-sans.

Type scale

Token Size / weight Sample
text-3xl · display 30 / 700 Welcome back, Lerato
text-2xl · display 24 / 600 Your bookings
text-xl · display 20 / 600 Listing performance
text-lg 18 / 500 Cape Town Boutique B&B
text-base 16 / 400 Three new booking requests are awaiting your confirmation.
text-sm 14 / 400 Check-in: Fri 12 Jul · 2 nights · 2 guests
text-xs 12 / 500 Booking reference
font-mono JetBrains Mono · 14 VILO-2026-AB1234
Foundations

Spacing & layout

Stay on Tailwind's default 4-pt scale. No custom values. Always prefer flex/grid + gap over per-element margins.

Scale

8
p-2 · gap-2 — tight
12
p-3 · compact
16
p-4 · standard card
24
p-6 · panel
32
p-8 · page (desktop)
48
p-12 · section
64
p-16 · hero

Breakpoints

PrefixWidthWielo context
0Mobile · default · bottom nav
sm:640Large mobile
md:768Tablet · 2-col grids
lg:1024Desktop · sidebar appears
xl:1280Wide · max content 1280px

Container

Page wrapper: max-w-7xl mx-auto px-4 lg:px-8
Reading width for prose: max-w-prose
Dashboard sidebar: 240–256px fixed
Foundations

Radius & shadow

Two radii and two shadows do all the work. Hierarchy comes from surfaces and borders, not depth tricks.

rounded-sm
6px · chips, table highlights
rounded (default)
10px · buttons, inputs
rounded-card
16px · cards, panels, modals
rounded-pill
∞ · badges, avatars, tags

Shadow scale

none
Default for cards at rest. Use a border instead.
shadow-card
0 1px 2px · 0 1px 1px
Subtle resting elevation.
shadow-lift
0 6px 24px · ink-tinted
Hover + popovers + modals.
Foundations

Motion

Three durations, one easing curve. Motion is functional — it signals state, it doesn't perform.

duration-150
150ms

Hovers, focus rings, button colour changes.

duration-200
200ms

Card lift, dropdown open, accordion toggle.

duration-300
300ms

Drawers, sheets, modal enter / leave.

easing
cubic-bezier(0.2, 0.8, 0.2, 1)

Use Tailwind's ease-out for entering elements, ease-in for leaving. Respect prefers-reduced-motion globally.

Foundations

Iconography

lucide-react only. Stroke-based, 1.5px stroke at 16/20/24. One icon per concept — see the canonical mapping below.

Bookings
Inbox
Listings
Reviews
Payments
Refunds
Settings
Subscription
Staff
Calendar
Feature gate
Instant book
Check-in
Check-out
Guest
EFT
Verified
Notifications
h-4 w-4
Inline with text
h-5 w-5
Navigation
h-6 w-6
Feature icon
h-12 w-12
Empty state
Components

Buttons

Five variants. Three sizes. Always one primary per view. Use destructive sparingly — only for destructive actions (cancel booking, delete listing, ban user).

Variants — medium size
Primary
Secondary
Destructive
Ghost
Promo (deep)
Sizes
Mobile booking flow uses Large. Tables & row actions use Small.
States
With icon
Components

Form inputs

All forms run through React Hook Form + Zod. Visuals based on shadcn/ui with the focus ring re-tinted to brand primary.

Shown publicly on your profile and the directory.
Enter a valid email address.
Payment methods accepted
Settings
Instant booking
Guests book without your approval
Show on directory
Discoverable via /explore
Components

Avatars & user chips

Five sizes, three fallbacks (image, initials, icon). Always pillify with rounded-pill. Group/stack for shared listings or threads.

Sizes
AO
xs · 24
AO
sm · 32
AO
md · 40
AO
lg · 48
AO
xl · 64
Fallbacks
Photo
AO
Initials
Icon
Online
Stacked group
MK
+5
8 guests reviewed
User chips
AO
Amara Okafor
L
Lerato Naidoo · Host
J
James · Super Admin
Components

Tabs & segmented controls

Underline tabs for in-page section switching (e.g. listing editor). Pill segmented controls for binary or 2–3 view toggles. Vertical tabs in settings only.

Underline tabs
Tab body content goes here.
Pill segmented · primary
View toggle
Components

Star rating

Always text-amber-400 for stars — universally readable. Three sizes match avatar sizes. Interactive form variant for review submission.

Read-only
sm
4.0 (23)
5.0
Review submission
Great — would book again
Aggregate breakdown
4.8
86 reviews
5
67
4
15
3
3
2
1
1
0
Components

Date picker

The single most-used widget in the guest flow. Range selection with check-in / check-out, mid-range highlighting, blocked dates struck through, minimum-night warnings. Built on shadcn Calendar (react-day-picker under the hood).

Trigger
July 2026
Mo
Tu
We
Th
Fr
Sa
Su
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2 nights · Fri 10 Jul → Sun 12 Jul
R 2 400
Cell states
14
Available
8
Today
10
Selected
11
In range
15
Blocked
3
Past
Components

Availability calendar

The host's calendar view. Larger cells with price overlays, drag-to-block selection, confirmed bookings rendered as filled spans with guest initials. iCal-synced bookings (Airbnb / Booking.com) get a subtle channel dot.

July 2026
Available
Booked
Host-blocked
Pending
Airbnb sync
Mo
Tu
We
Th
Fr
Sa
Su
1
2
3
4
5
6R1.2k
7R1.2k
8today
9R1.2k
10A. Okafor
11A. Okafor
12check-out
13R1.2k
14R1.2k
15blocked
16blocked
17blocked
18M. Cole · pending
19check-out
20via Airbnb
21
22check-out
23R1.2k
24R1.2k
25R1.5k
26R1.5k
27R1.2k
28R1.2k
29R1.2k
30R1.2k
31R1.2k
Booked
7 nights
Blocked
3 nights
Available
16 nights
Revenue
R 11 400
Components

Dialogs & sheets

Modals for booking actions, sheets for filters and detail on mobile. Backdrop is bg-brand-dark/60 backdrop-blur-sm. Always one primary CTA; destructive actions confirm twice.

Destructive confirmation

Cancel this booking?

Amara Okafor will be refunded R 1 200 (50% per your moderate policy) and notified by email. This can't be undone.

BookingVILO-2026-AB1234
Refund amountR 1 200,00
MethodPaystack · 5–7 business days
Booking detail dialog

Booking request

Pending
VILO-2026-AB1234
AO
Amara Okafor
Verified guest · 4 prior bookings
Check-in
Fri 10 Jul 2026
From 14:00
Check-out
Sun 12 Jul 2026
Before 11:00
2 guests 2 nights Paystack · paid
Guest message

“Arriving on the 10:30 flight from Joburg. Could we drop bags before check-in?”

Payout R 2 800
Mobile bottom sheet

Filter listings

Showing 248 in Cape Town

Listing type
Price per nightR 500 – R 3 500
Amenities
Wi-Fi Parking Pool Pet-friendly Air-con
Components

Notification modals

One shell, six intents. Every notification dialog is the same max-w-sm card — identical padding, icon chip, type scale and footer. Only the icon, its tint and the buttons change. Backdrop is bg-brand-dark/60 backdrop-blur-sm. Always one primary CTA; destructive actions confirm twice. This is the canonical shell for every popup, alert, confirm and error in the app — in code, use <Modal> / the imperative modal.* helpers (and <FormModal> for popups that contain a form).

success

Booking confirmed

Amara Okafor's stay at Karoo Cottage is locked in. We've emailed her the check-in details and added it to your calendar.

BookingVILO-2026-AB1234
PayoutR 2 800,00
Dates10 – 12 Jul 2026
info

Payout on the way

Your June payout of R 14 250 has been released to your bank. Paystack transfers usually clear within 1–2 business days.

warning

Calendar not synced

We couldn't reach your Google Calendar for 3 days. New bookings may double-book until you reconnect.

error

Payment couldn't be processed

The guest's card was declined, so this booking wasn't charged. Nothing has been confirmed — you can ask them to retry.

confirm

Send this quote to Amara?

She'll get a notification and can accept within 24 hours. You can still edit the price until she pays.

destructive

Cancel this booking?

Amara Okafor will be refunded R 1 200 (50% per your moderate policy) and notified by email. This can't be undone.

BookingVILO-2026-AB1234
Refund amountR 1 200,00
MethodPaystack · 5–7 days
Action / form modal — same shell, sized for forms (<FormModal>)

Add seasonal price

Karoo Cottage · overrides the base nightly rate

Components

Status badges

Exact tone-pairs from the spec. Never invent a new colour for a new state — propose adding a row here instead.

Confirmed Pending Pending EFT Checked in Completed Cancelled Declined Draft Expired
Non-status badges
Featured Instant book Verified host Accommodation Experience Pro Business
Components

Tables

Host booking list, admin user list, payments ledger — all share this base. Mobile rule: if it has more than 3 columns, render as cards on mobile (see Booking card pattern above).

Recent bookings
42 in the last 30 days
Reference Guest Dates Status Total
VILO-2026-AB1234
AO
Amara Okafor
10–12 Jul · 2n Pending R 2 400
VILO-2026-CD2891
MK
Marcus Khumalo
15–18 Jul · 3n Confirmed R 3 600
VILO-2026-EF4502
SP
Sarah Petersen
22 Jul · experience Checked in R 900
VILO-2026-GH9921
JD
Jessica Davids
03–05 Jul · 2n Completed R 2 200
VILO-2026-IJ7740
RM
Riaan Meyer
28–30 Jun · 2n Cancelled R 2 400
Showing 1–5 of 42
3 bookings selected
Loading skeleton
Empty table
No bookings match these filters
Try widening the date range or removing some filters.
Components

Cards

One container pattern, three sizes. Border + radius does the work — no resting shadow.

Bookings · 30d
42
+18% vs prev period
Revenue · 30d
R 38 400
+12%
Occupancy
74%
23 of 31 nights booked
guest
Amara Okafor
Pending
Cape Town Boutique B&B · Deluxe Suite
Fri 12 Jul → Sun 14 Jul 2 guests · 2 nights Paystack
VILO-2026-AB1234
R 2 400
AM
Marcus Petersen · 3 weeks ago

Spotless, generous breakfast, the host gave us local restaurant tips that turned the weekend around. Will book again next time we're in town.

Your response

Thanks Marcus — see you next time. The bay window room is yours whenever you need it.

Components

Upgrade prompt

Free-tier hosts hit feature gates throughout the dashboard. The prompt is inline, never blocking. It explains what's locked, which plan unlocks it, and the price — nothing more.

Direct booking is available on Basic and above.

R299/month — zero booking fees, ever.

Pro plan
Trial · 9 days left
5-day grace period after trial R599/mo from 8 Jun
Components

Empty states

Every data view needs one. Icon, headline, supportive sentence, optional CTA. No illustrations — they age badly.

No bookings yet

Bookings will appear here once guests start reserving your listing.

Your inbox is empty

Messages from guests will appear here as soon as they reach out.

Components

Toasts

Powered by Sonner. Plain English. Never error codes. Stack top-right on desktop, bottom on mobile.

Booking confirmed
VILO-2026-AB1234 — guest has been notified by email.
Subscription renews in 7 days
Your Pro plan will charge R599 on 30 May.
Could not confirm booking
Please try again — your guest hasn't been notified.
Components

Notifications panel

Bell-icon trigger in the top nav. Dropdown panel groups by today / earlier. Unread items get a dot + bold weight. Click marks-as-read; “Mark all read” clears the badge.

Trigger
5 unread
Notifications
Today
Earlier this week
Wielo patterns

Listing card

The directory's primary unit. 16:9 image, name + location, rating, price-from, badges. Render the verified + featured badges at most once per card.

cover · 16:9 Featured

Cape Town Boutique B&B

Sea Point · Cape Town
4.9 (86)
Instant book
from R 1 200 / night
cover · 16:9

Karoo Sunset Walking Tour

Prince Albert · 3 hours
4.7 (23)
Experience
R 450 / person
cover · 16:9

Drakensberg Lodge

uKhahlamba · KZN
New
Lodge
from R 2 100 / night
Wielo patterns

Booking artifacts

Booking reference, timeline, and price breakdown — three patterns that appear on every booking-related screen.

Booking reference
VILO-2026-AB1234
Always JetBrains Mono. Format: VILO-YYYY-XXNNNN
Booking timeline
Pending
12 Jun, 14:02
Confirmed
12 Jun, 16:48
Checked in
Fri 12 Jul
Completed
Sun 14 Jul
Price breakdown
R 1 200 × 2 nightsR 2 400
Cleaning feeR 250
Weekend surchargeR 150
Wielo platform feeR 0
Total R 2 800
Charged in ZAR via Paystack. Refundable up to 5 days before check-in.
Wielo patterns

Policy & pricing display

Show the rules clearly — guests should never be surprised by a refund outcome. Use a vertical timeline with day thresholds.

Moderate cancellation policy
Apply to most accommodation listings.
  1. 5+ days before check-in
    100% refund — no questions asked.
  2. 1 – 4 days before
    50% refund — cleaning fee retained.
  3. Less than 24 hours
    No refund.
Plan comparison
Use this layout for upgrade flows.
Free
Directory + enquiries
R0
Basic Most picked
1 listing · payments · full inbox
R299/mo
Pro
5 listings · priority ranking
R599/mo
Business
Unlimited · top placement
R1 199/mo
Wielo patterns

Payments & EFT

Three rails: Paystack (cards, instant EFT), PayPal (international), and Manual EFT (bank transfer with proof upload). The guest picks one; the host configures which to offer.

Method picker · guest checkout
Banking details · shown to guest
BankStandard Bank
Account holderBoutique Hosts (Pty) Ltd
Account number012 345 678
Branch code051001
ReferenceVILO-AB1234
Proof of payment uploader
Upload your EFT proof
PDF, JPG, or PNG · up to 5 MB
eft-proof-2026-07-08.pdf
428 KB · uploaded
Pricing summary · with refund preview
R 1 200 × 2 nightsR 2 400
Weekend surcharge (1n)R 150
Cleaning feeR 250
Returning-guest discount− R 100
Wielo platform feeR 0
Total R 2 700
Charged in ZAR via Paystack.
If you cancel today
R 2 700 refunded
100% per moderate policy · refunded in 5–7 business days
Cancel within 24h of check-inNo refund
1–4 days beforeR 1 350 refund
5+ days beforeR 2 700 refund
Wielo patterns

Onboarding stepper

5-step host onboarding. The stepper is always visible at the top — never hide progress. Each step is fully validated before "Continue" enables.

Horizontal stepper
3
4
5
Step 3 of 5

Tell us about your first listing

You can add more later — for now, the basics get you set up.

R
You can set weekend & seasonal rates later.
Wielo patterns

Inbox & messaging

Real-time inbox via Supabase Realtime. Two-pane on desktop, single-pane stack on mobile. Every booking + enquiry creates exactly one thread. System messages are auto-inserted on booking status changes.

Inbox
12 new
AO
Amara Okafor
Verified guest · Online
Pending VILO-2026-AB1234 · 10–12 Jul · 2 nights · 2 guests · R 2 400
Today
Amara requested a booking for 10–12 Jul · R 2 400 · paid via Paystack
AO
Hi Lerato! We're flying in from Joburg at 10:30. Could we drop bags before the official 14:00 check-in?
10:24
AO
Also — any restaurant recommendations near the property?
10:25
Of course! Bag drop is fine from 09:00. I'll put a note for the front desk.
10:31
restaurant-guide.pdf
320 KB · PDF
10:32
AO
Canned
No conversation selected
Pick a conversation
Or start a new one from a booking.
System message styles
Booking confirmed by host
EFT proof uploaded
Booking cancelled · R 1 200 refunded
Read receipts
Sent
Delivered
Read
Wielo patterns

Authentication screens

Sign-in / sign-up / forgot password / OAuth / magic link. Split-screen on desktop (brand panel left, form right). Single-column on mobile.

Sign in · split-screen
Zero booking fees.
Just a flat subscription.

Branded direct-booking pages, real-time inbox, integrated payments. Built for South African hosts.

POPIA-compliant 14-day free trial

Welcome back

Sign in to your Wielo dashboard.

Or with password
Forgot?
New to Wielo? Create a host account
Forgot password

Reset your password

We'll email you a reset link.

Magic link sent

Check your inbox

We sent a link to [email protected]. It expires in 15 minutes.

Verify email · 6-digit code
Wielo patterns

Map view

Keyless Leaflet + OpenStreetMap with a Wielo-tinted style. Pins are price pills; tap a pin opens a small listing card. Use clustering at low zoom levels.

Verified host
Cape Town Boutique B&B
Sea Point · Accommodation
4.9(86)
R 1 500 /night
Wielo patterns

Error pages

404, 500, offline and feature-gated states. Always offer a way out — never dead-end.

404
This listing has checked out

The page you're looking for has moved, been unpublished, or never existed.

500
Something went wrong on our end

We've been notified. Please try again — your data is safe.

trace: 7a3f-9b21-2266
You're offline

Some things won't work until you reconnect. We'll resync automatically.

Checking connection…
Implementation

tailwind.config.ts

Drop this into apps/web/tailwind.config.ts. Same config powers the mobile app via NativeWind — only the content array differs.

// apps/web/tailwind.config.ts
import type { Config } from 'tailwindcss';

const config: Config = {
  content: [
    './app/**/*.{ts,tsx}',
    './components/**/*.{ts,tsx}',
    './lib/**/*.{ts,tsx}',
  ],
  darkMode: 'class',
  theme: {
    extend: {
      colors: {
        brand: {
          primary:   '#10B981',  // emerald — primary actions, links, active nav
          secondary: '#064E3B',  // deep emerald — emphasis, featured, dark CTAs
          deep:      '#064E3B',  // alias of secondary
          accent:    '#D1FAE5',  // tint surface — hover, selected, badge bg
          dark:      '#0A1510',  // near-black — hero / footer / app icon
          light:     '#F0FDF4',  // page background
          ink:       '#052E1F',  // body text
          mute:      '#4A7C6A',  // muted / secondary text
          line:      '#DCEAE0',  // borders, dividers
        },
        status: {
          confirmed: '#10B981',
          pending:   '#F59E0B',
          cancelled: '#EF4444',
          completed: '#6366F1',
          draft:     '#94A3B8',
        },
      },
      fontFamily: {
        sans:    ['Inter', 'system-ui', 'sans-serif'],
        display: ['Plus Jakarta Sans', 'sans-serif'],
        mono:    ['JetBrains Mono', 'ui-monospace', 'monospace'],
      },
      borderRadius: {
        DEFAULT: '10px',   // buttons, inputs
        card:    '16px',   // cards, panels, modals
        pill:    '9999px', // badges, tags, avatars
        sm:      '6px',    // chips, table highlights
      },
      boxShadow: {
        card: '0 1px 2px rgba(6,78,59,0.04), 0 1px 1px rgba(6,78,59,0.03)',
        lift: '0 8px 28px -10px rgba(6,78,59,0.14), 0 2px 6px rgba(6,78,59,0.05)',
        ring: '0 0 0 4px rgba(16,185,129,0.15)',
        glow: '0 12px 32px -10px rgba(16,185,129,0.35)',
      },
      ringColor: {
        DEFAULT: '#10B981',
      },
      backgroundImage: {
        'brand-gradient':      'linear-gradient(135deg, #10B981 0%, #064E3B 100%)',
        'brand-gradient-dark': 'linear-gradient(145deg, #030806 0%, #0a1510 50%, #051209 100%)',
      },
      transitionTimingFunction: {
        out: 'cubic-bezier(0.2, 0.8, 0.2, 1)',
      },
    },
  },
  plugins: [
    require('@tailwindcss/forms')({ strategy: 'class' }),
    require('@tailwindcss/typography'),
    require('tailwindcss-animate'),
  ],
};

export default config;
Implementation

globals.css

CSS variables for surfaces + shadcn/ui token bridge. Light + dark covered.

/* apps/web/app/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    /* Wielo surfaces — used via bg-[var(--brand-surface)] etc. */
    --brand-surface:        #F0FDF4;
    --brand-surface-raised: #FFFFFF;
    --brand-surface-sunken: #E7FAEE;
    --brand-text:           #052E1F;
    --brand-text-muted:     #4A7C6A;
    --brand-border:         #DCEAE0;

    /* Brand gradient — reserve for logo + one hero element / screen */
    --brand-gradient:       linear-gradient(135deg, #10B981 0%, #064E3B 100%);
    --brand-gradient-dark:  linear-gradient(145deg, #030806 0%, #0a1510 50%, #051209 100%);

    /* shadcn/ui token bridge */
    --background: 142 76% 97%;
    --foreground: 158 71% 10%;
    --card: 0 0% 100%;
    --card-foreground: 158 71% 10%;
    --primary: 160 84% 39%;
    --primary-foreground: 0 0% 100%;
    --secondary: 161 86% 17%;
    --secondary-foreground: 0 0% 100%;
    --muted: 152 76% 90%;
    --muted-foreground: 158 26% 39%;
    --accent: 152 76% 90%;
    --accent-foreground: 158 71% 10%;
    --destructive: 0 84% 60%;
    --destructive-foreground: 0 0% 100%;
    --border: 144 23% 89%;
    --input: 144 23% 89%;
    --ring: 160 84% 39%;
    --radius: 10px;
  }

  .dark {
    --brand-surface:        #051209;
    --brand-surface-raised: #0a1510;
    --brand-surface-sunken: #030806;
    --brand-text:           #D1FAE5;
    --brand-text-muted:     #4A7C6A;
    --brand-border:         #143324;

    --background: 158 60% 5%;
    --foreground: 152 76% 90%;
    --card: 160 51% 7%;
    --card-foreground: 152 76% 90%;
    --primary: 160 84% 39%;
    --primary-foreground: 158 71% 10%;
    --secondary: 161 86% 17%;
    --secondary-foreground: 152 76% 90%;
    --muted: 158 36% 12%;
    --muted-foreground: 158 18% 56%;
    --accent: 158 36% 12%;
    --accent-foreground: 152 76% 90%;
    --destructive: 0 84% 60%;
    --destructive-foreground: 0 0% 100%;
    --border: 158 36% 14%;
    --input: 158 36% 14%;
    --ring: 160 84% 39%;
  }

  html, body {
    background: var(--brand-surface);
    color: var(--brand-text);
    font-family: 'Inter', system-ui, sans-serif;
    text-rendering: optimizeLegibility;
    -webkit-font-smoothing: antialiased;
  }

  h1, h2, h3 {
    font-family: 'Plus Jakarta Sans', sans-serif;
    letter-spacing: -0.01em;
  }

  /* Brand gradient utility — for the logo and ONE hero surface per screen */
  .bg-brand-gradient      { background: var(--brand-gradient); }
  .bg-brand-gradient-dark { background: var(--brand-gradient-dark); }

  @media (prefers-reduced-motion: reduce) {
    * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
  }
}
Implementation

Dark mode preview

Toggled via Tailwind's class strategy. Surfaces invert; the emerald primary stays the same so brand recognition holds in both modes.

Bookings · 30d
42
+18%
Revenue
R 38 400
+12%
Occupancy
74%
23 of 31 nights
AO
Amara Okafor
Pending
Cape Town Boutique B&B · 10–12 Jul
VILO-2026-AB1234
R 2 400
Buttons
Badges
Confirmed Pending Cancelled Completed
Input field
Dark surface: #051209 · raised #0a1510 · border #143324 .dark
Implementation

Mobile screens

Reference screens in a phone bezel, showing how the same tokens compose on small viewports. NativeWind uses identical class names — these designs ship 1:1 to React Native.

9:41
Explore
cover Featured
Cape Town B&B
4.9
Sea Point
R 1 200 /night
cover
Karoo Sunset Tour
R 450 /person
Guest · Explore
9:41
Booking
Pending
cover
Cape Town Boutique B&B
VILO-2026-AB1234
Check-in
Fri 10 Jul
Check-out
Sun 12 Jul
2 nights · 2 guests R 2 400
Free cancellation up to 5 Jul
Host · Booking request
9:41
AO
Amara Okafor
online
Pending AB1234 · 10–12 Jul
Hi! Could we drop bags before check-in?
Of course — from 09:00.
Restaurant recommendations?
Message…
Host · Inbox thread
Implementation

Do & don't

Pin these somewhere visible. They cover ~90% of the design decisions you'll hit while building.

Do
  • Use bg-brand-primary for primary actions, links, active nav.
  • Reach for amber (brand.secondary) only for featured/promo/price emphasis.
  • Use status tones for booking statuses only — never for unrelated success/warning UI.
  • Format currency through the shared formatCurrency() util — never raw numbers.
  • Build forms with React Hook Form + Zod, no manual useState.
  • Match shadcn/ui sizing — extend via wrappers, never edit components/ui/.
  • Design mobile-first. Add lg: overrides for desktop, never the reverse.
  • Use flex/grid + gap-* for layout — not per-element margins.
Don't
  • Don't use raw Tailwind colours (blue-500, green-600) for brand purposes.
  • Don't stack shadows — shadow-card at rest, shadow-lift on hover, that's it.
  • Don't put gradients on backgrounds or buttons. Flat surfaces only.
  • Don't use font sizes below 12px or weight above 700.
  • Don't show technical error messages in toasts — Sentry gets the stack, users get plain English.
  • Don't reach for spinners as the default loading state — match the shape of the real content with skeletons.
  • Don't store remote data in Zustand. Supabase reads go through TanStack Query.
  • Don't block free-tier hosts with modals — show the inline upgrade prompt instead.
When in doubt, document the answer.
If a UI question isn't covered here, decide once, add the answer to DESIGN_SYSTEM.md, then keep building.