---
name: dux-design-md
description: Authors or updates the project's DESIGN.md using the getdesign.md alpha spec. Use when the user says 'author DESIGN.md', 'update DESIGN.md', or selects DM from Mayasura's menu.
---

# dux-design-md

## Overview

This workflow produces the project's `DESIGN.md` — the single source of visual truth that every other `dux` skill reads from. It follows the **getdesign.md v2 nine-section schema**: YAML front matter declaring nine canonical blocks on top, nine canonical prose sections below, with token-and-rationale paired in the same file.

The nine YAML blocks (in canonical order):

1. `colors` — semantic color roles
2. `typography` — named text styles
3. `spacing` — t-shirt-sized space scale
4. `layout` — grid, breakpoints, container widths, density modes
5. `components` — UI primitives composed of token refs
6. `motion` — easing, duration, reduced-motion fallback
7. `voice` — tone, do/dont copy rules
8. `brand` — name, tagline, mission, prohibited associations
9. `anti_patterns` — visual / brand / accessibility "do NOT" rules

v2 expands the v1 6-block schema (colors / typography / rounded / spacing / components) by adding the four concerns AI-generated UI consistently fails on: layout discipline, motion language, voice/copy rules, and explicit anti-patterns. `rounded` may still appear and is honored by tooling, but is no longer a required block — radius values live inside `components` or as a small `spacing`-adjacent group as the brand requires.

The workflow runs under Mayasura, the Visual Architect — that persona is established by `dux-agent-mayasura` and carries through unchanged. Do not re-greet, do not adopt a new identity, do not break character.

Args: optional intent verb (`create`, `update`, `seed`). Without an arg, infer from the state of `{design_md_path}` and what the user has said.

## Conventions

- Bare paths (e.g. `references/spec.md`) resolve from the skill root.
- `{skill-root}` resolves to this skill's installed directory.
- `{project-root}`-prefixed paths resolve from the project working directory.
- `{skill-name}` resolves to the skill directory's basename.

## On Activation

Load module config from `{project-root}/_bmad/config.yaml` and `{project-root}/_bmad/config.user.yaml` — root level plus the `dux` section. Bind:

- `{design_md_path}` from `dux.design_md_path` (default `{project-root}/docs/DESIGN.md`)
- `{design_output_folder}` from `dux.design_output_folder` (default `{project-root}/_bmad-output/design`)
- `{brand_reference_path}` from `dux.brand_reference_path` (default empty)

If `dux` config is missing, fall back to defaults and mention once that `dux-setup` can persist the configuration. Do not block.

Bind workspace files (peers of DESIGN.md, namespaced to avoid colliding with sibling docs):

- `{design_workspace} = dirname({design_md_path})`
- `{decision_log} = {design_workspace}/.dux-design-md.decision-log.md`
- `{design_addendum} = {design_workspace}/.dux-design-md.addendum.md`

The decision log is canonical memory for this workflow — load-bearing visual decisions and their rationale live there, not in conversation.

## Intent Routing

Infer from `{design_md_path}` state and user input:

| Situation | Route |
| --- | --- |
| File absent, user has not specified | **Create** |
| File absent, user named a reference (Linear/Stripe/Vercel/…) | **Seed** then iterate as Create |
| File present, user wants targeted change | **Update** |
| File present, user wants a full rewrite | Confirm explicitly; on confirm, run **Create** |

On any route, if `{decision_log}` exists, read it before changing the file. Update routes that contradict a prior decision must surface the conflict in conversation before applying.

If `--headless` is passed, accept a JSON brief on stdin or via `--brief <path>` describing intent + inputs and proceed without conversational discovery. Every assumption made without the user goes into the decision log.

## Stage 1 — Discovery

Reserved for Create and Seed routes. Update routes start at Stage 3 with the change brief.

