Skip to main content

Tokens

The single source of truth for color, spacing, radius, and elevation across vambora-web. Tokens are CSS variables in web/src/app/globals.css. Tailwind theme references the variables; shadcn components consume them; feature code never reaches for raw hex.

When vambora-android arrives, its Material 3 theme mirrors the same hex values — the documented values here are the contract.

Surface (dark)

TokenHSLHex (approx)Use
--background240 10% 4%#09090bApp body background
--foreground0 0% 98%#fafafaDefault text
--card240 6% 10%#18181bCard / surface fill
--card-foreground0 0% 98%#fafafaText on card
--popover240 6% 10%#18181bPopover / dropdown fill
--secondary240 4% 16%#27272aSecondary chrome
--muted240 4% 16%#27272aDisabled, hint surfaces
--muted-foreground240 5% 65%#a1a1aaSubdued text
--border240 4% 16%#27272aAll borders
--input240 4% 16%#27272aForm control borders
--ring43 96% 56%#fbbf24Focus rings (= primary)

Brand / route

TokenHSLHexModal
--route-bus43 96% 56%#fbbf24City buses (SPPO)
--route-brt217 91% 60%#3b82f6BRT (TransOeste, TransCarioca, etc.)
--route-vlt142 71% 45%#22c55eVLT (light rail)

Semantic

TokenMaps toUse
--primary--route-busPrimary CTAs, focus ring; bus is the dominant modal
--accent--route-brtHighlights, secondary CTAs
--destructive0 72% 51% (#dc2626)Errors, irreversible actions

Spacing

Use the Tailwind scale (gap-1, gap-2, gap-4, gap-6, …) only via the <Stack> primitive's gap prop when you can. The mapping:

Stack gapTailwindrem
xsgap-10.25
smgap-20.5
mdgap-41
lggap-61.5

Padding and margin go through the Tailwind scale directly (p-2, px-4). Arbitrary values like p-[13px] are forbidden — see lint:design.

Radius

TokenValueTailwind
--radius0.5remrounded-md (default)

Typography

No custom typography variables — the Tailwind scale is enough. Use the <Heading> and <Text> primitives to apply it consistently:

ComponentVariantTailwind
Headingdisplaytext-3xl font-semibold tracking-tight
Headingpagetext-base font-semibold tracking-tight
Headingsectiontext-sm uppercase tracking-wider text-muted-foreground
Textbodytext-sm leading-6
Textcaptiontext-xs leading-5
Textlabeltext-xs font-medium uppercase tracking-wider

MapLibre bridge

MapLibre's paint properties don't accept var(--…) references, so the route colors are mirrored as a TS constant in web/src/shared/components/map/route-colors.ts. If a token's HSL changes in globals.css, change it there too. The kitchen-sink page renders both side-by-side so drift is visible.

Themes (color modes)

MVP ships dark only. The :root and .dark blocks in globals.css are intentionally identical right now. Light theme arrives in Phase 2 — see plan.md non-goals.