Skip to content

Style Guide

Claude product-architect (Opus 4.6) edited this page Feb 22, 2026 · 2 revisions

Style Guide

This page is the canonical reference for the Cornerstone design system. It documents every design token, component pattern, and visual convention in use. All new UI work must follow these specifications.

Managed by: ux-designer agent Token source: client/src/styles/tokens.css


Table of Contents

  1. Design Token Architecture
  2. Color Palette (Layer 1)
  3. Semantic Tokens (Layer 2)
  4. Shadow Scale
  5. Typography
  6. Spacing
  7. Border Radius
  8. Transitions
  9. Z-Index Scale
  10. Component Patterns
  11. Dark Mode Implementation Guide
  12. Brand Identity

Design Token Architecture

Cornerstone uses a 3-layer token system defined in client/src/styles/tokens.css.

Layer 1 — Palette      Raw named color values (hex) — source of truth
    ↓
Layer 2 — Semantic     Purpose-driven aliases using var() references to Layer 1
    ↓
Layer 3 — Dark mode    Scoped overrides on [data-theme="dark"] — replaces Layer 2 values only

Rules:

  • Always reference Layer 2 semantic tokens in component CSS Modules (var(--color-bg-primary))
  • Never use raw hex values in .module.css files — zero exceptions
  • Only use Layer 1 palette tokens directly when no semantic alias covers the use case
  • Layer 3 requires no changes to component code — theming is automatic via CSS cascade

Verification command — must return zero results:

grep -rn '#[0-9a-fA-F]' client/src --include="*.module.css"

Color Palette (Layer 1)

These are raw color values. Reference these only through Layer 2 semantic tokens in component CSS.

Grayscale

Token Hex Usage
--color-white #ffffff Pure white
--color-black #000000 Pure black
--color-gray-50 #f9fafb Near-white backgrounds
--color-gray-100 #f3f4f6 Subtle backgrounds, tertiary surfaces
--color-gray-200 #e5e7eb Default borders
--color-gray-300 #d1d5db Strong borders, dividers
--color-gray-400 #9ca3af Placeholder text, disabled text
--color-gray-500 #6b7280 Muted text
--color-gray-600 #4b5563 Subtle text
--color-gray-700 #374151 Secondary text, sidebar hover
--color-gray-800 #1f2937 Sidebar background (light mode), inverse bg
--color-gray-900 #111827 Primary text, darkest surface

Blue (Primary / Action)

Token Hex Usage
--color-blue-100 #dbeafe Primary background tint
--color-blue-200 #bfdbfe Primary background tint (hover)
--color-blue-400 #60a5fa Sidebar focus ring
--color-blue-500 #3b82f6 Primary action, focus border, favicon
--color-blue-600 #2563eb Primary hover
--color-blue-700 #1d4ed8 Primary active
--color-blue-800 #1e40af Badge text on light bg

Red (Danger / Error)

Token Hex Usage
--color-red-50 #fef2f2 Danger background (very light)
--color-red-100 #fee2e2 Danger background, blocked badge bg
--color-red-200 #fecaca Danger border
--color-red-400 #ef4444 Danger input border
--color-red-500 #dc2626 Danger action
--color-red-600 #b91c1c Danger hover
--color-red-700 #991b1b Danger active, badge text

Green (Success)

Note: these values map to a mix of Tailwind's green and emerald scales.

Token Hex Usage
--color-green-50 #f0fdf4 Success background (very light)
--color-green-100 #d1fae5 Emerald-100; completed badge bg, success badge bg
--color-green-150 #dcfce7 Green-100 (non-standard step); user active badge bg
--color-green-200 #bbf7d0 Success border
--color-green-500 #10b981 Success action
--color-green-600 #059669 Success hover
--color-green-700 #166534 Success text on light bg, user active badge text
--color-green-900 #065f46 Completed badge text

Semantic Tokens (Layer 2)

Reference these tokens in all component CSS. They automatically adapt to dark mode via Layer 3 overrides.

Backgrounds

Token Light Value Dark Value Usage
--color-bg-primary #ffffff (white) #1a1a2e Main page background
--color-bg-secondary #f9fafb (gray-50) #16213e Sidebar of cards, secondary panels
--color-bg-tertiary #f3f4f6 (gray-100) #1e293b Code blocks, inset regions
--color-bg-inverse #1f2937 (gray-800) #f3f4f6 (gray-100) Tooltips, dark callouts
--color-bg-hover #f9fafb (gray-50) #1e293b Row/item hover state