The single most important job of this stage: **extract a specific visual point of view**. Bland inputs ("modern", "minimal", "clean", "professional") are placeholders for thought, not directions. Push back until the user names at least one of: a designer, a studio, a brand, an era, or a movement they want this to feel like — and at least one anti-reference (something the brand explicitly is *not*).

What you need before authoring tokens:

- **Hero references** — three or more concrete examples (sites, brands, products) and what about each one matters. "Linear" is not enough; "Linear's tight letter-spacing and how the dark canvas reads as engineering, not entertainment" is.
- **Anti-references** — what this brand must not look like. Usually exposes constraints faster than positive references do.
- **Atmosphere words** — paired with concrete pairings (color/material/era), never standalone adjectives. "Warm, like Kinfolk magazine 2013" not "warm and friendly".
- **The single voltage** — what is the *one* moment that should feel the loudest? Often a single accent color, a single hero typeface, a single elevation cue. If the user wants three accent colors and four headline fonts, push back; restraint is what makes systems read as designed.
- **Product surface** — what kind of UI is this for? Marketing site, dense product, content app, dashboard? Affects density, typography scale, component pressure.
- **Constraints** — accessibility floor (AA non-negotiable for `dux`), dark mode required or one-canvas, any locked-in brand assets (logo color, mandated typeface).

Use `{brand_reference_path}` if non-empty — read it as ground truth, not inspiration. If it conflicts with what the user is saying in conversation, surface the conflict.

When discovery is grounded, write a short discovery summary to the decision log and proceed.

## Stage 2 — Seed from Reference (Optional)

When the user names a reference in the awesome-design-md catalog (Linear, Stripe, Vercel, Apple, Tesla, Notion, Figma, Supabase, etc.), produce a starter DESIGN.md grounded in that reference's known visual language as a *starting point* — not a copy. The user will diverge from it.

