---
# ──────────────────────────────────────────────────────────────────────────
# DESIGN.md — the project's design contract (C2).
#
# YAML frontmatter (between the --- markers) is the machine-readable part:
# it's what `apt:plan`, `apt:mockup`, `apt-tools design-validate`, and
# `apt-tools design-lint` consume. Everything below the frontmatter is the
# human-readable narrative in Stitch 9-section format.
#
# Run `apt-tools design-validate` after editing to confirm the schema holds.
# ──────────────────────────────────────────────────────────────────────────
meta:
  name: "{project_name}"
  version: "0.1.0"
  updated: "{timestamp}"
  description: "{one-line description of the aesthetic — replace on edit}"
  # inspiration: paper   # optional typeui-sh slug the design draws from

tokens:
  colors:
    bg: "#0B0B0F"
    surface: "#16161D"
    accent: "#7C5CFF"
    text: "#F5F5F7"
    border: "#2A2A32"
    status:
      success: "#22C55E"
      warning: "#F59E0B"
      danger: "#EF4444"
      info: "#3B82F6"

  typography:
    families:
      primary: "Söhne, Inter, ui-sans-serif, system-ui"
      display: "Söhne Breit, Inter, ui-sans-serif, system-ui"
      mono: "JetBrains Mono, ui-monospace, SFMono-Regular"
    # 13 / 14 / 16 / 18 / 20 / 24 / 30 / 36 / 48
    scale:
      - "13px"
      - "14px"
      - "16px"
      - "18px"
      - "20px"
      - "24px"
      - "30px"
      - "36px"
      - "48px"
    weights: [400, 500, 600, 700]
    lineHeight:
      tight: "1.15"
      snug: "1.35"
      normal: "1.5"

  # 4pt base scale: 4, 8, 12, 16, 24, 32, 48, 64, 96
  spacing:
    - "4px"
    - "8px"
    - "12px"
    - "16px"
    - "24px"
    - "32px"
    - "48px"
    - "64px"
    - "96px"

  radius:
    none: "0px"
    sm: "4px"
    md: "8px"
    lg: "12px"
    xl: "16px"
    full: "9999px"

  shadow:
    sm: "0 1px 2px rgba(0,0,0,0.30)"
    md: "0 4px 12px rgba(0,0,0,0.35)"
    lg: "0 12px 32px rgba(0,0,0,0.45)"

  motion:
    duration:
      fast: "120ms"
      base: "180ms"
      slow: "280ms"
    easing:
      standard: "cubic-bezier(0.2, 0, 0, 1)"
      emphatic: "cubic-bezier(0.3, 0, 0, 1.2)"

components:
  button:
    radius: md
    paddingX: "16px"
    paddingY: "8px"
    fontWeight: 500
  card:
    radius: lg
    padding: "24px"
    shadow: md
  input:
    radius: md
    paddingX: "12px"
    paddingY: "8px"
    borderWidth: "1px"

