Imported from Yash-1511/bmad-dux-visual-architect (
skills/dux-design-md/SKILL.md). Install upstream withnpx skills add Yash-1511/bmad-dux-visual-architect --skill dux-design-md. Copyright stays with the author.
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):
colors— semantic color rolestypography— named text stylesspacing— t-shirt-sized space scalelayout— grid, breakpoints, container widths, density modescomponents— UI primitives composed of token refsmotion— easing, duration, reduced-motion fallbackvoice— tone, do/dont copy rulesbrand— name, tagline, mission, prohibited associationsanti_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}fromdux.design_md_path(default{project-root}/docs/DESIGN.md){design_output_folder}fromdux.design_output_folder(default{project-root}/_bmad-output/design){brand_reference_path}fromdux.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:
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:
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:
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:
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:
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— neverblue-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.
- Read
{decision_log}and{design_addendum}(if present) before any conversation about the change. The standing record is the context the change enters against. - 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).
- If the change contradicts a prior logged decision, surface the conflict explicitly before applying. The user must override consciously.
- 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.
- Append a new entry to the decision log explaining the change. Overrides also write to
{design_addendum}so the rejected reasoning is preserved. - 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|seedreferences: array of named brands/designers/studios (and optionally anti-references)voltage: the single hero moment (color, type, elevation, motion)surface: marketing | product | dashboard | contentconstraints: 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:
{"status": "blocked", "reason": "<one-line cause>", "design_md": "{design_md_path}", "decision_log": "{decision_log}"}
On success:
{"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.