Reference seeds are not stored as files inside this skill (they live upstream at https://getdesign.md and in the awesome-design-md repo). Produce the seed from the LLM's knowledge of the named reference; the user will correct any drift in Stage 3 and Stage 4.

Note the seed reference in the decision log.

## Stage 3 — Author YAML Tokens (9-section schema)

Write the YAML front matter blocks in canonical order. Refer to `references/spec.md` for the full schema and `references/token-vocabulary.md` for naming conventions.

The nine YAML blocks:

### 1. `colors`
Every entry is a role, never a hue. `primary`, `canvas`, `surface-card`, `hairline`, `ink`, `body`, `muted`, `on-primary`, plus semantic roles (`success`, `warning`, `danger`) when the product surface uses them.

### 2. `typography`
Named text styles (`display-lg`, `display-md`, `heading-lg`, `body-md`, `body-sm`, `button`, `caption`, `code`), each with `fontFamily`, `fontSize`, `fontWeight`, `lineHeight`, `letterSpacing`. Roles, not HTML elements.

### 3. `spacing`
`xs` through `xl` plus `section` for marketing-scale gaps.

### 4. `layout`
Structural rhythm of the surface. Required keys:

```yaml
layout:
  grid: { columns: 12, gutter: 24, margin: 32 }       # px values
  breakpoints: { sm: 640, md: 768, lg: 1024, xl: 1280, 2xl: 1536 }   # px, ascending
  container_max: { sm: 640, md: 720, lg: 960, xl: 1200 }             # px
  density: [comfortable, compact]
```

`grid.columns` is integer; `gutter` and `margin` are pixel values. `breakpoints` MUST be in ascending order. `density` is the list of supported density modes.

### 5. `components`
UI primitives composed entirely of `{token.refs}`. Never inline hex or px. Each component declares the subset of properties it owns (background, text, typography, padding, border, radius).

### 6. `motion`
Motion language declared once, referenced everywhere:

```yaml
motion:
  easing:
    standard: "cubic-bezier(0.4, 0, 0.2, 1)"
    decelerate: "cubic-bezier(0, 0, 0.2, 1)"
    accelerate: "cubic-bezier(0.4, 0, 1, 1)"
    emphasized: "cubic-bezier(0.2, 0, 0, 1)"
  duration:
    xs: 80     # ms
    sm: 120
    md: 200
    lg: 320
    xl: 480
  reduced_motion_fallback: "Disable transforms and scale animations; preserve opacity-only crossfades. Honor prefers-reduced-motion: reduce."
```

Easing values must reference one of the four named slots (`standard`, `decelerate`, `accelerate`, `emphasized`). Durations are positive ms integers. `reduced_motion_fallback` is required prose; never empty.

### 7. `voice`
Copy stance for any visible string in the product:

```yaml
voice:
  tone: [direct, warm, technical]            # 1-5 one-word descriptors
  do:
    - "Verify your email."
    - "Pick a workspace."
  dont:
    - "Hey there, friend!"
    - "Oopsie, something blew up!"
```

`tone` has 1–5 entries. `do` and `dont` each have at least one example. No single entry exceeds 120 characters — voice is a stance, not a style guide.

### 8. `brand`
Identity and stance:

```yaml
brand:
  name: "Acme"
  tagline: "Engineering at the speed of thought."
  mission: "Help engineering teams ship safer software faster."    # one sentence, ≤200 chars
  prohibited_associations: ["crypto-bro", "Web3 hype"]
```

`name`, `tagline`, and `mission` are all required. `mission` is one sentence, ≤200 characters. `prohibited_associations` lists the brand voices this brand is explicitly NOT.

### 9. `anti_patterns`
Concrete "do NOT" rules grouped by concern:

```yaml
anti_patterns:
  visual:
    - "do NOT introduce a second accent color"
    - "do NOT use raw px values; reference tokens"
  brand:
    - "do NOT compare us to crypto / Web3 startups"
  accessibility:
    - "do NOT ship body text under 4.5:1 contrast"
    - "do NOT animate when prefers-reduced-motion is set"
```

At least one anti-pattern across the three sub-arrays is required. These are read by downstream skills (`dux-screen`, `dux-prototype`) and emitted as comments in generated HTML for traceability.

### Hard rules (apply to all nine blocks)

- Token names carry intent. `primary`, `canvas`, `hairline` — never `blue-1`, `accent-2`, `gray-100`.
- Every component property that has a token equivalent must use a `{token.ref}`, never an inline literal.
- Variants are their own component entries (`button-primary-hover`, `button-primary-disabled`), not nested state objects.
- Motion in CSS references `{motion.duration.*}` / `{motion.easing.*}`, never raw ms or bezier strings.

After writing the YAML, run a self-check: every color used in any component resolves; every typography ref resolves; every spacing/motion ref resolves. If anything orphans, fix before proceeding.

## Stage 4 — Author Prose

Write the nine canonical prose sections in order. `references/spec.md` carries the section list and what each one is for. Two things matter most:

**Section 1 — Overview.** Lead with opinion, not description. Atmosphere first, then **Key Characteristics** as a bullet list of the most-quoted facts. This section is what an agent (or human) reads to decide *why* this system looks the way it does. Refuse the temptation to summarize the YAML in English. Add rationale that isn't in the YAML.

**Sections on Colors / Typography / Layout / Elevation.** For each token defined in YAML, write one sentence in prose that says what role it plays and why. The YAML supplies the value; the prose supplies the reason. An agent making a tight judgment call uses the reason.

**Section on Components.** This is the structural payload. Every YAML `components.*` key MUST have a matching prose entry, and every prose entry MUST correspond to a YAML key. The 1:1 invariant is enforced by `dux-design-md-lint` and is non-negotiable.

**Section on Responsive Behavior.** A breakpoint table, touch-target rules with WCAG-AAA assessment, and a collapsing-strategy bullet list. State the breakpoint widths in pixels.

**Section on Known Gaps.** Non-empty, ever. List what this DESIGN.md does *not* cover — animation timings, dark mode, sub-brands, edge states — explicitly. Silence about gaps is itself a gap.

After writing, run a coverage self-check before saving: every YAML `components.*` key has a prose entry; every prose `{token.ref}` resolves to a YAML token; every meaningful foreground/background pairing has been at least eyeballed for contrast (lint will compute the exact ratios).

## Stage 5 — Write and Finalize

Write the assembled DESIGN.md to `{design_md_path}`. Update `updated` semantics in the YAML if the spec version requires it (alpha spec does not — `version: alpha` stays).

Append a session entry to `{decision_log}` covering: route taken (Create / Update / Seed-then-Create), discovery summary, reference seed if any, every YAML decision that wasn't obvious from the user's input (especially color choices, the typography scale ratio, and any component the user did not explicitly request), every place where Mayasura pushed back on bland input and what won out, and every gap left for Known Gaps with the reason it was deferred.

Audit the log before handoff: every meaningful entry is either reflected in DESIGN.md, captured in `{design_addendum}` (if it didn't earn a spot but the user wants it preserved), or explicitly flagged as process noise. The user ends the session knowing how their thinking was handled.

## Stage 6 — Handoff to Lint

Offer to run `dux-design-md-lint` (menu code DL) immediately. The contrast audit and structural coverage check are this workflow's natural downstream. If the user accepts, dispatch DL with `{design_md_path}` as input. If declined, mention that DL can be invoked anytime and exit.

## Update Mode

Triggered when `{design_md_path}` exists and the user wants a targeted change.

1. Read `{decision_log}` and `{design_addendum}` (if present) before any conversation about the change. The standing record is the context the change enters against.
2. Parse the change request as a "change signal" — additive (new component, new breakpoint), corrective (palette refinement, contrast fix from lint), or revisionary (overhaul of a section).
3. If the change contradicts a prior logged decision, surface the conflict explicitly before applying. The user must override consciously.
4. Apply the change preserving the YAML↔prose 1:1 invariant. A new component requires both a YAML entry and a prose entry — never one without the other.
5. Append a new entry to the decision log explaining the change. Overrides also write to `{design_addendum}` so the rejected reasoning is preserved.
6. Offer DL handoff as in Stage 6.

## Headless Mode

`--headless` accepts a brief on stdin or via `--brief <path>`. The brief is YAML/JSON with at minimum:

- `intent`: `create` | `update` | `seed`
- `references`: array of named brands/designers/studios (and optionally anti-references)
- `voltage`: the single hero moment (color, type, elevation, motion)
- `surface`: marketing | product | dashboard | content
- `constraints`: array of strings (e.g. `"dark mode required"`, `"must use Inter"`)
- `change` (update only): description of the targeted change

Every assumption made without conversational pushback — chosen palette ratios, typography scale, component coverage — is recorded in `{decision_log}` as an `assumption` entry. Exit with non-zero status if the brief is too thin to author DESIGN.md without inventing a point of view; in that case, write a `blocked` entry to the log with the missing inputs and emit:

```json
{"status": "blocked", "reason": "<one-line cause>", "design_md": "{design_md_path}", "decision_log": "{decision_log}"}
```

On success:

```json
{"status": "complete", "intent": "<route>", "design_md": "{design_md_path}", "decision_log": "{decision_log}"}
```

## Non-Negotiables

- **Specificity.** Bland input does not produce DESIGN.md. Push back until references are concrete.
- **YAML↔prose 1:1.** Every component lives in both places or in neither.
- **Token refs in prose.** Prose references colors, type, spacing, and components via `{group.name}` syntax. Inline hex or px in prose is a defect.
- **WCAG AA floor.** Lint enforces it; this workflow should at minimum warn when a chosen pairing looks risky.
- **Known Gaps non-empty.** State what isn't covered.
- **Mayasura persona.** Carries through from `dux-agent-mayasura`. Do not re-greet, do not break character.
