Design Systemv1.0
GoDND Platform

A design system for running trips, not reading dashboards

The living reference for the GoDND operator portal. It has two parts: the guidelines and token architecture that govern every decision, and a component library showing each building block and its variants — rendered live from the same code that ships in the product.

3
Token tiers
6
Non-negotiable rules
Roboto
One typeface
AA
Contrast floor, every pair
How the tokens work. Colors, type, spacing and radius live as CSS custom properties in a three-tier cascade (primitive → semantic → component). The Tailwind theme mirrors the semantic layer, so a utility class and hand-written CSS resolve to the exact same variable. Change a primitive once and it propagates everywhere.
Guidelines

Six non-negotiable rules

An operator opens the portal to find out what needs them — a trip awaiting approval, a guest waiting on a reply. Every rule serves that question, and each is enforced in code rather than left to discretion.

  1. Status color is never hardcoded

    Every dot, badge, KPI value and notice resolves through statusForExperience() or worstStatus(). The status atoms take a Status, not a color — there is no prop a raw hex could arrive through.

  2. Status colors never carry identity

    Green, amber and red say whether something needs the operator — nothing else. Experience types, regions, categories and chart series use brand, info and neutral, so a red line never reads as an alarm by accident.

  3. Sort by what needs attention

    Lists lead with what is blocked: changes requested, then under review, then live. Bookings by nearest departure, enquiries by longest unanswered. Alphabetical order buries the one row that matters.

  4. No icon-only control without a label

    Every icon button carries a visible label or an aria-label. The collapsed 66px nav expands on hover and on keyboard focus, so its labels are reachable without a mouse.

  5. Never a blank screen, never a lost draft

    Lists load their rows on arrival, and an empty list names the next action. The experience builder saves every step as the operator types — closing the tab never costs work.

  6. Every number has context

    “213 bookings” alone says little. Pair each value with its comparison: versus last month, seats left of capacity, or the group total a variable price produces.

Methodology

Atomic design

The interface is composed bottom-up. Tokens feed atoms, atoms compose into molecules and organisms, and templates arrange them into the pages operators actually use.

  1. Tokens

    Foundation
    Three-tier CSS custom properties mirrored by the Tailwind theme — the vocabulary every atom is built from.
    --brand · --card-border · --critical-tint · --brand-solid · radius · type scale
  2. Atoms

    Indivisible
    The smallest units — one element, one job.
    StatusDot · Badge · Button · TextInput · Select · Checkbox · TokenChip · Avatar · Sparkline
  3. Molecules

    Small groups
    A handful of atoms bound into a reusable unit.
    StatusBadge · KpiCard · PillTabs · Field · AffixInput · ChipSelect · Notice · SearchInput
  4. Organisms

    Sections
    Standalone, composed regions of the interface.
    ExperiencesBoard · Panel · Sidebar · PageBar · RecordDrawer · WizardRail · DropdownMenu · Toast
  5. Templates

    Layout
    Page skeletons — arrangement without real data.
    AppShell (nav + one floating surface card) · list + record drawer · rail + step form
  6. Pages

    Instances
    Templates filled with an operator's real data.
    Experiences · Experience detail · New experience (7 steps) · Design system
Tokens

Three-tier token architecture

No component ever names a raw value. Each tier references the one beneath it, so intent stays legible and a single edit ripples predictably through the whole platform.

Layer 1
Primitive
Raw, context-free values. The only place a hex or px is written.
--emerald-700: #047857
Layer 2
Semantic
Role-based aliases the platform consumes. Says what, not which.
--brand: var(--emerald-700)
Layer 3
Component
Element-specific decisions built on the tiers below.
--brand-solid: var(--emerald-700)
Tokens · Layer 1

Primitive tokens

The raw palette, named by hue and lightness, never by purpose.

Neutral ramp · slate

A blue-tinted grayscale. Every surface, border and text color is drawn from here.
--white 
--slate-25 
--slate-50 
--slate-75 
--slate-100 
--slate-125 
--slate-150 
--slate-200 
--slate-500 
--slate-550 
--slate-600 
--slate-900 

