main
Design Engineering Playground
A TypeScript project for small interface components, built the way a design engineer would build them. Each one uses shared design tokens, works with a keyboard and a screen reader, and has motion that respects the user's settings.
What's here
| Piece | Details worth noticing |
|---|---|
Design tokens (src/app/globals.css, src/tokens/tokens.ts) |
Semantic colour names such as surface, muted and accent, never raw hex values in components. Light, dark and system themes come from one variable swap. A test fails if a token is missing from any theme. |
| Button | Primary, secondary, ghost and danger variants. Spring press feedback. A loading state that keeps the button's width and its accessible name, and sets aria-busy. |
| Segmented control | A WAI-ARIA radio group with a roving tabindex. Arrow, Home and End keys work. One shared-layout indicator glides between options. |
| Switch | role="switch". Works controlled or uncontrolled through useControllableState. The label is clickable and the description is wired to aria-describedby. |
| Theme toggle | An inline pre-paint script means no flash on reload. Reads the saved preference through useSyncExternalStore, so hydration stays safe. |
| Motion system | Named springs and durations in tokens.ts. MotionConfig reducedMotion="user", plus a CSS fallback for reduced motion. |
Stack
Next.js 16 (App Router), React 19, TypeScript, Tailwind CSS v4, Motion, Vitest and Testing Library, ESLint, and Prettier with Tailwind class sorting.
Getting started
npm install
npm run dev # http://localhost:3000
| Script | What it does |
|---|---|
npm run check |
Runs typecheck, lint, format check and tests. Run it before pushing. |
npm test |
Unit and accessibility tests (Vitest + Testing Library) |
npm run build |
Production build. Every page is statically prerendered. |
npm run format |
Runs Prettier, which also sorts Tailwind classes |
Deploying
Live at https://playground.jun-liu.net.
Merging to main deploys automatically: the server builds the Dockerfile and
swaps the running container. If a build fails, the running version stays up.
The image uses Next's standalone output and runs as the unprivileged node
user on a read-only filesystem.
Structure
src/
app/ routes: / (component specimens), /tokens
components/ui/ the reusable components and their tests
components/site/ page chrome and demos
hooks/ useControllableState
lib/ theme store, cn()
tokens/ token metadata, motion presets, token tests
Principles
- Tokens, not values. Components use semantic names only, so a theme is just data.
- Accessible by construction. Tests query by role and accessible name, the way assistive technology does. A broken label fails the build.
- Motion with intent. A few named springs keep every interaction feeling like one system, and reduced-motion users get instant state changes.
- No layout shift. Loading states, theme changes and hover effects never move surrounding content.
Roadmap
- Command menu (⌘K) with fuzzy search and list virtualisation
- Tooltip and popover with collision-aware positioning
- Toast stack with swipe-to-dismiss
- Playwright visual regression and axe checks in CI
- Case-study write-ups for each component: the problem, the options, and the decision
- Deploy
- Link from a portfolio
Description
Interface components built with care for motion, accessibility and design tokens.
https://playground.jun-liu.net
113 KiB
Languages
TypeScript
88.8%
CSS
6.4%
Dockerfile
3.4%
JavaScript
1.4%