Busflow Docs

Internal documentation portal

Skip to content

Design System Architecture

The busflow design system follows a federated approach. A single monorepo of UI components (@busflow/ui-core, based on ShadCN/Tailwind) radically alters appearance, density, and ergonomics based on the application context.

For token definitions see config-tailwind. For component implementations see ui-core. For visual testing see histoire.

Core Philosophy: "One Codebase, Three Realities"

Instead of building three separate <Button> components for the Passenger, Dispatcher, and Driver, we build one polymorphic <Button> that is context-aware. We separate component logic (state, accessibility) from presentation (colors, typography) and ergonomics (touch targets, layout).

The Three Architectural Layers

1. Primitives Layer (Skeleton)

The underlying Radix UI / ShadCN foundation. Handles ARIA attributes, keyboard navigation, and state management (open/closed, focused/blurred). Remains identical across all environments.

2. Token Context Layer (Skin)

Strict usage of semantic Tailwind CSS variables (bg-primary, text-foreground). Hardcoded hex values or utility colors (bg-blue-500) are strictly forbidden inside ui-core.

The layout or router level applies three CSS theme scopes (.theme-passenger, .theme-dispatcher, .theme-driver), instantly swapping the entire palette without touching component code.

3. Adaptive Ergonomics Layer (Muscle)

Components use Tailwind contextual modifiers to change physical shape based on their parent theme class. A <Dialog> centers by default but docks to the bottom inside .theme-driver. A <Button> forces min-h-14 (56px) for thumb-tapping in the driver cockpit.


The Three Environments

Element.theme-passenger (B2C).theme-dispatcher (B2B).theme-driver (Mobile)
VibeNostalgic Modernism / Boutique HotelNeo-Corporate / Functional MinimalismTactile Utility / Rugged
Typographyfont-serif headings (Playfair Display), font-sans datafont-sans exclusively (Geist/Inter), tabular numsfont-sans exclusively, scaled up
BackgroundAlabaster (#F9F7F1)Slate-tinted off-whiteOff-black (minimize night-driving glare)
PrimaryDeep Forest Green (#1B3B2E)Deep Navy BlueHigh-visibility Bright Blue
CornersSoft (8px)Sharp (4px)Tactile (6px)
ShadowsWarm, dispersed (shadow-soft-warm)Minimal shadow-smNone on static; shadow-tactile on buttons
TransitionsSweeping ease-in-out 300msInstant / Snappy (150ms)Instant / Snappy (150ms)
ErgonomicsStandardHigh data densityOversized touch targets (min 56px), bottom-docked modals

Component Polymorphism Examples

<Button>

  • Passenger: Forest Green bg, shadow-soft-warm, shadow deepens smoothly over 300ms on hover.
  • Dispatcher: Navy Blue bg, 4px sharp corners, ring-offset for fast keyboard tab-navigation.
  • Driver: Forced min-h-14 (56px) for thumb-tapping. active:translate-y-[1px] for physical "press" feedback.

<Card>

  • Passenger: High-quality stationery. font-serif text-2xl header, no borders — warm dispersed shadow only.
  • Dispatcher: Tight data container. p-4 padding, crisp 1px border, no drop shadows.
  • Driver: Rugged basics. p-6 padding, contrasting dark gray bg, thick semantic left-borders for status.

<Dialog>

  • Passenger: Warm glassmorphism backdrop (backdrop-blur-md bg-stone-900/20), centered, serif typography.
  • Dispatcher: Centered, sharp corners, instant snappy animations.
  • Driver: Bottom-docked Action Sheet at 100% width for one-handed thumb operation.

Color Mode Architecture

The design system supports a two-dimensional theming model: role themes and color modes compose independently via CSS class stacking on <html>.

The Two Dimensions

DimensionControlsCSS ClassWho decides
Role themeColors, typography, radii, shadows — the personality.theme-dispatcher, .theme-passenger, .theme-driverThe app itself (build-time)
Color modeLight ↔ dark surface/text swap within the active theme.darkThe user (or system preference)

They compose via class stacking: <html class="theme-dispatcher dark"> applies the dispatcher palette in dark mode. Every .theme-x.dark combination defines a curated dark palette — not a generic inversion.

Composable Layering

text
@busflow/ui-core         →  useColorMode()       Low-level .dark class manager

@busflow/ui-domain       →  <ThemeProvider>       Business-aware orchestrator
                             <ThemeToggle>         UI toggle widget
                             useTheme()            Context consumer

apps/*                   →  Wraps root in         Passes theme name, done
                             <ThemeProvider>
  • useColorMode() manages .dark on <html>, persists preference to localStorage, and listens to prefers-color-scheme.
  • <ThemeProvider> consults the theme registry to determine if dark mode is supported, applies the theme-* class, and provides context via inject.
  • <ThemeToggle> reads the injected context and auto-hides when dark mode is unsupported (driver app).

Per-App Mode Rules

AppThemeDark modeDefault
workspacetheme-dispatcher✅ User toggleablesystem
passengertheme-passenger✅ User toggleablelight
drivertheme-driver❌ Forced darkdark

FOUC Prevention

Each app's index.html includes a synchronous inline <script> that reads the user's persisted preference from localStorage and applies .dark before the first paint. This prevents a flash of light mode for dark-mode users. The script mirrors useColorMode()'s resolution logic (including prefers-color-scheme fallback for 'system' mode).


Package Index

PackageContentPath
config-tailwindDesign tokens (colors, typography, spacing, shadows), theme scopes, consumer setuppackages/config-tailwind/
ui-coreGeneric presentational components (ShadCN/Tailwind), theme polymorphism, layout patternspackages/ui-core/
ui-domainDomain-specific components (seat map, PDF viewer, trip cards), consumer matrixpackages/ui-domain/
histoireVisual component workshop, story authoring guide, regression testingstudio/histoire/

Internal documentation — Busflow