# Pulse Modern Design System 🎨 This document outlines the standard UI primitives, tokens, and components that conform to Pulse's single-source-of-truth semantic design spec. **Goal:** All new pages, panels, toggles, buttons, and text should map exactly to these shared specifications instead of relying on manually typing raw Tailwind color configs (like `bg-gray-800` or `#1a1c23`). ## Core Principles 1. **Never hardcode hex values** or use static `gray` / `white` / `slate` labels for structural layout colors. 2. Use the **Semantic Tokens**. These are declared as Tailwind `@theme` colors in `index.css` and resolve through its CSS variables to flawlessly support light/dark transitions without needing a literal `dark:` prefix in the layout classes. ## Enforcement - `npm run lint` is a hard gate for design-system regressions in `src/components` and `src/pages`. - `npm run lint:theme` is a hard gate for theme governance across the whole frontend (`src/**` + `index.html`). - `npm run lint:headers` audits header composition and page-level header usage policy. - Routed surfaces must use shared header primitives (`PageHeader`, `SectionHeader`, `SettingsPanel`, `OperationsPanel`) instead of raw `

` markup. - Visible form labels must either use `for` with a matching control ID or wrap a native labelable control; use a heading, paragraph, or span for non-control captions. - Shared form controls must keep a stable accessible name as their value changes; a placeholder is input guidance, not a label. - Compact pointer targets must remain at least 24 by 24 CSS pixels, including icon and copy actions, per [WCAG 2.2 Target Size (Minimum)](https://www.w3.org/TR/WCAG22/#target-size-minimum). - Product motion must honor the operating-system `prefers-reduced-motion` setting. The global stylesheet completes animations and transitions immediately under that preference; do not override that safeguard or make motion the only indication of state. - Only explicit non-visual route wrappers are exempt from header primitive requirements. - Theme ownership policy: - Only `src/utils/theme.ts` and `index.html` may read/write theme keys (`pulseThemePreference`, `darkMode`, `pulse_dark_mode`) or toggle the root `dark` class. - Full-screen shells (`min-h-screen`) must use semantic backgrounds (`bg-base`, `bg-surface`, `bg-surface-alt`) instead of hardcoded palettes. - CI must fail on: - orphaned utility prefixes (for example `dark:hover:` or `group-hover:` with no class after them) - `dark:bg-white` / `dark:hover:bg-white` - low-contrast class combinations like `bg-base` + `text-white` - theme owner violations or non-semantic full-screen backgrounds reported by `lint:theme` - If a UI requires exceptional styling, prefer adding a semantic token or shared component rather than bypassing the rule. --- ## 🏗 Tokens Use standard Tailwind prefixes with these dynamic keys: | Token | Utility Example | Represents | When to Use | |:---|:---|:---|:---| | `base` | `bg-base` | Application background | The absolute bottom layer of the page, or inactive tracker backgrounds. | | `surface` | `bg-surface` | Elevated structural elements | Cards, panels, dialog boxes, standard button backgrounds, table headers. | | `surface-hover` | `hover:bg-surface-hover` | Interactive state | When a table row, button, or list item is hovered or active. | | `surface-alt` | `bg-surface-alt` | Slightly elevated surface | Selected states, inline badges, secondary panels, form group backgrounds. | | `border-subtle` | `border-border-subtle` | Faint divider | Very subtle dividers that don't need sharp contrast. | | `border` | `border-border` | Standard structure borders | Outline for panels, buttons, dialog boxes, and inputs. | | `base-content` | `text-base-content` | Primary readable text | The main text color instead of absolute black/white. | | `muted` | `text-muted` | De-emphasized text | Secondary tags, descriptions, disabled labels, or table column headers. | **Example of what NOT to do:** ```tsx // ❌ BAD
``` **Example of the spec:** ```tsx // ✅ GOOD
``` --- ## 🧩 CSS Component Classes Reusable component classes defined in `index.css` using `@layer base`. Use these instead of repeating the same token combinations: | Class | What it provides | Use for | |:---|:---|:---| | `.card` | `bg-surface border border-border shadow-sm rounded-md` | Content panels, dialog boxes | | `.form-input` | Full input styling (border, bg, text, focus ring, disabled state) | Text inputs, textareas | | `.form-select` | Same as `.form-input` + dropdown arrow padding | Select dropdowns | | `.table-row` | `border-b border-border hover:bg-surface-hover` | Table body rows | ```tsx // ❌ BAD — repeating tokens manually // ✅ GOOD — use the component class ``` --- ## 🧩 Standard Components Before building a native toggle or stylized button from scratch, heavily favor the standardized layout elements contained in `/src/components/shared/`. ### 1. `Card` (`/src/components/shared/Card.tsx`) The absolute core container for content across Pulse. Automatically maps to `bg-surface` and standard borders/shadows/padding. ```tsx import { Card } from '@/components/shared/Card'; Main metric panel content! ``` ### 2. `Button` (`/src/components/shared/Button.tsx`) A unified standard button built on semantic tokens instead of inline styling. **Variants:** `primary` | `secondary` (default) | `danger` | `ghost` | `outline` ```tsx import { Button } from '@/components/shared/Button'; ``` ### 3. `Toggle` (`/src/components/shared/Toggle.tsx`) Dynamic switch mapped to `base`, `surface`, and `blue`. ```tsx import { Toggle } from '@/components/shared/Toggle'; ``` ### 4. `FilterButtonGroup` (`/src/components/shared/FilterButtonGroup.tsx`) Segmented radio-style navigation controls. Uses `muted` for inactive buttons, and maps the active card to a highlighted `surface`. ```tsx import { FilterButtonGroup } from '@/components/shared/FilterButtonGroup'; ``` ### 5. `Table` primitives (`/src/components/shared/Table.tsx`) Wraps the raw HTML table structure with strict structural tags dynamically handling their hover states, borders, font weights, and tracking properties. ```tsx import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from '@/components/shared/Table'; Status Online
``` ### 6. Form utilities (`/src/components/shared/Form.ts`) Pre-composed class strings for form elements. Import individual exports instead of composing tokens manually. ```tsx import { formControl, formSelect, formLabel, formField, formCheckbox } from '@/components/shared/Form';
```