Emerald · brand

GoDND green. 600 is the base for dots and rails; 700 is the AA-safe step for brand text and for fills that carry a white label; 800 is the hover / pressed step.
--emerald-50 
--emerald-600 
--emerald-700 
--emerald-800 

Blue · info

Informational only — notices, the info status, links inside prose. Kept separate from brand so an info toast never reads as a brand action.
--blue-50 
--blue-600 
--blue-700 
--blue-800 

Violet · accent

Onboarding and walkthrough moments only — never status, never a category.
--violet-50 
--violet-600 

Status hues

Reserved for status. Each hue has a base (600) for dots and borders, and a deeper step (700) for status text and for fills that carry white text.
--green-600 
--green-700 
--amber-600 
--amber-700 
--red-600 
--red-700 
Tokens · Layer 2

Semantic tokens

What the platform actually references. Each maps to exactly one primitive.

Semantic tokens
TokenPrimitiveValueUsage
--canvas--slate-25App gutter / page background — a shade darker than the card
--card--whiteThe single floating surface card
--panel--slate-50Inset panels, inputs, the pill-tab track
--panel-2--slate-100Row hover, pressed panel
--raised--whiteMenus, popovers, toast
--border-subtle--slate-75Hairline dividers inside a panel
--border-panel--slate-150Panel, control and drawer borders
--border-strong--slate-200Hover / emphasized borders
--text-primary--slate-900Headings, values, primary copy
--text-secondary--slate-600Labels, secondary copy
--text-muted--slate-550Captions, hints, meta — clears AA on every surface
--brand--emerald-700Focus, active rail, brand text and icons
--brand-muted--emerald-50Active nav / tab / row background
--accent--violet-600Onboarding & walkthrough accent
--accent-muted--violet-50Accent surface
--healthy--green-600Live and bookable — nothing to do
--warning--amber-600Waiting on someone — under review, payment pending
--critical--red-600Blocked until the operator acts — changes requested
--info--blue-600Informational notices
--neutral--slate-500Draft, disabled, archived — not trading, nothing wrong
Tokens · Layer 3

Component tokens

Interaction states and composite decisions. The -solid steps exist for one reason: a fill that carries white text has to clear 4.5:1, which the base status hues do not.

Component tokens
TokenResolves toValueUsage
--brand-solid--emerald-700Primary button, avatar, selected calendar day
--brand-hover--emerald-800Primary fill hover
--brand-active--emerald-800Primary fill pressed
--brand-on-muted--emerald-700Text / icon on a --brand-muted surface
--on-brand--whiteText on any solid fill
--healthy-solid--green-700Status badge fill · healthy
--warning-solid--amber-700Status badge fill · warning
--critical-solid--red-700Status badge fill · critical
--info-solid--blue-600Status badge fill · info
--neutral-solid--slate-500Status badge fill · neutral
--card-border--slate-125Hairline on every card, panel and table surface
--card-shadowslate-900 · 5% + 10%Floating-card elevation
--critical-fg--red-700Status as text — on a critical tint or a plain surface
--warning-fg--amber-700Status as text — on a warning tint or a plain surface
--healthy-fg--green-700Status as text — on a healthy tint or a plain surface
--critical-tintred-600 · 10%Badge / notice critical fill
--warning-tintamber-600 · 12%Badge / notice warning fill
--healthy-tintgreen-600 · 12%Badge / notice healthy fill
--info-tintblue-600 · 10%Info surface fill
--focus-ring0 0 0 3px · emerald-700 · 18%Text input / select focus glow
Foundations

Color

The card is white and the canvas sits a shade darker, so the single surface card reads as floating. Everything else inside it is a quieter step of the same slate.