Text

Token Light Value Dark Value Usage
--color-text-primary #111827 (gray-900) #f1f5f9 Headings, prominent labels
--color-text-secondary #374151 (gray-700) #cbd5e1 Sub-headings, labels
--color-text-body #374151 (gray-700) #94a3b8 Body copy, descriptions
--color-text-muted #6b7280 (gray-500) #64748b Helper text, metadata
--color-text-subtle #4b5563 (gray-600) #94a3b8 Less-prominent secondary copy
--color-text-inverse #ffffff (white) #111827 (gray-900) Text on dark/inverse surfaces
--color-text-placeholder #9ca3af (gray-400) #475569 Form input placeholders
--color-text-disabled #9ca3af (gray-400) #475569 Disabled form elements

Borders

Token Light Value Dark Value Usage
--color-border #e5e7eb (gray-200) #334155 Default borders, dividers
--color-border-strong #d1d5db (gray-300) #475569 Emphasized borders, table lines
--color-border-focus #3b82f6 (blue-500) #60a5fa (blue-400) Focused input border color

Primary Action (Blue)

Token Light Value Dark Value Usage
--color-primary #3b82f6 #60a5fa Button background, links
--color-primary-hover #2563eb #3b82f6 Button hover state
--color-primary-active #1d4ed8 #2563eb Button active/pressed
--color-primary-text #ffffff #111827 Text on primary button
--color-primary-bg #dbeafe rgba(59,130,246,0.15) Subtle primary tint (tags, etc.)
--color-primary-bg-hover #bfdbfe rgba(59,130,246,0.25) Subtle primary tint hover
--color-primary-badge-text #1e40af #93c5fd Text inside primary-tinted badges

Danger / Error (Red)

Token Light Value Dark Value Usage
--color-danger #dc2626 #f87171 Destructive button, error icon
--color-danger-hover #b91c1c #ef4444 Destructive button hover
--color-danger-active #991b1b #dc2626 Destructive button pressed
--color-danger-text #ffffff #111827 Text on danger button
--color-danger-bg #fef2f2 rgba(239,68,68,0.1) Error banner background
--color-danger-bg-strong #fee2e2 rgba(239,68,68,0.2) Error badge background
--color-danger-border #fecaca rgba(239,68,68,0.3) Error banner border
--color-danger-text-on-light #991b1b #fca5a5 Error text on light bg
--color-danger-input-border #ef4444 #ef4444 Invalid input border

Success (Green)

Token Light Value Dark Value Usage
--color-success #10b981 #34d399 Success icon, checkmark
--color-success-hover #059669 #10b981 Success button hover
--color-success-bg #f0fdf4 rgba(16,185,129,0.1) Success banner background
--color-success-border #bbf7d0 rgba(16,185,129,0.3) Success banner border
--color-success-text-on-light #166534 #6ee7b7 Success text on light bg
--color-success-badge-bg #d1fae5 rgba(16,185,129,0.15) Success badge background
--color-success-badge-bg-alt #dcfce7 rgba(16,185,129,0.2) Alternate success badge background
--color-success-badge-text #065f46 #a7f3d0 Success badge text

Sidebar

Token Light Value Dark Value Usage
--color-sidebar-bg #1f2937 (gray-800) #0f172a Sidebar panel background
--color-sidebar-text #f9fafb (gray-50) #e2e8f0 Sidebar text and icons
--color-sidebar-hover #374151 (gray-700) #1e293b Nav item hover background
--color-sidebar-active #3b82f6 (blue-500) #3b82f6 Active nav item background
--color-sidebar-focus-ring #60a5fa (blue-400) #60a5fa Keyboard focus ring in sidebar
--color-sidebar-separator #374151 (gray-700) #334155 Horizontal rule in sidebar

Focus Rings (Color values)

Token Light Value Dark Value Usage
--color-focus-ring rgba(59,130,246,0.3) rgba(96,165,250,0.4) Standard blue focus ring color
--color-focus-ring-subtle rgba(59,130,246,0.1) rgba(96,165,250,0.15) Subtle focus ring
--color-focus-ring-danger rgba(220,38,38,0.1) rgba(239,68,68,0.15) Focus ring on danger element
--color-focus-ring-primary-alt rgba(37,99,235,0.1) rgba(96,165,250,0.15) Focus ring on alternate primary

