Skip to main content

UX Standards — 24: Global CSS and Design Tokens

Governs: CSS architecture, design token variables, shared stylesheet structure, and Tailwind CSS conventions. Parent rules: See 00-OVERVIEW-AND-CSS-RULES.md first.


CSS Architecture Overview

The frontend uses a layered CSS architecture:

app/globals.css (app-level)
└── @tailwind base, components, utilities
└── @import packages/shared/styles/globals.css

packages/shared/styles/globals.css (shared layer)
└── @import ./_variables.css ← Design tokens (CSS custom properties)
└── @import ./_typography.css ← Semantic typography classes
└── @import ./_layout.css ← Logo, header, sidebar component classes
└── @import ./_utilities.css ← Brand color utility classes
└── @import ./_workflow-nodes.css ← Workflow canvas node styles
└── @import ./_anna.css ← Anna AI popup styles
└── @import ./_forms.css ← Form component styles
└── * { @apply border-border }
└── body { @apply bg-background text-foreground; font-family: var(--font-sans) }

Rule: All Tailwind @tailwind directives are declared in the app-level globals.css — not in the shared package. This allows the shared package to be imported by multiple apps without duplication.


File Locations

FilePurpose
packages/shared/styles/globals.cssShared entry point — imports all partials
packages/shared/styles/_variables.cssAll CSS custom properties (tokens)
packages/shared/styles/_typography.cssSemantic typography component classes
packages/shared/styles/_layout.cssLogo, header, sidebar structural classes
packages/shared/styles/_utilities.cssBrand color utility classes
packages/shared/styles/_workflow-nodes.cssReactFlow workflow node colors
packages/shared/styles/_anna.cssAnna AI popup-specific styles
packages/shared/styles/_forms.cssForm component styles
src/app/globals.cssApp-level entry — Tailwind directives + app-specific overrides

Design Token System

All colors, sizing, and spacing use CSS custom properties (HSL values) defined in _variables.css. Tailwind classes reference these via the hsl(var(--token)) pattern.

RULE: Never use hardcoded colors. Always use semantic tokens.

// ✅ CORRECT — uses semantic token
className="bg-primary text-primary-foreground"

// ❌ WRONG — hardcoded color
className="bg-green-600 text-white"

Core Theme Tokens

Defined in :root (light mode) and overridden in .dark:

Surface Tokens

TokenLightDarkPurpose
--background0 0% 100%240 10% 3.9%Page background
--foreground240 10% 3.9%0 0% 98%Primary text
--card0 0% 100%240 10% 3.9%Card background
--card-foreground240 10% 3.9%0 0% 98%Card text
--popover0 0% 100%240 10% 3.9%Popover background
--popover-foreground240 10% 3.9%0 0% 98%Popover text

Brand / Primary Tokens

