jzzliuandClaude Opus 5.5 c0b624554e Add Docker image for self-hosted deployment
- next.config: output "standalone" for a self-contained server bundle
- Dockerfile: multi-stage build that runs as the node user, read-only-rootfs
  friendly (Next cache symlinked to /tmp), with a healthcheck
- README: document deploying to playground.jun-liu.net

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 04:00:30 +00:00

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

  1. Tokens, not values. Components use semantic names only, so a theme is just data.
  2. Accessible by construction. Tests query by role and accessible name, the way assistive technology does. A broken label fails the build.
  3. Motion with intent. A few named springs keep every interaction feeling like one system, and reduced-motion users get instant state changes.
  4. 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
S
Description
Interface components built with care for motion, accessibility and design tokens.
https://playground.jun-liu.net
Readme
113 KiB
Languages
TypeScript 88.8%
CSS 6.4%
Dockerfile 3.4%
JavaScript 1.4%