--canvas Gutter
--card Surface card
--panel Panel
--panel-2 Hover
--raised Raised / overlay
--brandBrand
--brand-mutedBrand muted
--accentAccent
--healthyHealthy
--warningWarning
--criticalCritical
--infoInfo
Reserved. Green, amber and red mean status and nothing else. An experience type, a region or a chart series never borrows them — there, color must signal identity, and a red category would read as an alarm.
One hue, three jobs. Each status hue has a base step for dots, rails and borders; an -fg step for status written as text; and a -solid step for fills that carry a white label. Picking the step by job is what keeps every pair at AA.

Contrast

Computed live from the tokens · WCAG AA is 4.5:1
Contrast ratios for key text pairs
PairTokensRatio
Primary text on card--text-primary on --card—
Secondary text on card--text-secondary on --card—
Muted text on card--text-muted on --card—
Muted text on panel--text-muted on --panel—
Muted text on hover row--text-muted on --panel-2—
Brand text on card--brand on --card—
Critical text on card--critical-fg on --card—
Warning text on card--warning-fg on --card—
Brand-on-muted on muted--brand-on-muted on --brand-muted—
Primary button label--on-brand on --brand-solid—
Primary button, hover--on-brand on --brand-hover—
Healthy badge label--on-brand on --healthy-solid—
Warning badge label--on-brand on --warning-solid—
Critical badge label--on-brand on --critical-solid—
Neutral badge label--on-brand on --neutral-solid—
Foundations

Typography

One typeface: Roboto, for everything. Numerals take tabular figures globally, so prices, dates and counts line up in columns without a second, monospaced face.

kpi-value · 30 / 600₹67,000
summary-pill · 25 / 7008 experiences
chart-value · 20 / 700213 bookings
page-title · 15 / 600New experience
drawer-title · 14 / 6007 Day Immersive Experience in Meghalaya
panel-header · 13.5 / 600Upcoming departures
body · 12.5 / 400Lists lead with whatever needs the operator.
label · 10.5 / 600 · capsNext availability
caption · 11 / 400 · muted↑ 18% vs last month
Roboto · 400 / 500 / 600 / 700
AaAaAaAa— interface
Roboto · tabular figures
₹1,11,500
₹98,000
Foundations

Spacing & radius

A 14px gutter frames the app; radii step 6 → 9 → 10 → 12px from controls up to the surface card.

--radius-sm6pxChips, inputs, buttons
--radius-md9pxPill tabs, menus, notices
--radius-lg10pxPanels, cards, tables
4pxPill-tab track padding
8pxCard scroll · vertical
12pxPanel head · vertical
14px--gutter · canvas
16pxPanel & cell · horizontal
18pxMain column gap
--nav-w178pxSidebar (always expanded)
--gutter14pxCanvas padding
Foundations

Elevation

Depth is carried by soft, cool-gray shadows. The higher the layer, the larger and softer the cast.

Surface card--card-shadow
Menu--shadow-dropdown
Popover--shadow-pop
Toast--shadow-toast
Drawer--shadow-drawer
Component library

Buttons

One button family drives every action. The primary fill uses --brand-solid with the --brand-hover / --brand-active steps; everything else stays quiet on a hairline border.

Header buttons

Button · .hbtn · active · icon · small

Primary & emphasis

primary · primary disabled · brand-lit · danger

Links as buttons

buttonClass() on a <Link> — navigation keeps its semantics

Segmented toggle

.seg-toggle · two or three mutually exclusive views
Component library

Status & badges

Status indicators are the heart of the system. Every one takes a Status resolved in lib/status.ts — none of them accepts a color.

Status dot

StatusDot · sizes sm / md / lg
sm
md
lg

Table status dot

.status-dot — critical and warning carry a halo
criticalwarninghealthy

Status badge

StatusBadge · solid -solid fill, white label
HealthyWarningCriticalInfoNo data

Pill badges

Badge · .kpi-chip · .flag — tint + on-tint foreground
healthywarningcriticalinfoneutral↑ CriticalWarningOverdueOn time

Experience state → status