TokenLightDarkPurpose
--primary127 43% 29% (#2B6B32)127 45% 40%Anshin Green — CTAs, active states
--primary-foreground0 0% 98%0 0% 98%Text on primary

Semantic State Tokens

TokenLight ValuePurpose
--secondary240 4.8% 95.9%Secondary surfaces
--muted240 4.8% 95.9%Muted backgrounds
--muted-foreground240 3.8% 46.1%Secondary text, placeholders
--accent240 4.8% 95.9%Hover states
--destructive0 55% 45%Errors, delete actions
--success127 43% 29%Success states (same as primary)
--warning45 93% 47%Warning states (amber)
--info199 89% 48%Info states (sky blue)

Border / Input Tokens

TokenValuePurpose
--border240 5.9% 90%Default borders
--input240 5.9% 90%Input field borders
--ring240 5.9% 10%Focus rings
--radius0.625remDefault border radius

Brand Color Tokens

Reserved for brand-specific elements only (logos, brand accents — NOT for general UI):

--anshin-green: 127 43% 29%; /* #2B6B32 */
--anshin-green-light: 127 45% 40%; /* #30A46C — dark mode variant */
--anshin-blue: 233 36% 50%; /* #515EAF */
--anshin-gray: 0 0% 61%; /* #9C9C9C — secondary text */

Chart Color Tokens

Used exclusively by data visualization components:

TokenLightDarkColor
--chart-1220 70% 50%220 65% 60%Blue
--chart-2160 60% 45%160 55% 55%Teal
--chart-330 80% 55%30 75% 60%Orange
--chart-4280 65% 60%280 60% 65%Purple
--chart-5340 75% 55%340 70% 60%Pink
--chart-success127 43% 40%127 43% 50%Green
--chart-warning45 90% 50%45 85% 55%Amber
--chart-danger0 70% 55%0 65% 60%Red

Used by the sidebar component (shadcn Sidebar):

TokenPurpose
--sidebar-backgroundSidebar panel background
--sidebar-foregroundSidebar text
--sidebar-primaryActive nav item background
--sidebar-primary-foregroundActive nav item text
--sidebar-accentHover state
--sidebar-accent-foregroundHover text
--sidebar-borderSidebar border
--sidebar-ringFocus ring in sidebar
--sidebar-width16rem (expanded)
--sidebar-width-mobile18rem
--sidebar-width-collapsed4rem (icon-only)

Typography Tokens

Used by semantic typography classes (not inline Tailwind):

TokenValueUsage
--font-sansvar(--font-inter, ui-sans-serif, ...)Base font
--header-title-font-size1.125rem.header-title
--header-title-font-weight600.header-title
--header-title-letter-spacing-0.025em.header-title
--body-font-size0.875rem.body-text
--caption-font-size0.75rem.caption-text
--header-height4remHeader height constraint

Sizing Tokens

Icon Sizing

TokenValueUsage
--icon-size-sm1remSmall icons
--icon-size-md1.25remMedium icons
--icon-size-lg1.5remLarge icons
--icon-stroke-width1.5Lucide stroke width
--header-icon-size1.25remHeader icon buttons
--header-button-size2.25rem (= h-9 w-9)Header button size
--header-avatar-size2remUser avatar in header
--sidebar-icon-size1remNav item icons

Logo Sizing

TokenValueUsage
--logo-header-width12.5remHeader logo max-width
--logo-header-height2.25remHeader logo height
--logo-login-size5remLogin page shield logo
--logo-sidebar-size2.5remSidebar logo square

Spacing

TokenValueUsage
--content-padding1.5remPage content padding
--content-gap1.5remBetween content sections
--card-padding1.5remCard internal padding
--section-gap1.5remBetween sections
--element-gap1remBetween elements

Workflow Node Color Tokens

Used by _workflow-nodes.css and the ReactFlow canvas:

Node Type--node-* baseColor
Start--node-startGreen (142 76% 36%)
End (Success)--node-end-successGreen
End (Failure)--node-end-failureRed (0 84% 60%)
End (Cancelled)--node-end-cancelledGray
DocType Workflow--node-workflowBlue (217 91% 60%)
Subprocess--node-subprocessPurple (262 83% 58%)
Gateway--node-gatewayAmber (38 92% 50%)
Action--node-actionEmerald (160 84% 39%)
Timer--node-timerIndigo (239 84% 67%)
Signal--node-signalPink (330 81% 60%)

Each node type has 3 variants: --node-{type} (base), --node-{type}-bg (fill), --node-{type}-border (stroke).


Anna AI Tokens

Used by the /anna-popup route:

--anna-gradient-start: 160 84% 39%; /* emerald-600 */
--anna-gradient-mid: 142 71% 45%; /* green-500 */
--anna-gradient-end: 174 84% 32%; /* teal-600 */
--anna-accent-start: 160 84% 60%; /* emerald-400 */
--anna-accent-end: 187 92% 69%; /* cyan-400 */
--anna-titlebar-bg: 222 47% 11%; /* slate-900 */
--anna-titlebar-text: 160 60% 80%; /* emerald-300 */
--anna-user-msg: 160 84% 39%; /* User message bubble */
--anna-assistant-msg: 151 81% 96%; /* Anna response bubble */
--anna-context-bar-bg: 220 14% 96%; /* Context bar background */

Semantic Typography Classes (_typography.css)

Use these classes instead of inline Tailwind font utilities for consistent typography across components:

.header-title /* Page/section header: 1.125rem, fw-600, ls -0.025em */
.card-title /* Card/dialog title: 1.25rem, fw-600, ls -0.025em */
.card-description /* Card subtitle: 0.875rem, fw-400, muted-foreground */
.form-label /* Field label: 0.875rem, fw-500 */
.body-text /* Body copy: 0.875rem, fw-400 */
.footer-text /* Footer: 0.75rem, fw-400, muted */
.caption-text /* Captions, help text: 0.75rem, muted */

Layout Component Classes (_layout.css)

Structural classes for logo, header, and sidebar elements:

/* Logo variants */
.logo-header /* width: 12.5rem, height: 2.25rem — header wordmark */
.logo-login /* width: 5rem — login page shield */
.logo-sidebar /* width/height: 2.5rem — sidebar square */

/* Header icons */
.header-icon-button /* h-9 w-9 ghost button style */
.header-icon /* h-5 w-5 icon inside header button */
.header-avatar /* h-8 w-8 rounded avatar */

/* Sidebar */
.sidebar-icon /* h-4 w-4 nav item icon */
.sidebar-nav-active /* Active nav state */
.sidebar-nav-hover /* Hover nav state */

Utility Classes (_utilities.css)

Brand color utilities that map to semantic tokens:

.bg-anshin-green → hsl(var(--primary))
.text-anshin-green → hsl(var(--primary))
.bg-success → hsl(var(--success))
.text-success → hsl(var(--success))
.bg-warning → hsl(var(--warning))
.text-warning → hsl(var(--warning))
.bg-info → hsl(var(--info))
.text-info → hsl(var(--info))

App-Level globals.css Pattern

Each app's globals.css follows this pattern:

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

/* Import shared styles */
@import '../../../packages/shared/styles/globals.css';

/* App-specific overrides (if any) */

Tailwind Config Integration

The tailwind.config.ts maps semantic tokens to Tailwind utility classes:

colors: {
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))',
},
success: {
DEFAULT: 'hsl(var(--success))',
foreground: 'hsl(var(--success-foreground))',
},
// ... all semantic tokens
}

This allows Tailwind utilities like bg-primary, text-primary-foreground, bg-success, etc. to automatically use the CSS variable values, which change with the .dark class.


Dark Mode

Dark mode is applied by adding the dark class to <html>:

// next-themes (class strategy)
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>

The .dark class in _variables.css overrides all :root values. No additional CSS is needed in components — semantic tokens handle light/dark automatically.


Violation Checklist

  • All colors use semantic tokens — NEVER hardcode hex, rgb, or Tailwind color names
  • @tailwind directives ONLY in app-level globals.css
  • Shared styles imported via @import from packages/shared/styles/globals.css
  • Typography uses semantic classes (.header-title, .body-text) for headings/body
  • Brand colors (--anshin-*) only for logos and explicit brand elements
  • Logo sizing uses .logo-header, .logo-sidebar, .logo-login classes
  • Header buttons use token-based sizing: h-9 w-9 (= --header-button-size)
  • Chart colors use --chart-* tokens only
  • Dark mode via next-themes with attribute="class" — NOT media strategy
  • Workflow node colors use --node-* tokens from _variables.css
  • Anna popup colors use --anna-* tokens