Overlays / Backdrops

Token Light Value Dark Value Usage
--color-overlay rgba(0,0,0,0.5) rgba(0,0,0,0.7) Modal backdrop
--color-overlay-light rgba(0,0,0,0.1) rgba(0,0,0,0.3) Hover overlay on images
--color-overlay-medium rgba(0,0,0,0.15) rgba(0,0,0,0.4) Medium-weight overlay
--color-overlay-danger rgba(153,27,27,0.1) rgba(239,68,68,0.15) Danger action overlay
--color-overlay-delete rgba(220,38,38,0.1) rgba(239,68,68,0.15) Delete confirmation overlay

Status Badge Tokens

Token Light Value Dark Value Usage
--color-status-not-started-bg #e5e7eb (gray-200) #334155 Not Started badge background
--color-status-not-started-text #374151 (gray-700) #94a3b8 Not Started badge text
--color-status-in-progress-bg #dbeafe (blue-100) rgba(59,130,246,0.2) In Progress badge background
--color-status-in-progress-text #1e40af (blue-800) #93c5fd In Progress badge text
--color-status-completed-bg #d1fae5 (green-100) rgba(16,185,129,0.15) Completed badge background
--color-status-completed-text #065f46 (green-900) #a7f3d0 Completed badge text
--color-status-blocked-bg #fee2e2 (red-100) rgba(239,68,68,0.15) Blocked badge background
--color-status-blocked-text #991b1b (red-700) #fca5a5 Blocked badge text

Role Badge Tokens

Token Light Value Dark Value Usage
--color-role-admin-bg #dbeafe (blue-100) rgba(59,130,246,0.2) Admin role badge background
--color-role-admin-text #1e40af (blue-800) #93c5fd Admin role badge text
--color-role-member-bg #f3f4f6 (gray-100) #334155 Member role badge background
--color-role-member-text #374151 (gray-700) #94a3b8 Member role badge text

User Status Badge Tokens

Token Light Value Dark Value Usage
--color-user-active-bg #dcfce7 (green-150) rgba(16,185,129,0.15) Active user badge background
--color-user-active-text #166534 (green-700) #6ee7b7 Active user badge text
--color-user-inactive-bg #fee2e2 (red-100) rgba(239,68,68,0.15) Inactive user badge background
--color-user-inactive-text #991b1b (red-700) #fca5a5 Inactive user badge text

Shadow Scale

Shadows automatically deepen in dark mode via Layer 3 overrides.

Token Light Value Dark Value Usage
--shadow-sm 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06) 0 1px 3px rgba(0,0,0,0.3), 0 1px 2px rgba(0,0,0,0.2) Cards, small surfaces
--shadow-md 0 4px 6px rgba(0,0,0,0.1) 0 4px 6px rgba(0,0,0,0.3) Raised elements
--shadow-lg 0 10px 15px rgba(0,0,0,0.1), 0 4px 6px rgba(0,0,0,0.05) 0 10px 15px rgba(0,0,0,0.3), 0 4px 6px rgba(0,0,0,0.2) Dropdowns, popovers
--shadow-xl 0 20px 25px rgba(0,0,0,0.15) 0 20px 25px rgba(0,0,0,0.4) Modals
--shadow-xl-strong 0 20px 25px -5px rgba(0,0,0,0.1), 0 10px 10px -5px rgba(0,0,0,0.04) 0 20px 25px -5px rgba(0,0,0,0.3), 0 10px 10px -5px rgba(0,0,0,0.2) Large modals
--shadow-2xl 0 20px 25px rgba(0,0,0,0.2) 0 20px 25px rgba(0,0,0,0.5) Full-page overlays
--shadow-inset-deep 0 10px 25px rgba(0,0,0,0.2) 0 10px 25px rgba(0,0,0,0.4) Sidebar depth, inset panels
--shadow-kbd 0 1px 0 rgba(0,0,0,0.05) 0 1px 0 rgba(255,255,255,0.1) Keyboard shortcut keys
--shadow-focus 0 0 0 3px rgba(59,130,246,0.3) 0 0 0 3px rgba(96,165,250,0.4) Standard focus ring
--shadow-focus-subtle 0 0 0 3px rgba(59,130,246,0.1) 0 0 0 3px rgba(96,165,250,0.15) Subtle focus ring
--shadow-focus-primary-alt 0 0 0 3px rgba(37,99,235,0.1) 0 0 0 3px rgba(96,165,250,0.15) Alternate primary focus ring
--shadow-focus-danger 0 0 0 3px rgba(220,38,38,0.1) 0 0 0 3px rgba(239,68,68,0.15) Focus ring on danger element