statusForExperience() · the one resolver lists and drawers use
How each experience state maps to a status
StateStatusBadge
rejectedcriticalChanges requested
under_reviewwarningUnder review
activehealthyActive
draftneutralDraft
disabledneutralDisabled
archivedneutralArchived
Component library

Metric cards

KpiCard is the workhorse — label, status chip, value, sparkline and a comparison caption, all colored by the resolved status. Summary pills give the count at a glance.

KPI card

KpiCard · comparison is a required prop (rule 06)
Unanswered enquiriesCritical
3
Oldest waiting 26 hours
Awaiting approvalWarning
1experience
Submitted 2 days ago
Bookings this month
213
↑ 18% vs August

Summary pills

.summary-strip · total / critical / warning / healthy
8
Experiences
1
Changes requested
1
Under review
4
Live

Sparkline

Sparkline · inline SVG, area + line, color from a token
Component library

Tables & panels

Panel is the universal card with a header row. Tables right-align every number, lead each row with its status, and sort by what needs attention (rule 03).

Panel + data table

Panel › .data-table · row header · right-aligned numerics

Experiences

needs attention first
Sample experiences, sorted by what needs attention
ExperienceStateBookingsSeats leftBase price
Rafting, Camping & Cycling in Upper AssamChanges requested08 of 8₹98,000
Raw Experience in MeghalayaUnder review04 of 4₹18,500
7 Day Immersive Experience in MeghalayaActive2133 of 10₹67,000

Pill tabs

PillTabs · link tabs for server state, button tabs for view state

Compact tabs

.tabbar · .tab.active — dense contexts, such as inside a drawer
Component library

Feedback & empty states

When something needs the operator, the notice says what and why as a first-class element instead of hiding it in a tooltip. Empty states always offer the next action.

Notice

Notice · left-border status accent
Changes requested on “Rafting in Upper Assam”
The cancellation policy doesn’t match the refund window. Edit step 6, then send it for approval again.
“Raw Experience in Meghalaya” is under review
Approval usually takes one working day. It stays bookable on your own site meanwhile.
Showing sample data
Add your Supabase keys to .env.local to see your own experiences.
Preview with sample data
You’re looking at a walkthrough of the operator portal. The experiences below are representative — real ones appear here once the workspace is connected to a live database.
Published to the marketplace
Travellers can find and book this experience now.

Empty state

EmptyState · icon + title + description + action

No drafts

Experiences you start but don't send for approval are kept here, saved as you type.

Toast

.toast-bar · transient, auto-dismisses after 5s
Help Center isn’t built yet — this previews where it will live.

Skeleton

.skeleton · the loading shape of what is coming
Component library

Forms

Every control is native and shares one focus treatment — a --brand border and the --focus-ring glow. Controls sit quiet on --panel until touched; labels, hints and errors are wired to the control for assistive tech.

Text fields

Field › TextInput · hint · error

Shown on the marketplace and on your own site.

Select, affix & text area

Select · AffixInput · TextArea

Choices

Checkbox · RadioGroup · .switch · ChipSelect
Food included
  • Meghalaya

Search

.search-wrap · .search-input
Component library

Overlays

Menus, the record drawer and tooltips sit on --raised or --card with a soft shadow. Each is built on a Radix primitive in shadcn's composition, so keyboard, focus and Escape behave correctly by default.

Dropdown menu

DropdownMenu · Radix · arrow keys, typeahead, Escape

Record drawer

RecordDrawer · non-modal, the list stays usable underneath

Tooltip

Tooltip · shows on hover and on keyboard focus
Component library

Navigation

The sidebar item is the atom of navigation — an always-labeled icon with a brand rail when active, and a genuinely disabled state for modules that aren't built yet.

Sidebar item

.nav-item · default / active / disabled
HomeExperiencesBookings

Step rail

.rail-item · the wizard's secondary navigation
Basic InfoItinerary Builder2Pricing Strategy4

Breadcrumb

.card-crumbs · back arrow steps up one level; the last crumb is the page heading
DashboardExperiencesNew experience

Avatar

.avatar · gradient monogram on --brand-solid