tanghoong design
  1. Design system
  2. Agent prompt

11 — Agent prompt

Agent prompt

Paste this into a new repo's CLAUDE.md, AGENTS.md or system prompt. It is written to be enough on its own — an agent that has only this file should produce on-system markup without seeing the CSS.

For an agent, /llms.txt is usually the better answer /llms.txt is the entire system — tokens, layout, every class, the known traps — as one plain-text file. One fetch, no crawling, and it stays accurate because it is maintained alongside the CSS. Use the prompt below when you want the rules pinned inside the repo rather than fetched from a URL.

How to use it

  1. Copy assets/css/ into the new repo

    All eight files except docs.css, which styles this reference site only.

  2. Paste the prompt below into CLAUDE.md at the repo root

    Under a heading like ## Design system. It is read on every session.

  3. Add the project's own exceptions underneath

    A different accent, an extra component. Keep them below the pasted block so it stays diffable against this page.

When the system and the prompt disagree The CSS wins. This prompt is a description of tokens.css and its component layers, not a second source of truth — if you change a token, update the block below in the same commit.

The prompt

Copy the whole block. It is about 90 lines and deliberately written as rules an agent can check its own output against, not as prose about design values.

CLAUDE.md — design system block
# Design system — tanghoong.com

All UI in this repo follows the shared system at https://design.tanghoong.com.
The stylesheets in `assets/css/` are the source of truth. This file describes
them so you can write correct markup without reading all of the CSS.

## Non-negotiables

1. Never write a literal colour, font size, radius, shadow or duration in a
   component. Every value comes from a CSS custom property in `tokens.css`.
   If the value you need does not exist, add it to `tokens.css` — do not inline it.
2. Never remove a focus outline without replacing it. Focus is `:focus-visible`,
   2px `var(--focus)`, 2px offset.
3. Every interactive control clears a 44px touch target.
4. Every form field has a real `<label for>`. A placeholder is not a label.
5. Colour never carries meaning alone. Pair every status colour with a word.
6. Reach for the native element first: `<dialog>` for modals and drawers,
   `<details>` for menus, accordions and the mobile nav, real `<button>`
   and `<a>`. Do not rebuild what the platform gives you.

## Tokens

Surfaces  --bg (page) · --bg-sunken (bands, wells) · --bg-elevated (cards,
          menus, modals) · --bg-glass (sticky header only)
Ink       --text (content) · --text-2 (supporting) · --text-3 (labels, meta)
Lines     --hairline (separators, inputs) · --hairline-soft (card outlines)
Accent    --accent · --accent-ink (text on filled) · --accent-wash (12% tint)
Status    --ok --warn --danger --info, each with a matching -wash
Type      --font-sans (system stack) · --font-mono
          --fs-h1 --fs-h2 --fs-h3 --fs-lead --fs-stat
          --fs-body (17px) --fs-sm (15px) --fs-xs (13px)
Space     --s-1 (4px) through --s-32 (128px) on a 4px scale
Measure   --container (1120) --container-prose (720) --container-mid (940)
          --container-wide (1320) · --gutter · --section-y · --nav-h (56px)
Radius    --r-sm (10) --r-md (16) --r-lg (20) --r-xl (28) --r-chip (pill)
Shadow    --sh-sm --sh-md --sh-lg — all resolve to none in dark theme
Motion    --dur-fast (.15s) --dur (.26s) --dur-slow (.42s) · --ease

Themes: every colour is declared once with `light-dark()`. An explicit choice
sets `data-theme="light"|"dark"` on `<html>`; no attribute means follow the OS.
Never define a colour only inside a media query.

## Layout

- Page = a stack of `<section class="section s-page|s-sunken">`, alternating
  surfaces. Inside each, one `.container` (add `--prose|--mid|--wide`).
- The section owns vertical rhythm; the grid owns gaps. Components carry no
  outer margin. Never put `margin-bottom` on a component.
- Grids are mobile-first single column with `minmax(0, 1fr)` tracks.
  `.grid--2 --3 --4`, `.cards --2 --3`.
- Two-column reading layout: `.with-aside` + `aside.rail`.
- Breakpoints (range syntax only): 640, 720, 900, 960.

## Components — the class vocabulary

Buttons   .btn + .btn--filled|--tinted|--quiet|--outline|--danger
          sizes .btn--sm|--lg|--icon|--block · .iconbtn for bordered icon
          buttons · .link-more for an inline arrow link
          Exactly one --filled per view.
Labels    .chip (passive: a topic, a tag) vs .badge (state: --ok --warn
          --danger --info --solid). A chip has no meaning; a badge does.
Surfaces  .card (elevated, lifts on hover only when it is an <a>)
          .panel (sunken well, no hover) · .band (centred CTA)
          .note + --ok|--warn|--danger|--info (inline message)
