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
| File | Purpose |
|---|---|
packages/shared/styles/globals.css | Shared entry point — imports all partials |
packages/shared/styles/_variables.css | All CSS custom properties (tokens) |
packages/shared/styles/_typography.css | Semantic typography component classes |
packages/shared/styles/_layout.css | Logo, header, sidebar structural classes |
packages/shared/styles/_utilities.css | Brand color utility classes |
packages/shared/styles/_workflow-nodes.css | ReactFlow workflow node colors |
packages/shared/styles/_anna.css | Anna AI popup-specific styles |
packages/shared/styles/_forms.css | Form component styles |
src/app/globals.css | App-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
| Token | Light | Dark | Purpose |
|---|---|---|---|
--background | 0 0% 100% | 240 10% 3.9% | Page background |
--foreground | 240 10% 3.9% | 0 0% 98% | Primary text |
--card | 0 0% 100% | 240 10% 3.9% | Card background |
--card-foreground | 240 10% 3.9% | 0 0% 98% | Card text |
--popover | 0 0% 100% | 240 10% 3.9% | Popover background |
--popover-foreground | 240 10% 3.9% | 0 0% 98% | Popover text |
Brand / Primary Tokens
| Token | Light | Dark | Purpose |
|---|---|---|---|
--primary | 127 43% 29% (#2B6B32) | 127 45% 40% | Anshin Green — CTAs, active states |
--primary-foreground | 0 0% 98% | 0 0% 98% | Text on primary |
Semantic State Tokens
| Token | Light Value | Purpose |
|---|---|---|
--secondary | 240 4.8% 95.9% | Secondary surfaces |
--muted | 240 4.8% 95.9% | Muted backgrounds |
--muted-foreground | 240 3.8% 46.1% | Secondary text, placeholders |
--accent | 240 4.8% 95.9% | Hover states |
--destructive | 0 55% 45% | Errors, delete actions |
--success | 127 43% 29% | Success states (same as primary) |
--warning | 45 93% 47% | Warning states (amber) |
--info | 199 89% 48% | Info states (sky blue) |
Border / Input Tokens
| Token | Value | Purpose |
|---|---|---|
--border | 240 5.9% 90% | Default borders |
--input | 240 5.9% 90% | Input field borders |
--ring | 240 5.9% 10% | Focus rings |
--radius | 0.625rem | Default 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:
| Token | Light | Dark | Color |
|---|---|---|---|
--chart-1 | 220 70% 50% | 220 65% 60% | Blue |
--chart-2 | 160 60% 45% | 160 55% 55% | Teal |
--chart-3 | 30 80% 55% | 30 75% 60% | Orange |
--chart-4 | 280 65% 60% | 280 60% 65% | Purple |
--chart-5 | 340 75% 55% | 340 70% 60% | Pink |
--chart-success | 127 43% 40% | 127 43% 50% | Green |
--chart-warning | 45 90% 50% | 45 85% 55% | Amber |
--chart-danger | 0 70% 55% | 0 65% 60% | Red |
Sidebar Tokens
Used by the sidebar component (shadcn Sidebar):
| Token | Purpose |
|---|---|
--sidebar-background | Sidebar panel background |
--sidebar-foreground | Sidebar text |
--sidebar-primary | Active nav item background |
--sidebar-primary-foreground | Active nav item text |
--sidebar-accent | Hover state |
--sidebar-accent-foreground | Hover text |
--sidebar-border | Sidebar border |
--sidebar-ring | Focus ring in sidebar |
--sidebar-width | 16rem (expanded) |
--sidebar-width-mobile | 18rem |
--sidebar-width-collapsed | 4rem (icon-only) |
Typography Tokens
Used by semantic typography classes (not inline Tailwind):
| Token | Value | Usage |
|---|---|---|
--font-sans | var(--font-inter, ui-sans-serif, ...) | Base font |
--header-title-font-size | 1.125rem | .header-title |
--header-title-font-weight | 600 | .header-title |
--header-title-letter-spacing | -0.025em | .header-title |
--body-font-size | 0.875rem | .body-text |
--caption-font-size | 0.75rem | .caption-text |
--header-height | 4rem | Header height constraint |
Sizing Tokens
Icon Sizing
| Token | Value | Usage |
|---|---|---|
--icon-size-sm | 1rem | Small icons |
--icon-size-md | 1.25rem | Medium icons |
--icon-size-lg | 1.5rem | Large icons |
--icon-stroke-width | 1.5 | Lucide stroke width |
--header-icon-size | 1.25rem | Header icon buttons |
--header-button-size | 2.25rem (= h-9 w-9) | Header button size |
--header-avatar-size | 2rem | User avatar in header |
--sidebar-icon-size | 1rem | Nav item icons |
Logo Sizing
| Token | Value | Usage |
|---|---|---|
--logo-header-width | 12.5rem | Header logo max-width |
--logo-header-height | 2.25rem | Header logo height |
--logo-login-size | 5rem | Login page shield logo |
--logo-sidebar-size | 2.5rem | Sidebar logo square |
Spacing
| Token | Value | Usage |
|---|---|---|
--content-padding | 1.5rem | Page content padding |
--content-gap | 1.5rem | Between content sections |
--card-padding | 1.5rem | Card internal padding |
--section-gap | 1.5rem | Between sections |
--element-gap | 1rem | Between elements |
Workflow Node Color Tokens
Used by _workflow-nodes.css and the ReactFlow canvas:
| Node Type | --node-* base | Color |
|---|---|---|
| Start | --node-start | Green (142 76% 36%) |
| End (Success) | --node-end-success | Green |
| End (Failure) | --node-end-failure | Red (0 84% 60%) |
| End (Cancelled) | --node-end-cancelled | Gray |
| DocType Workflow | --node-workflow | Blue (217 91% 60%) |
| Subprocess | --node-subprocess | Purple (262 83% 58%) |
| Gateway | --node-gateway | Amber (38 92% 50%) |
| Action | --node-action | Emerald (160 84% 39%) |
| Timer | --node-timer | Indigo (239 84% 67%) |
| Signal | --node-signal | Pink (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
-
@tailwinddirectives ONLY in app-levelglobals.css - Shared styles imported via
@importfrompackages/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-loginclasses - Header buttons use token-based sizing:
h-9 w-9(=--header-button-size) - Chart colors use
--chart-*tokens only - Dark mode via
next-themeswithattribute="class"— NOTmediastrategy - Workflow node colors use
--node-*tokens from_variables.css - Anna popup colors use
--anna-*tokens