diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..8755b66 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,586 @@ +--- +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: "#69B3E7" + blue-04: "#CBEDFD" + blue-05: "#EBF5FF" + red-01: "#3E0014" + red-02: "#D7153A" + red-03: "#F5C5D0" + red-04: "#FDDDE5" + red-05: "#FFF5F8" + orange-01: "#7A3300" + orange-02: "#EC6608" + orange-03: "#F5B98A" + orange-04: "#FEF0E4" + green-01: "#005C35" + green-02: "#00A651" + green-03: "#89E5B3" + green-04: "#E0F8EA" + grey-01: "#22272B" + grey-02: "#6D7278" + grey-03: "#C0C0C0" + grey-04: "#E0E0E0" + off-white: "#F4F4F4" + white: "#FFFFFF" + # Semantic (use these in components) + primary: "{colors.blue-02}" + primary-dark: "{colors.blue-01}" + 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.off-white}" + surface: "{colors.white}" +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: 6px + lg: 10px + xl: 16px + full: 9999px +spacing: + unit: 4px + scale: "Tailwind default (4px base: 1=4px, 2=8px, 3=12px, 4=16px, 6=24px, 8=32px)" +components: + button-primary-default: + backgroundColor: "{colors.primary-dark}" + textColor: "{colors.white}" + typography: "{typography.body}" + rounded: "{rounded.DEFAULT}" + height: 48px + button-primary-danger: + backgroundColor: "{colors.red-02}" + textColor: "{colors.white}" + button-secondary-default: + backgroundColor: transparent + textColor: "{colors.primary-dark}" + input: + backgroundColor: "{colors.white}" + textColor: "{colors.text}" + typography: "{typography.body}" + rounded: "{rounded.DEFAULT}" + height: 48px + card-surface: + backgroundColor: "{colors.surface}" + rounded: "{rounded.xl}" + badge-info: + backgroundColor: "{colors.blue-02}" + textColor: "{colors.white}" + rounded: "{rounded.full}" + alert-info: + backgroundColor: "{colors.blue-05}" + rounded: "{rounded.DEFAULT}" +--- + +## 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 blue/grey palette with semantic colour 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 three layers: + +1. **Palette** — Raw values (`blue-01` through `blue-05`, `grey-01` through `grey-04`, etc.). Never use these directly in component code. +2. **Semantic** — Purpose-based aliases (`primary`, `error`, `success`, `warning`, `text`, `surface`, `bg`, `border`). Use these for general UI. +3. **Domain** — Component-specific tokens (`button-default`, `control-border`, `badge-info`, `alert-error-bg`, `tag-navy`, etc.). Use these within their respective components. + +The primary brand colour is `blue-01` (#002664, dark navy) for interactive elements like buttons and links. `blue-02` (#146CFD) is the brighter accent used 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. This may change in future. + +## 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` (6px) — buttons, inputs, alerts +- `rounded-lg` (10px) — cards, dialogs +- `rounded-xl` (16px) — large containers +- `rounded-full` (9999px) — badges, circular buttons + +## Components + +### 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' + +