# Structured anti-pattern catalog (C11). Each entry is specific enough for
# `apt-tools design-lint` to grep-match in generated HTML/CSS. These 15 rules
# are universal — they encode AI aesthetic defaults that don't belong in any
# shipped UI. Project-specific rules can be appended below in a local fork.
antiPatterns:
  # ─── layout ───────────────────────────────────────────────────────────
  - id: no-side-stripe-borders
    category: layout
    pattern: "border-left:\\s*(?:[3-9]|\\d{2,})px"
    why: "Side-stripe callouts (>= 3px left borders) are an AI tell — they read as generic CMS snippets, not designed surfaces."
    rewrite: "Use a subtle background tint, a top-aligned icon, or a semantic text color instead of a left stripe."
  - id: no-nested-cards
    category: layout
    pattern: "class=\"[^\"]*rounded-(?:xl|2xl|3xl)[^\"]*shadow[^\"]*\"[^>]*>\\s*<[^>]*class=\"[^\"]*rounded-(?:xl|2xl|3xl)[^\"]*shadow"
    why: "A rounded-shadowed card containing another rounded-shadowed card reads as 'I didn't know how to group these'. Flatten."
    rewrite: "Use section-level whitespace and a single card, or swap the outer card for a plain div."
  # ─── typography ───────────────────────────────────────────────────────
  - id: no-gradient-text
    category: typography
    pattern: "background-clip:\\s*text"
    why: "Gradient text (background-clip: text) is the single strongest AI aesthetic. It reads as try-hard landing-page filler."
    rewrite: "Use a solid color from tokens.colors.text or tokens.colors.accent. Reserve gradients for non-type surfaces."
  - id: no-inter-only
    category: typography
    pattern: "font-family:\\s*['\"]?Inter['\"]?\\s*[,;]?\\s*$"
    why: "Inter as the sole declared font is the AI-default and signals zero type choice was made."
    rewrite: "Declare a primary family with intentional fallbacks (see tokens.typography.families.primary) or pick a distinctive display face."
  - id: no-all-bold-headings
    category: typography
    pattern: "<h[1-3][^>]*class=\"[^\"]*font-black\\b"
    why: "`font-black` on every heading collapses type hierarchy into shouting. Weight differentiates; shouting doesn't."
    rewrite: "Reserve font-black for one hero moment per page; use semibold/bold for most headings."
  - id: no-uppercase-body
    category: typography
    pattern: "<p[^>]*class=\"[^\"]*uppercase\\b"
    why: "ALL-CAPS body text kills readability. Uppercase belongs on labels and eyebrow text only."
    rewrite: "Use tracking-widest or small-caps on short labels; keep body at normal case."
  # ─── color ────────────────────────────────────────────────────────────
  - id: no-purple-saas-gradient-hero
    category: color
    pattern: "linear-gradient\\([^)]*#[68][0-9a-f]{2}[a-f0-9]f[a-f0-9]"
    why: "Purple→pink hero gradients are the generic SaaS tell."
    rewrite: "Use a single tokens.colors.accent flood or a neutral photograph."
  - id: no-cyan-on-dark
    category: color
    pattern: "color:\\s*#(?:0[0-9a-f]f[0-9a-f]ff|00ffff|22d3ee|06b6d4|0891b2)\\b"
    why: "Bright cyan/electric-teal text on dark backgrounds is the 2015 futuristic-dashboard trope — high chroma, low information density."
    rewrite: "Use a desaturated accent from tokens.colors.accent or a neutral color at reduced alpha."
  - id: no-rainbow-accents
    category: color
    pattern: "(?:text|bg|border)-(?:red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-500[^\"]*\"[^>]*(?:text|bg|border)-(?:red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-500"
    why: "Multiple saturated Tailwind 500-series accents on the same surface reads as 'I couldn't decide'. Commit to one accent."
    rewrite: "Use tokens.colors.accent for primary actions; reserve status colors for semantic contexts only."
  # ─── motion ───────────────────────────────────────────────────────────
  - id: no-bounce-easing
    category: motion
    pattern: "cubic-bezier\\(0\\.68,\\s*-0\\.55"
    why: "Overshoot/bounce easings feel juvenile on UI surfaces."
    rewrite: "Use tokens.motion.easing.standard for UI, emphatic only for hero-level moments."
  - id: no-slow-default
    category: motion
    pattern: "transition(?:-[a-z]+)?:\\s*(?:all|[a-z-]+)\\s+(?:[5-9]\\d{2}|\\d{4,})ms"
    why: "Transitions >= 500ms on UI interactions feel sluggish; users interpret them as jank."
    rewrite: "Keep UI transitions under 280ms (tokens.motion.duration.slow). Reserve 500ms+ for hero entrances only."
  - id: no-permanent-spinner
    category: motion
    pattern: "animation:\\s*spin\\s+\\d+(?:\\.\\d+)?s\\s+linear\\s+infinite"
    why: "A perpetual spinner (`animation: spin … infinite`) left in a mockup for 'loading state' always ships to prod."
    rewrite: "Use discrete loading/empty/error variants side-by-side, or replace with a skeleton."
  # ─── interaction ──────────────────────────────────────────────────────
  - id: no-missing-focus
    category: interaction
    pattern: "<button[^>]*class=\"[^\"]*(?:focus:outline-none|outline-none)[^\"]*\"(?![^>]*focus-visible:)"
    why: "Removing the focus outline without replacing it breaks keyboard accessibility. This is the AI's laziest shortcut."
    rewrite: "Either keep the default outline or replace with `focus-visible:outline-2 focus-visible:outline-offset-2`."
  # ─── content ──────────────────────────────────────────────────────────
  - id: no-placeholder-names
    category: content
    pattern: "\\b(John|Jane)\\s+Doe\\b"
    why: "John/Jane Doe placeholder strings leak into shipped UI when the AI skips real sample data."
    rewrite: "Use sample data that reflects the actual product (real-looking names, real tasks, real copy)."
  - id: no-lorem-ipsum
    category: content
    pattern: "\\bLorem\\s+ipsum\\s+dolor\\s+sit\\s+amet\\b"
    why: "Lorem ipsum in a shipped mockup means no one thought about what the copy should actually say."
    rewrite: "Write real copy that reflects the feature. If the feature is too early for copy, render a single-word label instead."

inspiration:
  - paper
---

