--- name: ADS 3.0 Design System description: React component library implementing the NSW Department of Education's ADS 3.0 design language version: alpha colors: # Palette (do not reference directly in components) blue-01: "#002664" blue-02: "#146CFD" blue-03: "#8CE0FF" blue-04: "#CBEDFD" red-01: "#630019" red-02: "#D7153A" red-03: "#FFB8C1" red-04: "#FFE6EA" orange-01: "#941B00" orange-02: "#F3631B" orange-03: "#FFCE99" orange-04: "#FDEDDF" green-01: "#004000" green-02: "#00AA45" green-03: "#A8EDB3" green-04: "#DBFADF" teal-01: "#0B3F47" teal-02: "#2E808E" teal-03: "#8CDBE5" teal-04: "#D1EEEA" brown-01: "#523719" brown-02: "#B68D5D" brown-03: "#E8D0B5" brown-04: "#EDE3D7" purple-01: "#441170" purple-02: "#8055F1" purple-03: "#CEBFFF" purple-04: "#E6E1FD" fuchsia-01: "#65004D" fuchsia-02: "#D912AE" fuchsia-03: "#F4B5E6" fuchsia-04: "#FDDEF2" yellow-01: "#694800" yellow-02: "#FAAF05" yellow-03: "#FDE79A" yellow-04: "#FFF4CF" grey-01: "#22272B" grey-02: "#495054" grey-03: "#CDD3D6" grey-04: "#EBEBEB" grey-05: "#F2F2F2" white: "#FFFFFF" # Semantic (use these in components) primary: "{colors.blue-01}" info: "{colors.blue-02}" secondary: "{colors.blue-04}" error: "{colors.red-02}" success: "{colors.green-02}" warning: "{colors.orange-02}" text: "{colors.grey-01}" text-secondary: "{colors.grey-02}" border: "{colors.grey-04}" bg: "{colors.grey-05}" surface: "{colors.white}" # Form controls control-border: "{colors.grey-03}" control-border-hover: "{colors.grey-01}" control-checked: "{colors.blue-01}" control-checked-hover: "{colors.blue-02}" control-focus-ring: "{colors.blue-04}" control-label: "{colors.blue-01}" control-description: "{colors.grey-02}" control-error: "{colors.red-02}" control-bg: "{colors.white}" control-bg-readonly: "{colors.grey-05}" # Button button-default: "{colors.blue-01}" button-danger: "{colors.red-02}" button-neutral: "{colors.grey-01}" button-subtle-bg: "{colors.blue-04}" button-subtle-text: "{colors.blue-01}" # Switch switch-on: "{colors.green-02}" switch-on-hover: "{colors.green-01}" typography: h1: fontFamily: Public Sans Variable fontSize: 48px fontWeight: "700" lineHeight: 1.25 h2: fontFamily: Public Sans Variable fontSize: 32px fontWeight: "700" lineHeight: 1.25 h3: fontFamily: Public Sans Variable fontSize: 24px fontWeight: "600" lineHeight: 1.333 h4: fontFamily: Public Sans Variable fontSize: 20px fontWeight: "600" lineHeight: 1.4 h5: fontFamily: Public Sans Variable fontSize: 16px fontWeight: "600" lineHeight: 1.5 h6: fontFamily: Public Sans Variable fontSize: 14px fontWeight: "600" lineHeight: 1.43 intro: fontFamily: Public Sans Variable fontSize: 20px fontWeight: "400" lineHeight: 1.4 body: fontFamily: Public Sans Variable fontSize: 16px fontWeight: "400" lineHeight: 1.5 small: fontFamily: Public Sans Variable fontSize: 14px fontWeight: "400" lineHeight: 1.357 caption: fontFamily: Public Sans Variable fontSize: 12px fontWeight: "400" lineHeight: 1.5 rounded: sm: 4px DEFAULT: 8px lg: 16px xl: 24px full: 9999px spacing: unit: 4px scale: "Tailwind default (4px base: 1=4px, 2=8px, 3=12px, 4=16px, 6=24px, 8=32px)" --- ## Overview ADS 3.0 (Adaptive Design System) is a React component library based on the NSW Department of Education's digital design language. It uses Public Sans as its typeface, a structured colour palette with semantic aliases, and consistent border radii and spacing. Components are built with React 19, TypeScript (strict), and Tailwind CSS v4. All visual values come from design tokens defined in `src/tokens/tokens.css`. Components never use raw colour values — they reference semantic or domain-specific token classes via Tailwind utilities. ## Colors The palette is organised in layers, from raw to consumable: 1. **Palette** — Raw values across 10 colour families (blue, red, orange, green, teal, brown, purple, fuchsia, yellow, grey) with 4 shades each (`-01` darkest to `-04` lightest), plus grey-05 and white. Never use these directly in component code. 2. **Semantic** — Purpose-based aliases (`primary`, `info`, `secondary`, `error`, `success`, `warning`, `text`, `text-secondary`, `surface`, `bg`, `border`). Use these for general UI. 3. **Form control** — Shared interactive-state tokens for all form components: `control-border`, `control-border-hover`, `control-checked`, `control-checked-hover`, `control-focus-ring`, `control-label`, `control-description`, `control-error`, `control-bg`, `control-bg-readonly`. 4. **Button** — Intent tokens: `button-default` (navy), `button-danger` (red), `button-neutral` (dark grey), `button-subtle-bg`, `button-subtle-text`. 5. **Switch** — On-state tokens: `switch-on` (success green), `switch-on-hover`. 6. **Badge** — Status colour tokens: `badge-navy`, `badge-info`, `badge-info-light`, `badge-success`, `badge-success-light`, `badge-error`, `badge-error-light`, `badge-warning`, `badge-warning-light`, `badge-neutral`, plus contrast text tokens (`badge-on-success-light`, `badge-on-error-light`, `badge-on-warning-light`). 7. **Chip** — Border/fill state tokens: `chip-border`, `chip-text`, `chip-bg`, `chip-selected-bg`, `chip-selected-text`. 8. **Tag** — 11-colour system, each with a `-light` variant: navy, blue, green, red, orange, grey, teal, brown, purple, fuchsia, yellow. 9. **Alert** — Background, border, and icon tokens for 5 variants: `alert-{variant}-bg`, `alert-{variant}-border`, `alert-{variant}-icon` (info, warning, error, success, neutral). 10. **Avatar** — `avatar` (background), `avatar-text`. 11. **TopBar** — `topbar` (background, navy). 12. **SideNav** — `nav-bg`, `nav-text`, `nav-icon`, `nav-active`, `nav-divider`. The primary brand colour is `blue-01` (#002664, dark navy) used for buttons, links, and navigation. `blue-02` (#146CFD) is the brighter accent for info states, focus rings, and highlights. ## Typography The system uses **Public Sans Variable** (loaded via `@fontsource-variable/public-sans`). The type scale runs from `caption` (12px) to `h1` (48px). Headings use weight 600–700; body text uses 400. Use Tailwind's `text-{scale}` utilities: `text-h1`, `text-h2`, `text-h3`, `text-h4`, `text-h5`, `text-h6`, `text-intro`, `text-body`, `text-small`, `text-caption`. ## Layout & Spacing Spacing follows Tailwind's default 4px base scale. Common values: - `p-2` (8px), `p-3` (12px), `p-4` (16px), `p-6` (24px) for padding - `gap-2` (8px), `gap-3` (12px), `gap-4` (16px) for flex/grid gaps - `space-y-4` (16px) for stacked content No custom spacing tokens are defined — the Tailwind defaults are sufficient. ## Elevation & Depth Two shadow levels: - `shadow-default` — subtle lift for cards and surfaces (`0 1px 3px rgba(0,0,0,0.08)`) - `shadow-md` — elevated elements like dropdowns and dialogs (`0 4px 6px -1px rgba(0,0,0,0.1)`) ## Shapes Border radii use the custom scale: - `rounded-sm` (4px) — small elements, tags - `rounded-default` (8px) — buttons, inputs, alerts, chips - `rounded-lg` (16px) — cards, dialogs - `rounded-xl` (24px) — large containers, page headers - `rounded-full` (9999px) — badges, avatars, circular buttons ## Page Layout Pages are composed using the **AppShell** template, which provides the fixed TopBar, collapsible SideNav, and scrollable content area. Content within the main area follows these conventions: ### Shell structure ``` ┌─────────────────────────────────────────┐ │ TopBar (h-16, fixed top, full width) │ ├────────┬────────────────────────────────┤ │SideNav │ Content area (scrollable) │ │(w-[360]│ │ │ or │ PageHeader (optional) │ │ w-20 │ │ │collapsed│ Main content │ │) │ │ └────────┴────────────────────────────────┘ ``` ### Content patterns **Dashboard** — Use a responsive grid for content cards: - 2-column grid at `lg` breakpoint: `grid grid-cols-1 lg:grid-cols-2 gap-6` - Stat cards in a horizontal row: `flex gap-4` or `grid grid-cols-3 gap-4` - Content padding: `p-6` or `p-8` - Section spacing: `space-y-6` **List page** — Stat summary row above a scrollable list: - Stat cards row: `flex gap-4` with Card components - List section: Card wrapping a vertical list with `divide-y divide-border` - Each list item: `px-6 py-4` with flex layout for metadata **Form page** — Stepped form with vertical stepper: - Content max-width: `max-w-3xl` for readable line length - Form sections: `space-y-6` between groups - Stepper: vertical list on left, form content fills remaining space - Submit actions: right-aligned with `flex justify-end gap-3` ### Spacing conventions - Page content padding: `p-6` (24px) minimum - Section gap: `gap-6` (24px) between major sections - Card internal padding: `p-5` or `p-6` - Form field spacing: `space-y-4` (16px) between fields, `space-y-6` (24px) between groups --- ## Components ### Atoms #### Button Interactive button with variant/intent/size matrix. | Prop | Type | Default | Description | |------|------|---------|-------------| | `variant` | `'primary' \| 'secondary' \| 'tertiary'` | `'primary'` | Visual weight | | `intent` | `'default' \| 'danger' \| 'subtle' \| 'neutral'` | `'default'` | Semantic purpose | | `size` | `'default' \| 'comfortable' \| 'compact'` | `'default'` | Height and padding | | `loading` | `boolean` | `false` | Shows spinner, disables interaction | | `leftIcon` | `ReactNode` | — | Icon before label | | `rightIcon` | `ReactNode` | — | Icon after label | ```tsx import { Button } from '@/components/atoms/Button' ``` #### IconButton Icon-only button with required `aria-label`. | Prop | Type | Default | Description | |------|------|---------|-------------| | `variant` | `'primary' \| 'secondary' \| 'tertiary'` | `'primary'` | Visual weight | | `intent` | `'default' \| 'danger' \| 'neutral'` | `'default'` | Semantic purpose | | `size` | `'default' \| 'large' \| 'compact' \| 'small' \| 'xsmall'` | `'default'` | Button dimensions | | `shape` | `'circle' \| 'square'` | `'circle'` | Border radius | | `icon` | `ReactNode` | — | **Required.** The icon element | | `aria-label` | `string` | — | **Required.** Accessible label | ```tsx import { IconButton } from '@/components/atoms/IconButton' import { X, Settings } from 'lucide-react' } aria-label="Close" variant="tertiary" /> } aria-label="Settings" shape="square" size="compact" /> ``` #### Input Text input with label, description, hint, error, and icon slots. | Prop | Type | Default | Description | |------|------|---------|-------------| | `label` | `string` | — | **Required.** Visible label | | `description` | `string` | — | Help text below label | | `hint` | `string` | — | Inline hint (right-aligned) | | `error` | `string` | — | Error message (replaces description) | | `variant` | `'outlined' \| 'stacked'` | `'outlined'` | Label position | | `size` | `'default' \| 'compact'` | `'default'` | Input height | | `leftIcon` | `ReactNode` | — | Icon inside input (left) | | `rightIcon` | `ReactNode` | — | Icon inside input (right) | ```tsx import { Input } from '@/components/atoms/Input' import { Search, Mail } from 'lucide-react' } /> } /> ``` #### Textarea Multi-line text input with optional auto-resize. | Prop | Type | Default | Description | |------|------|---------|-------------| | `label` | `string` | — | **Required.** Visible label | | `description` | `string` | — | Help text below label | | `hint` | `string` | — | Inline hint | | `error` | `string` | — | Error message | | `variant` | `'outlined' \| 'stacked'` | `'outlined'` | Label position | | `resize` | `'vertical' \| 'horizontal' \| 'both' \| 'none'` | `'vertical'` | Resize handle | | `autoResize` | `boolean` | `false` | Auto-grow with content | ```tsx import { Textarea } from '@/components/atoms/Textarea'