Data      .stats/.stat__fig · .rows/.row · .facts (dl) · .empty · .meter
          .table inside .table-wrap (the wrapper scrolls, never the body)
Nav       .site-head/.head-nav · .site-foot/.foot-grid · .crumb · .tabs
          .navsheet (mobile, a details element)
Forms     .field > .label + .input|.textarea|.select + .hint|.error
          .check (checkbox/radio row) · .check--boxed · .switch
          .input-group with .input-group__addon · .fieldset + .fieldset__legend
          .form-grid --2 with .span-2 · .form-actions
          Errors use aria-invalid="true" plus aria-describedby — not a class.
Overlays  .modal and .drawer (both <dialog>, opened with .showModal())
          .menu (<details>) · .accordion__item (<details>) · .toast · .tip
Prose     .prose on the wrapper, then unclassed HTML inside it.
          .lede · .eyebrow · .quote · .steps · .codeblock · .kbd
Utility   .t2 .t3 .sm .xs .mono .nums .measure .cluster .stack .vh
          .mt-2 .mt-4 .mt-6 .mt-8 .mt-12 .mt-16 (top margin only)

## Writing markup

- Build the page from the classes above before writing any new CSS. If a new
  component is genuinely needed, add it to the right layer file and give it a
  comment saying what it is for and when not to use it.
- Prefer `inline-size`/`block-size` and `inset-*`/`margin-inline` over the
  physical properties — the layout is direction-agnostic and the CV site
  ships in English and Chinese.
- SVG icons: 16–20px, `fill="none" stroke="currentColor" stroke-width="1.8–2"`,
  always `aria-hidden="true"`. An icon-only control needs `aria-label`.
- Wrap every table in `.table-wrap`. The page body must never scroll sideways.
- Long-form copy caps at 68ch. Leads and quotes cap at 56–60ch.

## What to avoid

- A second primary button in the same view.
- A shadow in dark theme (they resolve to `none`; use `--bg-elevated`).
- A tooltip carrying information not available anywhere else.
- Validating a field on every keystroke — validate on blur and on submit.
- A fifth breakpoint. Try `clamp()`, `minmax()` or `auto-fill` first.
- Any animation that ignores `prefers-reduced-motion`.
- Copying token values or contrast figures into this repo's own documents.
  Link https://design.tanghoong.com/llms.txt instead. A copy goes stale
  silently and keeps being read as authoritative; this repo may keep its own
  decisions, but not a copy of the spec.

The &lt; escapes above are for this page only — the copy button gives you the plain text with real angle brackets.

Token quick reference

The same values as a table, for when you need to check one without loading the stylesheet.

Colour tokens — light / dark
TokenLightDarkUse
--bg#fbfbfd#000000Page ground
--bg-sunken#f5f5f7#0a0a0cBands, wells, chips
--bg-elevated#ffffff#1c1c1eCards, menus, modals
--text#1d1d1f#f5f5f7Body and headings
--text-2#515154#a1a1a6Supporting copy
--text-3#6e6e73#86868bLabels, metadata
--hairline#0000001a#ffffff21Separators, inputs
--hairline-soft#0000000f#ffffff14Card outlines
--accent#03744e#4dff9bPrimary action, current item
--accent-ink#ffffff#04140bText on a filled accent
--ok#03744e#4dff9bSuccess
--warn#8a5a00#f5c451Caution
--danger#b3261e#ff6b62Error, destructive
--info#0a5ec2#6bb6ffNeutral notice

Review checklist

Run this against any new page before it ships. It catches almost everything that makes a page look off-system.

  • No literal hex, px font size, radius or duration outside tokens.css
  • Both themes checked, including the toggle in both directions
  • Tabbed the whole page — every control shows the focus ring, nothing is skipped or trapped
  • Every field has a label; every error has aria-invalid and aria-describedby
  • Every icon-only control has an aria-label; every decorative SVG has aria-hidden
  • Exactly one .btn--filled per view
  • No horizontal scroll at 320 px; tables scroll inside their wrapper
  • Headings run h1 → h2 → h3 with no level skipped
  • Checked with prefers-reduced-motion: reduce forced on
  • Print preview is clean — no chrome, nothing cut off

Short version

For a tool with a tight context budget, or a commit-message-length reminder.

Condensed
Follow https://design.tanghoong.com. Values come from tokens.css custom
properties only — never literal colours, sizes, radii or durations. Surfaces:
--bg, --bg-sunken, --bg-elevated. Ink: --text, --text-2, --text-3. One accent,
one .btn--filled per view. Space on the --s-* 4px scale. Sections own vertical
rhythm; components carry no outer margin. Native elements first: dialog for
modals, details for menus and accordions, real labels on every field. 44px
touch targets, visible :focus-visible ring, colour never alone. Light and dark
from one light-dark() token set.