Typography

Font Family

The application uses the system-ui font stack — no web font loading, no layout shift:

font-family:
  system-ui,
  -apple-system,
  BlinkMacSystemFont,
  'Segoe UI',
  Roboto,
  Oxygen,
  Ubuntu,
  Cantarell,
  'Helvetica Neue',
  sans-serif;

This is set globally in client/src/styles/index.css and should not be overridden per-component.

Font Sizes

Token Value Pixels Usage
--font-size-2xs 0.8125rem 13px Between xs and sm — fine print, dense metadata
--font-size-xs 0.75rem 12px Micro labels, timestamps
--font-size-sm 0.875rem 14px Form labels, secondary text, badges
--font-size-base 1rem 16px Body text, inputs
--font-size-lg 1.125rem 18px Slightly emphasized text
--font-size-xl 1.25rem 20px Card titles, section headings
--font-size-2xl 1.5rem 24px Page sub-headings
--font-size-3xl 1.875rem 30px Major page headings
--font-size-4xl 2rem 32px Hero headings, large dashboard numbers

Font Weights

Token Value Usage
--font-weight-normal 400 Body text
--font-weight-medium 500 Labels, metadata with slight emphasis
--font-weight-semibold 600 Section headings, button text
--font-weight-bold 700 Page titles, badges, strong emphasis

Spacing

All spacing tokens follow a 4px base grid. Use these tokens for padding, margin, and gap.

Token Value Pixels Common Usage
--spacing-0 0 0 Reset
--spacing-px 1px 1px Hairline borders, fine adjustments
--spacing-0-5 0.125rem 2px Micro gaps
--spacing-1 0.25rem 4px Icon padding, tight inline spacing
--spacing-1-5 0.375rem 6px Dense list items
--spacing-2 0.5rem 8px Default inner padding small elements
--spacing-2-5 0.625rem 10px Medium-small gap
--spacing-3 0.75rem 12px Badge padding, compact list items
--spacing-4 1rem 16px Standard inner padding (cards, inputs)
--spacing-5 1.25rem 20px Section spacing
--spacing-6 1.5rem 24px Card gap, form field spacing
--spacing-7 1.75rem 28px Wide gap
--spacing-8 2rem 32px Major section padding
--spacing-10 2.5rem 40px Large section separation
--spacing-12 3rem 48px Page-level vertical rhythm
--spacing-16 4rem 64px Hero-level spacing

Border Radius

Token Value Pixels Usage
--radius-sm 0.25rem 4px Small elements: badges, keyboard keys
--radius-md 0.375rem 6px Inputs, buttons, dropdowns, popovers
--radius-lg 0.5rem 8px Cards, modals, dialog sections
--radius-circle 50% -- Circular elements (avatar, icon button)
--radius-full 9999px -- Pills: tags, status badges

Transitions

Token Value Usage
--transition-fast 0.1s ease Opacity fade (sidebar close button)
--transition-normal 0.15s ease Most interactive elements (buttons, inputs)
--transition-medium 0.2s ease Hover effects on larger elements
--transition-slow 0.3s ease Sidebar slide, modals

Composite Transition Tokens

Pre-built multi-property transitions for consistency across components:

Token Value Usage
--transition-input border-color 0.15s ease, box-shadow 0.15s ease Form input focus transitions
--transition-button background-color 0.15s ease, box-shadow 0.15s ease Button background + shadow
--transition-button-border background-color 0.15s ease, border-color 0.15s ease Outline button hover

Z-Index Scale

Token Value Usage
--z-dropdown 10 Dropdown menus, date pickers
--z-overlay 50 Backdrop overlays (below sidebar)
--z-sidebar 100 Mobile sidebar panel
--z-modal 1000 Modal dialogs (above everything)

Component Patterns

How to Use Tokens in CSS Modules

Always reference Layer 2 semantic tokens with var():

/* CORRECT */
.card {
  background: var(--color-bg-primary);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--spacing-6);
  color: var(--color-text-body);
}