# Design System — {project_name}

> **Updated:** {timestamp}
> **Contract:** this file is the source of truth for visual and interaction design. Machine-readable tokens live in the YAML frontmatter above. The 9 sections below explain the *why* and give agents copy-paste prompts for component generation.

## 1. Visual Theme

Describe the overall aesthetic in one paragraph. What does this product feel like? What adjectives are on the wall? What would a newcomer say after 10 seconds? Anchor it against a known reference ("like Linear, but warmer" / "like a 90s terminal but readable" / "IKEA-catalog clean").

**Example:** Dark-first, typographic-led, minimal chrome. Focus on content density with a single high-saturation accent for action. Feels closer to a tool than to a landing page.

## 2. Color Palette

See `tokens.colors` in the frontmatter for exact values. Narrative notes:

- **Background (`bg`)** carries the whole surface; `surface` is for elevated cards and modals.
- **Accent** is used sparingly — primary buttons, active tab underlines, focused inputs, key data points. If `accent` appears more than ~3 times on a screen, reduce.
- **Status colors** follow the semantic convention (success / warning / danger / info) and are only used in semantic contexts, never decoratively.

## 3. Typography

See `tokens.typography` in the frontmatter. Narrative rules:

- **Display family** is for page titles and hero moments only. Everything else uses `primary`.
- **Mono** is for identifiers (IDs, timestamps, code, file paths) and never for prose.
- Use the scale tokens directly; never inline custom sizes.

## 4. Component Stylings

Token-level component overrides live under `components` in the frontmatter. Narrative notes on the core primitives:

- **Button**: `md` radius, 16px horizontal padding, medium weight. Primary uses `accent` bg; secondary uses `surface` bg with `border` outline; tertiary is text-only.
- **Card**: `lg` radius, 24px padding, `md` shadow. Avoid nested cards — flatten layouts into sections instead.
- **Input**: `md` radius, 1px border, 12px horizontal padding. Focus state uses `accent` 2px outline with 0 offset.

## 5. Layout

- 12-column grid on wide viewports; stack on narrow.
- Max content width: 1280px for dense tools, 720px for reading surfaces.
- Section padding uses the spacing scale (prefer `32px` and `64px`).
- Avoid centered hero layouts for tool surfaces — they waste cognitive real estate.

## 6. Depth

- Use `shadow.md` for interactive surfaces that sit *on top* of the background (cards, dropdowns, tooltips).
- Use `shadow.lg` sparingly for modals and command palettes.
- Do not stack shadows; pick one elevation per surface.

## 7. Do's and Don'ts

**Do**
- Use tokens from the frontmatter. Every hex, px, and ms value in the codebase should match a token.
- Lean on whitespace over separators. Empty space is cheaper than borders.
- Write sample data that reflects the actual domain.

**Don't**
- Use any of the `antiPatterns` in the frontmatter. `apt-tools design-lint` will flag them.
- Introduce new fonts without updating `tokens.typography.families`.
- Mix color systems (don't sprinkle ad-hoc hex values alongside tokens).

## 8. Responsive

- **Narrow** (<640px): single column, full-width content, collapsed navigation.
- **Medium** (640–1024px): 8-column grid, condensed headers.
- **Wide** (>1024px): 12-column grid, sidebar + content.
- Touch targets are 44px min on narrow viewports.

## 9. Agent Prompt Guide

Copy-paste starters for `apt:mockup` and ad-hoc generation. Each prompt assumes the agent has already loaded the frontmatter above.

### Build a button

> Render a primary button using `tokens.colors.accent` on a `tokens.colors.bg` background. Radius from `components.button.radius`. Padding from `components.button.paddingX/paddingY`. Font weight from `components.button.fontWeight`. Focus state: 2px outline at `tokens.colors.accent`, 0 offset, no shadow.

### Build a card

> Render a card on `tokens.colors.surface`, 1px `tokens.colors.border` outline, `components.card.radius` corners, `components.card.padding` inner padding, `tokens.shadow.md` shadow. No gradient, no inner borders, no nested cards.

### Build a data table

> Render a dense data table: `tokens.typography.scale[2]` body size, `tokens.spacing[2]` row padding, `tokens.colors.border` horizontal row separators (no verticals), `tokens.colors.surface` hover state. Numeric columns right-aligned, text columns left-aligned. Use `tokens.typography.families.mono` for IDs and timestamps.

### Reject criteria

Before handing off to `apt:plan`, every generated mockup must pass:

- `apt-tools design-validate DESIGN.md` → exit 0
- `apt-tools design-lint mockups/*.html` → zero violations against `antiPatterns`
- Manual read-through against §7 Do's and Don'ts
