Skip to content
Back to skills

Ui Contract Design

ASecurity

Align an existing visual spec (design.md) against the structural spec - add the division-of-labor statement, move structural content out leaving a pointer, replace old component-style names with allowlist names, and move literal values into the token file so design.md holds rules rather than values. Use when a project has visual conventions that need to become a spec with a clean boundary. Or originate one when there are none at all - choose a brand direction on attributes rather than colours...

  • 3 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added October 13, 2026
designgo

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned October 13, 2026

npx -y skills add yannikzz/ui-contract --skill ui-contract-design --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ui Contract Design?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ui Contract Design
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/yannikzz-ui-contract-design/badge)](https://www.skillsdirectory.com/skills/yannikzz-ui-contract-design)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ui-contract-design
description: "Align an existing visual spec (design.md) against the structural spec - add the division-of-labor statement, move structural content out leaving a pointer, replace old component-style names with allowlist names, and move literal values into the token file so design.md holds rules rather than values. Use when a project has visual conventions that need to become a spec with a clean boundary. Or originate one when there are none at all - choose a brand direction on attributes rather than colours, derive the values, tune them on a page. Chinese: 生成品牌/配色规范/token 命名/主题调校."
---

# Visual spec — `design.md`

Produces one artifact: `design.md`, plus references into the project's **token file**. Skeleton in [design-md-outline.md](design-md-outline.md). **Read it.**

Two branches, same artifact and same completion criteria — so this is one unit, not two:

| What the project has | Branch |
|---|---|
| A visual spec, a token source, or mockups | **align** — this file, below |
| None of those | **originate** — [originate.md](originate.md). **Read it before asking anything.** |

For the interview discipline, call the Skill tool with "decision-interview".
For which document a thing belongs in, call the Skill tool with "ui-contract-glossary".
For the deliverable's language and terminology, call the Skill tool with "deliverable-language".
For handling change requests, call the Skill tool with "spec-before-code".

## Preconditions
**Artifact paths come from `docs/ui-contract/config.md`'s paths table.** If that file doesn't exist, the specs are at the repo root. Never assume a path the config contradicts.


| Condition | Action |
|---|---|
| `layout.md` does not exist | **Stop.** The allowlist is what region names get aligned against; without it this step produces a second vocabulary. Call the Skill tool with "ui-contract-layout" first |
| `design.md` exists but `layout.md` does not | **Stop and say so.** Aligning a boundary against a document that doesn't exist means inventing the other half |
| No visual conventions exist anywhere — no `design.md`, no token source, no mockups — **and the visual half is authorised**: `docs/ui-contract/config.md` says so, or the user asked for it in this session | **Go to `originate.md`.** Do not start from this file's align procedure — there is nothing to align against |
| No visual conventions exist anywhere, **and nothing authorises the visual half** | **Stop, and say why.** `originate` invents brand decisions out of nothing; entered without somebody answering for them, it produces a specification of decisions nobody made — and nothing in the output reveals that. Report what the structural half produced and what this unit would settle, and leave it there |
| Nothing exists **and** the user asks for "something that looks good, you pick" | Still `originate.md`, still the interview. **Do not hand over a default palette.** A palette the user rubber-stamps is no longer their spec, which is the failure this whole method exists to prevent |

## The scope gate is on `originate` only, and that is deliberate

The two branches need different permissions, and giving them the same one breaks the other:

| Branch | What it does to the user's material | What authorises it |
|---|---|---|
| `align` | Slims a document they already have — structural content moves out, a pointer stays | **"May the existing visual spec be edited?"** in `config.md`. It touches somebody's file; it invents no brand decision |
| `originate` | Produces a brand direction, a scale and a set of values where none existed | **The scope authorisation.** These are decisions somebody has to answer for |

Gating `align` on scope would block a boundary cleanup that the structural half needs anyway —
an existing visual spec almost always has structural content mixed into it, and leaving it there
gives the project two vocabularies. That is the failure `align` exists to prevent, and it has
nothing to do with the one the handover exists to prevent.

## Start by reading `layout.md`'s ⏳ list

Structure and visuals are two dimensions of one interface, so a few numbers belong to neither alone. `layout.md` marks those ⏳ — a fixed panel width, a minimum usable width, a page size — and this step is where they get settled.

**Read that list first.** A type scale chosen without knowing that nine fields and five actions have to fit in 360px is a scale somebody will revisit.

**Settling them is this unit's job**, and it is done here whoever is answering. The ⏳ list is
always brought in as a list — "these three are waiting on your scale" — never folded silently
into the structural interview, because the two interviews sit in different halves. What
`config.md` changes is only **when** this interview happens, not how it runs:

| The handover was | When this runs |
|---|---|
| **Soft** — the same person answers for both halves | Straight after the hand-back, in the same sitting, with the ⏳ list on the table |
| **Hard** — somebody else, or nobody yet | Whenever that person picks it up. The ⏳ list travelled to them as the note; they should not have to read the whole structural spec to find those three numbers |

Answers go back into `layout.md`, by its own change process — this unit does not edit it directly.

**Do not require the two people to be in a room together.** That is often impossible, and a mechanism that assumes it simply fails in that half of the world. The marker travels between them precisely so the meeting is not the mechanism — this whole plugin exists because "somebody will remember to" is not one either.

**A ⏳ line can come back refuted rather than filled.** Building the projection can show that a
marked number does not hold at all — nine fields and five actions do not fit in 360px, whatever
the scale. That is a different fact from "waiting for a number", and it must not reuse the ⏳
marker: one says *give me a value*, the other says *the direction you were going to give a value
for is dead*.

**Mark it ⛔ in `layout.md`, in the number's own table cell, with what refuted it** — the marker
and its placement rule are defined in `ui-contract-layout`'s outline, §3 "A third outcome". Note
what that section records: `ui-contract-audit`'s check 10 does not report ⛔ yet, so a refuted line
is invisible to the health check. **Name the refuted lines explicitly in the hand-back**; do not
rely on the check to carry them.

## This step aligns; it does not rewrite

If the project already has a visual spec, **do not rewrite it.** Four things:

1. **Add the division-of-labor statement** at the top, naming the cut ("does it change when the brand skin changes") and naming the token file as the value authority.
2. **Move structural content out**: region trees, column widths, page archetypes go to `layout.md`. Leave a one-line "structure: see `layout.md` §X" behind, and rename the section if its title promised structure.
3. **Move literal values out to the token file.** Hex codes, px sizes, radius literals become token references. If the project already has a token source (`tailwind.config.*`, `tokens.css`, a theme file), **that is the token file** — do not create a second one.
4. **Align region names with the allowlist.** Old component-style names (camel-cased widget names) must be replaced with allowlist names, or an agent reading both docs gets two vocabularies — and the allowlist mechanism fails on the spot.

**Token names are not part of that.** Region names get aligned; token names do not. Record what the project already calls them in the declaration table, marked `this project`, and change nothing. Renaming tokens turns a boundary alignment into a repo-wide rename that touches every consumer — a change request, which goes through `spec-before-code`.

Appendices that record **mockup** reality (value-drift mapping tables and the like) **stay untouched** — they document the mockup, not the spec.

## The document is the name authority

The token file holds the **values**; this document settles which **names** exist. Say so in the opening blockquote, and carry the declaration table (`design-md-outline.md` §Opening) with it.

This has to be written down for the same reason the value authority is: without it, a borrowed naming convention quietly becomes the authority instead, and **nothing in the output shows it** — a token file produced either way is byte-for-byte the same. The health check reads its whitelist from that table, so a missing table degrades the check to nothing.

The same applies to `tokens.input.json` when the `originate` branch produced one:

| | Records | On conflict |
|---|---|---|
| `tokens.input.json` | where the user left the knobs (an input record) | does not count |
| the token file | the values that are currently correct | wins |

Its keys are the names from the declaration table. Per-stack mapping changes the format, never the names.

## When there is no token file yet

Say so **in the document, not only in the report** — the reader who never sees the report is the one this has to reach. Write the token references as they should be, and two things with them:

- **Name no path that is not on disk.** Write "the token file is the value authority; this project does not have one yet" rather than pointing at `src/tokens.css` and hoping. Following a pointer to a missing file is how a reader concludes the whole spec is stale.
- **Say who closes the gap.** On the `align` branch no unit generates the token file — the asset layer produces structural components only, so whoever builds it fills in the values under the names written here. **On the `originate` branch this section does not apply**: that branch derives the values and writes the token file itself, so a deliverable from it should never contain "there is no token file yet".

Do **not** silently invent values back into `design.md` to make it self-contained. A document that quietly re-absorbs its values is exactly the second value authority this rule exists to prevent.

## Before you call this document done

- [ ] **Every ⏳ in `layout.md` was resolved, refuted, or is explicitly left open with a reason** — and read **before** settling the scales, not after: a scale chosen without knowing what has to fit at it is a scale that will be revisited. "Resolved" and "refuted" are different endings; the line-level item near the bottom of this list says how each is marked
- [ ] **Resolving one means editing `layout.md`** — the ⏳ becomes a plain number. Not the type size written into `layout.md`, not the width copied into `design.md`: both directions are the bleed the split exists to prevent
- [ ] `design.md` contains no literal color or size values; the token file is the single value authority
- [ ] Structural content has been moved out and only a one-line pointer remains
- [ ] Every region name matches the allowlist; no old component-style naming survives in the normative body
- [ ] Mockup-reality appendices were left alone
- [ ] If no token file exists yet, `design.md` says so in its own body, and no path to a missing file is written anywhere in it
- [ ] The declaration table is present in the opening section, one row per token, each row marked `borrowed` / `this project` / `not used` — **those three keywords in ASCII even in a non-English deliverable**, because the health check anchors on them and a translation silently turns the check off
- [ ] Every row marked `borrowed` is actually in the borrowed set; no `sidebar-*` and no `chart-*` among them
- [ ] Chapters the borrowed set does not cover say so, and their names follow the pairing rule
- [ ] On the `originate` branch: the three directions differ on at least two axes, and each cost is a consequence in this product rather than an adjective
- [ ] On the `originate` branch: every derived value traces to a coordinate, and the **hue's basis is named in `design.md` §1** — one of the four in `originate.md` §The gap the axes do not close, or an explicit "none applies" that marks the hue as the weakest value in the set. The axes fix count and saturation but not hue, so it is the one slot with no source
- [ ] On the `originate` branch: no value in `tokens.input.json` is a framework default (the list is in `originate.md` §Do not emit a framework default) — emitting one means the coordinates never reached the values
- [ ] On the `originate` branch: the hue's basis is stated **without a literal colour** — not the chosen value, not the default being contrasted against. `design.md` carries no hex anywhere, and explaining a choice is not an exemption from that
- [ ] On the `originate` branch: **the token file itself was written** — the real one, at the path and format the paths table gives, committed. Not described, not left for another unit: the old write-the-record-and-let-another-unit-land-it handoff landed 2 of 5 runs (2026-08-25)
- [ ] On the `originate` branch: `tokens.input.json` was also written, at **`.ui-contract-local/tokens.input.json`** — pinned, not configurable, because the instrument and a grader both need one answer
- [ ] On the `originate` branch: **the user was not told to keep or commit the knobs record.** It is not a deliverable; describing it as one invites somebody to maintain a file nothing reads
- [ ] On the `originate` branch: **the two are not confused in the deliverable** (identity table above). Describing the record as where the values live is the second value authority this split exists to prevent
- [ ] **If the token file landed while the asset layer is still without one, `ui-contract-assets` was called again.** It runs twice on a greenfield project and both runs are correct; pass 2 consumes the token file. This unit does not touch the asset layer itself — values are authored here, consumed there, and reversing that gives the project two value authorities
- [ ] **Every ⏳ line in `layout.md` was either settled, refuted, or explicitly left open with a reason** — settled means it became a plain number; refuted means it carries ⛔ and what refuted it (definition: `ui-contract-layout`'s outline §3); left open means the reason is written down. A ⏳ that is merely still there is none of the three. This is the same condition as the first item in this list, stated at the line level — refuted lines were also **named in the hand-back**, because check 10 cannot see them
- [ ] Reported to the user: which sections were touched, how many names were replaced, what was deliberately left alone

Files in this skill

  • SKILL.md14.3 KB
  • assets/theme-studio.html.template17.8 KB
  • design-md-outline.md18.4 KB
  • originate.md18.9 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.