/* WRONG — hardcoded values break dark mode */
.card {
  background: #ffffff;
  border: 1px solid #e5e7eb;
}

When to Use Semantic vs Palette Tokens

  • Always prefer semantic tokens — they adapt automatically to dark mode
  • Use palette tokens directly only when creating a new component that genuinely needs a specific hue not yet covered by a semantic alias (e.g., a new badge type)
  • If you find yourself using palette tokens repeatedly for the same purpose, add a semantic alias to tokens.css at all 3 layers

Status Badge Pattern

Use the --color-status-* token family for work item status indicators. Apply --radius-full for pill shape:

.badge {
  display: inline-flex;
  align-items: center;
  padding: var(--spacing-1) var(--spacing-3);
  border-radius: var(--radius-full);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
}

.notStarted {
  background: var(--color-status-not-started-bg);
  color: var(--color-status-not-started-text);
}

.inProgress {
  background: var(--color-status-in-progress-bg);
  color: var(--color-status-in-progress-text);
}

.completed {
  background: var(--color-status-completed-bg);
  color: var(--color-status-completed-text);
}

.blocked {
  background: var(--color-status-blocked-bg);
  color: var(--color-status-blocked-text);
}

Form Input Pattern

Standard input with focus ring and error state:

.input {
  width: 100%;
  padding: var(--spacing-2) var(--spacing-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-bg-primary);
  color: var(--color-text-primary);
  font-size: var(--font-size-base);
  transition: var(--transition-input);
}

.input::placeholder {
  color: var(--color-text-placeholder);
}

.input:focus {
  outline: none;
  border-color: var(--color-border-focus);
  box-shadow: var(--shadow-focus);
}

/* Error state */
.inputError {
  border-color: var(--color-danger-input-border);
}

.inputError:focus {
  box-shadow: var(--shadow-focus-danger);
}

Button Pattern

Three button variants — primary, danger, and outline:

/* Primary button */
.buttonPrimary {
  background: var(--color-primary);
  color: var(--color-primary-text);
  border: none;
  border-radius: var(--radius-md);
  padding: var(--spacing-2) var(--spacing-4);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  cursor: pointer;
  transition: var(--transition-button);
}

.buttonPrimary:hover {
  background: var(--color-primary-hover);
}

.buttonPrimary:active {
  background: var(--color-primary-active);
}

.buttonPrimary:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

/* Danger button */
.buttonDanger {
  background: var(--color-danger);
  color: var(--color-danger-text);
  /* ... same shape/font/cursor tokens ... */
}

.buttonDanger:hover {
  background: var(--color-danger-hover);
}
.buttonDanger:active {
  background: var(--color-danger-active);
}
.buttonDanger:focus-visible {
  box-shadow: var(--shadow-focus-danger);
}

/* Outline / secondary button */
.buttonOutline {
  background: transparent;
  color: var(--color-text-primary);
  border: 1px solid var(--color-border-strong);
  /* ... same shape/font/cursor tokens ... */
  transition: var(--transition-button-border);
}

.buttonOutline:hover {
  background: var(--color-bg-hover);
  border-color: var(--color-border-focus);
}

.buttonOutline:focus-visible {
  box-shadow: var(--shadow-focus-subtle);
}

Touch Target Sizes

All interactive elements on mobile and tablet must meet the 44px minimum touch target:

@media (max-width: 1024px) {
  .button {
    min-height: 44px;
  }
}

NavLink Active State (CSS Modules + React Router)

React Router does not add a literal active class when using CSS Modules. Use the function form:

<NavLink
  to="/work-items"
  className={({ isActive }) => `${styles.navLink} ${isActive ? styles.active : ''}`}
>
  Work Items
</NavLink>

Dark Mode Implementation Guide

How the 3-Layer System Works

tokens.css :root {
  --color-bg-primary: #ffffff;          /* Layer 1 + 2: palette alias */
}

tokens.css [data-theme="dark"] {
  --color-bg-primary: #1a1a2e;          /* Layer 3: dark override */
}

MyComponent.module.css {
  .card { background: var(--color-bg-primary); }   /* no change needed */
}

When document.documentElement.dataset.theme = "dark", the browser's CSS cascade automatically picks up the [data-theme="dark"] overrides. No JavaScript or class toggling in components.

ThemeContext

Location: client/src/contexts/ThemeContext.tsx

import { useTheme } from '../../contexts/ThemeContext.js';

function MyComponent() {
  const { theme, resolvedTheme, setTheme } = useTheme();
  // theme: 'light' | 'dark' | 'system'  (user preference)
  // resolvedTheme: 'light' | 'dark'       (actual applied theme)
}

Types exported:

  • ThemePreference = 'light' | 'dark' | 'system'
  • ResolvedTheme = 'light' | 'dark'
  • ThemeContextValue — shape of the context value

Provider setup (already in App.tsx):

<BrowserRouter>
  <ThemeProvider>
    <AuthProvider>{/* app content */}</AuthProvider>
  </ThemeProvider>
</BrowserRouter>

ThemeToggle Component

Location: client/src/components/ThemeToggle/ThemeToggle.tsx

A single button that cycles through Light → Dark → System preferences. It is placed inside the sidebar footer and is the only UI affordance for changing the theme.

Cycle order: lightdarksystemlight → ...

Button label shows the current theme; aria-label announces the next theme (what clicking will do):

aria-label="Switch to Dark mode"   (when current is Light)
aria-label="Switch to System mode" (when current is Dark)
aria-label="Switch to Light mode"  (when current is System)

localStorage Persistence

The theme preference is stored under the key 'theme' in localStorage. Valid values: 'light', 'dark', 'system'. Defaults to 'system' if not set or invalid.

System Preference Detection

When theme === 'system', ThemeContext reads window.matchMedia('(prefers-color-scheme: dark)') and subscribes to change events. The resolved theme updates automatically when the OS switches between light and dark mode.

How to Add New Dark Mode Tokens

When adding a new semantic token that needs a dark mode override:

  1. Layer 2 — Add the token in :root with a light-mode value referencing a palette token:

    :root {
      --color-my-new-token: var(--color-blue-100);
    }
  2. Layer 3 — Add the override in [data-theme="dark"]:

    [data-theme='dark'] {
      --color-my-new-token: rgba(59, 130, 246, 0.15);
    }
  3. Use in components as normal:

    .element {
      background: var(--color-my-new-token);
    }

Test Infrastructure Note

jsdom (used by Jest) does not implement window.matchMedia. The test setup at client/src/test/setupTests.ts provides a polyfill. When writing components that call useTheme(), tests must either:

  • Wrap in <ThemeProvider> (integration-style), or
  • Mock the ThemeContext module with jest.unstable_mockModule()

Brand Identity

Logo

Component: client/src/components/Logo/Logo.tsx

The Cornerstone logo is a keystone / arch motif — the central wedge stone of an arch, flanked by two column blocks on a shared base. It represents the product's role as the foundational element of a home-building project.

Design principles:

  • Drawn as a single compound path using the even-odd fill rule (fillRule="evenodd" clipRule="evenodd") so the arch opening is transparent (no background colour required)
  • Uses fill="currentColor"no hardcoded hex values in SVG attributes
  • Control the colour via CSS on the wrapping element: color: var(--color-sidebar-focus-ring)
  • Accessible: role="img" and aria-label="Cornerstone" for screen readers

Usage:

import { Logo } from '../../components/Logo/Logo.js';

// In sidebar (inherits white color from sidebar text context)
<Logo size={32} className={styles.logo} />;

Sizes: The logo renders cleanly from 16px (very small) to 200px+. Default size prop is 32.

Color context:

  • Sidebar: wrap in an element with color: var(--color-sidebar-text) — logo becomes white
  • Light page: wrap in an element with color: var(--color-text-primary) — logo becomes dark
  • No hardcoded color — always inherits from context

Favicon

File: client/public/favicon.svg

The favicon is a standalone SVG that does NOT use CSS custom properties (browser tab icons don't process CSS variables). It uses explicit #3b82f6 (blue-500) fill on the keystone path.

The CopyWebpackPlugin in client/webpack.config.cjs copies client/public/ into the dist/ output directory so the favicon is served correctly in both development and production.

Color Identity

The brand primary color is blue-500 (#3b82f6). This appears in:

  • The favicon fill
  • Primary action buttons
  • Active nav item in the sidebar
  • Focus borders on form inputs
  • The --color-primary semantic token (light mode)

Last updated: EPIC-12 Story 12.5 — Style Guide Documentation Managed by the ux-designer agent

Clone this wiki locally