From 439dc08f7659ed07036d2b8b7e973b98dff500c7 Mon Sep 17 00:00:00 2001 From: chaxus Date: Sun, 25 Oct 2026 12:22:20 +0800 Subject: [PATCH] docs(design): grade every design rule as MUST, SHOULD or MAY - Rewrite DESIGN, INTERACTION, ACCEPTANCE, WORKFLOW_PATTERNS and the ranui adapter as ID-tagged rule bullets (183 MUST, 271 SHOULD, 11 MAY). A MUST names its proof: browser, recording, unit, review, a verify:design check or a test path. - Add bin/design-rules.ts: it rejects normative wording outside a graded rule, malformed tags, duplicate IDs, missing or unknown proofs, and renders design/RULES.md plus the ranui adapter index. - Run it from sync:design and verify:docs, ship docs/RULES.md in the ranui package and the agent plugin, and add node tests. Co-Authored-By: Claude Opus 5.5 --- bin/design-rules.ts | 264 ++++ bin/sync-design-docs.ts | 3 +- bin/test/design-rules.test.ts | 225 ++++ design/ACCEPTANCE.md | 296 +++-- design/DESIGN.md | 816 +++++++----- design/INTERACTION.md | 179 +-- design/README.md | 1 + design/RULES.md | 401 ++++++ design/WORKFLOW_PATTERNS.md | 184 ++- package.json | 4 +- packages/ranui/docs/ACCEPTANCE.md | 296 +++-- packages/ranui/docs/DESIGN.md | 1108 +++++++++++------ packages/ranui/docs/INTERACTION.md | 179 +-- packages/ranui/docs/RULES.md | 491 ++++++++ packages/ranui/docs/WORKFLOW_PATTERNS.md | 184 ++- packages/ranui/docs/adapters/DESIGN.md | 292 +++-- packages/ranui/docs/adapters/RULES.md | 85 ++ packages/ranui/package.json | 1 + .../skills/design/references/ACCEPTANCE.md | 296 +++-- .../ran/skills/design/references/DESIGN.md | 816 +++++++----- .../skills/design/references/INTERACTION.md | 179 +-- .../ran/skills/design/references/README.md | 1 + plugins/ran/skills/design/references/RULES.md | 403 ++++++ .../design/references/WORKFLOW_PATTERNS.md | 184 ++- .../skills/design/references/manifest.json | 26 +- 25 files changed, 5047 insertions(+), 1867 deletions(-) create mode 100644 bin/design-rules.ts create mode 100644 bin/test/design-rules.test.ts create mode 100644 design/RULES.md create mode 100644 packages/ranui/docs/RULES.md create mode 100644 packages/ranui/docs/adapters/RULES.md create mode 100644 plugins/ran/skills/design/references/RULES.md diff --git a/bin/design-rules.ts b/bin/design-rules.ts new file mode 100644 index 0000000000..7c43635c51 --- /dev/null +++ b/bin/design-rules.ts @@ -0,0 +1,264 @@ +import { format } from 'prettier'; +import { existsSync, readFileSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +/** + * Rule indexes and the graded sources each one lists. A file joins a list once every normative + * sentence in it carries an ID. `links` is the index's relative path to its sources' rendered pages. + */ +export const ruleIndexes = [ + { + path: 'design/RULES.md', + sources: ['design/DESIGN.md', 'design/INTERACTION.md', 'design/ACCEPTANCE.md', 'design/WORKFLOW_PATTERNS.md'], + links: './', + portable: true, + }, + { + path: 'packages/ranui/docs/adapters/RULES.md', + sources: ['packages/ranui/docs/adapters/DESIGN.md'], + links: '../', + portable: false, + }, +]; +const verifierPath = 'packages/ranui/bin/verify-design-rules.ts'; +/** Adapter sources and the portable page bin/sync-design-docs.ts appends them to. */ +const appendedTo: Record = { 'packages/ranui/docs/adapters/DESIGN.md': 'design/DESIGN.md' }; + +export type Level = 'MUST' | 'SHOULD' | 'MAY'; +export interface Rule { + id: string; + level: Level; + text: string; + proof?: string; + source: string; + section: string; + line: number; +} + +const RULE_START = /^- \*\*([A-Z]+(?:-[A-Z]+)*-\d+) · (MUST|SHOULD|MAY)\*\* (.+)$/; +const PROOF = /\s*_Proof: (.+?)_\s*$/; +// 看起来像规则标签、但没对上 RULE_START 的行,多半是手误(等级小写、分隔符写错)。 +const TAG_LIKE = /^\s*- \*\*[A-Z]+-/; +// 规则条目之外出现这些词,说明有规范性句子漏标了等级。 +// 全大写的 MUST/SHOULD 是等级名本身,不算漏标。祈使句("Use …")无法机械识别,靠评审。 +// 标题不检查:已有锚点被别处链接,改标题会断链。 +const NORMATIVE = /\b([Mm]ust|[Nn]ever|[Aa]lways|[Ss]hould|[Ss]hall|[Dd]o not|[Dd]on['’]t)\b/; +const proofKinds = new Set(['browser', 'recording', 'review', 'unit']); + +/** GitHub-style heading anchor, without the duplicate suffix. */ +export const slug = (heading: string) => + heading + .toLowerCase() + .replace(/[^\p{L}\p{N}\s_-]/gu, '') + .trim() + .replace(/\s/g, '-'); + +/** Heading anchor counts of a document, so a source appended to it gets the anchors of the combined page. */ +export function headingAnchors(content: string): Map { + const anchors = new Map(); + let fence = ''; + for (const line of content.split(/\r?\n/)) { + const marker = line.match(/^\s*(`{3,}|~{3,})/)?.[1]; + if (marker && (!fence || marker.startsWith(fence))) fence = fence ? '' : marker; + const heading = !fence && line.match(/^#{1,6} (.+)$/); + if (heading) anchors.set(slug(heading[1]!), (anchors.get(slug(heading[1]!)) ?? 0) + 1); + } + return anchors; +} + +/** Parse one graded source; returns its rules and every problem found. */ +export function parseRules( + source: string, + content: string, + anchors = new Map(), +): { rules: Rule[]; problems: string[] } { + const rules: Rule[] = []; + const problems: string[] = []; + let section = ''; + let fence = ''; + let comment = false; + let current: Rule | undefined; + const close = () => { + if (!current) return; + const proof = current.text.match(PROOF); + if (proof) { + current.proof = proof[1]!.trim(); + current.text = current.text.replace(PROOF, ''); + } + if (current.text.includes('_Proof:')) + problems.push(`${current.source}:${current.line} ${current.id}: the proof ends the rule`); + rules.push(current); + current = undefined; + }; + content.split(/\r?\n/).forEach((line, index) => { + const at = `${source}:${index + 1}`; + // 只有同种、不短于开头的围栏才算闭合。 + const marker = line.match(/^\s*(`{3,}|~{3,})/)?.[1]; + if (marker && (!fence || marker.startsWith(fence))) { + fence = fence ? '' : marker; + return; + } + if (fence) return; + if (comment || line.trimStart().startsWith(''); + return; + } + if (current && /^ {2}\S/.test(line) && !/^ {2}[-*+] /.test(line)) { + current.text += ` ${line.trim()}`; + return; + } + close(); + const heading = line.match(/^#{1,6} (.+)$/); + if (heading) { + const base = slug(heading[1]!); + const seen = anchors.get(base) ?? 0; + anchors.set(base, seen + 1); + section = seen ? `${base}-${seen}` : base; + return; + } + const start = line.match(RULE_START); + if (start) { + if (!section) problems.push(`${at}: a rule needs a section heading above it`); + current = { id: start[1]!, level: start[2] as Level, text: start[3]!, source, section, line: index + 1 }; + return; + } + if (TAG_LIKE.test(line)) + problems.push(`${at}: malformed rule tag (expected "- **ID-1 · MUST** …"): ${line.trim()}`); + else if (/^ +\S/.test(line) && !/^ {0,3}[|>]/.test(line)) + problems.push(`${at}: indented text belongs to no rule (nested list or a gap after the rule): ${line.trim()}`); + else if (NORMATIVE.test(line)) problems.push(`${at}: normative wording outside a graded rule: ${line.trim()}`); + }); + close(); + return { rules, problems }; +} + +/** Validate IDs, levels and proofs across every graded source. */ +export function checkRules(rules: Rule[], root: string): string[] { + const problems: string[] = []; + const verifier = readFileSync(path.join(root, verifierPath), 'utf8'); + const checks = new Set([...verifier.matchAll(/^\s*id: '([a-z-]+)'/gm)].map((match) => match[1])); + const seen = new Map(); + for (const rule of rules) { + const at = `${rule.source}:${rule.line} ${rule.id}`; + const first = seen.get(rule.id); + if (first) problems.push(`${at}: duplicate ID, first used at ${first.source}:${first.line}`); + seen.set(rule.id, rule); + if (rule.level !== 'MUST') { + if (rule.proof) problems.push(`${at}: only MUST rules name a proof`); + continue; + } + if (!rule.proof) { + problems.push( + `${at}: a MUST rule names its proof (_Proof: browser|recording|review|unit|check:|test:_)`, + ); + continue; + } + for (const proof of rule.proof.split(/,\s*/)) { + const [kind, target] = proof.split(/:(.*)/s); + if (kind === 'check' && target) { + if (!checks.has(target)) problems.push(`${at}: unknown verify:design rule ${target}`); + } else if (kind === 'test' && target) { + if (!existsSync(path.join(root, target))) problems.push(`${at}: missing test ${target}`); + } else if (!proofKinds.has(proof)) problems.push(`${at}: unknown proof ${proof}`); + } + } + return problems; +} + +/** Render the rule index from the graded sources. */ +export function renderIndex(rules: Rule[], links = './', portable = true): string { + const cell = (text: string) => text.replaceAll('|', '\\|'); + const counts = (['MUST', 'SHOULD', 'MAY'] as const).map( + (level) => `${rules.filter((rule) => rule.level === level).length} ${level}`, + ); + const rows = rules.map((rule) => { + const link = `${links}${path.basename(rule.source)}#${rule.section}`; + return `| [${rule.id}](${link}) | ${rule.level} | ${cell(rule.text)} | ${rule.proof ?? ''} |`; + }); + const banner = + ''; + const table = ['| ID | Level | Rule | Proof |', '| --- | --- | --- | --- |', ...rows, '']; + if (!portable) + return [ + banner, + '', + '## ranui adapter rules', + '', + `ranui-specific rules (${counts.join(', ')}).`, + '', + ...table, + ].join('\n'); + return [ + banner, + '', + '# Rule index', + '', + `Every graded rule, with its level and proof (${counts.join(', ')}). Open a rule's ID for its section.`, + '', + 'Proof kinds: **browser** — real browser interaction ([browser proof](./ACCEPTANCE.md#browser-proof));', + '**recording** — continuous recording ([motion over time](./ACCEPTANCE.md#review-motion-over-time));', + '**unit** — a unit or state test in the host; **review** — inspection of the change itself;', + '`check:` — a `verify:design` rule; `test:` — a named test (paths from the repository root).', + '', + ...table, + ].join('\n'); +} + +function parseSources(root: string, sources: string[]): { rules: Rule[]; problems: string[] } { + const rules: Rule[] = []; + const problems: string[] = []; + for (const source of sources) { + const base = appendedTo[source]; + const anchors = base ? headingAnchors(readFileSync(path.join(root, base), 'utf8')) : undefined; + const parsed = parseRules(source, readFileSync(path.join(root, source), 'utf8'), anchors); + rules.push(...parsed.rules); + problems.push(...parsed.problems); + } + return { rules, problems }; +} + +/** Every graded rule across all indexes, with every problem; IDs are unique across indexes. */ +export function collectRules(root: string): { rules: Rule[]; problems: string[] } { + const { rules, problems } = parseSources( + root, + ruleIndexes.flatMap((index) => index.sources), + ); + problems.push(...checkRules(rules, root)); + return { rules, problems }; +} + +export async function syncRules(root: string, check: boolean): Promise { + const { rules, problems } = collectRules(root); + if (problems.length) throw new Error(`Design rule problems:\n${problems.join('\n')}`); + for (const index of ruleIndexes) { + const own = rules.filter((rule) => index.sources.includes(rule.source)); + const expected = await format(renderIndex(own, index.links, index.portable), { + parser: 'markdown', + printWidth: 120, + }); + const target = path.join(root, index.path); + if (!check) writeFileSync(target, expected); + else if (!existsSync(target) || readFileSync(target, 'utf8') !== expected) + throw new Error(`${index.path} is stale; run sync:design.`); + } +} + +/** Problems in one source on its own, for grading a file before it joins an index. */ +export function lintSource(root: string, source: string): string[] { + const { rules, problems } = parseSources(root, [source]); + return [...problems, ...checkRules(rules, root)]; +} + +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + const root = fileURLToPath(new URL('../', import.meta.url)); + const args = process.argv.slice(2); + if (args[0] === '--lint' && args.length === 2) { + const problems = lintSource(root, args[1]!); + console.log(problems.length ? problems.join('\n') : `${args[1]}: no problems.`); + process.exitCode = problems.length ? 1 : 0; + } else if (args.every((arg) => arg === '--check')) { + await syncRules(root, args.includes('--check')); + console.log(`Design rules ${args.includes('--check') ? 'verified' : 'indexed'}.`); + } else throw new Error('Usage: node bin/design-rules.ts [--check | --lint ]'); +} diff --git a/bin/sync-design-docs.ts b/bin/sync-design-docs.ts index cbd08f448f..9b3d0afe7a 100644 --- a/bin/sync-design-docs.ts +++ b/bin/sync-design-docs.ts @@ -10,6 +10,7 @@ export const guideNames = [ 'WORKFLOW_PATTERNS', 'REFERENCE_RESEARCH', 'INTERFACE_GUIDE', + 'RULES', ] as const; const repository = fileURLToPath(new URL('../', import.meta.url)); @@ -24,7 +25,7 @@ export async function renderDesignDocs(root: string): Promise `# Interaction standard\n\n## 1. Inputs\n\n${body}\n`; + +test('the repository rules are graded, unique, proven and indexed', async () => { + const { rules, problems } = collectRules(repo); + assert.deepEqual(problems, []); + assert.ok(rules.length > 0); + await syncRules(repo, true); +}); + +test('normative wording outside a graded rule is rejected, in tables too; code and the level names are not', () => { + const { problems } = parseRules( + 'design/X.md', + graded( + [ + 'MUST and SHOULD name the levels.', + '| Case | Restore only repopulates |', + '```', + 'never inside code', + '```', + 'A preview should match its file.', + ].join('\n'), + ), + ); + assert.equal(problems.length, 1); + assert.match(problems[0]!, /design\/X\.md:10: normative wording outside a graded rule/); +}); + +test('a rule keeps its continuation lines, section anchor and proof', () => { + const { rules, problems } = parseRules( + 'design/X.md', + graded('- **X-IN-1 · MUST** Submit dispatches\n once. _Proof: unit_\n- **X-IN-2 · SHOULD** Keep labels near.'), + ); + assert.deepEqual(problems, []); + assert.deepEqual( + rules.map(({ id, level, text, proof, section }) => ({ id, level, text, proof, section })), + [ + { id: 'X-IN-1', level: 'MUST', text: 'Submit dispatches once.', proof: 'unit', section: '1-inputs' }, + { id: 'X-IN-2', level: 'SHOULD', text: 'Keep labels near.', proof: undefined, section: '1-inputs' }, + ], + ); + assert.match( + renderIndex(rules), + /\| \[X-IN-1\]\(\.\/X\.md#1-inputs\) \| MUST \| Submit dispatches once\. \| unit \|/, + ); +}); + +test('missing, unknown or misplaced proofs and duplicate IDs are reported', () => { + const root = mkdtempSync(path.join(tmpdir(), 'ran-design-rules-')); + try { + mkdirSync(path.join(root, 'packages/ranui/bin'), { recursive: true }); + writeFileSync(path.join(root, 'packages/ranui/bin/verify-design-rules.ts'), " id: 'bare-colour',\n"); + const { rules } = parseRules( + 'design/X.md', + graded( + [ + '- **X-IN-1 · MUST** No proof.', + '- **X-IN-2 · MUST** Known check. _Proof: check:bare-colour_', + '- **X-IN-3 · MUST** Unknown check. _Proof: check:no-such-rule_', + '- **X-IN-4 · MUST** Missing test. _Proof: test:missing.test.ts_', + '- **X-IN-5 · SHOULD** Proof on a default. _Proof: review_', + '- **X-IN-2 · MAY** Duplicate.', + ].join('\n'), + ), + ); + const problems = checkRules(rules, root); + assert.equal(problems.length, 5); + for (const [id, reason] of [ + ['X-IN-1', /names its proof/], + ['X-IN-3', /unknown verify:design rule no-such-rule/], + ['X-IN-4', /missing test missing\.test\.ts/], + ['X-IN-5', /only MUST rules name a proof/], + ['X-IN-2', /duplicate ID/], + ] as const) + assert.ok( + problems.some((problem) => problem.includes(id) && reason.test(problem)), + `${id} ${reason}`, + ); + } finally { + rmSync(root, { recursive: true, force: true }); + } +}); + +test('CRLF sources parse like LF sources', () => { + const lf = graded('- **X-IN-1 · MUST** Dispatch once. _Proof: unit_'); + const { rules, problems } = parseRules('design/X.md', lf.replaceAll('\n', '\r\n')); + assert.deepEqual(problems, []); + assert.deepEqual( + rules.map((rule) => [rule.id, rule.proof]), + [['X-IN-1', 'unit']], + ); +}); + +test('typos, nested lists, detached continuations and mid-rule proofs are reported, comments are not', () => { + const { rules, problems } = parseRules( + 'design/X.md', + graded( + [ + '', + '- **X-IN-1 - MUST** Wrong separator.', + '- **X-IN-2 · must** Lower-case level.', + '- **X-IN-3 · MUST** Parent.', + ' - a nested child that must hide nothing', + '- **X-IN-4 · MUST** Detached.', + '', + ' _Proof: unit_', + '- **X-IN-5 · SHOULD** Mid _Proof: review_ rule.', + ].join('\n'), + ), + ); + assert.deepEqual( + rules.map((rule) => rule.id), + ['X-IN-3', 'X-IN-4', 'X-IN-5'], + ); + for (const reason of [ + /:6: malformed rule tag/, + /:7: malformed rule tag/, + /:9: indented text belongs to no rule/, + /:12: indented text belongs to no rule/, + /X-IN-5: the proof ends the rule/, + ]) + assert.ok( + problems.some((problem) => reason.test(problem)), + String(reason), + ); + assert.equal(problems.length, 5); +}); + +test('several proofs, underscores in paths, pipes and duplicate headings render correctly', () => { + const { rules } = parseRules( + 'design/X.md', + [ + '# X', + '## Same_name', + '- **X-A-1 · MUST** A | B. _Proof: unit, test:bin/test/a_b.test.ts_', + '## Same_name', + '- **X-A-2 · MAY** Again.', + ].join('\n'), + ); + assert.deepEqual( + rules.map(({ section, proof }) => [section, proof]), + [ + ['same_name', 'unit, test:bin/test/a_b.test.ts'], + ['same_name-1', undefined], + ], + ); + assert.match(renderIndex(rules), /\| A \\\| B\. \| unit, test:bin\/test\/a_b\.test\.ts \|/); +}); + +test('the index check rejects a stale index and regeneration repairs it', async () => { + const root = mkdtempSync(path.join(tmpdir(), 'ran-design-index-')); + try { + mkdirSync(path.join(root, 'packages/ranui/bin'), { recursive: true }); + mkdirSync(path.join(root, 'packages/ranui/docs/adapters'), { recursive: true }); + mkdirSync(path.join(root, 'design')); + writeFileSync(path.join(root, 'packages/ranui/bin/verify-design-rules.ts'), ''); + for (const source of ruleIndexes.flatMap((index) => index.sources)) + writeFileSync(path.join(root, source), graded('')); + writeFileSync(path.join(root, 'design/INTERACTION.md'), graded('- **X-IN-1 · SHOULD** Keep labels near.')); + await syncRules(root, false); + await syncRules(root, true); + writeFileSync(path.join(root, 'design/INTERACTION.md'), graded('- **X-IN-1 · MAY** Keep labels near.')); + await assert.rejects(syncRules(root, true), /RULES\.md is stale/); + await syncRules(root, false); + await syncRules(root, true); + } finally { + rmSync(root, { recursive: true, force: true }); + } +}); + +test('table cells are linted and a fence closes only on its own marker', () => { + const { problems } = parseRules( + 'design/X.md', + graded( + ['| Case | Restore must not submit |', '```', '~~~', 'never, still code', '```', 'after code, never'].join('\n'), + ), + ); + assert.deepEqual( + problems.map((problem) => problem.match(/:(\d+):/)?.[1]), + ['5', '10'], + ); +}); + +test('an adapter appended to a page takes the anchors of the combined page', () => { + const anchors = headingAnchors('# Ran\n## Rules\n```\n## not a heading\n```\n'); + const { rules } = parseRules('adapters/DESIGN.md', '## Rules\n- **R-A-1 · MAY** Optional.', anchors); + assert.equal(rules[0]!.section, 'rules-1'); +}); + +test('IDs are unique across indexes, and a single source can be linted alone', () => { + const root = mkdtempSync(path.join(tmpdir(), 'ran-design-ids-')); + try { + mkdirSync(path.join(root, 'packages/ranui/bin'), { recursive: true }); + mkdirSync(path.join(root, 'packages/ranui/docs/adapters'), { recursive: true }); + mkdirSync(path.join(root, 'design')); + writeFileSync(path.join(root, 'packages/ranui/bin/verify-design-rules.ts'), ''); + for (const source of ruleIndexes.flatMap((index) => index.sources)) + writeFileSync(path.join(root, source), graded('')); + writeFileSync(path.join(root, 'design/DESIGN.md'), graded('- **D-A-1 · MAY** Portable.')); + writeFileSync(path.join(root, 'packages/ranui/docs/adapters/DESIGN.md'), graded('- **D-A-1 · MAY** Adapter.')); + assert.ok(collectRules(root).problems.some((problem) => /D-A-1: duplicate ID/.test(problem))); + assert.deepEqual(lintSource(root, 'design/DESIGN.md'), []); + writeFileSync(path.join(root, 'design/DRAFT.md'), graded('A draft rule must be graded.')); + assert.equal(lintSource(root, 'design/DRAFT.md').length, 1); + } finally { + rmSync(root, { recursive: true, force: true }); + } +}); diff --git a/design/ACCEPTANCE.md b/design/ACCEPTANCE.md index 714a1eb709..28f660782f 100644 --- a/design/ACCEPTANCE.md +++ b/design/ACCEPTANCE.md @@ -4,130 +4,204 @@ Use this contract for a changed visual or interaction outcome. [DESIGN](./DESIGN appearance and composition; [INTERACTION](./INTERACTION.md) owns observable behavior. The [interface index](./INTERFACE_GUIDE.md) routes the task. Read only applicable sections. +Each rule carries an ID and a level. **MUST** is a defect when broken and names its proof; +**SHOULD** is the default, and a deviation states its reason for that case; **MAY** is optional. +The [rule index](./RULES.md) lists every graded rule with its proof. + ## Agree on the observable result -Before editing, settle the [design brief](./DESIGN.md#style-coherence-across-components-and-pages), -and state the affected task, views/states and independent expectations in that same note. Name the relevant owner, supported public API and host capability limits. Select -widths, themes, languages and input paths from the actual change; do not invent unrelated work. -For composition, state content/text/icon spines, intentional navigation columns, hit bounds -and vertical relationships. Do not derive the expected value from the implementation under test. +- **V-AGREE-1 · SHOULD** Before editing, settle the + [design brief](./DESIGN.md#style-coherence-across-components-and-pages), and state the affected + task, views/states and independent expectations in that same note. +- **V-AGREE-2 · SHOULD** Name the relevant owner, supported public API and host capability limits. +- **V-AGREE-3 · SHOULD** Select widths, themes, languages and input paths from the actual change, + without inventing unrelated work. +- **V-AGREE-4 · SHOULD** For composition, state content/text/icon spines, intentional navigation + columns, hit bounds and vertical relationships. +- **V-AGREE-5 · MUST** Do not derive the expected value from the implementation under test. + _Proof: review_ +- **V-AGREE-6 · SHOULD** Review in order: task/hierarchy, static composition, reachable combined + states, then motion. +- **V-AGREE-7 · SHOULD** Do not add animation to compensate for unresolved proportions or unclear + navigation. +- **V-AGREE-8 · SHOULD** When a review changes the intended design, update the existing brief first + and identify the evidence that prompted it. -Review in order: task/hierarchy, static composition, reachable combined states, then motion. -Do not add animation to compensate for unresolved proportions or unclear navigation. When a review -changes the intended design, update the existing brief first and identify the evidence that prompted it. This is a decision record, not a new approval ceremony for work the user already authorized. -Keep one rule owner. An API/token check verifies source contracts; an image comparison verifies -pixels; an agent run verifies whether guidance was followed. None substitutes for the others. -A fixture demonstrates its own result, not acceptance of a host application it did not render. +An API/token check verifies source contracts; an image comparison verifies pixels; an agent run +verifies whether guidance was followed. + +- **V-AGREE-9 · SHOULD** Keep one rule owner. +- **V-AGREE-10 · MUST** Do not let one kind of check substitute for another: an API/token check, an + image comparison and an agent run each establish only their own result. _Proof: review_ +- **V-AGREE-11 · MUST** A fixture demonstrates its own result, not acceptance of a host application + it did not render; do not claim it as host acceptance. _Proof: review_ ## Browser proof -Capture the actual running surface before the fix when reproducing a visible defect. After the -fix, capture the same relevant state and inspect the full pane with realistic copy and surrounding -chrome. A mockup, source diff, changed assertion or screenshot of an unrelated build is not proof. -Inspect the composition as well as the link count: relabeling a redundant return as a breadcrumb -can still repeat the same context and add an unnecessary header row. Trace ancestor reachability -through the parent before deciding another visible shortcut is needed. - -- Wait for component registration, pending content, fonts and rendered frames before measuring. - Fail on missing/zero-sized targets. Record any subpixel tolerance; keep it below a full CSS pixel - for exact spines (the source example uses 0.5px). - For live overlays, verify that capture preserves the viewport and includes the full trigger/panel; - a full-page screenshot can invalidate a crop by causing responsive reflow or portal repositioning. -- Measure the defect's layer: wrapper, inner content, rendered text Range, icon viewport and - actual pointer target are different. A Range is not exact glyph ink. Account for shadow parts - through supported test seams; do not introduce production shadow queries for acceptance. -- Exercise affected reflow, wrapped translations, optional content and input paths. A static - screenshot cannot prove focus return, keyboard navigation, touch discovery, interruptibility - or async settlement. Record a continuous interaction when the defect unfolds over time. -- For a layout assertion, prove it rejects the relevant defect: move only label text while the - frame stays fixed, introduce a doubled margin/phantom helper, or expand a title target into - filler. Confirm the mutation won the cascade, observe failure, restore and observe success. - Use changes relevant to the task; do not repeat the entire example suite for a tiny fix. -- Verify raw data when changing presentation. For code, copy the source payload, not highlighted - DOM text: preserve tabs, blank lines, final newline and streamed additions. Verify clipboard - success/failure, native selection and long-line scrolling independently. Document browser - selection normalization without allowing it to alter the raw clipboard payload. +- **V-PROOF-1 · MUST** When reproducing a visible defect, capture the actual running surface before + the fix. _Proof: browser_ +- **V-PROOF-2 · MUST** After the fix, capture the same relevant state and inspect the full pane with + realistic copy and surrounding chrome. _Proof: browser_ +- **V-PROOF-3 · MUST** Do not offer a mockup, source diff, changed assertion or screenshot of an + unrelated build as proof. _Proof: review_ +- **V-PROOF-4 · SHOULD** Inspect the composition as well as the link count: relabeling a redundant + return as a breadcrumb can still repeat the same context and add an unnecessary header row. +- **V-PROOF-5 · SHOULD** Trace ancestor reachability through the parent before deciding another + visible shortcut is needed. +- **V-PROOF-6 · MUST** Wait for component registration, pending content, fonts and rendered frames + before measuring. _Proof: browser_ +- **V-PROOF-7 · MUST** Fail on missing or zero-sized targets. _Proof: browser_ +- **V-PROOF-8 · MUST** Record any subpixel tolerance, and keep it below a full CSS pixel for exact + spines (the source example uses 0.5px). _Proof: review_ +- **V-PROOF-9 · MUST** For live overlays, verify that capture preserves the viewport and includes the + full trigger/panel; a full-page screenshot can invalidate a crop by causing responsive reflow or + portal repositioning. _Proof: browser_ +- **V-PROOF-10 · MUST** Measure the defect's layer: wrapper, inner content, rendered text Range, icon + viewport and actual pointer target are different. A Range is not exact glyph ink. + _Proof: browser_ +- **V-PROOF-11 · MUST** Account for shadow parts through supported test seams; do not introduce + production shadow queries for acceptance. _Proof: review_ +- **V-PROOF-12 · SHOULD** Exercise affected reflow, wrapped translations, optional content and input + paths. +- **V-PROOF-13 · MUST** Do not use a static screenshot as proof of focus return, keyboard navigation, + touch discovery, interruptibility or async settlement. _Proof: review_ +- **V-PROOF-14 · MUST** Record a continuous interaction when the defect unfolds over time. + _Proof: recording_ +- **V-PROOF-15 · MUST** For a layout assertion, prove it rejects the relevant defect: move only label + text while the frame stays fixed, introduce a doubled margin/phantom helper, or expand a title + target into filler. _Proof: browser_ +- **V-PROOF-16 · MUST** Confirm the mutation won the cascade, observe failure, restore and observe + success. _Proof: browser_ +- **V-PROOF-17 · SHOULD** Use mutations relevant to the task rather than repeating the entire example + suite for a tiny fix. +- **V-PROOF-18 · MUST** Verify raw data when changing presentation. _Proof: browser, unit_ +- **V-PROOF-19 · MUST** For code, copy the source payload, not highlighted DOM text: preserve tabs, + blank lines, final newline and streamed additions. _Proof: unit_ +- **V-PROOF-20 · MUST** Verify clipboard success/failure, native selection and long-line scrolling + independently. _Proof: browser_ +- **V-PROOF-21 · MUST** Document browser selection normalization without allowing it to alter the raw + clipboard payload. _Proof: unit, review_ The source-only [layout example](https://github.com/chaxus/ran/blob/main/packages/ranui/test/e2e/layout-contract.md) contains runnable commands and geometry utilities. Consumers adapt its relationships to their -host; these helpers are not new ranui exports. CSS zoom stress does not establish native browser -zoom or text-size accessibility support. Say which paths remain unverified. +host; these helpers are not new ranui exports. -### Review motion over time +- **V-PROOF-22 · MUST** Do not claim native browser zoom or text-size accessibility support from CSS + zoom stress. _Proof: review_ +- **V-PROOF-23 · MUST** Say which paths remain unverified. _Proof: review_ -For changed motion, inspect a continuous action at normal speed, then replay slowly where a -transition breaks. Include activation, intermediate state, completion and the reverse path; repeat -or reverse input before completion. Verify the final state, focus, event count and reading position -independently of the animation. A slow replay helps diagnose discontinuity; it is not a frame-rate -benchmark. Reduced motion needs its own visible-state check, not just a shorter recording. +### Review motion over time -Report a temporal defect as `element → input → observed frame/state → expected continuity`. -Use one representative real task across affected siblings to detect inconsistent motion language; -check the full composition again after local fixes. Preserve failed iterations as diagnostics, -separate from accepted captures. These checks complement the existing evidence binding below. +- **V-MOTION-1 · MUST** For changed motion, inspect a continuous action at normal speed, then replay + slowly where a transition breaks. _Proof: recording_ +- **V-MOTION-2 · MUST** Include activation, intermediate state, completion and the reverse path; + repeat or reverse input before completion. _Proof: recording_ +- **V-MOTION-3 · MUST** Verify the final state, focus, event count and reading position independently + of the animation. _Proof: browser_ +- **V-MOTION-4 · MUST** Use a slow replay to diagnose discontinuity, not as a frame-rate benchmark. + _Proof: review_ +- **V-MOTION-5 · MUST** Give reduced motion its own visible-state check, not just a shorter recording. + _Proof: browser_ +- **V-MOTION-6 · SHOULD** Report a temporal defect as + `element → input → observed frame/state → expected continuity`. +- **V-MOTION-7 · SHOULD** Use one representative real task across affected siblings to detect + inconsistent motion language. +- **V-MOTION-8 · SHOULD** Check the full composition again after local fixes. +- **V-MOTION-9 · SHOULD** Preserve failed iterations as diagnostics, separate from accepted captures. + +These checks complement the existing evidence binding below. ## Walk through the task -For a changed workflow or composition, start with a realistic goal expressed without naming the -control to use. Complete it from the actual entry point through result, recovery and return, as -relevant. Record the first choice, any ambiguous label, hesitation or backtracking, and whether -the result makes the next step clear. A shorter click path is not useful if the choices are unclear. -Use realistic content, including a representative long or failing case, rather than a polished -empty state alone. Fix task structure and content hierarchy before adding decorative effects. - -Report the most consequential observed problems (up to three), with the state and evidence that -show each one; do not invent findings to fill a quota. Repeat the affected path after correction. -An agent walkthrough can expose defects but is not a human usability study. When observing a real -participant, let them attempt the goal before explaining the interface; preserve that distinction -in the results below. +- **V-WALK-1 · SHOULD** For a changed workflow or composition, start with a realistic goal expressed + without naming the control to use. +- **V-WALK-2 · SHOULD** Complete the goal from the actual entry point through result, recovery and + return, as relevant. +- **V-WALK-3 · SHOULD** Record the first choice, any ambiguous label, hesitation or backtracking, and + whether the result makes the next step clear. + +A shorter click path is not useful if the choices are unclear. + +- **V-WALK-4 · SHOULD** Use realistic content, including a representative long or failing case, + rather than a polished empty state alone. +- **V-WALK-5 · SHOULD** Fix task structure and content hierarchy before adding decorative effects. +- **V-WALK-6 · SHOULD** Report the most consequential observed problems (up to three), with the state + and evidence that show each one. +- **V-WALK-7 · MUST** Do not invent findings to fill a quota. _Proof: review_ +- **V-WALK-8 · SHOULD** Repeat the affected path after correction. +- **V-WALK-9 · MUST** Report an agent walkthrough as a defect search, not as a human usability study. + _Proof: review_ +- **V-WALK-10 · SHOULD** When observing a real participant, let them attempt the goal before + explaining the interface, and preserve that distinction in the results below. ## Bind evidence to what actually ran -For each reviewed state, retain a compact record of: - -- **Runtime:** tested source revision/build and relevant dirty inputs or their fingerprint; - include shared styles/components and incoming data when they affect the result. A Git HEAD - alone is insufficient if the running build or working tree differs. -- **Scope/environment:** affected view/group membership, route/state, viewport, theme, locale, - browser and input or reduced-motion settings where relevant. -- **Media:** capture/recording locations and content identity (for example SHA-256), tied to - that runtime and state. Keep before and after distinct; never overwrite the only before image. -- **Review:** what the automated check and agent inspection actually established, separate - from any human decision and any unverified state. - -A short Markdown record is enough; an existing manifest may supply these fields. Do not add a -new evidence service or configuration framework for an ordinary change. Keep hashes beside the -files or in a referenced manifest so the claimed binding can be checked independently. - -**A review applies only to the evidence and runtime scope it inspected.** Replacing an image, -editing a recording, changing group membership or changing relevant runtime inputs makes the -affected review stale. After a shared style/component change, refresh every affected state, -rebuild if needed, then inspect the new evidence. Old captures may remain as history, not current -proof. A docs-only edit or unrelated source change does not invalidate identical runtime evidence; -record why its dependency scope is unaffected rather than recapturing everything by HEAD alone. +Each reviewed state gets a compact record with these fields: + +- **V-FRESH-1 · MUST** Runtime: record the tested source revision/build and relevant dirty inputs or + their fingerprint, including shared styles/components and incoming data when they affect the + result. A Git HEAD alone is insufficient if the running build or working tree differs. + _Proof: review_ +- **V-FRESH-2 · MUST** Scope/environment: record affected view/group membership, route/state, + viewport, theme, locale, browser and input or reduced-motion settings where relevant. + _Proof: review_ +- **V-FRESH-3 · MUST** Media: record capture/recording locations and content identity (for example + SHA-256), tied to that runtime and state. _Proof: review_ +- **V-FRESH-4 · MUST** Keep before and after distinct; never overwrite the only before image. + _Proof: review_ +- **V-FRESH-5 · MUST** Review: record what the automated check and agent inspection actually + established, separate from any human decision and any unverified state. _Proof: review_ +- **V-FRESH-6 · MAY** Use a short Markdown record; an existing manifest may supply these fields. +- **V-FRESH-7 · SHOULD** Do not add a new evidence service or configuration framework for an ordinary + change. +- **V-FRESH-8 · MUST** Keep hashes beside the files or in a referenced manifest so the claimed binding + can be checked independently. _Proof: review_ + +**A review applies only to the evidence and runtime scope it inspected.** + +- **V-FRESH-9 · MUST** Treat the affected review as stale after replacing an image, editing a + recording, changing group membership or changing relevant runtime inputs. _Proof: review_ +- **V-FRESH-10 · MUST** After a shared style/component change, refresh every affected state, rebuild + if needed, then inspect the new evidence. _Proof: browser, review_ +- **V-FRESH-11 · MUST** Present no old capture as current proof. _Proof: review_ +- **V-FRESH-12 · MUST** When keeping runtime evidence across a docs-only edit or unrelated source + change, record why its dependency scope is unaffected. _Proof: review_ + +Old captures may remain as history. A docs-only edit or unrelated source change leaves identical +runtime evidence valid, so recapturing everything by HEAD alone is unnecessary. ## Report authority truthfully -Keep these results separate, even in a one-paragraph report: - | Result | Who/what can establish it | What it does not establish | | ------------------- | ------------------------------------------------------------------------------- | ------------------------------------ | | Automated checks | Fresh command output or browser assertions for the declared runtime | Visual quality or a human decision | | Agent visual review | Agent inspected current captures and the affected interaction | User acceptance | | Human decision | An actual user's decision, with its message/action reference and evidence scope | Acceptance of later changed evidence | -Use explicit outcomes such as `automated: passed`, `agent review: passed with limits`, -`human: not requested`. If human review was requested and is outstanding, say `pending`; -if a user rejected it, preserve `rejected` and the finding until that user reviews the updated -scope. An agent may report its correction and fresh checks, but cannot invent a user decision -or clear a human rejection. Avoid an unattributed “accepted”, “approved” or “验收通过”. +- **V-REPORT-1 · MUST** Keep these three results separate, even in a one-paragraph report. + _Proof: review_ +- **V-REPORT-2 · MUST** Claim each result only from the source the table above names, and never for + what the table says it does not establish. _Proof: review_ +- **V-REPORT-3 · SHOULD** Use explicit outcomes such as `automated: passed`, + `agent review: passed with limits`, `human: not requested`. +- **V-REPORT-4 · MUST** If human review was requested and is outstanding, say `pending`. + _Proof: review_ +- **V-REPORT-5 · MUST** If a user rejected it, preserve `rejected` and the finding until that user + reviews the updated scope. _Proof: review_ +- **V-REPORT-6 · MUST** An agent may report its correction and fresh checks, but never invents a user + decision or clears a human rejection. _Proof: review_ +- **V-REPORT-7 · MUST** Avoid an unattributed “accepted”, “approved” or “验收通过”. _Proof: review_ User authorization controls whether to push, update a PR or take another external action. -This document adds no blanket human approval gate: finish and deliver authorized work while -reporting `not requested`/`pending` truthfully. Ask only when the actual authorization or a -necessary product decision is missing. Never label the agent's own visual check “user accepted”. +This document adds no blanket human approval gate. + +- **V-REPORT-8 · SHOULD** Finish and deliver authorized work while reporting + `not requested`/`pending` truthfully. +- **V-REPORT-9 · SHOULD** Ask only when the actual authorization or a necessary product decision is + missing. +- **V-REPORT-10 · MUST** Never label the agent's own visual check “user accepted”. _Proof: review_ A minimal delivery note can be: @@ -140,15 +214,27 @@ A minimal delivery note can be: ## Regression of the guidance itself -For opt-in or task-authorized live evaluation of skill/routing/acceptance changes, use the source-only -[six agent scenarios](https://github.com/chaxus/ran/tree/main/plugins/ran/evals). -Deterministic controls check the assessor without model calls; live runs require an available agent harness -and are not a mandatory gate for every prose edit. Freeze tasks, fixtures, tools and independent rubrics before live runs. Use fresh sessions with -matching model/settings; pin baseline and candidate guidance in separate workspaces. Retain -actual agent work, prompts, outcome checks and evidence. Record baseline passes as controls and -candidate failures as failures. Do not show workers the assessor's thresholds or repair patches. - -Seeded-defect rejection/restoration checks establish the assessor's sensitivity. Only live agent -runs establish behavior on those tasks; prose matching, hand-authored answers or corrected test -expectations do not. Inspect visual judgment independently of mechanical checks. Report sample -size, tools and uncovered conditions; six successful tasks do not prove reliability across models. +Deterministic controls check the assessor without model calls; live runs need an available agent +harness and are not a mandatory gate for every prose edit. + +- **V-EVAL-1 · SHOULD** For opt-in or task-authorized live evaluation of skill/routing/acceptance + changes, use the source-only + [six agent scenarios](https://github.com/chaxus/ran/tree/main/plugins/ran/evals). +- **V-EVAL-2 · MUST** Freeze tasks, fixtures, tools and independent rubrics before live runs. + _Proof: review_ +- **V-EVAL-3 · MUST** Use fresh sessions with matching model/settings. _Proof: review_ +- **V-EVAL-4 · MUST** Pin baseline and candidate guidance in separate workspaces. _Proof: review_ +- **V-EVAL-5 · MUST** Retain actual agent work, prompts, outcome checks and evidence. _Proof: review_ +- **V-EVAL-6 · MUST** Record baseline passes as controls and candidate failures as failures. + _Proof: review_ +- **V-EVAL-7 · MUST** Do not show workers the assessor's thresholds or repair patches. + _Proof: review_ + +Seeded-defect rejection/restoration checks establish the assessor's sensitivity. + +- **V-EVAL-8 · MUST** Claim behavior on those tasks only from live agent runs; prose matching, + hand-authored answers or corrected test expectations are no evidence of it. _Proof: review_ +- **V-EVAL-9 · MUST** Inspect visual judgment independently of mechanical checks. _Proof: review_ +- **V-EVAL-10 · MUST** Report sample size, tools and uncovered conditions. _Proof: review_ +- **V-EVAL-11 · MUST** Six successful tasks are no proof of reliability across models; do not claim + it from them. _Proof: review_ diff --git a/design/DESIGN.md b/design/DESIGN.md index d009aff33f..6ba6e64062 100644 --- a/design/DESIGN.md +++ b/design/DESIGN.md @@ -4,32 +4,47 @@ Design methods for the host product and its existing technology. Read only the r sections through [the task index](./INTERFACE_GUIDE.md). Library tokens and API mechanics belong to their implementation adapter, not this portable method. +Each rule carries an ID and a level. **MUST** is a defect when broken and names its proof; +**SHOULD** is the default, and a deviation states its reason for that case; **MAY** is optional. +The [rule index](./RULES.md) lists every graded rule with its proof. + ## 1. Color — a state ladder, not a palette -Choose semantic roles for content, surfaces, boundaries, actions and status. Define default, -hover, active, disabled and focus states together. Resolve light/dark values through the host’s -authoritative tokens; verify contrast in the actual composition. Brand accents are contextual. +- **D-COLOR-1 · SHOULD** Choose semantic roles for content, surfaces, boundaries, actions and status. +- **D-COLOR-2 · SHOULD** Define default, hover, active, disabled and focus states together. +- **D-COLOR-3 · SHOULD** Resolve light/dark values through the host’s authoritative tokens, and verify + contrast in the actual composition. + +Brand accents are contextual. ## 2. Spacing — a limited, rhythmic scale -Use the host’s existing spacing scale. Tight gaps group related parts; larger gaps separate -tasks or regions. Establish inside padding before outside gaps. Do not copy a reference -product’s dimensions without evaluating content, density and input method. +- **D-SPACE-1 · SHOULD** Use the host’s existing spacing scale. +- **D-SPACE-2 · SHOULD** Use tight gaps to group related parts and larger gaps to separate tasks or + regions. +- **D-SPACE-3 · SHOULD** Establish inside padding before outside gaps. +- **D-SPACE-4 · SHOULD** Do not copy a reference product’s dimensions without evaluating content, density + and input method. ## 3. Typography — choose a role, not a size -Choose heading, label, body, action and code roles with coherent size, weight and line height. -Verify actual scripts, fallback metrics and long content. Typography establishes hierarchy -before decorative containers. Keep a single authority for each role. +- **D-TYPE-1 · SHOULD** Choose heading, label, body, action and code roles with coherent size, weight and + line height, and keep a single authority for each role. +- **D-TYPE-2 · SHOULD** Verify actual scripts, fallback metrics and long content. +- **D-TYPE-3 · SHOULD** Establish hierarchy with typography before decorative containers. ## 4. Radius & elevation -Coordinate nested corner radii with their real inset. Elevation explains a floating layer, -not importance; use the host’s stacking roles and verify overlays against surrounding chrome. +- **D-ELEV-1 · SHOULD** Coordinate nested corner radii with their real inset. +- **D-ELEV-2 · SHOULD** Use elevation to explain a floating layer, not importance, through the host’s + stacking roles, and verify overlays against surrounding chrome. ## 5. Motion — prefer none -Timing below is a starting heuristic. Use the host system’s motion tokens and verify frequency, continuity and reduced motion in the actual task. +Timing below is a starting heuristic. + +- **D-MOTION-1 · SHOULD** Use the host system’s motion tokens and verify frequency, continuity and reduced + motion in the actual task. | Duration | Use | | -------- | ----------------------------------------------------------------------- | @@ -38,136 +53,185 @@ Timing below is a starting heuristic. Use the host system’s motion tokens and | ~200ms | Menu/popover motion when it clarifies the layer change | | ~300ms | Dialog motion when orientation benefits from it | -**Principle:** purpose and frequency determine whether motion earns time. Repeated interaction defaults -to immediate feedback; a larger change is not automatically a reason to animate. Use existing tokens -for purposeful motion, keep it quick and interruptible, and respect `prefers-reduced-motion`. +**Principle:** purpose and frequency determine whether motion earns time. -### Functional motion contract +- **D-MOTION-2 · SHOULD** Default repeated interaction to immediate feedback; a larger change is not + automatically a reason to animate. +- **D-MOTION-3 · SHOULD** Use existing tokens for purposeful motion and keep it quick and interruptible. + Reduced motion is graded under D-MOTION-10 and D-ACCESS-5. -Specify the trigger, state change and destination before choosing an effect. Preserve unrelated -content, drafts and reading position. Sibling components with the same interaction meaning should -share a motion language; consistency does not require every component to animate. +### Functional motion contract -When motion is useful, allow a new intent to interrupt or reverse it from the current presentation, -without a jump, stale completion or duplicate activation. Keep feedback immediate; animation is not -an input lock. During insertion/removal, avoid visibly squeezing text or passing exiting controls -across live actions; coordinate opacity and layout so the result stays readable. Reduced motion -must retain a visible, understandable final state. A spring is optional and need not overshoot. +- **D-MOTION-4 · SHOULD** Specify the trigger, state change and destination before choosing an effect. +- **D-MOTION-5 · MUST** Motion preserves unrelated content, drafts and reading position. _Proof: recording_ +- **D-MOTION-6 · SHOULD** Give sibling components with the same interaction meaning a shared motion + language; consistency does not require every component to animate. +- **D-MOTION-7 · MUST** When motion is used, a new intent can interrupt or reverse it from the current + presentation, without a jump, stale completion or duplicate activation. _Proof: recording_ +- **D-MOTION-8 · MUST** Feedback stays immediate; animation is not an input lock. _Proof: recording_ +- **D-MOTION-9 · SHOULD** During insertion/removal, avoid visibly squeezing text or passing exiting controls + across live actions; coordinate opacity and layout so the result stays readable. +- **D-MOTION-10 · MUST** Reduced motion retains a visible, understandable final state. _Proof: browser_ +- **D-MOTION-11 · MAY** Use a spring; it need not overshoot. See the optional [component craft research](./REFERENCE_RESEARCH.md#component-craft-设计垂点--wxaddesign) for examples and limits, and [temporal acceptance](./ACCEPTANCE.md#review-motion-over-time) for proof. -Theme palettes should settle coherently. Avoid implicit palette fades or `transition: all`; -explicitly opt in only where the host intends and verifies a coordinated effect. -Do not replay decorative title entrances or animate factual counts on each return to a working -surface. Keep readable content and actual values available immediately. A loading demonstration -may move to demonstrate loading; a decorative status pulse must not imply live activity. +- **D-MOTION-12 · SHOULD** Let theme palettes settle coherently. Avoid implicit palette fades or + `transition: all`; opt in explicitly only where the host intends and verifies a coordinated effect. +- **D-MOTION-13 · MUST** Factual values and readable content are shown immediately on return to a working + surface, without animated counts. _Proof: browser_ +- **D-MOTION-16 · SHOULD** Do not replay decorative title entrances on each return to a working surface. +- **D-MOTION-14 · MAY** A loading demonstration may move to demonstrate loading. +- **D-MOTION-15 · MUST** A status pulse appears only while the activity is live. _Proof: review_ ## 6. Content — copy is part of the system -- **Buttons:** name the outcome. Include the object when context does not establish it: "Deploy project", "Delete member". In a clearly named object or dialog, "Save" or "Delete" is sufficient; avoid generic "OK". See [contextual wording](#words-are-part-of-the-interface). -- **Errors:** say **what happened**, then **how to fix it**. ✅ "Build failed: the bundle exceeds the size limit. Reduce it or raise the limit." ❌ "Operation failed, please try again." -- **Confirmations / toasts:** state the completed **change**. ✅ "Project deleted". ❌ "Operation successful". Show completion feedback only after the operation is confirmed. -- Be specific; every sentence should remove guesswork, not add it. +- **D-COPY-1 · SHOULD** Buttons name the outcome. Include the object when context does not establish it: + "Deploy project", "Delete member". In a clearly named object or dialog, "Save" or "Delete" is sufficient; + avoid generic "OK". See [contextual wording](#words-are-part-of-the-interface). +- **D-COPY-2 · SHOULD** Errors say **what happened**, then **how to fix it**. ✅ "Build failed: the bundle + exceeds the size limit. Reduce it or raise the limit." ❌ "Operation failed, please try again." +- **D-COPY-3 · SHOULD** Confirmations and toasts state the completed **change**. ✅ "Project deleted". + ❌ "Operation successful". +- **D-COPY-4 · MUST** Show completion feedback only after the operation is confirmed. _Proof: unit, browser_ +- **D-COPY-5 · SHOULD** Be specific; every sentence removes guesswork rather than adding it. ## 7. Accessibility — the system serves everyone -- Maintain sufficient text-to-background contrast (**WCAG AA**). -- **Never signal state with color alone** — pair it with an icon or text (e.g. ✓ / ✕ labels, not just green/red). -- Every interactive element has a **visible focus ring** appropriate to its surface. Never remove it for "cleanliness". -- **Full keyboard navigation** — nothing is mouse-only. -- Respect `prefers-reduced-motion` and `prefers-color-scheme`. +- **D-ACCESS-1 · MUST** Text keeps sufficient contrast with its background (**WCAG AA**). _Proof: browser_ +- **D-ACCESS-2 · MUST** Never signal state with color alone; pair it with an icon or text (e.g. ✓ / ✕ labels, + not just green/red). _Proof: review_ +- **D-ACCESS-3 · MUST** Every interactive element has a **visible focus ring** appropriate to its surface; + never remove it for "cleanliness". _Proof: browser_ +- **D-ACCESS-4 · MUST** **Full keyboard navigation**: nothing is mouse-only. _Proof: browser_ +- **D-ACCESS-5 · MUST** Respect `prefers-reduced-motion`. _Proof: browser_ +- **D-ACCESS-6 · MUST** When the host supports both schemes, the initial render follows + `prefers-color-scheme`. _Proof: browser_ ## 8. Input & viewport — mobile and desktop, both -- Support mouse, keyboard and touch at narrow and wide widths. Reflow controls instead of - hiding the only route to an action. A hover affordance needs a click/tap alternative. -- Drags use Pointer Events including cancellation; scope `touch-action` to the actual drag - surface and pair it with a real handler. Preserve ordinary scrolling elsewhere. -- Prefer content/viewport-relative bounds (`min()`, `max()`, `clamp()`) to arbitrary - breakpoints. Use existing host breakpoints or introduce a justified shared role only when repeated needs emerge. -- A measured position becomes stale after resize, container reflow or scroll. Exercise - resizing into a narrow layout, not only loading there. Overlay placement must track its - trigger while open. The host owns lifecycle mechanics; [ranui engineering](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/CODING.md#3-state-and-ownership) is one implementation adapter. -- Test actual pointer/touch gestures, hover fallback and keyboard routes where affected; - checking two color schemes does not establish input parity. +- **D-INPUT-1 · MUST** Support mouse, keyboard and touch at narrow and wide widths. _Proof: browser_ +- **D-INPUT-2 · MUST** Reflow controls instead of hiding the only route to an action. _Proof: browser_ +- **D-INPUT-3 · MUST** A hover affordance has a click/tap alternative. _Proof: browser_ +- **D-INPUT-4 · MUST** Drags use Pointer Events, including cancellation. _Proof: browser_ +- **D-INPUT-5 · MUST** Scope `touch-action` to the actual drag surface and pair it with a real handler; + ordinary scrolling keeps working elsewhere. _Proof: browser_ +- **D-INPUT-6 · SHOULD** Prefer content/viewport-relative bounds (`min()`, `max()`, `clamp()`) to arbitrary + breakpoints. +- **D-INPUT-7 · SHOULD** Use existing host breakpoints, and introduce a justified shared role only when + repeated needs emerge. + +A measured position becomes stale after resize, container reflow or scroll. + +- **D-INPUT-8 · SHOULD** Exercise resizing into a narrow layout, not only loading there. +- **D-INPUT-9 · MUST** Overlay placement tracks its trigger while open. _Proof: browser_ + +The host owns lifecycle mechanics; [ranui engineering](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/CODING.md#3-state-and-ownership) is one implementation adapter. + +- **D-INPUT-10 · SHOULD** Test actual pointer/touch gestures, hover fallback and keyboard routes where + affected; checking two color schemes does not establish input parity. ## 9. Composition — the page, not the component -Choose the page structure in §10 first; then compose its components with these rules. +- **D-COMP-1 · SHOULD** Choose the page structure in §10 first; then compose its components with these + rules. ### Style coherence across components and pages -Before implementation, record a short design brief in the existing task note or product design -owner. Use the existing system; a small fix only needs the affected decisions. "Premium", "modern" -or "like Claude" is a direction to investigate, not an implementable specification. +- **D-COMP-2 · SHOULD** Before implementation, record a short design brief in the existing task note or + product design owner. Use the existing system; a small fix only needs the affected decisions. + The brief settles each applicable decision in the table below (D-COMP-2). + +"Premium", "modern" or "like Claude" is a direction to investigate, not an implementable specification. -| Decision | What the brief must settle | +| Decision | What the brief settles | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------ | | Task and hierarchy | The real content, primary action and first reading focus; what can recede or disappear. | | Proportion | Type roles/line heights, text measure, control density, icon weight and alignment spines using host tokens. | | Surface language | Which regions need containment, nested radius/inset relationships, semantic accent and elevation roles. | | State composition | A representative default plus long content, pending/failure and narrow input paths; include combined states. | | Motion | The state change it explains, trigger and frequency, interruption/reverse behavior and reduced-motion result; "none" is valid. | -| Reference decision | Where a reference is used, link its source, the principle adopted and the adaptation required by the host task. | +| Reference decision | Where a reference is used, link its source, the principle adopted and the adaptation the host task needs. | | Acceptance | The observable result and actual views/recordings that can demonstrate or reject it. | -Reuse the brief across sibling components and docs chrome; change emphasis according to the task. -Uniformity does not mean equal-sized controls or boxed sections everywhere. An expressive demo -must remain visibly distinct from its documentation chrome. Update the brief before changing the -visual direction; do not silently rewrite the standard to justify an implementation that failed review. +- **D-COMP-4 · SHOULD** Reuse the brief across sibling components and docs chrome; change emphasis + according to the task. + +Uniformity does not mean equal-sized controls or boxed sections everywhere. + +- **D-COMP-5 · SHOULD** Keep an expressive demo visibly distinct from its documentation chrome. +- **D-COMP-6 · SHOULD** Update the brief before changing the visual direction; do not silently rewrite the + standard to justify an implementation that failed review. +- **D-COMP-7 · SHOULD** Compare a representative composed still with different real content before + animating it, then inspect the state change and return path at normal speed. -Compare a representative composed still with different real content before animating it. Then -inspect the state change and return path at normal speed. A shared color alone cannot reconcile -inconsistent proportions, icon weight, containment or motion. When an example looks imported from -another product, fix that relationship at its owning token/component/style rather than adding a -page-specific patch. Keep reference observations and validation limits in the optional -[research index](./REFERENCE_RESEARCH.md#style-coherence-and-transition-research). +A shared color alone cannot reconcile inconsistent proportions, icon weight, containment or motion. + +- **D-COMP-8 · SHOULD** When an example looks imported from another product, fix that relationship at its + owning token/component/style rather than adding a page-specific patch. +- **D-COMP-9 · SHOULD** Keep reference observations and validation limits in the optional + [research index](./REFERENCE_RESEARCH.md#style-coherence-and-transition-research). ### Structure before containers -**Reach for spacing, alignment, weight and a hairline before a box.** A container is a -claim that its contents are a separate object. Most regions need a background, a hairline -and intentional spacing — nothing more. +- **D-BOX-1 · SHOULD** **Reach for spacing, alignment, weight and a hairline before a box.** + +A container is a claim that its contents are a separate object. Most regions need a background, a +hairline and intentional spacing — nothing more. + +- **D-BOX-2 · SHOULD** Reserve a box for **a genuinely separate object**: a live demo stage, an overlay, a + panel that floats. Not for a code block, a table, a list row, a previous/next link, or a cell in a + gallery. +- **D-BOX-3 · SHOULD** **Avoid redundant nested containers and doubled padding.** A card inside a padded + panel does not need another decorative frame or inset. +- **D-BOX-4 · SHOULD** Keep functionally required nested controls or surfaces, and coordinate their radii + with the actual inset; containment alone is not a reason to add a box. +- **D-BOX-5 · SHOULD** **Every persistent boundary needs a distinct job.** Start with headings, alignment + and proximity to group content. Add a border, surface fill or elevation only when that structure leaves + a real boundary unclear. +- **D-BOX-6 · SHOULD** Do not repeat the same grouping with a panel frame, a nested card, row rules and + another inset at the same time. -**A box is for a genuinely separate object** — a live demo stage, an overlay, a panel that -floats. Not for a code block, a table, a list row, a previous/next link, or a cell in a -gallery. +Faint lines are still extra hierarchy; reducing their contrast or shortening them leaves a redundant +enclosure redundant. -**Avoid redundant nested containers and doubled padding.** A card inside a padded panel -does not need another decorative frame or inset. Keep functionally required nested controls or -surfaces, and coordinate their radii with the actual inset; containment alone is not a reason to add a box. +The table below applies D-BOX-5 and D-BOX-8. -**Every persistent boundary needs a distinct job.** Start with headings, alignment and proximity -to group content. Add a border, surface fill or elevation only when that structure leaves a real -boundary unclear. Do not repeat the same grouping with a panel frame, a nested card, row rules -and another inset at the same time. Faint lines are still extra hierarchy; reducing their contrast -or shortening them does not justify a redundant enclosure. +- **D-BOX-8 · MUST** Keep keyboard focus and persistent selection cues; never remove them to make the + resting layout quieter. _Proof: browser_ -| Boundary | Keep when it communicates | Remove when it only repeats | -| ----------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------- | -| Field/control frame | Where values can be entered or a selection made | A decorative wrapper around already legible commands | -| Region frame or divider | An independent viewport, resize boundary or major change of task | A title and gap that already make the same group clear | -| Nested surface | A distinct floating layer, preview, drop target or interaction boundary | Another container inside an established settings pane | -| Focus/selection outline | Keyboard focus or persistent selection state | Never remove this state cue merely to make the resting layout quieter | +| Boundary | Keep when it communicates | Remove when it only repeats | +| ----------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------ | +| Field/control frame | Where values can be entered or a selection made | A decorative wrapper around already legible commands | +| Region frame or divider | An independent viewport, resize boundary or major change of task | A title and gap that already make the same group clear | +| Nested surface | A distinct floating layer, preview, drop target or interaction boundary | Another container inside an established settings pane | +| Focus/selection outline | Keyboard focus or persistent selection state | Keep this state cue even when the resting layout would look quieter without it | -Review the full pane or page, not only the cropped group. For every remaining stable outline, -fill or separator, name the relationship or interaction it communicates and check what becomes -ambiguous if it is removed. A frame is optional; a reachable, recognisable control is not. +- **D-BOX-9 · SHOULD** Review the full pane or page, not only the cropped group. For every remaining stable + outline, fill or separator, name the relationship or interaction it communicates and check what becomes + ambiguous if it is removed. + +A frame is optional; a reachable, recognisable control is not. ### Emphasis is a budget **Colour, weight, badges, fills and elevation are scarce.** If everything is emphasised, -nothing reads as important. Establish priority with structure and proximity first, and -spend emphasis on the one thing that deserves it. +nothing reads as important. + +- **D-COMP-10 · SHOULD** Establish priority with structure and proximity first, and spend emphasis on the + one thing that deserves it. -**Elevation explains stacking, not importance.** Shadows belong to surfaces that genuinely -float above content — popovers, menus, dialogs, notifications. Do not shadow every card. +**Elevation explains stacking, not importance.** + +- **D-ELEV-3 · SHOULD** Reserve shadows for surfaces that genuinely float above content: popovers, menus, + dialogs, notifications. Do not shadow every card. ### Spacing carries meaning -The scale in §2 is not only a rhythm; each step states a relationship. Choose the step by -what the gap _means_: +The scale in §2 is not only a rhythm; each step states a relationship. + +Choose the step by what the gap _means_ (D-SPACE-2): | Relationship | Relative spacing | | ------------------------ | ------------------------------- | @@ -178,68 +242,95 @@ what the gap _means_: | Major region boundary | Strongest deliberate separation | **Inside before outside:** a component's own padding is decided before the gap between -components. **Smaller gap means tighter relationship** — that is the only thing vertical -rhythm communicates, so do not undo it with a decorative divider. +components (D-SPACE-3). **Smaller gap means tighter relationship** — that is the only thing vertical +rhythm communicates. + +- **D-SPACE-6 · SHOULD** Do not undo a gap's relationship with a decorative divider. ### Vertical rhythm across sibling views -Repeat relationships, not fixed heights. Choose title/description, field/hint, group and region gaps -from the host scale according to their relationship. These are composition decisions, not universal -component dimensions. Translations grow naturally; missing descriptions leave no vacant row. +- **D-SPACE-7 · SHOULD** Repeat relationships, not fixed heights. Choose title/description, field/hint, + group and region gaps from the host scale according to their relationship. + +These are composition decisions, not universal component dimensions. + +- **D-SPACE-8 · SHOULD** Let translations grow naturally, and leave no vacant row for a missing + description. +- **D-SPACE-9 · SHOULD** Give each gap one owner: parent stack or explicit child margin. Reset authored + local heading/paragraph margins rather than adding them to the parent's gap. +- **D-SPACE-10 · SHOULD** Account for owned control padding, collapsing margins and inline baseline space; + do not compensate with transforms or smaller type. +- **D-SPACE-11 · SHOULD** Place hints with the field/action they explain. +- **D-SPACE-12 · MUST** Hidden helpers leave no accidental gap. _Proof: browser_ +- **D-SPACE-16 · SHOULD** A reserved async status line has a stated purpose and bounds. + Measure both axes after content settles; [ACCEPTANCE](./ACCEPTANCE.md) grades this as V-PROOF-6. -Give each gap one owner: parent stack or explicit child margin. Reset authored local heading/ -paragraph margins rather than adding them to the parent's gap. Account for owned control padding, -collapsing margins and inline baseline space; do not compensate with transforms or smaller type. -Place hints with the field/action they explain. Hidden helpers leave no accidental gap; a reserved -async status line needs a stated purpose and bounds. +`next.top - previous.bottom` measures frame separation, not glyph/line-box space. -Measure both axes after content settles. `next.top - previous.bottom` measures frame separation, -not glyph/line-box space. Keep leading, padding and hairlines distinct: 16px padding plus a 1px -rule is not a 17px spacing token. Use independent expectations and exercise wrapping, resizing, -helper/error visibility and relevant density. [ACCEPTANCE](./ACCEPTANCE.md#browser-proof) owns -settling and mutation checks for doubled margins, phantom rows and text-only drift. +- **D-SPACE-14 · SHOULD** Keep leading, padding and hairlines distinct: 16px padding plus a 1px rule is + not a 17px spacing token. +- **D-SPACE-15 · SHOULD** Use independent expectations and exercise wrapping, resizing, helper/error + visibility and relevant density. + +[ACCEPTANCE](./ACCEPTANCE.md#browser-proof) owns settling and mutation checks for doubled margins, +phantom rows and text-only drift. ### The measure governs text, not the column -**Choose a readable measure for the actual script and font.** For Latin prose, 60–75 -characters per line is a starting heuristic, not a universal range for every writing system. -Review realistic text at supported widths, zoom levels and font fallbacks; CJK and mixed-script -text need their own judgment. [WCAG visual presentation](https://www.w3.org/WAI/WCAG22/Understanding/visual-presentation.html) +- **D-TYPE-4 · SHOULD** **Choose a readable measure for the actual script and font.** + +For Latin prose, 60–75 characters per line is a starting heuristic, not a universal range for every +writing system. + +- **D-TYPE-5 · SHOULD** Review realistic text at supported widths, zoom levels and font fallbacks; CJK and + mixed-script text need their own judgment. + +[WCAG visual presentation](https://www.w3.org/WAI/WCAG22/Understanding/visual-presentation.html) also distinguishes CJK in its Level AAA criterion; it is not a mandatory library default. -A table, code fence, demo stage or image should retain the width its content needs. -Cap the text elements, not the column that holds them. -**Declare the measure in `rem`, never `em`.** `em` resolves against the element's own -font-size, so the larger the type, the longer the line — exactly backwards. +- **D-TYPE-6 · SHOULD** Let a table, code fence, demo stage or image retain the width its content needs. + Cap the text elements, not the column that holds them. +- **D-TYPE-7 · SHOULD** **Declare the measure in `rem`, never `em`.** -**Build hierarchy out of type before reaching for a box.** One step of size and one step -down in colour turns a title and its first sentence into a title and a deck, with no new -container and no new border. +`em` resolves against the element's own font-size, so the larger the type, the longer the line — +exactly backwards. + +- **D-TYPE-8 · SHOULD** **Build hierarchy out of type before reaching for a box.** + +One step of size and one step down in colour turns a title and its first sentence into a title and a +deck, with no new container and no new border. ### If the layout says two things are one object, the spacing must agree -**A shared border, a shared radius and a gap between them are a contradiction.** Decide -whether the elements are one object or two, and make every property say the same thing. +**A shared border, a shared radius and a gap between them are a contradiction.** -**When a value must equal another value, name it.** A gap that a child has to subtract is -not a literal in two places; it is one custom property referenced twice. +- **D-ALIGN-1 · SHOULD** Decide whether the elements are one object or two, and make every property say + the same thing. +- **D-ALIGN-2 · SHOULD** **When a value must equal another value, name it.** A gap that a child has to + subtract is not a literal in two places; it is one custom property referenced twice. ### Reserve a column only when its content exists **A column declared unconditionally is still a column, even on the pages that have nothing -to put in it.** Condition the track on the element actually being there (`:has()`), not on -a page type you assume implies it. +to put in it.** + +- **D-ALIGN-3 · SHOULD** Condition the track on the element actually being there (`:has()`), not on a + page type you assume implies it. ### Alignment is structure, not polish -**Name each alignment spine and the geometry it aligns.** A page content spine, navigation -column and nested form-label spine can differ intentionally. State which siblings share each one; -include headers, body copy, rows, actions and empty/loading/error content. A back button plus gap -may establish a separate title column: do not silently treat that title as aligned to body text. -A missing optional icon or badge must not move labels out of their declared column. A nested level -returns exactly to its parent's spine when it ends. +- **D-ALIGN-4 · SHOULD** **Name each alignment spine and the geometry it aligns.** State which siblings + share each one; include headers, body copy, rows, actions and empty/loading/error content. -Compare the right boxes. These are separate measurements, even when they happen to coincide: +A page content spine, navigation column and nested form-label spine can differ intentionally. + +- **D-ALIGN-5 · SHOULD** A back button plus gap may establish a separate title column: do not silently + treat that title as aligned to body text. +- **D-ALIGN-6 · MUST** A missing optional icon or badge never moves labels out of their declared column. + _Proof: browser_ +- **D-ALIGN-7 · SHOULD** A nested level returns exactly to its parent's spine when it ends. + Compare the right boxes. These are separate measurements, even when they happen to coincide; measuring the + defect's layer is graded in [ACCEPTANCE](./ACCEPTANCE.md) as V-PROOF-10. | Geometry | What to measure | | ------------- | ------------------------------------------------------------------------------------------------- | @@ -250,194 +341,259 @@ Compare the right boxes. These are separate measurements, even when they happen | Hit area | The actual event target and its hover/focus surface; verify points near its edges | A button frame aligned with a paragraph does not prove its text is aligned: internal padding can -move the label while the frame stays fixed. `Range` gives typographic geometry, not exact glyph ink; -inspect optical glyph corrections separately. For web components, measure the host, documented shadow parts and slotted text through supported -test seams; do not add production shadow queries or assume document CSS crosses the boundary. - -**Keep a header command's hit zone separate from layout filler.** A title/menu trigger should hug -its content plus deliberate padding, bounded by the available width. Let a separate flex/grid track -absorb remaining space. Define truncation/wrapping and adjacent action behavior for long translations. -Do not make an entire unused header strip clickable merely by giving the title button `flex: 1`. -A row that intentionally selects its whole width is a different role; record that contract explicitly. -Coarse-pointer targets may grow without stretching the hover fill across unrelated content. - -**Count every inset from the shared ancestor to the visible text.** Parent padding, nested panel -padding, a control's internal padding, borders and navigation columns all contribute. Remove a -redundant inset at its owner instead of cancelling it with an unexplained negative margin. Empty -states retain their region's content spine and width; extra breathing room must not introduce a -second left edge or make a compact action fill the region. Product-specific insets and target sizes -remain application aliases, not new foundation values or universal component defaults. - -**When two layout edges or gaps are meant to be equal, a one-pixel difference is a defect.** -Optical correction for an asymmetric glyph is a separate, documented asset/component decision: -reuse the correction at supported sizes while preserving exact sibling gaps, baselines and alignment -spines. It is not permission to nudge page margins until a screenshot looks balanced. +move the label while the frame stays fixed. `Range` gives typographic geometry, not exact glyph ink. + +- **D-ALIGN-9 · SHOULD** Inspect optical glyph corrections separately. + For web components, measure the host, documented shadow parts and slotted text through supported test + seams. V-PROOF-11 in [ACCEPTANCE](./ACCEPTANCE.md) owns this, including the ban on production shadow queries + and on assuming document CSS crosses the boundary. +- **D-ALIGN-11 · SHOULD** **Keep a header command's hit zone separate from layout filler.** A title/menu + trigger hugs its content plus deliberate padding, bounded by the available width. Let a separate + flex/grid track absorb remaining space. +- **D-ALIGN-12 · SHOULD** Define truncation/wrapping and adjacent action behavior for long translations. +- **D-ALIGN-13 · MUST** The title trigger's hit area does not span unused header space (for example through + `flex: 1` on the title button) unless D-ALIGN-14 applies. _Proof: browser_ +- **D-ALIGN-14 · SHOULD** A row that intentionally selects its whole width is a different role; record + that contract explicitly. +- **D-ALIGN-15 · MAY** Coarse-pointer targets may grow larger. +- **D-ALIGN-22 · SHOULD** A grown target's hover fill stays on the control's own content, not across unrelated + content. +- **D-ALIGN-16 · SHOULD** **Count every inset from the shared ancestor to the visible text.** Parent + padding, nested panel padding, a control's internal padding, borders and navigation columns all + contribute. +- **D-ALIGN-17 · SHOULD** Remove a redundant inset at its owner instead of cancelling it with an + unexplained negative margin. +- **D-ALIGN-18 · SHOULD** Empty states retain their region's content spine and width; extra breathing room + never introduces a second left edge or makes a compact action fill the region. +- **D-ALIGN-19 · SHOULD** Keep product-specific insets and target sizes as application aliases, not new + foundation values or universal component defaults. +- **D-ALIGN-20 · SHOULD** **When two layout edges or gaps are meant to be equal, a one-pixel difference is a + defect.** Optical glyph correction (D-ALIGN-21) is the only tolerance. +- **D-ALIGN-21 · SHOULD** Treat optical correction for an asymmetric glyph as a separate, documented + asset/component decision, reused at supported sizes while sibling gaps, baselines and spines stay exact. + +Optical correction is not permission to nudge page margins until a screenshot looks balanced. ### Selection, links and other semantic lies -**Selection is persistent state and must not look like hover.** Hover is transient; if the -current item is marked with the same fill hover uses, the interface has two names for one -appearance. +- **D-SEM-1 · MUST** **Selection is persistent state and never looks like hover.** _Proof: browser_ + +Hover is transient; if the current item is marked with the same fill hover uses, the interface has two +names for one appearance. -**Action and link roles remain distinguishable.** Verify inline links against surrounding prose, -including themes where action and body colors happen to match. +- **D-SEM-2 · MUST** **Action and link roles remain distinguishable.** _Proof: browser_ +- **D-SEM-3 · SHOULD** Verify inline links against surrounding prose, including themes where action and + body colors happen to match. -**A link leaves; a button acts.** Anything that changes application state is a button, -whatever it looks like. Do not style a command as a link to make it quiet, and do not let -a button-shaped link keep an underline. +**A link leaves; a button acts.** + +- **D-SEM-4 · MUST** Anything that changes application state is a button, whatever it looks like. + _Proof: review, browser_ +- **D-SEM-5 · SHOULD** A button-shaped link carries no underline. ### Command hierarchy in constrained panes -**Remove redundant group enclosures without erasing control affordance.** Headings, -alignment and proximity establish groups inside a settings pane; they do not automatically -make an icon and label look clickable. Do not require flat rows in every context. Use them -when a recognizable menu/list pattern already conveys action. Standalone commands may need -their own restrained button surface or functional stroke, visible before hover in every -supported theme. A button's own shape identifies its target; an enclosing card, muted group -fill and row rules must still pass the distinct-boundary test above. - -Do not rely on hover, pointer cursors, verb labels or keyboard focus alone for initial -discovery. Inspect the resting full pane with the pointer away, including touch use. Keep -hover, press, disabled and keyboard focus distinguishable after the control is discovered. -Long translated labels may wrap inside a single command column rather than creating a -ragged horizontal button cloud. Do not add navigation chevrons to immediate commands. - -Declare which visible columns are shared with adjacent checkboxes or controls: measure -icon/checkbox edges and text Ranges, not only button rectangles or alignment within the -command group. A hit target may extend into an owned gutter to preserve those columns; -name the extension, keep it inside its pane and verify non-overlap and focus visibility. - -Separate destructive commands from recovery and transfer actions. A danger label and icon -can communicate intent without a large red outline on every resting command; reserve the -strongest danger fill for explicit confirmation. Keep confirmation, cancellation, focus -return and busy guards. Acceptance covers resting discoverability, declared shared columns, -full-pane boundary hierarchy, target bounds, label clipping, contrast and interaction states. -An automated surface-difference check catches invisible control fills but does not prove -that users recognize the action: inspect the actual render as well. Reuse foundation roles -and coarse-input target requirements; this guidance changes no component defaults. +- **D-CMD-1 · SHOULD** **Remove redundant group enclosures without erasing control affordance.** + +Headings, alignment and proximity establish groups inside a settings pane; by themselves they leave an +icon and label without a clickable look. + +- **D-CMD-2 · SHOULD** Use flat rows when a recognizable menu/list pattern already conveys action; do not + require them in every context. +- **D-CMD-3 · SHOULD** Give a standalone command that needs it its own restrained button surface or + functional stroke, visible before hover in every supported theme. +- **D-CMD-4 · SHOULD** Let a button's own shape identify its target; an enclosing card, muted group fill + and row rules still pass the distinct-boundary test (D-BOX-5). +- **D-CMD-5 · MUST** Initial discovery does not rely on hover, pointer cursors, verb labels or keyboard + focus alone. _Proof: browser_ +- **D-CMD-6 · SHOULD** Inspect the resting full pane with the pointer away, including touch use. +- **D-CMD-7 · MUST** Hover, press, disabled and keyboard focus stay distinguishable after the control is + discovered. _Proof: browser_ +- **D-CMD-8 · SHOULD** Wrap long translated labels inside one command column instead of a ragged horizontal + button cloud. +- **D-CMD-9 · MUST** Do not add navigation chevrons to immediate commands. _Proof: review_ +- **D-CMD-10 · SHOULD** Declare which visible columns are shared with adjacent checkboxes or controls: + measure icon/checkbox edges and text Ranges, not only button rectangles or alignment within the command + group. +- **D-CMD-11 · MAY** A hit target may extend into an owned gutter to preserve those columns; name the + extension. +- **D-CMD-12 · MUST** An extended hit target stays inside its pane, does not overlap other targets, and + keeps focus visible. _Proof: browser_ +- **D-CMD-13 · SHOULD** Separate destructive commands from recovery and transfer actions. +- **D-CMD-14 · SHOULD** Let a danger label and icon communicate intent without a large red outline on + every resting command; reserve the strongest danger fill for explicit confirmation. +- **D-CMD-15 · MUST** Destructive commands keep confirmation, cancellation, focus return and busy guards. + _Proof: browser_ +- **D-CMD-16 · SHOULD** Accept a command pane against its resting state, not only its hover state. + +Acceptance covers resting discoverability, declared shared columns, full-pane boundary hierarchy, target +bounds, label clipping, contrast and interaction states. + +- **D-CMD-17 · SHOULD** Inspect the actual render as well: an automated surface-difference check catches + invisible control fills but does not prove that users recognize the action. +- **D-CMD-18 · SHOULD** Reuse foundation roles and coarse-input target requirements; this guidance changes + no component defaults. ### One product, one token set -Use the product’s authoritative semantic tokens. Aliases may improve readability, but resolve -from that source rather than inventing a competing palette. Coordinate third-party components -through supported theme APIs and verify the composed result. +- **D-TOKEN-1 · SHOULD** Use the product’s authoritative semantic tokens. Aliases may improve readability, + but resolve from that source rather than inventing a competing palette. +- **D-TOKEN-2 · SHOULD** Coordinate third-party components through supported theme APIs and verify the + composed result. ### Density follows control roles Spacing, intrinsic dimensions and hit targets solve different problems. A small glyph can have -a larger native button target. Measure actual hit bounds, focus and adjacent gaps; derive repeated -host aliases from foundation tokens, not one application's dimensions copied across the library. +a larger native button target. -Choose target bounds for the actual input method without enlarging every glyph or reserving -space for hidden controls. Use supported public theme APIs. Name icon-only controls accessibly -and document legitimate viewport, hairline, artwork and optical geometry exceptions. +- **D-TOKEN-3 · SHOULD** Measure actual hit bounds, focus and adjacent gaps. +- **D-TOKEN-4 · SHOULD** Derive repeated host aliases from foundation tokens, not one application's + dimensions copied across the library. +- **D-TOKEN-5 · SHOULD** Choose target bounds for the actual input method without enlarging every glyph or + reserving space for hidden controls. Use supported public theme APIs. +- **D-TOKEN-6 · MUST** Name icon-only controls accessibly. _Proof: browser_ +- **D-TOKEN-7 · SHOULD** Document legitimate viewport, hairline, artwork and optical geometry exceptions. ### Evaluate craft through evidence -Polish follows task clarity, not a preset of cards, blur or entrances. Choose a deliberate -visual direction using realistic objects, copy and states; a generic empty shell cannot establish -product quality. Working interfaces need sustained reading and frequent actions; marketing pages -may need a different composition. Evaluate the smallest intervention against real content, language, -device and repeated use. Primary references answer -specific questions: [Linear hierarchy](https://linear.app/now/behind-the-latest-design-refresh), +Polish follows task clarity, not a preset of cards, blur or entrances. + +- **D-CRAFT-1 · SHOULD** Choose a deliberate visual direction using realistic objects, copy and states; a + generic empty shell cannot establish product quality. + +Working interfaces need sustained reading and frequent actions; marketing pages may need a different +composition. + +- **D-CRAFT-2 · SHOULD** Evaluate the smallest intervention against real content, language, device and + repeated use. + +Primary references answer specific questions: +[Linear hierarchy](https://linear.app/now/behind-the-latest-design-refresh), [Geist typography](https://vercel.com/geist/typography), [optical alignment](https://craft.gustavofior.com/optical-alignment) and -[interruptibility](https://craft.gustavofior.com/interruptibility). Adapt their principles with -the host’s tokens and actual capabilities; do not copy a product's pixel sizes or import recipes as rules. -[Don't Make Me Think](https://sensible.com/dont-make-me-think/) informs scanning, hierarchy and -clear choices; [taste-skill](https://github.com/leonxlnx/taste-skill) informs deliberate direction -and critique; [ryugi](https://github.com/Innei/ryugi) informs executable work and fresh evidence. -These references inform decisions, not additional instruction sets. A stylistic preference needs -context; a requirement must identify the task or observable contract it protects. - -Choose references by the job they support: logo/app identity, marketing composition and frequent -working interactions solve different problems. Record the observed pattern and its adaptation; -verify the result with real content. The optional [research index](./REFERENCE_RESEARCH.md) maps -sources to questions and demonstrates this translation without adding another rule set. - -When direction is unresolved, compare two small real-host variants with the same content, width -and theme, changing one dimension at a time. Explain what each difference does for the task. +[interruptibility](https://craft.gustavofior.com/interruptibility). + +- **D-CRAFT-3 · SHOULD** Adapt their principles with the host’s tokens and actual capabilities; do not + copy a product's pixel sizes or import recipes as rules. +- **D-CRAFT-4 · SHOULD** Treat references as inputs to decisions, not additional instruction sets; for + example, [Don't Make Me Think](https://sensible.com/dont-make-me-think/) informs scanning, hierarchy and + clear choices. + +[taste-skill](https://github.com/leonxlnx/taste-skill) informs deliberate direction and critique; +[ryugi](https://github.com/Innei/ryugi) informs executable work and fresh evidence. + +- **D-CRAFT-5 · SHOULD** A stylistic preference needs context; a requirement identifies the task or + observable contract it protects. +- **D-CRAFT-6 · SHOULD** Choose references by the job they support: logo/app identity, marketing + composition and frequent working interactions solve different problems. +- **D-CRAFT-7 · SHOULD** Record the observed pattern and its adaptation; verify the result with real + content. + +The optional [research index](./REFERENCE_RESEARCH.md) maps sources to questions and demonstrates this +translation without adding another rule set. + +- **D-CRAFT-8 · SHOULD** When direction is unresolved, compare two small real-host variants with the same + content, width and theme, changing one dimension at a time. Explain what each difference does for the + task. + A clear direction or small correction proceeds directly; there is no mandatory approval ritual. -- Balance short headings progressively with `text-wrap: balance`, preserving normal wrapping. - Do not insert locale-dependent manual breaks or clip text to preserve a screenshot. Check cost - and support before using `pretty` on long live content. -- Tabular figures stabilize digit advances if the font supports them; reserve a meaningful - column for digit-count changes such as 99 → 100 without truncating values. -- Correct asymmetric glyphs in a reusable asset/component, preserving exact layout spines. - Derive nested corner geometry from actual inset; remove an unnecessary nested surface first. -- Motion needs a job: immediate frequent feedback, interruptible transitions, reduced motion and - instant themes. Optional delight must not delay typing or obscure status. Pause offscreen - decoration and profile realistic content before adopting costly effects. See §5. +- **D-CRAFT-9 · SHOULD** Balance short headings progressively with `text-wrap: balance`, preserving normal + wrapping. +- **D-CRAFT-10 · MUST** Headings contain no locale-dependent manual line breaks and are not clipped to + preserve a screenshot. _Proof: browser_ +- **D-CRAFT-11 · SHOULD** Check cost and support before using `pretty` on long live content. +- **D-CRAFT-12 · SHOULD** Use tabular figures to stabilize digit advances if the font supports them. +- **D-CRAFT-13 · SHOULD** Reserve a meaningful column for digit-count changes such as 99 → 100. +- **D-CRAFT-14 · MUST** A digit-count change never truncates the value. _Proof: browser_ +- **D-CRAFT-15 · SHOULD** Correct asymmetric glyphs in a reusable asset/component, preserving exact layout + spines. +- **D-CRAFT-16 · SHOULD** Derive nested corner geometry from actual inset; remove an unnecessary nested + surface first. +- **D-CRAFT-17 · SHOULD** Give motion a job: immediate frequent feedback, interruptible transitions, + reduced motion and instant themes. See §5. +- **D-CRAFT-18 · MUST** Optional delight never delays typing or obscures status. _Proof: recording_ +- **D-CRAFT-19 · SHOULD** Pause offscreen decoration and profile realistic content before adopting costly + effects. ### Words are part of the interface -Avoid unnecessary secondary labels and descriptions. Let the layout do the explaining. -A heading, grouping, proximity or obvious control should not need another sentence repeating -its purpose. Remove copy with no new decision, constraint or consequence; do not replace it -with a decorative badge or tooltip. Preserve accessible names, field labels, units, errors, -risk disclosures and useful first-use guidance. Test whether the task remains understandable. - -Let context carry context: `Users` rather than `User Management`; do not repeat a dialog's -question in its body. Name results (`Save`, `Move`, `Delete`), not gestures (`Click to save`). -Include the object only when context does not establish it. `Cancel` leaves without committing. -Use sentence case, no period on short labels, and a period on explanatory sentences. -Use one `…` when an action needs more input before completing; reserve ALL CAPS for short -statuses, eyebrows and acronyms. +- **D-COPY-6 · SHOULD** Avoid unnecessary secondary labels and descriptions. Let the layout do the + explaining. +- **D-COPY-7 · SHOULD** A heading, grouping, proximity or obvious control needs no other sentence + repeating its purpose. +- **D-COPY-8 · SHOULD** Remove copy with no new decision, constraint or consequence; do not replace it + with a decorative badge or tooltip. +- **D-COPY-9 · MUST** Preserve accessible names, field labels, units, errors and risk disclosures. + _Proof: browser_ +- **D-COPY-10 · SHOULD** Preserve useful first-use guidance, and test whether the task remains + understandable. +- **D-COPY-11 · SHOULD** Let context carry context: `Users` rather than `User Management`; do not repeat a + dialog's question in its body. +- **D-COPY-12 · SHOULD** Name results (`Save`, `Move`, `Delete`), not gestures (`Click to save`). Include + the object only when context does not establish it. +- **D-COPY-13 · MUST** `Cancel` leaves without committing. _Proof: browser_ +- **D-COPY-14 · SHOULD** Use sentence case, no period on short labels, and a period on explanatory + sentences. +- **D-COPY-15 · SHOULD** Use one `…` when an action needs more input before completing; reserve ALL CAPS + for short statuses, eyebrows and acronyms. ### Review checklist for a composed surface -Apply these questions to the affected surface and reachable changed states. Skip irrelevant variants; -a small patch does not require redesigning unrelated pages. A "no" within scope is a finding: +- **D-REVIEW-1 · SHOULD** Apply these questions to the affected surface and reachable changed states. + Skip irrelevant variants; a small patch does not require redesigning unrelated pages. A "no" within + scope is a finding. 1. Can a new reader recognise the purpose and the primary action without guessing? 2. Does every control's label, state and result describe one consistent outcome? -3. Is emphasis scarce — does the core thing get the weight while colour, badges and - primary buttons stay rare? +3. Is emphasis scarce — does the core thing get the weight while colour, badges and primary buttons stay rare? 4. Could this do less: any entry point, option or state removable without weakening the task? -5. Are named spines exact at the visible text/content layer, including nested and empty states, - with intentional navigation columns and no doubled padding? Do adjacent controls share their declared - icon/text columns? Are title hit zones content-hugging? Do repeated vertical relationships use - consistent gaps with one owner, including wrapped copy and hidden helper/error content? -6. Is running text comfortable in its actual scripts, fonts and zoom levels, and is - everything that is **not** running text free of that measure cap? -7. Does every element that looks like part of one object agree — border, radius **and** - spacing — and does every reserved column actually have content? Does each persistent frame, - fill or divider communicate a distinct boundary instead of nesting the same group again? -8. Does it hold in relevant changed states: empty, loading, failure, long translations, narrow - width, supported themes, reduced motion and **RTL**, where affected? -9. For layout or interaction changes, has the affected surface been checked in a real browser - at the relevant widths and themes? Are standalone actions recognizable before hover, including - touch use, without redundant group enclosures? +5. Are named spines exact at the visible text/content layer, including nested and empty states, with intentional navigation columns and no doubled padding? +6. Do adjacent controls share their declared icon/text columns? +7. Are title hit zones content-hugging? +8. Do repeated vertical relationships use consistent gaps with one owner, including wrapped copy and hidden helper/error content? +9. Is running text comfortable in its actual scripts, fonts and zoom levels, and is everything that is **not** running text free of that measure cap? +10. Does every element that looks like part of one object agree — border, radius **and** spacing — and does every reserved column actually have content? Does each persistent frame, fill or divider communicate a distinct boundary instead of nesting the same group again? +11. Does it hold in relevant changed states: empty, loading, failure, long translations, narrow width, supported themes, reduced motion and **RTL**, where affected? +12. For layout or interaction changes, has the affected surface been checked in a real browser at the relevant widths and themes? Are standalone actions recognizable before hover, including touch use, without redundant group enclosures? A happy-path screenshot does not prove keyboard behavior, focus, dynamic content, -resizing or failure recovery. Follow [ACCEPTANCE](./ACCEPTANCE.md) for independent expectations, -actual runtime/capture identity, browser measurements and separate automated, agent and human -results. The source-only [browser example](https://github.com/chaxus/ran/blob/main/packages/ranui/test/e2e/layout-contract.md) +resizing or failure recovery. + +- **D-REVIEW-2 · SHOULD** Follow [ACCEPTANCE](./ACCEPTANCE.md) for independent expectations, actual + runtime/capture identity, browser measurements and separate automated, agent and human results. + +The source-only [browser example](https://github.com/chaxus/ran/blob/main/packages/ranui/test/e2e/layout-contract.md) contains setup commands and test utility details; they are not public package APIs. ## 10. Information architecture — the shape before the composition -Choose structure before composition for information-dense consoles, dashboards and workbenches. +- **D-IA-1 · SHOULD** Choose structure before composition for information-dense consoles, dashboards and + workbenches. + Complete API data is not a page design: the reader still needs a clear order for understanding -objects, their state and relationships, then acting. Single-conversion and landing pages need -their own task structure rather than a dense-console template. +objects, their state and relationships, then acting. + +- **D-IA-2 · SHOULD** Give single-conversion and landing pages their own task structure rather than a + dense-console template. ### Answer three questions before naming a component -1. **What must the reader see on arrival?** That is the primary information. -2. **What else has to be visible for it to make sense?** Related resources, related models, - context. +- **D-IA-3 · SHOULD** Settle all three answers before choosing components. + +1. **What does the reader need to see on arrival?** That is the primary information. +2. **What else has to be visible for it to make sense?** Related resources, related models, context. 3. **What do they do next?** Judge, act, or keep thinking. -**A page has exactly one primary model.** Supporting models may help the reader understand or -operate it; they may not compete for the first screen. Prefer a clear, unambiguous next choice -over an arbitrary click budget. Test whether readers recognize the next action, not merely -whether the destination is reachable. +- **D-IA-4 · SHOULD** **A page has exactly one primary model.** Supporting models may help the reader + understand or operate it; they may not compete for the first screen. +- **D-IA-5 · SHOULD** Prefer a clear, unambiguous next choice over an arbitrary click budget. Test whether + readers recognize the next action, not merely whether the destination is reachable. ### The shape follows the task, not the payload -Choose a skeleton from the reader's question, not an API array or route ID: +- **D-IA-6 · SHOULD** Choose a skeleton from the reader's question, not an API array or route ID, using + the table below. | Task | Useful shape | | --------------------------------------------- | -------------------------------------------------------- | @@ -452,13 +608,17 @@ Choose a skeleton from the reader's question, not an API array or route ID: | Find occupancy/conflicts or location | Calendar or map/canvas | | Work on the next item / change rules | Master-detail workbench / configuration form | -Do not confuse historical events with future steps, a filter with kanban, or ordered prose -with a field grid. A date alone does not justify a calendar. Ship multiple views only when -each serves a distinct task worth its filter/state/action maintenance. +- **D-IA-7 · SHOULD** Do not confuse historical events with future steps, a filter with kanban, or ordered + prose with a field grid. A date alone does not justify a calendar. +- **D-IA-8 · SHOULD** Ship multiple views only when each serves a distinct task worth its + filter/state/action maintenance. ### Each kind of information has a place -| Information | Belongs | Must not end up | +- **D-IA-9 · SHOULD** Lead with identity and status in the title or summary region, and keep actions, + feedback and evidence beside their subject, as the table below lays out. + +| Information | Belongs | Wrong place | | ------------- | ------------------------------------------------------------ | ---------------------------------------- | | Identity | Title, object summary | The last column, or behind a tab | | Status | Title or summary region | Findable only in a detail field | @@ -469,62 +629,78 @@ each serves a distinct task worth its filter/state/action maintenance. | Actions | Primary in the title region, the rest beside their object | Buried under "more" | | Feedback | Beside the action, keeping task context | A global toast detached from its subject | -**Every fact has exactly one authoritative location.** Elsewhere shows a summary or an entry -point that links back to it. - -Default reading order — page identity → current status or exception → primary task and primary -action → the information the judgement needs → relationships, changes, evidence → secondary -information and low-frequency actions. **One primary action per task region**, and it is the -most likely next step, not the most destructive one (§9, "Emphasis is a budget", governs how -much weight it may take). +- **D-IA-10 · SHOULD** **Every fact has exactly one authoritative location.** Elsewhere shows a summary or + an entry point that links back to it. +- **D-IA-11 · SHOULD** Follow the default reading order: page identity → current status or exception → + primary task and primary action → the information the judgement needs → relationships, changes, + evidence → secondary information and low-frequency actions. +- **D-IA-12 · SHOULD** **One primary action per task region**, and it is the most likely next step, not + the most destructive one (§9, "Emphasis is a budget", governs how much weight it may take). ### Density is effective information, not controls per inch Tightening spacing raises visual density and leaves effective density where it was. Removing irrelevant fields and putting the comparison in one place raises the real thing. -**Use at most two adjacent density levels on one page** — spacious for first use and risky -confirmation, standard for most lists and details, compact for expert workbenches. Container -padding and line height may tighten; readable body text, a visible focus ring and pointer -target size may not (§8). +- **D-IA-13 · SHOULD** **Use at most two adjacent density levels on one page**: spacious for first use and + risky confirmation, standard for most lists and details, compact for expert workbenches. +- **D-IA-14 · MAY** Container padding and line height may tighten. +- **D-IA-15 · MUST** Readable body text, a visible focus ring and pointer target size do not shrink with + density (§7, §8). _Proof: browser_ ### Containers do not fix structure -**One contextual return per view.** Do not show an outer “Back to chat” and an inner -“Back to artifacts” together. The detail view returns to its immediate parent; ancestors remain -reachable through the route hierarchy or clearly named breadcrumbs. Verify the full journey, -including focus return and long translated destination names. If text does not fit, reflow or -use a meaningful shorter destination; an ellipsis or tooltip must not hide the only destination cue. -Choose one composition for the parent context. Do not add a breadcrumb merely to relabel a -redundant return shortcut, or repeat that parent as an inert crumb above its return command. +- **D-IA-16 · SHOULD** **One contextual return per view.** Do not show an outer “Back to chat” and an + inner “Back to artifacts” together. +- **D-IA-17 · SHOULD** The detail view returns to its immediate parent. +- **D-IA-18 · MUST** Ancestors remain reachable through the route hierarchy or clearly named breadcrumbs. + _Proof: browser_ +- **D-IA-19 · SHOULD** Verify the full journey, including focus return and long translated destination + names. +- **D-IA-27 · MUST** Returning to the parent restores focus to the originating item. _Proof: browser_ +- **D-IA-20 · MUST** If text does not fit, reflow or use a meaningful shorter destination; an ellipsis or + tooltip never hides the only destination cue. _Proof: browser_ +- **D-IA-21 · SHOULD** Choose one composition for the parent context. Do not add a breadcrumb merely to + relabel a redundant return shortcut, or repeat that parent as an inert crumb above its return command. + When returning through the parent already reaches the ancestor, that journey preserves access -without adding another detail-header row. Use breadcrumbs when the hierarchy itself helps the task. +without adding another detail-header row. -**A modal is not a navigation layer.** Anything needing a copyable link, history, a -side-by-side comparison, or work that survives a refresh gets a route. Reserve a modal for a -short confirmation or a single field, a popover or menu for transient context, and a disclosure -row for progressive disclosure. +- **D-IA-22 · SHOULD** Use breadcrumbs when the hierarchy itself helps the task. -**Tabs carry peer views of one object** — its conversation, its checks, its diff — never -unrelated modules; that is navigation's job. +**A modal is not a navigation layer.** -If the chosen library lacks a suitable primitive, use existing semantic elements and host tokens; -verify the required interaction rather than inventing unsupported APIs. +- **D-IA-23 · SHOULD** Give a route to anything needing a copyable link, history, a side-by-side + comparison, or work that survives a refresh. +- **D-IA-24 · SHOULD** Reserve a modal for a short confirmation or a single field, a popover or menu for + transient context, and a disclosure row for progressive disclosure. +- **D-IA-25 · SHOULD** **Tabs carry peer views of one object** — its conversation, its checks, its diff — + never unrelated modules; that is navigation's job. +- **D-IA-26 · SHOULD** If the chosen library lacks a suitable primitive, use existing semantic elements + and host tokens; verify the required interaction rather than inventing unsupported APIs. ## Verification checklist (before shipping UI) -Select checks for the affected surface, supported variants and changed behavior; do not create -unrelated flows merely to satisfy the list. Repository CI still runs its complete gates. - -- [ ] Primary task and primary action are unmistakable. -- [ ] Affected layout/theme changes work at relevant **narrow and wide** widths and supported themes. -- [ ] Works with **mouse and touch** — any drag/gesture uses Pointer Events, not mouse-only; any - `trigger="hover"` has a tap fallback. -- [ ] All changed **states** exercised (hover, active, focus, disabled, loading, empty, error). -- [ ] **Keyboard / focus** behavior verified; visible focus everywhere. -- [ ] Relevant edge cases: long text, large numbers and the host's supported scripts/locales. -- [ ] Spacing comes from the scale; type uses a role; color uses semantic tokens. -- [ ] Run the host’s applicable checks and inspect actual browser evidence. -- [ ] Copy follows §6; nothing signals state by color alone (§7). -- [ ] For a dense surface: one primary model, the skeleton chosen from the question rather - than the payload, and every fact with one authoritative location (§10). +- **D-VERIFY-1 · SHOULD** Select checks for the affected surface, supported variants and changed behavior; + do not create unrelated flows merely to satisfy the list. + +Repository CI still runs its complete gates. + +- **D-VERIFY-2 · SHOULD** Primary task and primary action are unmistakable. + +Affected layout/theme changes work at relevant **narrow and wide** widths and supported themes (D-INPUT-1). +Mouse and touch both work: drags use Pointer Events and any `trigger="hover"` has a tap fallback (D-INPUT-3, +D-INPUT-4). Keyboard and visible focus are covered by D-ACCESS-3 and D-ACCESS-4. + +- **D-VERIFY-5 · SHOULD** Exercise all changed **states** (hover, active, focus, disabled, loading, empty, + error). +- **D-VERIFY-7 · SHOULD** Cover relevant edge cases: long text, large numbers and the host's supported + scripts/locales. +- **D-VERIFY-8 · SHOULD** Spacing comes from the scale; type uses a role; color uses semantic tokens. +- **D-VERIFY-9 · SHOULD** Run the host’s applicable checks and inspect actual browser evidence. +- **D-VERIFY-10 · SHOULD** Copy follows §6. + +Color-only state signals are covered by D-ACCESS-2. + +- **D-VERIFY-12 · SHOULD** For a dense surface: one primary model, the skeleton chosen from the question + rather than the payload, and every fact with one authoritative location (§10). diff --git a/design/INTERACTION.md b/design/INTERACTION.md index d469472955..2546d09327 100644 --- a/design/INTERACTION.md +++ b/design/INTERACTION.md @@ -6,79 +6,116 @@ This file owns **observable behavior**, independent of the screen's domain or vi Use [WORKFLOW_PATTERNS.md](./WORKFLOW_PATTERNS.md) only for a relevant multi-step task. No workflow infrastructure is implied by these general rules. +Each rule carries an ID and a level. **MUST** is a defect when broken and names its proof; +**SHOULD** is the default, and a deviation states its reason for that case; **MAY** is optional. +The [rule index](./RULES.md) lists every graded rule with its proof. + ## 1. Inputs, drafts and intent -Keep labels, prerequisites and validation near the affected field. Distinguish a missing value from -an unavailable service. Explain why submission is unavailable when the prerequisite is not obvious. -Use appropriate native input semantics and autocomplete; preserve platform editing conventions. +- **X-INPUT-1 · MUST** Keep labels, prerequisites and validation messages near the field they affect. + _Proof: browser_ +- **X-INPUT-2 · SHOULD** Distinguish a missing value from an unavailable service, and explain why + submission is unavailable when the prerequisite is not obvious. +- **X-INPUT-3 · SHOULD** Use the appropriate native input semantics and autocomplete, and preserve + platform editing conventions; expose keyboard shortcuts where they help. -| Transition | Required decision | +| Transition | Expected behavior | | ------------------------------- | -------------------------------------------------------------------------------- | | Empty → valid form | Enable the relevant submit action; leave context controls usable | | Submit → running | Dispatch once; expose busy state without unexpectedly moving focus | | Running → new edits | Specify whether edits affect this job, a later job or a retained draft | -| Failed → restore | Restore only repopulates fields; it must not submit them | +| Failed → restore | Restore only repopulates fields; it does not submit them | | Failed → retry | Retry dispatches again; preserve the original scope or ask about changed context | | Settings/source change → return | Keep unfinished input and the user's selection where possible | -Enter submits only where the field's documented convention calls for it, outside IME composition; -check composition state and `isComposing`. Preserve multiline entry and expose shortcuts when useful. -Dictation updates text and never submits by itself; retain the text that preceded capture. -Do not rebuild a focused input on each search response, stream update, resize or status change. - -The latest intent remains authoritative: an older query must not replace newer results, and an -import preview for one file must not appear under another. Use the host’s async identity and cancellation mechanisms; ranui contributors can consult -[CODING §3](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/CODING.md#3-state-and-ownership). Retaining a draft does not prove it was saved: -promise persistence only after it succeeds. - -Search highlights literal matches in titles or snippets using safe text nodes or equivalent -escaping, never query-as-HTML. Keep enough surrounding text to explain why an item matched. +- **X-INPUT-4 · MUST** Submit dispatches once per activation, and Restore only repopulates fields without + submitting them. _Proof: unit_ +- **X-INPUT-5 · SHOULD** Follow the remaining rows of the input transition table. +- **X-INPUT-6 · MUST** Enter submits only where the field's documented convention calls for it, and never + during IME composition (check `isComposing`). Multiline entry keeps working. _Proof: browser_ +- **X-INPUT-7 · MUST** Dictation updates text and never submits by itself; the text that preceded the + capture is retained. _Proof: browser_ +- **X-INPUT-8 · MUST** Do not rebuild a focused input on a search response, stream update, resize or + status change. _Proof: browser_ +- **X-INPUT-9 · MUST** The latest intent wins: an older query never replaces newer results, and a preview + for one file never appears under another. _Proof: unit_ +- **X-INPUT-10 · SHOULD** Implement X-INPUT-9 with the host’s async identity and cancellation mechanisms; + ranui contributors can consult + [CODING §3](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/CODING.md#3-state-and-ownership). +- **X-INPUT-11 · MUST** Promise persistence only after it succeeds; a retained draft is not a saved one. + _Proof: unit_ +- **X-INPUT-12 · MUST** Highlight search matches with safe text nodes or equivalent escaping, never by + inserting the query as HTML. _Proof: unit_ +- **X-INPUT-13 · SHOULD** Keep enough text around a highlighted match to explain why the item matched. ## 2. Reachable controls and predictable focus -Use native semantics and supported component contracts. Name icon-only actions accessibly. -Secondary actions may appear on hover **and keyboard focus**, with a persistent tap route on -coarse input. Keep essential recovery visible. Disabled controls remain quiet on hover and -cannot dispatch. Preserve a stable target box and focus ring as labels or status change. - -Use public attributes/events/slots/variables/parts. A keyboard-safe primitive does not prove a composed -dialog is safe: a focus trap may not discover a closed-shadow button in a footer. Verify initial focus, -Tab/Shift+Tab, Escape/Cancel, Enter and focus restoration. Fix the supported integration or use native -buttons in its supported slot; do not patch private shadow roots or add misleading host tab stops. - -A collapsed region must still identify its contents and state; disclosure must not hide an -unresolved actionable error. Collapsing a region containing focus returns focus to its toggle. -Use the collapsed summary to help readers decide whether to expand. When the expanded body -already displays that content, omit the repeated summary and keep the title and state visible. -Removing the focused object moves focus to the next meaningful action or a stable parent control. -Do not move focus merely because a background request completed. +- **X-FOCUS-1 · SHOULD** Build controls from native semantics and supported component contracts. +- **X-FOCUS-2 · MUST** Every icon-only action has an accessible name. _Proof: browser_ +- **X-FOCUS-3 · MUST** A secondary action revealed on hover is also revealed on keyboard focus and has a + persistent tap route on coarse input. _Proof: browser_ +- **X-FOCUS-4 · SHOULD** Keep essential recovery actions visible rather than revealed on hover. +- **X-FOCUS-5 · MUST** Disabled controls stay quiet on hover and cannot dispatch. _Proof: browser_ +- **X-FOCUS-6 · MUST** The target box and focus ring keep their size and position while a label or status + changes. _Proof: browser_ +- **X-FOCUS-7 · MUST** Integrate through public attributes, events, slots, variables and parts; do not + patch private shadow roots. _Proof: review_ +- **X-FOCUS-8 · MUST** Do not add host tab stops that lead nowhere or duplicate an inner control. + _Proof: browser_ +- **X-FOCUS-9 · MUST** A composed dialog places initial focus inside itself, keeps Tab and Shift+Tab + within it, closes on Escape and Cancel, activates its default action on Enter, and returns focus to + the opener when it closes. _Proof: browser_ +- **X-FOCUS-10 · SHOULD** When a focus trap cannot reach a control (for example a closed-shadow button in + a footer), fix the supported integration or place native buttons in its supported slot. +- **X-FOCUS-11 · MUST** A collapsed region still identifies its contents and state, and disclosure never + hides an unresolved actionable error. _Proof: browser_ +- **X-FOCUS-12 · MUST** Collapsing a region that contains focus returns focus to its toggle; removing the + focused object moves focus to the next meaningful action or a stable parent control. _Proof: browser_ +- **X-FOCUS-13 · MUST** Do not move focus merely because a background request completed. _Proof: browser_ +- **X-FOCUS-14 · SHOULD** Write the collapsed summary to help readers decide whether to expand; when the + expanded body already shows that content, omit the repeated summary and keep the title and state. + +A keyboard-safe primitive does not make a composed dialog safe; X-FOCUS-9 is about the composition. ## 3. Feedback and reading position -Associate feedback with the object acted on. A “Copied” acknowledgment beside Copy is easier -to associate than an unrelated global notification. Show it only after clipboard success; handle -rejection locally, preserving button geometry and focus. Use a toast for changes beyond the current -object or when local feedback would not be noticed. Do not announce every streamed character. -Bind async feedback to the operation's source snapshot. If code changes while Copy is pending, -the older completion must not mark the new content “Copied”; clear or identify stale feedback. -Verify delayed success and failure across an intervening stream update, not just an isolated delay. - -For live logs or streaming results, follow only while the reader is at the latest content. Scrolling -away suspends following; a “Jump to latest” action restores it without stealing reading position. -Use a supported follow/pin contract from the host or chosen component. Keep job and persistence -state in the application; an append-only reading log is not a job manager. +- **X-FEEDBACK-1 · SHOULD** Show feedback next to the object acted on. A “Copied” acknowledgment beside + Copy is easier to associate than an unrelated global notification; a toast suits changes beyond the + current object or feedback that would otherwise go unnoticed. +- **X-FEEDBACK-2 · MUST** Show a success acknowledgment only after the operation succeeded (for Copy, + after clipboard success), and handle rejection locally while keeping button geometry and focus. + _Proof: browser_ +- **X-FEEDBACK-3 · MUST** Async feedback belongs to the operation's source snapshot: when content changes + while Copy is pending, the older completion never marks the new content “Copied”, and stale feedback + is cleared or identified as stale. _Proof: unit, browser_ +- **X-FEEDBACK-4 · MUST** Do not announce every streamed character to assistive technology. + _Proof: browser_ +- **X-FEEDBACK-5 · MUST** Live logs and streaming results follow only while the reader is at the latest + content; scrolling away suspends following, and “Jump to latest” restores it without stealing the + reading position. _Proof: recording_ +- **X-FEEDBACK-6 · SHOULD** Use a supported follow/pin contract from the host or chosen component, and keep + job and persistence state in the application; an append-only reading log is not a job manager. + +For X-FEEDBACK-3, exercise delayed success and failure across an intervening stream update, not only an +isolated delay. ## 4. Progress, failure and recovery -Show a known phase: “Reading files”, “Checking permissions” or “Writing export”. A spinner signals -activity, not a percentage. Show percentage only with a measurable denominator; elapsed time is -secondary and must not masquerade as a completion estimate. Announce meaningful phase changes, -not every timer tick or streamed character. - -An error answers **what failed, whether anything changed, and what to do next**. For example: -“The import stopped before saving. Your selected files are still here. Retry the import.” -Only make that promise if the host can establish it. Preserve in-memory input if storage fails; -explain that persistence failed separately. Put credential-free diagnostics in optional details. +- **X-RECOVER-1 · SHOULD** Name a known phase (“Reading files”, “Checking permissions”, “Writing export”) + rather than showing only a spinner, and announce meaningful phase changes, not every timer tick. +- **X-RECOVER-2 · MUST** Show a percentage only with a measurable denominator; elapsed time is secondary + and never presented as a completion estimate. _Proof: review_ +- **X-RECOVER-3 · MUST** An error answers what failed, whether anything changed, and what to do next, and + promises only what the host can establish. _Proof: review_ +- **X-RECOVER-4 · MUST** Preserve in-memory input when storage fails, and report the persistence failure + separately. _Proof: unit_ +- **X-RECOVER-5 · SHOULD** Put credential-free diagnostics in optional details. +- **X-RECOVER-6 · SHOULD** Give one failure one coherent recovery, not duplicate rows and toasts. +- **X-RECOVER-7 · MUST** A timeout does not prove rollback: establish whether a mutating operation + committed before retrying, using the host's idempotency or deduplication contract where available. + _Proof: unit_ + +For example: “The import stopped before saving. Your selected files are still here. Retry the import.” | Condition | Useful response | Avoid | | ------------------------------------ | ----------------------------------------------------------- | ------------------------------------------------------------------ | @@ -88,24 +125,27 @@ explain that persistence failed separately. Put credential-free diagnostics in o | User cancelled | Mark cancelled; retain recoverable output | Red failure styling or restarting paused work | | Context changed | Identify stale output; refresh against current data | Applying an old result to a newer document silently | -One failure should have one coherent recovery, not duplicate rows and toasts. A timeout does not -prove rollback: establish whether a mutating operation committed before retrying, using the host's -idempotency/deduplication contract where available. Do not promise that Cancel undoes a completed write. - ## 5. Cancel, retry and completion are different outcomes -Keep Cancel and Submit independently reachable when both are supported, with clear accessible -names and stable positions. A cancellation request is not an acknowledged cancellation: show its -pending state until confirmed. If the service keeps running and the client can only stop waiting, -name that limitation instead of claiming the job stopped. Distinguish cancellation from failure. - -Retry dispatches work again; Restore only repopulates editable inputs. Do not silently broaden scope -or switch destinations on retry. Confirm a completed write before reporting saved/applied success; -retain recoverable partial output. Offer Undo only when the host can actually reverse the operation. +- **X-CANCEL-1 · MUST** Keep Cancel and Submit independently reachable when both are supported, with + clear accessible names and stable positions. _Proof: browser_ +- **X-CANCEL-2 · MUST** A cancellation request is not an acknowledged cancellation: show its pending state + until confirmed, and when the client can only stop waiting, say so instead of claiming the job stopped. + _Proof: unit_ +- **X-CANCEL-3 · MUST** Cancellation is not shown as failure. _Proof: browser_ +- **X-CANCEL-4 · MUST** Retry dispatches work again without silently broadening scope or switching + destinations; Restore only repopulates editable inputs. _Proof: unit_ +- **X-CANCEL-5 · MUST** Report saved or applied success only after the write is confirmed, and retain + recoverable partial output. _Proof: unit_ +- **X-CANCEL-6 · MUST** Offer Undo only when the host can actually reverse the operation, and never promise + that Cancel undoes a completed write. _Proof: review_ ## 6. Verify what a person can observe -Choose evidence by the defect, rather than requiring every possible test for a small change: +- **X-VERIFY-1 · SHOULD** Choose evidence by the defect, using the layers below, rather than running every + possible test for a small change. +- **X-VERIFY-2 · SHOULD** Include offline and failure paths when the task depends on external work. +- **X-VERIFY-3 · SHOULD** Report the tested commit and any remaining external prerequisites. | Evidence layer | Relevant regression | | -------------- | ------------------------------------------------------------------------------------------- | @@ -115,7 +155,6 @@ Choose evidence by the defect, rather than requiring every possible test for a s | Cross-surface | Affected forms, search, history, settings, import/export, previews and confirmations | | Variants | Relevant themes, widths, resize, keyboard/coarse input, translations/RTL and reduced motion | -Include offline/failure paths when the task depends on external work. Static audits cannot prove -focus restoration, the winning cascade or preserved reading position. Report the tested commit and -remaining external prerequisites. Library contributors use [CODING's checklist](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/CODING.md#checklist-before-opening-a-pull-request); +Static audits cannot prove focus restoration, the winning cascade or a preserved reading position. +Library contributors use [CODING's checklist](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/CODING.md#checklist-before-opening-a-pull-request); consumer applications use the host's relevant commands, not ranui's source-checkout commands. diff --git a/design/README.md b/design/README.md index 2537289ad8..26330528da 100644 --- a/design/README.md +++ b/design/README.md @@ -8,6 +8,7 @@ capabilities; use the existing product’s visual language and implementation co | Find the relevant owner | [Task index](./INTERFACE_GUIDE.md) | | Structure, proportion, wording, visual coherence or motion | [Design](./DESIGN.md) | | Input, focus, progress and recovery | [Interaction](./INTERACTION.md) | +| Every graded rule with its level and proof | [Rule index](./RULES.md) | | A supported multi-step workflow | [Optional patterns](./WORKFLOW_PATTERNS.md) | | Browser proof, recordings and evidence freshness | [Acceptance](./ACCEPTANCE.md) | | Research an unresolved design question | [References](./REFERENCE_RESEARCH.md) | diff --git a/design/RULES.md b/design/RULES.md new file mode 100644 index 0000000000..25db7cdb18 --- /dev/null +++ b/design/RULES.md @@ -0,0 +1,401 @@ + + +# Rule index + +Every graded rule, with its level and proof (152 MUST, 230 SHOULD, 6 MAY). Open a rule's ID for its section. + +Proof kinds: **browser** — real browser interaction ([browser proof](./ACCEPTANCE.md#browser-proof)); +**recording** — continuous recording ([motion over time](./ACCEPTANCE.md#review-motion-over-time)); +**unit** — a unit or state test in the host; **review** — inspection of the change itself; +`check:` — a `verify:design` rule; `test:` — a named test (paths from the repository root). + +| ID | Level | Rule | Proof | +| -------------------------------------------------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | +| [D-COLOR-1](./DESIGN.md#1-color--a-state-ladder-not-a-palette) | SHOULD | Choose semantic roles for content, surfaces, boundaries, actions and status. | | +| [D-COLOR-2](./DESIGN.md#1-color--a-state-ladder-not-a-palette) | SHOULD | Define default, hover, active, disabled and focus states together. | | +| [D-COLOR-3](./DESIGN.md#1-color--a-state-ladder-not-a-palette) | SHOULD | Resolve light/dark values through the host’s authoritative tokens, and verify contrast in the actual composition. | | +| [D-SPACE-1](./DESIGN.md#2-spacing--a-limited-rhythmic-scale) | SHOULD | Use the host’s existing spacing scale. | | +| [D-SPACE-2](./DESIGN.md#2-spacing--a-limited-rhythmic-scale) | SHOULD | Use tight gaps to group related parts and larger gaps to separate tasks or regions. | | +| [D-SPACE-3](./DESIGN.md#2-spacing--a-limited-rhythmic-scale) | SHOULD | Establish inside padding before outside gaps. | | +| [D-SPACE-4](./DESIGN.md#2-spacing--a-limited-rhythmic-scale) | SHOULD | Do not copy a reference product’s dimensions without evaluating content, density and input method. | | +| [D-TYPE-1](./DESIGN.md#3-typography--choose-a-role-not-a-size) | SHOULD | Choose heading, label, body, action and code roles with coherent size, weight and line height, and keep a single authority for each role. | | +| [D-TYPE-2](./DESIGN.md#3-typography--choose-a-role-not-a-size) | SHOULD | Verify actual scripts, fallback metrics and long content. | | +| [D-TYPE-3](./DESIGN.md#3-typography--choose-a-role-not-a-size) | SHOULD | Establish hierarchy with typography before decorative containers. | | +| [D-ELEV-1](./DESIGN.md#4-radius--elevation) | SHOULD | Coordinate nested corner radii with their real inset. | | +| [D-ELEV-2](./DESIGN.md#4-radius--elevation) | SHOULD | Use elevation to explain a floating layer, not importance, through the host’s stacking roles, and verify overlays against surrounding chrome. | | +| [D-MOTION-1](./DESIGN.md#5-motion--prefer-none) | SHOULD | Use the host system’s motion tokens and verify frequency, continuity and reduced motion in the actual task. | | +| [D-MOTION-2](./DESIGN.md#5-motion--prefer-none) | SHOULD | Default repeated interaction to immediate feedback; a larger change is not automatically a reason to animate. | | +| [D-MOTION-3](./DESIGN.md#5-motion--prefer-none) | SHOULD | Use existing tokens for purposeful motion and keep it quick and interruptible. Reduced motion is graded under D-MOTION-10 and D-ACCESS-5. | | +| [D-MOTION-4](./DESIGN.md#functional-motion-contract) | SHOULD | Specify the trigger, state change and destination before choosing an effect. | | +| [D-MOTION-5](./DESIGN.md#functional-motion-contract) | MUST | Motion preserves unrelated content, drafts and reading position. | recording | +| [D-MOTION-6](./DESIGN.md#functional-motion-contract) | SHOULD | Give sibling components with the same interaction meaning a shared motion language; consistency does not require every component to animate. | | +| [D-MOTION-7](./DESIGN.md#functional-motion-contract) | MUST | When motion is used, a new intent can interrupt or reverse it from the current presentation, without a jump, stale completion or duplicate activation. | recording | +| [D-MOTION-8](./DESIGN.md#functional-motion-contract) | MUST | Feedback stays immediate; animation is not an input lock. | recording | +| [D-MOTION-9](./DESIGN.md#functional-motion-contract) | SHOULD | During insertion/removal, avoid visibly squeezing text or passing exiting controls across live actions; coordinate opacity and layout so the result stays readable. | | +| [D-MOTION-10](./DESIGN.md#functional-motion-contract) | MUST | Reduced motion retains a visible, understandable final state. | browser | +| [D-MOTION-11](./DESIGN.md#functional-motion-contract) | MAY | Use a spring; it need not overshoot. | | +| [D-MOTION-12](./DESIGN.md#functional-motion-contract) | SHOULD | Let theme palettes settle coherently. Avoid implicit palette fades or `transition: all`; opt in explicitly only where the host intends and verifies a coordinated effect. | | +| [D-MOTION-13](./DESIGN.md#functional-motion-contract) | MUST | Factual values and readable content are shown immediately on return to a working surface, without animated counts. | browser | +| [D-MOTION-16](./DESIGN.md#functional-motion-contract) | SHOULD | Do not replay decorative title entrances on each return to a working surface. | | +| [D-MOTION-14](./DESIGN.md#functional-motion-contract) | MAY | A loading demonstration may move to demonstrate loading. | | +| [D-MOTION-15](./DESIGN.md#functional-motion-contract) | MUST | A status pulse appears only while the activity is live. | review | +| [D-COPY-1](./DESIGN.md#6-content--copy-is-part-of-the-system) | SHOULD | Buttons name the outcome. Include the object when context does not establish it: "Deploy project", "Delete member". In a clearly named object or dialog, "Save" or "Delete" is sufficient; avoid generic "OK". See [contextual wording](#words-are-part-of-the-interface). | | +| [D-COPY-2](./DESIGN.md#6-content--copy-is-part-of-the-system) | SHOULD | Errors say **what happened**, then **how to fix it**. ✅ "Build failed: the bundle exceeds the size limit. Reduce it or raise the limit." ❌ "Operation failed, please try again." | | +| [D-COPY-3](./DESIGN.md#6-content--copy-is-part-of-the-system) | SHOULD | Confirmations and toasts state the completed **change**. ✅ "Project deleted". ❌ "Operation successful". | | +| [D-COPY-4](./DESIGN.md#6-content--copy-is-part-of-the-system) | MUST | Show completion feedback only after the operation is confirmed. | unit, browser | +| [D-COPY-5](./DESIGN.md#6-content--copy-is-part-of-the-system) | SHOULD | Be specific; every sentence removes guesswork rather than adding it. | | +| [D-ACCESS-1](./DESIGN.md#7-accessibility--the-system-serves-everyone) | MUST | Text keeps sufficient contrast with its background (**WCAG AA**). | browser | +| [D-ACCESS-2](./DESIGN.md#7-accessibility--the-system-serves-everyone) | MUST | Never signal state with color alone; pair it with an icon or text (e.g. ✓ / ✕ labels, not just green/red). | review | +| [D-ACCESS-3](./DESIGN.md#7-accessibility--the-system-serves-everyone) | MUST | Every interactive element has a **visible focus ring** appropriate to its surface; never remove it for "cleanliness". | browser | +| [D-ACCESS-4](./DESIGN.md#7-accessibility--the-system-serves-everyone) | MUST | **Full keyboard navigation**: nothing is mouse-only. | browser | +| [D-ACCESS-5](./DESIGN.md#7-accessibility--the-system-serves-everyone) | MUST | Respect `prefers-reduced-motion`. | browser | +| [D-ACCESS-6](./DESIGN.md#7-accessibility--the-system-serves-everyone) | MUST | When the host supports both schemes, the initial render follows `prefers-color-scheme`. | browser | +| [D-INPUT-1](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | MUST | Support mouse, keyboard and touch at narrow and wide widths. | browser | +| [D-INPUT-2](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | MUST | Reflow controls instead of hiding the only route to an action. | browser | +| [D-INPUT-3](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | MUST | A hover affordance has a click/tap alternative. | browser | +| [D-INPUT-4](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | MUST | Drags use Pointer Events, including cancellation. | browser | +| [D-INPUT-5](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | MUST | Scope `touch-action` to the actual drag surface and pair it with a real handler; ordinary scrolling keeps working elsewhere. | browser | +| [D-INPUT-6](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | SHOULD | Prefer content/viewport-relative bounds (`min()`, `max()`, `clamp()`) to arbitrary breakpoints. | | +| [D-INPUT-7](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | SHOULD | Use existing host breakpoints, and introduce a justified shared role only when repeated needs emerge. | | +| [D-INPUT-8](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | SHOULD | Exercise resizing into a narrow layout, not only loading there. | | +| [D-INPUT-9](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | MUST | Overlay placement tracks its trigger while open. | browser | +| [D-INPUT-10](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | SHOULD | Test actual pointer/touch gestures, hover fallback and keyboard routes where affected; checking two color schemes does not establish input parity. | | +| [D-COMP-1](./DESIGN.md#9-composition--the-page-not-the-component) | SHOULD | Choose the page structure in §10 first; then compose its components with these rules. | | +| [D-COMP-2](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | Before implementation, record a short design brief in the existing task note or product design owner. Use the existing system; a small fix only needs the affected decisions. The brief settles each applicable decision in the table below (D-COMP-2). | | +| [D-COMP-4](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | Reuse the brief across sibling components and docs chrome; change emphasis according to the task. | | +| [D-COMP-5](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | Keep an expressive demo visibly distinct from its documentation chrome. | | +| [D-COMP-6](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | Update the brief before changing the visual direction; do not silently rewrite the standard to justify an implementation that failed review. | | +| [D-COMP-7](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | Compare a representative composed still with different real content before animating it, then inspect the state change and return path at normal speed. | | +| [D-COMP-8](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | When an example looks imported from another product, fix that relationship at its owning token/component/style rather than adding a page-specific patch. | | +| [D-COMP-9](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | Keep reference observations and validation limits in the optional [research index](./REFERENCE_RESEARCH.md#style-coherence-and-transition-research). | | +| [D-BOX-1](./DESIGN.md#structure-before-containers) | SHOULD | **Reach for spacing, alignment, weight and a hairline before a box.** | | +| [D-BOX-2](./DESIGN.md#structure-before-containers) | SHOULD | Reserve a box for **a genuinely separate object**: a live demo stage, an overlay, a panel that floats. Not for a code block, a table, a list row, a previous/next link, or a cell in a gallery. | | +| [D-BOX-3](./DESIGN.md#structure-before-containers) | SHOULD | **Avoid redundant nested containers and doubled padding.** A card inside a padded panel does not need another decorative frame or inset. | | +| [D-BOX-4](./DESIGN.md#structure-before-containers) | SHOULD | Keep functionally required nested controls or surfaces, and coordinate their radii with the actual inset; containment alone is not a reason to add a box. | | +| [D-BOX-5](./DESIGN.md#structure-before-containers) | SHOULD | **Every persistent boundary needs a distinct job.** Start with headings, alignment and proximity to group content. Add a border, surface fill or elevation only when that structure leaves a real boundary unclear. | | +| [D-BOX-6](./DESIGN.md#structure-before-containers) | SHOULD | Do not repeat the same grouping with a panel frame, a nested card, row rules and another inset at the same time. | | +| [D-BOX-8](./DESIGN.md#structure-before-containers) | MUST | Keep keyboard focus and persistent selection cues; never remove them to make the resting layout quieter. | browser | +| [D-BOX-9](./DESIGN.md#structure-before-containers) | SHOULD | Review the full pane or page, not only the cropped group. For every remaining stable outline, fill or separator, name the relationship or interaction it communicates and check what becomes ambiguous if it is removed. | | +| [D-COMP-10](./DESIGN.md#emphasis-is-a-budget) | SHOULD | Establish priority with structure and proximity first, and spend emphasis on the one thing that deserves it. | | +| [D-ELEV-3](./DESIGN.md#emphasis-is-a-budget) | SHOULD | Reserve shadows for surfaces that genuinely float above content: popovers, menus, dialogs, notifications. Do not shadow every card. | | +| [D-SPACE-6](./DESIGN.md#spacing-carries-meaning) | SHOULD | Do not undo a gap's relationship with a decorative divider. | | +| [D-SPACE-7](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Repeat relationships, not fixed heights. Choose title/description, field/hint, group and region gaps from the host scale according to their relationship. | | +| [D-SPACE-8](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Let translations grow naturally, and leave no vacant row for a missing description. | | +| [D-SPACE-9](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Give each gap one owner: parent stack or explicit child margin. Reset authored local heading/paragraph margins rather than adding them to the parent's gap. | | +| [D-SPACE-10](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Account for owned control padding, collapsing margins and inline baseline space; do not compensate with transforms or smaller type. | | +| [D-SPACE-11](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Place hints with the field/action they explain. | | +| [D-SPACE-12](./DESIGN.md#vertical-rhythm-across-sibling-views) | MUST | Hidden helpers leave no accidental gap. | browser | +| [D-SPACE-16](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | A reserved async status line has a stated purpose and bounds. Measure both axes after content settles; [ACCEPTANCE](./ACCEPTANCE.md) grades this as V-PROOF-6. | | +| [D-SPACE-14](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Keep leading, padding and hairlines distinct: 16px padding plus a 1px rule is not a 17px spacing token. | | +| [D-SPACE-15](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Use independent expectations and exercise wrapping, resizing, helper/error visibility and relevant density. | | +| [D-TYPE-4](./DESIGN.md#the-measure-governs-text-not-the-column) | SHOULD | **Choose a readable measure for the actual script and font.** | | +| [D-TYPE-5](./DESIGN.md#the-measure-governs-text-not-the-column) | SHOULD | Review realistic text at supported widths, zoom levels and font fallbacks; CJK and mixed-script text need their own judgment. | | +| [D-TYPE-6](./DESIGN.md#the-measure-governs-text-not-the-column) | SHOULD | Let a table, code fence, demo stage or image retain the width its content needs. Cap the text elements, not the column that holds them. | | +| [D-TYPE-7](./DESIGN.md#the-measure-governs-text-not-the-column) | SHOULD | **Declare the measure in `rem`, never `em`.** | | +| [D-TYPE-8](./DESIGN.md#the-measure-governs-text-not-the-column) | SHOULD | **Build hierarchy out of type before reaching for a box.** | | +| [D-ALIGN-1](./DESIGN.md#if-the-layout-says-two-things-are-one-object-the-spacing-must-agree) | SHOULD | Decide whether the elements are one object or two, and make every property say the same thing. | | +| [D-ALIGN-2](./DESIGN.md#if-the-layout-says-two-things-are-one-object-the-spacing-must-agree) | SHOULD | **When a value must equal another value, name it.** A gap that a child has to subtract is not a literal in two places; it is one custom property referenced twice. | | +| [D-ALIGN-3](./DESIGN.md#reserve-a-column-only-when-its-content-exists) | SHOULD | Condition the track on the element actually being there (`:has()`), not on a page type you assume implies it. | | +| [D-ALIGN-4](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | **Name each alignment spine and the geometry it aligns.** State which siblings share each one; include headers, body copy, rows, actions and empty/loading/error content. | | +| [D-ALIGN-5](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | A back button plus gap may establish a separate title column: do not silently treat that title as aligned to body text. | | +| [D-ALIGN-6](./DESIGN.md#alignment-is-structure-not-polish) | MUST | A missing optional icon or badge never moves labels out of their declared column. | browser | +| [D-ALIGN-7](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | A nested level returns exactly to its parent's spine when it ends. Compare the right boxes. These are separate measurements, even when they happen to coincide; measuring the defect's layer is graded in [ACCEPTANCE](./ACCEPTANCE.md) as V-PROOF-10. | | +| [D-ALIGN-9](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | Inspect optical glyph corrections separately. For web components, measure the host, documented shadow parts and slotted text through supported test seams. V-PROOF-11 in [ACCEPTANCE](./ACCEPTANCE.md) owns this, including the ban on production shadow queries and on assuming document CSS crosses the boundary. | | +| [D-ALIGN-11](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | **Keep a header command's hit zone separate from layout filler.** A title/menu trigger hugs its content plus deliberate padding, bounded by the available width. Let a separate flex/grid track absorb remaining space. | | +| [D-ALIGN-12](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | Define truncation/wrapping and adjacent action behavior for long translations. | | +| [D-ALIGN-13](./DESIGN.md#alignment-is-structure-not-polish) | MUST | The title trigger's hit area does not span unused header space (for example through `flex: 1` on the title button) unless D-ALIGN-14 applies. | browser | +| [D-ALIGN-14](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | A row that intentionally selects its whole width is a different role; record that contract explicitly. | | +| [D-ALIGN-15](./DESIGN.md#alignment-is-structure-not-polish) | MAY | Coarse-pointer targets may grow larger. | | +| [D-ALIGN-22](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | A grown target's hover fill stays on the control's own content, not across unrelated content. | | +| [D-ALIGN-16](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | **Count every inset from the shared ancestor to the visible text.** Parent padding, nested panel padding, a control's internal padding, borders and navigation columns all contribute. | | +| [D-ALIGN-17](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | Remove a redundant inset at its owner instead of cancelling it with an unexplained negative margin. | | +| [D-ALIGN-18](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | Empty states retain their region's content spine and width; extra breathing room never introduces a second left edge or makes a compact action fill the region. | | +| [D-ALIGN-19](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | Keep product-specific insets and target sizes as application aliases, not new foundation values or universal component defaults. | | +| [D-ALIGN-20](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | **When two layout edges or gaps are meant to be equal, a one-pixel difference is a defect.** Optical glyph correction (D-ALIGN-21) is the only tolerance. | | +| [D-ALIGN-21](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | Treat optical correction for an asymmetric glyph as a separate, documented asset/component decision, reused at supported sizes while sibling gaps, baselines and spines stay exact. | | +| [D-SEM-1](./DESIGN.md#selection-links-and-other-semantic-lies) | MUST | **Selection is persistent state and never looks like hover.** | browser | +| [D-SEM-2](./DESIGN.md#selection-links-and-other-semantic-lies) | MUST | **Action and link roles remain distinguishable.** | browser | +| [D-SEM-3](./DESIGN.md#selection-links-and-other-semantic-lies) | SHOULD | Verify inline links against surrounding prose, including themes where action and body colors happen to match. | | +| [D-SEM-4](./DESIGN.md#selection-links-and-other-semantic-lies) | MUST | Anything that changes application state is a button, whatever it looks like. | review, browser | +| [D-SEM-5](./DESIGN.md#selection-links-and-other-semantic-lies) | SHOULD | A button-shaped link carries no underline. | | +| [D-CMD-1](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | **Remove redundant group enclosures without erasing control affordance.** | | +| [D-CMD-2](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Use flat rows when a recognizable menu/list pattern already conveys action; do not require them in every context. | | +| [D-CMD-3](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Give a standalone command that needs it its own restrained button surface or functional stroke, visible before hover in every supported theme. | | +| [D-CMD-4](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Let a button's own shape identify its target; an enclosing card, muted group fill and row rules still pass the distinct-boundary test (D-BOX-5). | | +| [D-CMD-5](./DESIGN.md#command-hierarchy-in-constrained-panes) | MUST | Initial discovery does not rely on hover, pointer cursors, verb labels or keyboard focus alone. | browser | +| [D-CMD-6](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Inspect the resting full pane with the pointer away, including touch use. | | +| [D-CMD-7](./DESIGN.md#command-hierarchy-in-constrained-panes) | MUST | Hover, press, disabled and keyboard focus stay distinguishable after the control is discovered. | browser | +| [D-CMD-8](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Wrap long translated labels inside one command column instead of a ragged horizontal button cloud. | | +| [D-CMD-9](./DESIGN.md#command-hierarchy-in-constrained-panes) | MUST | Do not add navigation chevrons to immediate commands. | review | +| [D-CMD-10](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Declare which visible columns are shared with adjacent checkboxes or controls: measure icon/checkbox edges and text Ranges, not only button rectangles or alignment within the command group. | | +| [D-CMD-11](./DESIGN.md#command-hierarchy-in-constrained-panes) | MAY | A hit target may extend into an owned gutter to preserve those columns; name the extension. | | +| [D-CMD-12](./DESIGN.md#command-hierarchy-in-constrained-panes) | MUST | An extended hit target stays inside its pane, does not overlap other targets, and keeps focus visible. | browser | +| [D-CMD-13](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Separate destructive commands from recovery and transfer actions. | | +| [D-CMD-14](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Let a danger label and icon communicate intent without a large red outline on every resting command; reserve the strongest danger fill for explicit confirmation. | | +| [D-CMD-15](./DESIGN.md#command-hierarchy-in-constrained-panes) | MUST | Destructive commands keep confirmation, cancellation, focus return and busy guards. | browser | +| [D-CMD-16](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Accept a command pane against its resting state, not only its hover state. | | +| [D-CMD-17](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Inspect the actual render as well: an automated surface-difference check catches invisible control fills but does not prove that users recognize the action. | | +| [D-CMD-18](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Reuse foundation roles and coarse-input target requirements; this guidance changes no component defaults. | | +| [D-TOKEN-1](./DESIGN.md#one-product-one-token-set) | SHOULD | Use the product’s authoritative semantic tokens. Aliases may improve readability, but resolve from that source rather than inventing a competing palette. | | +| [D-TOKEN-2](./DESIGN.md#one-product-one-token-set) | SHOULD | Coordinate third-party components through supported theme APIs and verify the composed result. | | +| [D-TOKEN-3](./DESIGN.md#density-follows-control-roles) | SHOULD | Measure actual hit bounds, focus and adjacent gaps. | | +| [D-TOKEN-4](./DESIGN.md#density-follows-control-roles) | SHOULD | Derive repeated host aliases from foundation tokens, not one application's dimensions copied across the library. | | +| [D-TOKEN-5](./DESIGN.md#density-follows-control-roles) | SHOULD | Choose target bounds for the actual input method without enlarging every glyph or reserving space for hidden controls. Use supported public theme APIs. | | +| [D-TOKEN-6](./DESIGN.md#density-follows-control-roles) | MUST | Name icon-only controls accessibly. | browser | +| [D-TOKEN-7](./DESIGN.md#density-follows-control-roles) | SHOULD | Document legitimate viewport, hairline, artwork and optical geometry exceptions. | | +| [D-CRAFT-1](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Choose a deliberate visual direction using realistic objects, copy and states; a generic empty shell cannot establish product quality. | | +| [D-CRAFT-2](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Evaluate the smallest intervention against real content, language, device and repeated use. | | +| [D-CRAFT-3](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Adapt their principles with the host’s tokens and actual capabilities; do not copy a product's pixel sizes or import recipes as rules. | | +| [D-CRAFT-4](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Treat references as inputs to decisions, not additional instruction sets; for example, [Don't Make Me Think](https://sensible.com/dont-make-me-think/) informs scanning, hierarchy and clear choices. | | +| [D-CRAFT-5](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | A stylistic preference needs context; a requirement identifies the task or observable contract it protects. | | +| [D-CRAFT-6](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Choose references by the job they support: logo/app identity, marketing composition and frequent working interactions solve different problems. | | +| [D-CRAFT-7](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Record the observed pattern and its adaptation; verify the result with real content. | | +| [D-CRAFT-8](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | When direction is unresolved, compare two small real-host variants with the same content, width and theme, changing one dimension at a time. Explain what each difference does for the task. | | +| [D-CRAFT-9](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Balance short headings progressively with `text-wrap: balance`, preserving normal wrapping. | | +| [D-CRAFT-10](./DESIGN.md#evaluate-craft-through-evidence) | MUST | Headings contain no locale-dependent manual line breaks and are not clipped to preserve a screenshot. | browser | +| [D-CRAFT-11](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Check cost and support before using `pretty` on long live content. | | +| [D-CRAFT-12](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Use tabular figures to stabilize digit advances if the font supports them. | | +| [D-CRAFT-13](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Reserve a meaningful column for digit-count changes such as 99 → 100. | | +| [D-CRAFT-14](./DESIGN.md#evaluate-craft-through-evidence) | MUST | A digit-count change never truncates the value. | browser | +| [D-CRAFT-15](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Correct asymmetric glyphs in a reusable asset/component, preserving exact layout spines. | | +| [D-CRAFT-16](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Derive nested corner geometry from actual inset; remove an unnecessary nested surface first. | | +| [D-CRAFT-17](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Give motion a job: immediate frequent feedback, interruptible transitions, reduced motion and instant themes. See §5. | | +| [D-CRAFT-18](./DESIGN.md#evaluate-craft-through-evidence) | MUST | Optional delight never delays typing or obscures status. | recording | +| [D-CRAFT-19](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Pause offscreen decoration and profile realistic content before adopting costly effects. | | +| [D-COPY-6](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Avoid unnecessary secondary labels and descriptions. Let the layout do the explaining. | | +| [D-COPY-7](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | A heading, grouping, proximity or obvious control needs no other sentence repeating its purpose. | | +| [D-COPY-8](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Remove copy with no new decision, constraint or consequence; do not replace it with a decorative badge or tooltip. | | +| [D-COPY-9](./DESIGN.md#words-are-part-of-the-interface) | MUST | Preserve accessible names, field labels, units, errors and risk disclosures. | browser | +| [D-COPY-10](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Preserve useful first-use guidance, and test whether the task remains understandable. | | +| [D-COPY-11](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Let context carry context: `Users` rather than `User Management`; do not repeat a dialog's question in its body. | | +| [D-COPY-12](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Name results (`Save`, `Move`, `Delete`), not gestures (`Click to save`). Include the object only when context does not establish it. | | +| [D-COPY-13](./DESIGN.md#words-are-part-of-the-interface) | MUST | `Cancel` leaves without committing. | browser | +| [D-COPY-14](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Use sentence case, no period on short labels, and a period on explanatory sentences. | | +| [D-COPY-15](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Use one `…` when an action needs more input before completing; reserve ALL CAPS for short statuses, eyebrows and acronyms. | | +| [D-REVIEW-1](./DESIGN.md#review-checklist-for-a-composed-surface) | SHOULD | Apply these questions to the affected surface and reachable changed states. Skip irrelevant variants; a small patch does not require redesigning unrelated pages. A "no" within scope is a finding. | | +| [D-REVIEW-2](./DESIGN.md#review-checklist-for-a-composed-surface) | SHOULD | Follow [ACCEPTANCE](./ACCEPTANCE.md) for independent expectations, actual runtime/capture identity, browser measurements and separate automated, agent and human results. | | +| [D-IA-1](./DESIGN.md#10-information-architecture--the-shape-before-the-composition) | SHOULD | Choose structure before composition for information-dense consoles, dashboards and workbenches. | | +| [D-IA-2](./DESIGN.md#10-information-architecture--the-shape-before-the-composition) | SHOULD | Give single-conversion and landing pages their own task structure rather than a dense-console template. | | +| [D-IA-3](./DESIGN.md#answer-three-questions-before-naming-a-component) | SHOULD | Settle all three answers before choosing components. | | +| [D-IA-4](./DESIGN.md#answer-three-questions-before-naming-a-component) | SHOULD | **A page has exactly one primary model.** Supporting models may help the reader understand or operate it; they may not compete for the first screen. | | +| [D-IA-5](./DESIGN.md#answer-three-questions-before-naming-a-component) | SHOULD | Prefer a clear, unambiguous next choice over an arbitrary click budget. Test whether readers recognize the next action, not merely whether the destination is reachable. | | +| [D-IA-6](./DESIGN.md#the-shape-follows-the-task-not-the-payload) | SHOULD | Choose a skeleton from the reader's question, not an API array or route ID, using the table below. | | +| [D-IA-7](./DESIGN.md#the-shape-follows-the-task-not-the-payload) | SHOULD | Do not confuse historical events with future steps, a filter with kanban, or ordered prose with a field grid. A date alone does not justify a calendar. | | +| [D-IA-8](./DESIGN.md#the-shape-follows-the-task-not-the-payload) | SHOULD | Ship multiple views only when each serves a distinct task worth its filter/state/action maintenance. | | +| [D-IA-9](./DESIGN.md#each-kind-of-information-has-a-place) | SHOULD | Lead with identity and status in the title or summary region, and keep actions, feedback and evidence beside their subject, as the table below lays out. | | +| [D-IA-10](./DESIGN.md#each-kind-of-information-has-a-place) | SHOULD | **Every fact has exactly one authoritative location.** Elsewhere shows a summary or an entry point that links back to it. | | +| [D-IA-11](./DESIGN.md#each-kind-of-information-has-a-place) | SHOULD | Follow the default reading order: page identity → current status or exception → primary task and primary action → the information the judgement needs → relationships, changes, evidence → secondary information and low-frequency actions. | | +| [D-IA-12](./DESIGN.md#each-kind-of-information-has-a-place) | SHOULD | **One primary action per task region**, and it is the most likely next step, not the most destructive one (§9, "Emphasis is a budget", governs how much weight it may take). | | +| [D-IA-13](./DESIGN.md#density-is-effective-information-not-controls-per-inch) | SHOULD | **Use at most two adjacent density levels on one page**: spacious for first use and risky confirmation, standard for most lists and details, compact for expert workbenches. | | +| [D-IA-14](./DESIGN.md#density-is-effective-information-not-controls-per-inch) | MAY | Container padding and line height may tighten. | | +| [D-IA-15](./DESIGN.md#density-is-effective-information-not-controls-per-inch) | MUST | Readable body text, a visible focus ring and pointer target size do not shrink with density (§7, §8). | browser | +| [D-IA-16](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | **One contextual return per view.** Do not show an outer “Back to chat” and an inner “Back to artifacts” together. | | +| [D-IA-17](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | The detail view returns to its immediate parent. | | +| [D-IA-18](./DESIGN.md#containers-do-not-fix-structure) | MUST | Ancestors remain reachable through the route hierarchy or clearly named breadcrumbs. | browser | +| [D-IA-19](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | Verify the full journey, including focus return and long translated destination names. | | +| [D-IA-27](./DESIGN.md#containers-do-not-fix-structure) | MUST | Returning to the parent restores focus to the originating item. | browser | +| [D-IA-20](./DESIGN.md#containers-do-not-fix-structure) | MUST | If text does not fit, reflow or use a meaningful shorter destination; an ellipsis or tooltip never hides the only destination cue. | browser | +| [D-IA-21](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | Choose one composition for the parent context. Do not add a breadcrumb merely to relabel a redundant return shortcut, or repeat that parent as an inert crumb above its return command. | | +| [D-IA-22](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | Use breadcrumbs when the hierarchy itself helps the task. | | +| [D-IA-23](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | Give a route to anything needing a copyable link, history, a side-by-side comparison, or work that survives a refresh. | | +| [D-IA-24](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | Reserve a modal for a short confirmation or a single field, a popover or menu for transient context, and a disclosure row for progressive disclosure. | | +| [D-IA-25](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | **Tabs carry peer views of one object** — its conversation, its checks, its diff — never unrelated modules; that is navigation's job. | | +| [D-IA-26](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | If the chosen library lacks a suitable primitive, use existing semantic elements and host tokens; verify the required interaction rather than inventing unsupported APIs. | | +| [D-VERIFY-1](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Select checks for the affected surface, supported variants and changed behavior; do not create unrelated flows merely to satisfy the list. | | +| [D-VERIFY-2](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Primary task and primary action are unmistakable. | | +| [D-VERIFY-5](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Exercise all changed **states** (hover, active, focus, disabled, loading, empty, error). | | +| [D-VERIFY-7](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Cover relevant edge cases: long text, large numbers and the host's supported scripts/locales. | | +| [D-VERIFY-8](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Spacing comes from the scale; type uses a role; color uses semantic tokens. | | +| [D-VERIFY-9](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Run the host’s applicable checks and inspect actual browser evidence. | | +| [D-VERIFY-10](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Copy follows §6. | | +| [D-VERIFY-12](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | For a dense surface: one primary model, the skeleton chosen from the question rather than the payload, and every fact with one authoritative location (§10). | | +| [X-INPUT-1](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Keep labels, prerequisites and validation messages near the field they affect. | browser | +| [X-INPUT-2](./INTERACTION.md#1-inputs-drafts-and-intent) | SHOULD | Distinguish a missing value from an unavailable service, and explain why submission is unavailable when the prerequisite is not obvious. | | +| [X-INPUT-3](./INTERACTION.md#1-inputs-drafts-and-intent) | SHOULD | Use the appropriate native input semantics and autocomplete, and preserve platform editing conventions; expose keyboard shortcuts where they help. | | +| [X-INPUT-4](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Submit dispatches once per activation, and Restore only repopulates fields without submitting them. | unit | +| [X-INPUT-5](./INTERACTION.md#1-inputs-drafts-and-intent) | SHOULD | Follow the remaining rows of the input transition table. | | +| [X-INPUT-6](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Enter submits only where the field's documented convention calls for it, and never during IME composition (check `isComposing`). Multiline entry keeps working. | browser | +| [X-INPUT-7](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Dictation updates text and never submits by itself; the text that preceded the capture is retained. | browser | +| [X-INPUT-8](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Do not rebuild a focused input on a search response, stream update, resize or status change. | browser | +| [X-INPUT-9](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | The latest intent wins: an older query never replaces newer results, and a preview for one file never appears under another. | unit | +| [X-INPUT-10](./INTERACTION.md#1-inputs-drafts-and-intent) | SHOULD | Implement X-INPUT-9 with the host’s async identity and cancellation mechanisms; ranui contributors can consult [CODING §3](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/CODING.md#3-state-and-ownership). | | +| [X-INPUT-11](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Promise persistence only after it succeeds; a retained draft is not a saved one. | unit | +| [X-INPUT-12](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Highlight search matches with safe text nodes or equivalent escaping, never by inserting the query as HTML. | unit | +| [X-INPUT-13](./INTERACTION.md#1-inputs-drafts-and-intent) | SHOULD | Keep enough text around a highlighted match to explain why the item matched. | | +| [X-FOCUS-1](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | SHOULD | Build controls from native semantics and supported component contracts. | | +| [X-FOCUS-2](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | Every icon-only action has an accessible name. | browser | +| [X-FOCUS-3](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | A secondary action revealed on hover is also revealed on keyboard focus and has a persistent tap route on coarse input. | browser | +| [X-FOCUS-4](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | SHOULD | Keep essential recovery actions visible rather than revealed on hover. | | +| [X-FOCUS-5](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | Disabled controls stay quiet on hover and cannot dispatch. | browser | +| [X-FOCUS-6](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | The target box and focus ring keep their size and position while a label or status changes. | browser | +| [X-FOCUS-7](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | Integrate through public attributes, events, slots, variables and parts; do not patch private shadow roots. | review | +| [X-FOCUS-8](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | Do not add host tab stops that lead nowhere or duplicate an inner control. | browser | +| [X-FOCUS-9](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | A composed dialog places initial focus inside itself, keeps Tab and Shift+Tab within it, closes on Escape and Cancel, activates its default action on Enter, and returns focus to the opener when it closes. | browser | +| [X-FOCUS-10](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | SHOULD | When a focus trap cannot reach a control (for example a closed-shadow button in a footer), fix the supported integration or place native buttons in its supported slot. | | +| [X-FOCUS-11](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | A collapsed region still identifies its contents and state, and disclosure never hides an unresolved actionable error. | browser | +| [X-FOCUS-12](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | Collapsing a region that contains focus returns focus to its toggle; removing the focused object moves focus to the next meaningful action or a stable parent control. | browser | +| [X-FOCUS-13](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | Do not move focus merely because a background request completed. | browser | +| [X-FOCUS-14](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | SHOULD | Write the collapsed summary to help readers decide whether to expand; when the expanded body already shows that content, omit the repeated summary and keep the title and state. | | +| [X-FEEDBACK-1](./INTERACTION.md#3-feedback-and-reading-position) | SHOULD | Show feedback next to the object acted on. A “Copied” acknowledgment beside Copy is easier to associate than an unrelated global notification; a toast suits changes beyond the current object or feedback that would otherwise go unnoticed. | | +| [X-FEEDBACK-2](./INTERACTION.md#3-feedback-and-reading-position) | MUST | Show a success acknowledgment only after the operation succeeded (for Copy, after clipboard success), and handle rejection locally while keeping button geometry and focus. | browser | +| [X-FEEDBACK-3](./INTERACTION.md#3-feedback-and-reading-position) | MUST | Async feedback belongs to the operation's source snapshot: when content changes while Copy is pending, the older completion never marks the new content “Copied”, and stale feedback is cleared or identified as stale. | unit, browser | +| [X-FEEDBACK-4](./INTERACTION.md#3-feedback-and-reading-position) | MUST | Do not announce every streamed character to assistive technology. | browser | +| [X-FEEDBACK-5](./INTERACTION.md#3-feedback-and-reading-position) | MUST | Live logs and streaming results follow only while the reader is at the latest content; scrolling away suspends following, and “Jump to latest” restores it without stealing the reading position. | recording | +| [X-FEEDBACK-6](./INTERACTION.md#3-feedback-and-reading-position) | SHOULD | Use a supported follow/pin contract from the host or chosen component, and keep job and persistence state in the application; an append-only reading log is not a job manager. | | +| [X-RECOVER-1](./INTERACTION.md#4-progress-failure-and-recovery) | SHOULD | Name a known phase (“Reading files”, “Checking permissions”, “Writing export”) rather than showing only a spinner, and announce meaningful phase changes, not every timer tick. | | +| [X-RECOVER-2](./INTERACTION.md#4-progress-failure-and-recovery) | MUST | Show a percentage only with a measurable denominator; elapsed time is secondary and never presented as a completion estimate. | review | +| [X-RECOVER-3](./INTERACTION.md#4-progress-failure-and-recovery) | MUST | An error answers what failed, whether anything changed, and what to do next, and promises only what the host can establish. | review | +| [X-RECOVER-4](./INTERACTION.md#4-progress-failure-and-recovery) | MUST | Preserve in-memory input when storage fails, and report the persistence failure separately. | unit | +| [X-RECOVER-5](./INTERACTION.md#4-progress-failure-and-recovery) | SHOULD | Put credential-free diagnostics in optional details. | | +| [X-RECOVER-6](./INTERACTION.md#4-progress-failure-and-recovery) | SHOULD | Give one failure one coherent recovery, not duplicate rows and toasts. | | +| [X-RECOVER-7](./INTERACTION.md#4-progress-failure-and-recovery) | MUST | A timeout does not prove rollback: establish whether a mutating operation committed before retrying, using the host's idempotency or deduplication contract where available. | unit | +| [X-CANCEL-1](./INTERACTION.md#5-cancel-retry-and-completion-are-different-outcomes) | MUST | Keep Cancel and Submit independently reachable when both are supported, with clear accessible names and stable positions. | browser | +| [X-CANCEL-2](./INTERACTION.md#5-cancel-retry-and-completion-are-different-outcomes) | MUST | A cancellation request is not an acknowledged cancellation: show its pending state until confirmed, and when the client can only stop waiting, say so instead of claiming the job stopped. | unit | +| [X-CANCEL-3](./INTERACTION.md#5-cancel-retry-and-completion-are-different-outcomes) | MUST | Cancellation is not shown as failure. | browser | +| [X-CANCEL-4](./INTERACTION.md#5-cancel-retry-and-completion-are-different-outcomes) | MUST | Retry dispatches work again without silently broadening scope or switching destinations; Restore only repopulates editable inputs. | unit | +| [X-CANCEL-5](./INTERACTION.md#5-cancel-retry-and-completion-are-different-outcomes) | MUST | Report saved or applied success only after the write is confirmed, and retain recoverable partial output. | unit | +| [X-CANCEL-6](./INTERACTION.md#5-cancel-retry-and-completion-are-different-outcomes) | MUST | Offer Undo only when the host can actually reverse the operation, and never promise that Cancel undoes a completed write. | review | +| [X-VERIFY-1](./INTERACTION.md#6-verify-what-a-person-can-observe) | SHOULD | Choose evidence by the defect, using the layers below, rather than running every possible test for a small change. | | +| [X-VERIFY-2](./INTERACTION.md#6-verify-what-a-person-can-observe) | SHOULD | Include offline and failure paths when the task depends on external work. | | +| [X-VERIFY-3](./INTERACTION.md#6-verify-what-a-person-can-observe) | SHOULD | Report the tested commit and any remaining external prerequisites. | | +| [V-AGREE-1](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | Before editing, settle the [design brief](./DESIGN.md#style-coherence-across-components-and-pages), and state the affected task, views/states and independent expectations in that same note. | | +| [V-AGREE-2](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | Name the relevant owner, supported public API and host capability limits. | | +| [V-AGREE-3](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | Select widths, themes, languages and input paths from the actual change, without inventing unrelated work. | | +| [V-AGREE-4](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | For composition, state content/text/icon spines, intentional navigation columns, hit bounds and vertical relationships. | | +| [V-AGREE-5](./ACCEPTANCE.md#agree-on-the-observable-result) | MUST | Do not derive the expected value from the implementation under test. | review | +| [V-AGREE-6](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | Review in order: task/hierarchy, static composition, reachable combined states, then motion. | | +| [V-AGREE-7](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | Do not add animation to compensate for unresolved proportions or unclear navigation. | | +| [V-AGREE-8](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | When a review changes the intended design, update the existing brief first and identify the evidence that prompted it. | | +| [V-AGREE-9](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | Keep one rule owner. | | +| [V-AGREE-10](./ACCEPTANCE.md#agree-on-the-observable-result) | MUST | Do not let one kind of check substitute for another: an API/token check, an image comparison and an agent run each establish only their own result. | review | +| [V-AGREE-11](./ACCEPTANCE.md#agree-on-the-observable-result) | MUST | A fixture demonstrates its own result, not acceptance of a host application it did not render; do not claim it as host acceptance. | review | +| [V-PROOF-1](./ACCEPTANCE.md#browser-proof) | MUST | When reproducing a visible defect, capture the actual running surface before the fix. | browser | +| [V-PROOF-2](./ACCEPTANCE.md#browser-proof) | MUST | After the fix, capture the same relevant state and inspect the full pane with realistic copy and surrounding chrome. | browser | +| [V-PROOF-3](./ACCEPTANCE.md#browser-proof) | MUST | Do not offer a mockup, source diff, changed assertion or screenshot of an unrelated build as proof. | review | +| [V-PROOF-4](./ACCEPTANCE.md#browser-proof) | SHOULD | Inspect the composition as well as the link count: relabeling a redundant return as a breadcrumb can still repeat the same context and add an unnecessary header row. | | +| [V-PROOF-5](./ACCEPTANCE.md#browser-proof) | SHOULD | Trace ancestor reachability through the parent before deciding another visible shortcut is needed. | | +| [V-PROOF-6](./ACCEPTANCE.md#browser-proof) | MUST | Wait for component registration, pending content, fonts and rendered frames before measuring. | browser | +| [V-PROOF-7](./ACCEPTANCE.md#browser-proof) | MUST | Fail on missing or zero-sized targets. | browser | +| [V-PROOF-8](./ACCEPTANCE.md#browser-proof) | MUST | Record any subpixel tolerance, and keep it below a full CSS pixel for exact spines (the source example uses 0.5px). | review | +| [V-PROOF-9](./ACCEPTANCE.md#browser-proof) | MUST | For live overlays, verify that capture preserves the viewport and includes the full trigger/panel; a full-page screenshot can invalidate a crop by causing responsive reflow or portal repositioning. | browser | +| [V-PROOF-10](./ACCEPTANCE.md#browser-proof) | MUST | Measure the defect's layer: wrapper, inner content, rendered text Range, icon viewport and actual pointer target are different. A Range is not exact glyph ink. | browser | +| [V-PROOF-11](./ACCEPTANCE.md#browser-proof) | MUST | Account for shadow parts through supported test seams; do not introduce production shadow queries for acceptance. | review | +| [V-PROOF-12](./ACCEPTANCE.md#browser-proof) | SHOULD | Exercise affected reflow, wrapped translations, optional content and input paths. | | +| [V-PROOF-13](./ACCEPTANCE.md#browser-proof) | MUST | Do not use a static screenshot as proof of focus return, keyboard navigation, touch discovery, interruptibility or async settlement. | review | +| [V-PROOF-14](./ACCEPTANCE.md#browser-proof) | MUST | Record a continuous interaction when the defect unfolds over time. | recording | +| [V-PROOF-15](./ACCEPTANCE.md#browser-proof) | MUST | For a layout assertion, prove it rejects the relevant defect: move only label text while the frame stays fixed, introduce a doubled margin/phantom helper, or expand a title target into filler. | browser | +| [V-PROOF-16](./ACCEPTANCE.md#browser-proof) | MUST | Confirm the mutation won the cascade, observe failure, restore and observe success. | browser | +| [V-PROOF-17](./ACCEPTANCE.md#browser-proof) | SHOULD | Use mutations relevant to the task rather than repeating the entire example suite for a tiny fix. | | +| [V-PROOF-18](./ACCEPTANCE.md#browser-proof) | MUST | Verify raw data when changing presentation. | browser, unit | +| [V-PROOF-19](./ACCEPTANCE.md#browser-proof) | MUST | For code, copy the source payload, not highlighted DOM text: preserve tabs, blank lines, final newline and streamed additions. | unit | +| [V-PROOF-20](./ACCEPTANCE.md#browser-proof) | MUST | Verify clipboard success/failure, native selection and long-line scrolling independently. | browser | +| [V-PROOF-21](./ACCEPTANCE.md#browser-proof) | MUST | Document browser selection normalization without allowing it to alter the raw clipboard payload. | unit, review | +| [V-PROOF-22](./ACCEPTANCE.md#browser-proof) | MUST | Do not claim native browser zoom or text-size accessibility support from CSS zoom stress. | review | +| [V-PROOF-23](./ACCEPTANCE.md#browser-proof) | MUST | Say which paths remain unverified. | review | +| [V-MOTION-1](./ACCEPTANCE.md#review-motion-over-time) | MUST | For changed motion, inspect a continuous action at normal speed, then replay slowly where a transition breaks. | recording | +| [V-MOTION-2](./ACCEPTANCE.md#review-motion-over-time) | MUST | Include activation, intermediate state, completion and the reverse path; repeat or reverse input before completion. | recording | +| [V-MOTION-3](./ACCEPTANCE.md#review-motion-over-time) | MUST | Verify the final state, focus, event count and reading position independently of the animation. | browser | +| [V-MOTION-4](./ACCEPTANCE.md#review-motion-over-time) | MUST | Use a slow replay to diagnose discontinuity, not as a frame-rate benchmark. | review | +| [V-MOTION-5](./ACCEPTANCE.md#review-motion-over-time) | MUST | Give reduced motion its own visible-state check, not just a shorter recording. | browser | +| [V-MOTION-6](./ACCEPTANCE.md#review-motion-over-time) | SHOULD | Report a temporal defect as `element → input → observed frame/state → expected continuity`. | | +| [V-MOTION-7](./ACCEPTANCE.md#review-motion-over-time) | SHOULD | Use one representative real task across affected siblings to detect inconsistent motion language. | | +| [V-MOTION-8](./ACCEPTANCE.md#review-motion-over-time) | SHOULD | Check the full composition again after local fixes. | | +| [V-MOTION-9](./ACCEPTANCE.md#review-motion-over-time) | SHOULD | Preserve failed iterations as diagnostics, separate from accepted captures. | | +| [V-WALK-1](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | For a changed workflow or composition, start with a realistic goal expressed without naming the control to use. | | +| [V-WALK-2](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | Complete the goal from the actual entry point through result, recovery and return, as relevant. | | +| [V-WALK-3](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | Record the first choice, any ambiguous label, hesitation or backtracking, and whether the result makes the next step clear. | | +| [V-WALK-4](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | Use realistic content, including a representative long or failing case, rather than a polished empty state alone. | | +| [V-WALK-5](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | Fix task structure and content hierarchy before adding decorative effects. | | +| [V-WALK-6](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | Report the most consequential observed problems (up to three), with the state and evidence that show each one. | | +| [V-WALK-7](./ACCEPTANCE.md#walk-through-the-task) | MUST | Do not invent findings to fill a quota. | review | +| [V-WALK-8](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | Repeat the affected path after correction. | | +| [V-WALK-9](./ACCEPTANCE.md#walk-through-the-task) | MUST | Report an agent walkthrough as a defect search, not as a human usability study. | review | +| [V-WALK-10](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | When observing a real participant, let them attempt the goal before explaining the interface, and preserve that distinction in the results below. | | +| [V-FRESH-1](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Runtime: record the tested source revision/build and relevant dirty inputs or their fingerprint, including shared styles/components and incoming data when they affect the result. A Git HEAD alone is insufficient if the running build or working tree differs. | review | +| [V-FRESH-2](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Scope/environment: record affected view/group membership, route/state, viewport, theme, locale, browser and input or reduced-motion settings where relevant. | review | +| [V-FRESH-3](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Media: record capture/recording locations and content identity (for example SHA-256), tied to that runtime and state. | review | +| [V-FRESH-4](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Keep before and after distinct; never overwrite the only before image. | review | +| [V-FRESH-5](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Review: record what the automated check and agent inspection actually established, separate from any human decision and any unverified state. | review | +| [V-FRESH-6](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MAY | Use a short Markdown record; an existing manifest may supply these fields. | | +| [V-FRESH-7](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | SHOULD | Do not add a new evidence service or configuration framework for an ordinary change. | | +| [V-FRESH-8](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Keep hashes beside the files or in a referenced manifest so the claimed binding can be checked independently. | review | +| [V-FRESH-9](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Treat the affected review as stale after replacing an image, editing a recording, changing group membership or changing relevant runtime inputs. | review | +| [V-FRESH-10](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | After a shared style/component change, refresh every affected state, rebuild if needed, then inspect the new evidence. | browser, review | +| [V-FRESH-11](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Present no old capture as current proof. | review | +| [V-FRESH-12](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | When keeping runtime evidence across a docs-only edit or unrelated source change, record why its dependency scope is unaffected. | review | +| [V-REPORT-1](./ACCEPTANCE.md#report-authority-truthfully) | MUST | Keep these three results separate, even in a one-paragraph report. | review | +| [V-REPORT-2](./ACCEPTANCE.md#report-authority-truthfully) | MUST | Claim each result only from the source the table above names, and never for what the table says it does not establish. | review | +| [V-REPORT-3](./ACCEPTANCE.md#report-authority-truthfully) | SHOULD | Use explicit outcomes such as `automated: passed`, `agent review: passed with limits`, `human: not requested`. | | +| [V-REPORT-4](./ACCEPTANCE.md#report-authority-truthfully) | MUST | If human review was requested and is outstanding, say `pending`. | review | +| [V-REPORT-5](./ACCEPTANCE.md#report-authority-truthfully) | MUST | If a user rejected it, preserve `rejected` and the finding until that user reviews the updated scope. | review | +| [V-REPORT-6](./ACCEPTANCE.md#report-authority-truthfully) | MUST | An agent may report its correction and fresh checks, but never invents a user decision or clears a human rejection. | review | +| [V-REPORT-7](./ACCEPTANCE.md#report-authority-truthfully) | MUST | Avoid an unattributed “accepted”, “approved” or “验收通过”. | review | +| [V-REPORT-8](./ACCEPTANCE.md#report-authority-truthfully) | SHOULD | Finish and deliver authorized work while reporting `not requested`/`pending` truthfully. | | +| [V-REPORT-9](./ACCEPTANCE.md#report-authority-truthfully) | SHOULD | Ask only when the actual authorization or a necessary product decision is missing. | | +| [V-REPORT-10](./ACCEPTANCE.md#report-authority-truthfully) | MUST | Never label the agent's own visual check “user accepted”. | review | +| [V-EVAL-1](./ACCEPTANCE.md#regression-of-the-guidance-itself) | SHOULD | For opt-in or task-authorized live evaluation of skill/routing/acceptance changes, use the source-only [six agent scenarios](https://github.com/chaxus/ran/tree/main/plugins/ran/evals). | | +| [V-EVAL-2](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Freeze tasks, fixtures, tools and independent rubrics before live runs. | review | +| [V-EVAL-3](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Use fresh sessions with matching model/settings. | review | +| [V-EVAL-4](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Pin baseline and candidate guidance in separate workspaces. | review | +| [V-EVAL-5](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Retain actual agent work, prompts, outcome checks and evidence. | review | +| [V-EVAL-6](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Record baseline passes as controls and candidate failures as failures. | review | +| [V-EVAL-7](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Do not show workers the assessor's thresholds or repair patches. | review | +| [V-EVAL-8](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Claim behavior on those tasks only from live agent runs; prose matching, hand-authored answers or corrected test expectations are no evidence of it. | review | +| [V-EVAL-9](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Inspect visual judgment independently of mechanical checks. | review | +| [V-EVAL-10](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Report sample size, tools and uncovered conditions. | review | +| [V-EVAL-11](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Six successful tasks are no proof of reliability across models; do not claim it from them. | review | +| [W-RECIPE-1](./WORKFLOW_PATTERNS.md#optional-workflow-patterns) | SHOULD | For each selected recipe, establish what the application supports before choosing controls. | | +| [W-RECIPE-2](./WORKFLOW_PATTERNS.md#optional-workflow-patterns) | SHOULD | Keep the state for jobs, configuration, selected sources and revisions in their existing owners. Views read that state and dispatch commands instead of duplicating service clients and request flags in each panel. | | +| [W-QUEUE-1](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | SHOULD | Settle the contract for each state in the queue table above. | | +| [W-QUEUE-2](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | MUST | New work never silently replaces or cancels the active job. Without queue support, retain the new input and explain the constraint. | unit | +| [W-QUEUE-3](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | MUST | Editing a pending item in a paused queue neither dispatches it nor resumes the queue. | unit | +| [W-QUEUE-4](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | SHOULD | Summarize queued work in one line, such as “2 queued” or “Paused · 2 queued”. | | +| [W-QUEUE-5](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | SHOULD | Collapse queue details when unneeded; expand into rows rather than nested cards. | | +| [W-QUEUE-6](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | MUST | Each row has an identifiable item and offers only supported actions: Edit, Remove, or Run now. | browser | +| [W-QUEUE-7](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | SHOULD | Define whether Run now waits, preempts or runs concurrently. | | +| [W-QUEUE-8](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | SHOULD | Represent preparation within the row instead of adding a large panel full of disabled commands. | | +| [W-QUEUE-9](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | MUST | Queue editing preserves the current draft, and cancelling editing restores it. | unit | +| [W-QUEUE-10](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | MUST | Reconnection does not auto-dispatch queued work unless the user chose that policy. | unit | +| [W-QUEUE-11](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | SHOULD | Apply [general focus and cancel rules](./INTERACTION.md) when removing a row, collapsing the queue or stopping work. | | +| [W-SETTINGS-1](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | Saving configuration and checking availability are separate actions with separate outcomes, and each shows what it accomplished. | unit, browser | +| [W-SETTINGS-2](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Derive controls from capability and state, not a single `connected` boolean. | | +| [W-SETTINGS-3](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Make each state in the settings table above distinguishable to the user. | | +| [W-SETTINGS-4](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | Reconnecting an account whose authorization is required or expired keeps saved work. | unit | +| [W-SETTINGS-5](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | Invalidate verification when relevant endpoint, account or credential settings change. | unit | +| [W-SETTINGS-6](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Treat browser online/offline events as hints, not as proof that a particular endpoint works. | | +| [W-SETTINGS-7](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Reveal settings for the chosen path. A local conversion path may need cache/download/device controls; a remote path may need endpoint/account controls. | | +| [W-SETTINGS-8](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | Hide irrelevant fields **and their stale status messages**. | browser | +| [W-SETTINGS-9](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Settings headers, field groups, account rows and unavailable/empty states retain the named content and navigation spines from [DESIGN's alignment contract](./DESIGN.md#alignment-is-structure-not-polish). | | +| [W-SETTINGS-10](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Keep a title/menu command content-hugging; a separate track absorbs header filler. | | +| [W-SETTINGS-11](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Count nested panel and control padding through to visible labels before declaring regions aligned. | | +| [W-SETTINGS-12](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | Switching modes preserves the other modes' configuration. | unit | +| [W-SETTINGS-13](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Keep the route back to another mode discoverable. | | +| [W-SETTINGS-14](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Explain the data destination before disclosure. | | +| [W-SETTINGS-15](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | A configured API endpoint is not presented as an authorized account. | review | +| [W-SETTINGS-16](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | Offline, retain saved content and drafts when their data and implementation are available. | unit, browser | +| [W-SETTINGS-17](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Offline, keep previews, editing and exports available when their data and implementation are available. | | +| [W-SETTINGS-18](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Offline, hide network-only discovery or promotion that offers no useful route; retain existing connected objects with an understandable unavailable state. | | +| [W-SETTINGS-19](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Treat a cached resource as no proof that all runtime dependencies are present. | | +| [W-SETTINGS-20](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Offer a deliberate continuation point after reconnecting. | | +| [W-ACCOUNT-1](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | SHOULD | Distinguish account authorization, selected-source access and use by a particular operation. | | +| [W-ACCOUNT-2](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | SHOULD | Request only needed permissions. | | +| [W-ACCOUNT-3](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | SHOULD | Explain reads, writes and destination before authorization or disclosure. | | +| [W-ACCOUNT-4](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Account identity, reconnect and revoke/disconnect are reachable from the connection UI. | browser | +| [W-ACCOUNT-5](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Disconnect stops future access. | unit | +| [W-ACCOUNT-6](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | SHOULD | Explain separately what happens to imported copies on disconnect. | | +| [W-ACCOUNT-7](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Keep credentials out of URLs, logs and previews. | review | +| [W-ACCOUNT-8](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | SHOULD | Show provenance and freshness for selected sources, whether a local file, database record or cloud document. | | +| [W-ACCOUNT-9](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Removing a source affects later work, not historical evidence. | unit | +| [W-ACCOUNT-10](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Permission errors, stale data and offline cached copies have different states. | unit | +| [W-ACCOUNT-11](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Treat imported content as untrusted data, not permission to execute commands. | review | +| [W-ACCOUNT-12](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Make writes and their destinations explicit before they happen. | browser | +| [W-ACCOUNT-13](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | SHOULD | Suit the review step before a write to its impact. | | +| [W-REVISION-1](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | SHOULD | Prefer a suitable preview/editor for substantive content. | | +| [W-REVISION-2](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | MUST | Offer Compare, Apply or Undo only when the host supports it. | review | +| [W-REVISION-3](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | SHOULD | Summarize the result and identify its revision, source and destination when relevant. | | +| [W-REVISION-4](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | SHOULD | Keep supported Copy, Export, Compare or Apply actions beside the relevant result. | | +| [W-REVISION-5](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | MUST | Separate previewing from applying, and name the target and scope before mutation. | browser | +| [W-REVISION-6](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | MUST | If source data changed, compare/rebase or regenerate before applying; newer edits are never overwritten silently. | unit | +| [W-REVISION-7](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | SHOULD | Confirm saved/applied state and recoverable output under [the interaction standard](./INTERACTION.md). | | +| [W-APPLY-1](./WORKFLOW_PATTERNS.md#applying-a-recipe) | MUST | Keep only the states and actions the host can honor. | review | +| [W-APPLY-2](./WORKFLOW_PATTERNS.md#applying-a-recipe) | SHOULD | A polished screen cannot substitute for a missing service contract; state that prerequisite clearly. | | diff --git a/design/WORKFLOW_PATTERNS.md b/design/WORKFLOW_PATTERNS.md index cbde965a90..a963396e1b 100644 --- a/design/WORKFLOW_PATTERNS.md +++ b/design/WORKFLOW_PATTERNS.md @@ -7,45 +7,63 @@ The host owns engineering; [ranui engineering](https://github.com/chaxus/ran/blo is an optional library adapter. These are host application decisions, not additional component APIs, storage guarantees or bundled services. -For each selected recipe, establish what the application supports before choosing controls. Keep -the state for jobs, configuration, selected sources and revisions in their existing owners. Views -read that state and dispatch commands instead of duplicating service clients and request flags -in each panel. +Each rule carries an ID and a level. **MUST** is a defect when broken and names its proof; +**SHOULD** is the default, and a deviation states its reason for that case; **MAY** is optional. +The [rule index](./RULES.md) lists every graded rule with its proof. + +- **W-RECIPE-1 · SHOULD** For each selected recipe, establish what the application supports before + choosing controls. +- **W-RECIPE-2 · SHOULD** Keep the state for jobs, configuration, selected sources and revisions in + their existing owners. Views read that state and dispatch commands instead of duplicating service + clients and request flags in each panel. ## 1. Batch work and queues **Use when:** people benefit from staging another import, upload or export while work runs. **Host contract:** dispatch, preparation, concurrency/preemption, cancellation acknowledgment, -partial writes, duplicate prevention and persistence. Without queue support, retain new input -and explain the constraint instead of silently replacing the active job. - -| State | Main action | Contract to settle | -| --------------------- | ---------------------------- | ------------------------------------------------------ | -| Ready | Upload files / Export report | Scope, destination and duplicate-submit prevention | -| Running | Cancel, if supported | What stops, what remains and whether a write committed | -| Running with new work | Add to queue, if supported | Never silently replace or cancel the active job | -| Queue paused | Resume queue | Editing a pending item does not dispatch or resume it | -| Failed | Retry / Restore inputs | Use the distinct outcomes in INTERACTION §5 | - -Summarize queued work in one line, such as “2 queued” or “Paused · 2 queued”. Collapse details when -unneeded; expand into rows rather than nested cards. Each row has an identifiable item and only -supported actions: Edit, Remove, or Run now. Define whether Run now waits, preempts or runs concurrently. -Represent preparation within the row instead of adding a large panel full of disabled commands. - -Queue editing preserves the current draft; cancelling editing restores it. Reconnection does not -auto-dispatch unless the user chose that policy. Apply [general focus and cancel rules](./INTERACTION.md) -when removing a row, collapsing the queue or stopping work. +partial writes, duplicate prevention and persistence. + +| State | Main action | Contract to settle | +| --------------------- | ---------------------------- | ----------------------------------------------------------- | +| Ready | Upload files / Export report | Scope, destination and duplicate-submit prevention | +| Running | Cancel, if supported | What stops, what remains and whether a write committed | +| Running with new work | Add to queue, if supported | The active job keeps running; new input is kept (W-QUEUE-2) | +| Queue paused | Resume queue | Editing a pending item does not dispatch or resume it | +| Failed | Retry / Restore inputs | Use the distinct outcomes in INTERACTION §5 | + +- **W-QUEUE-1 · SHOULD** Settle the contract for each state in the queue table above. +- **W-QUEUE-2 · MUST** New work never silently replaces or cancels the active job. Without queue + support, retain the new input and explain the constraint. _Proof: unit_ +- **W-QUEUE-3 · MUST** Editing a pending item in a paused queue neither dispatches it nor resumes the + queue. _Proof: unit_ +- **W-QUEUE-4 · SHOULD** Summarize queued work in one line, such as “2 queued” or “Paused · 2 queued”. +- **W-QUEUE-5 · SHOULD** Collapse queue details when unneeded; expand into rows rather than nested cards. +- **W-QUEUE-6 · MUST** Each row has an identifiable item and offers only supported actions: Edit, Remove, + or Run now. _Proof: browser_ +- **W-QUEUE-7 · SHOULD** Define whether Run now waits, preempts or runs concurrently. +- **W-QUEUE-8 · SHOULD** Represent preparation within the row instead of adding a large panel full of + disabled commands. +- **W-QUEUE-9 · MUST** Queue editing preserves the current draft, and cancelling editing restores it. + _Proof: unit_ +- **W-QUEUE-10 · MUST** Reconnection does not auto-dispatch queued work unless the user chose that + policy. _Proof: unit_ +- **W-QUEUE-11 · SHOULD** Apply [general focus and cancel rules](./INTERACTION.md) when removing a row, + collapsing the queue or stopping work. ## 2. Capability-aware settings and offline work **Use when:** a task offers distinct providers, modes, devices or network-dependent capabilities. **Host contract:** configuration storage, a real verification operation, reachability and local -runtime dependencies. Saving configuration and checking availability must be separate outcomes. +runtime dependencies. + +- **W-SETTINGS-1 · MUST** Saving configuration and checking availability are separate actions with + separate outcomes, and each shows what it accomplished. _Proof: unit, browser_ +- **W-SETTINGS-2 · SHOULD** Derive controls from capability and state, not a single `connected` boolean. -Derive controls from capability and state, not a single `connected` boolean. A remote storage account, -a database service or a local converter can be configured without currently being usable. +A remote storage account, a database service or a local converter can be configured without +currently being usable. -| State | What the user should understand | +| State | What the user understands | | ------------------------------ | --------------------------------------------------------------------- | | Unconfigured | A required choice or credential is missing | | Configured, unverified | Settings are saved; availability has not been established | @@ -54,22 +72,37 @@ a database service or a local converter can be configured without currently bein | Service unreachable | Saved configuration does not prove present reachability | | Device offline | Network operations are unavailable; available local work may continue | -Invalidate verification when relevant endpoint, account or credential settings change. Browser -online/offline events are hints, not proof that a particular endpoint works. Separate saving from -testing configuration and show what each action accomplished. - -Reveal settings for the chosen path. A local conversion path may need cache/download/device controls; -a remote path may need endpoint/account controls. Hide irrelevant fields **and their stale status -messages**. Settings headers, field groups, account rows and unavailable/empty states retain the -named content and navigation spines from [DESIGN's alignment contract](./DESIGN.md#alignment-is-structure-not-polish). -Keep a title/menu command content-hugging; a separate track absorbs header filler. Count nested -panel and control padding through to visible labels before declaring regions aligned. Preserve other modes' configuration and keep the route back discoverable. Explain the -data destination before disclosure; do not confuse an API endpoint with an authorized account. - -Offline, retain saved content, drafts, previews, editing and exports when their data and implementation -are available. Hide network-only discovery or promotion that offers no useful route; retain existing -connected objects with an understandable unavailable state. A cached resource is not proof that all -runtime dependencies are present. Offer a deliberate continuation point after reconnecting. +- **W-SETTINGS-3 · SHOULD** Make each state in the settings table above distinguishable to the user. +- **W-SETTINGS-4 · MUST** Reconnecting an account whose authorization is required or expired keeps saved + work. _Proof: unit_ +- **W-SETTINGS-5 · MUST** Invalidate verification when relevant endpoint, account or credential settings + change. _Proof: unit_ +- **W-SETTINGS-6 · SHOULD** Treat browser online/offline events as hints, not as proof that a particular + endpoint works. +- **W-SETTINGS-7 · SHOULD** Reveal settings for the chosen path. A local conversion path may need + cache/download/device controls; a remote path may need endpoint/account controls. +- **W-SETTINGS-8 · MUST** Hide irrelevant fields **and their stale status messages**. _Proof: browser_ +- **W-SETTINGS-9 · SHOULD** Settings headers, field groups, account rows and unavailable/empty states + retain the named content and navigation spines from + [DESIGN's alignment contract](./DESIGN.md#alignment-is-structure-not-polish). +- **W-SETTINGS-10 · SHOULD** Keep a title/menu command content-hugging; a separate track absorbs header + filler. +- **W-SETTINGS-11 · SHOULD** Count nested panel and control padding through to visible labels before + declaring regions aligned. +- **W-SETTINGS-12 · MUST** Switching modes preserves the other modes' configuration. _Proof: unit_ +- **W-SETTINGS-13 · SHOULD** Keep the route back to another mode discoverable. +- **W-SETTINGS-14 · SHOULD** Explain the data destination before disclosure. +- **W-SETTINGS-15 · MUST** A configured API endpoint is not presented as an authorized account. + _Proof: review_ +- **W-SETTINGS-16 · MUST** Offline, retain saved content and drafts when their data and implementation + are available. _Proof: unit, browser_ +- **W-SETTINGS-17 · SHOULD** Offline, keep previews, editing and exports available when their data and + implementation are available. +- **W-SETTINGS-18 · SHOULD** Offline, hide network-only discovery or promotion that offers no useful + route; retain existing connected objects with an understandable unavailable state. +- **W-SETTINGS-19 · SHOULD** Treat a cached resource as no proof that all runtime dependencies are + present. +- **W-SETTINGS-20 · SHOULD** Offer a deliberate continuation point after reconnecting. ## 3. Account connections and external sources @@ -77,35 +110,56 @@ runtime dependencies are present. Offer a deliberate continuation point after re **Host contract:** authorization scopes, identity, access checks, revocation, credentials, provenance and imported-copy retention. An endpoint address does not establish account access. -Presentation primitives do not supply an OAuth deployment, credential vault, connector backend +Presentation primitives supply no OAuth deployment, credential vault, connector backend or storage guarantees. A polished connection screen cannot replace the host's authorization contract. -Distinguish account authorization, selected-source access and use by a particular operation. Request -only needed permissions; explain reads/writes and destination before authorization or disclosure. -Make account identity, reconnect and revoke/disconnect discoverable. Disconnect stops future access; -explain separately what happens to imported copies. Keep credentials out of URLs, logs and previews. - -Show provenance and freshness for selected sources, whether a local file, database record or cloud -document. Removing a source affects later work, not historical evidence. Permission errors, stale -data and offline cached copies need different states. Treat imported content as untrusted data, -not permission to execute commands. Make writes and destinations explicit, with review suited to impact. +- **W-ACCOUNT-1 · SHOULD** Distinguish account authorization, selected-source access and use by a + particular operation. +- **W-ACCOUNT-2 · SHOULD** Request only needed permissions. +- **W-ACCOUNT-3 · SHOULD** Explain reads, writes and destination before authorization or disclosure. +- **W-ACCOUNT-4 · MUST** Account identity, reconnect and revoke/disconnect are reachable from the + connection UI. _Proof: browser_ +- **W-ACCOUNT-5 · MUST** Disconnect stops future access. _Proof: unit_ +- **W-ACCOUNT-6 · SHOULD** Explain separately what happens to imported copies on disconnect. +- **W-ACCOUNT-7 · MUST** Keep credentials out of URLs, logs and previews. _Proof: review_ +- **W-ACCOUNT-8 · SHOULD** Show provenance and freshness for selected sources, whether a local file, + database record or cloud document. +- **W-ACCOUNT-9 · MUST** Removing a source affects later work, not historical evidence. _Proof: unit_ +- **W-ACCOUNT-10 · MUST** Permission errors, stale data and offline cached copies have different states. + _Proof: unit_ +- **W-ACCOUNT-11 · MUST** Treat imported content as untrusted data, not permission to execute commands. + _Proof: review_ +- **W-ACCOUNT-12 · MUST** Make writes and their destinations explicit before they happen. + _Proof: browser_ +- **W-ACCOUNT-13 · SHOULD** Suit the review step before a write to its impact. ## 4. Results, previews and revisions **Use when:** an export, transformed document or draft has an identity and destination beyond a -transient status message. Prefer a suitable preview/editor for substantive content. +transient status message. **Host contract:** result/revision identity, source version, target scope, commit acknowledgment, -conflict handling and any reversible history. Do not offer Compare, Apply or Undo without support. - -1. Summarize the result and identify its revision, source and destination when relevant. -2. Keep supported Copy, Export, Compare or Apply actions beside the relevant result. -3. Separate previewing from applying. Name the target and scope before mutation. -4. If source data changed, compare/rebase or regenerate before applying; never overwrite newer edits silently. -5. Confirm saved/applied state and recoverable output under [the interaction standard](./INTERACTION.md). +conflict handling and any reversible history. + +- **W-REVISION-1 · SHOULD** Prefer a suitable preview/editor for substantive content. +- **W-REVISION-2 · MUST** Offer Compare, Apply or Undo only when the host supports it. _Proof: review_ +- **W-REVISION-3 · SHOULD** Summarize the result and identify its revision, source and destination when + relevant. +- **W-REVISION-4 · SHOULD** Keep supported Copy, Export, Compare or Apply actions beside the relevant + result. +- **W-REVISION-5 · MUST** Separate previewing from applying, and name the target and scope before + mutation. _Proof: browser_ +- **W-REVISION-6 · MUST** If source data changed, compare/rebase or regenerate before applying; newer + edits are never overwritten silently. _Proof: unit_ +- **W-REVISION-7 · SHOULD** Confirm saved/applied state and recoverable output under + [the interaction standard](./INTERACTION.md). ## Applying a recipe -Keep only the states and actions the host can honor. Verify its relevant transitions: paused work -stays paused, changed configuration invalidates verification, source removal affects future work, -and stale revisions cannot overwrite new edits. Include interrupted/offline work when relevant. -A polished screen cannot substitute for a missing service contract; state that prerequisite clearly. +- **W-APPLY-1 · MUST** Keep only the states and actions the host can honor. _Proof: review_ +- **W-APPLY-2 · SHOULD** A polished screen cannot substitute for a missing service contract; state that + prerequisite clearly. + +The key transitions already carry MUST rules with unit proof: paused work stays paused (W-QUEUE-3), +changed configuration invalidates verification (W-SETTINGS-5), source removal affects only future +work (W-ACCOUNT-9), and stale revisions cannot overwrite new edits (W-REVISION-6). Their tests cover +interrupted and offline work where relevant. diff --git a/package.json b/package.json index 89ef2cd760..e910384b77 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "lint:prettier": "prettier --check --cache .", "format": "prettier --write --cache .", "cpd": "jscpd ./packages", - "verify:docs": "node bin/sync-design-docs.ts --check && pnpm verify:agent-plugin && pnpm -F ranui doc:api:check && pnpm -F ranui doc:style:check && pnpm -F ranui doc:changelog:check && pnpm -F ranuts doc:api:check && pnpm -F docs check:langs", + "verify:docs": "node bin/design-rules.ts --check && node bin/sync-design-docs.ts --check && pnpm verify:agent-plugin && pnpm -F ranui doc:api:check && pnpm -F ranui doc:style:check && pnpm -F ranui doc:changelog:check && pnpm -F ranuts doc:api:check && pnpm -F docs check:langs", "verify:design": "node bin/run-checks.ts verify:design", "verify:packages": "node bin/verify-packages.ts", "verify:consumers": "pnpm -F ranuts verify:package && node bin/verify-package-consumers.ts", @@ -31,7 +31,7 @@ "release:publish": "node bin/release.ts publish", "verify:release": "node bin/verify-release-install.ts", "typecheck:tooling": "tsc --project tsconfig.tooling.json", - "sync:design": "node bin/sync-design-docs.ts", + "sync:design": "node bin/design-rules.ts && node bin/sync-design-docs.ts", "sync:agent-plugin": "node bin/sync-agent-plugin.ts", "verify:agent-plugin": "node bin/sync-agent-plugin.ts --check" }, diff --git a/packages/ranui/docs/ACCEPTANCE.md b/packages/ranui/docs/ACCEPTANCE.md index caa2c8f28a..cb646e81c0 100644 --- a/packages/ranui/docs/ACCEPTANCE.md +++ b/packages/ranui/docs/ACCEPTANCE.md @@ -6,130 +6,204 @@ Use this contract for a changed visual or interaction outcome. [DESIGN](./DESIGN appearance and composition; [INTERACTION](./INTERACTION.md) owns observable behavior. The [interface index](./INTERFACE_GUIDE.md) routes the task. Read only applicable sections. +Each rule carries an ID and a level. **MUST** is a defect when broken and names its proof; +**SHOULD** is the default, and a deviation states its reason for that case; **MAY** is optional. +The [rule index](./RULES.md) lists every graded rule with its proof. + ## Agree on the observable result -Before editing, settle the [design brief](./DESIGN.md#style-coherence-across-components-and-pages), -and state the affected task, views/states and independent expectations in that same note. Name the relevant owner, supported public API and host capability limits. Select -widths, themes, languages and input paths from the actual change; do not invent unrelated work. -For composition, state content/text/icon spines, intentional navigation columns, hit bounds -and vertical relationships. Do not derive the expected value from the implementation under test. +- **V-AGREE-1 · SHOULD** Before editing, settle the + [design brief](./DESIGN.md#style-coherence-across-components-and-pages), and state the affected + task, views/states and independent expectations in that same note. +- **V-AGREE-2 · SHOULD** Name the relevant owner, supported public API and host capability limits. +- **V-AGREE-3 · SHOULD** Select widths, themes, languages and input paths from the actual change, + without inventing unrelated work. +- **V-AGREE-4 · SHOULD** For composition, state content/text/icon spines, intentional navigation + columns, hit bounds and vertical relationships. +- **V-AGREE-5 · MUST** Do not derive the expected value from the implementation under test. + _Proof: review_ +- **V-AGREE-6 · SHOULD** Review in order: task/hierarchy, static composition, reachable combined + states, then motion. +- **V-AGREE-7 · SHOULD** Do not add animation to compensate for unresolved proportions or unclear + navigation. +- **V-AGREE-8 · SHOULD** When a review changes the intended design, update the existing brief first + and identify the evidence that prompted it. -Review in order: task/hierarchy, static composition, reachable combined states, then motion. -Do not add animation to compensate for unresolved proportions or unclear navigation. When a review -changes the intended design, update the existing brief first and identify the evidence that prompted it. This is a decision record, not a new approval ceremony for work the user already authorized. -Keep one rule owner. An API/token check verifies source contracts; an image comparison verifies -pixels; an agent run verifies whether guidance was followed. None substitutes for the others. -A fixture demonstrates its own result, not acceptance of a host application it did not render. +An API/token check verifies source contracts; an image comparison verifies pixels; an agent run +verifies whether guidance was followed. + +- **V-AGREE-9 · SHOULD** Keep one rule owner. +- **V-AGREE-10 · MUST** Do not let one kind of check substitute for another: an API/token check, an + image comparison and an agent run each establish only their own result. _Proof: review_ +- **V-AGREE-11 · MUST** A fixture demonstrates its own result, not acceptance of a host application + it did not render; do not claim it as host acceptance. _Proof: review_ ## Browser proof -Capture the actual running surface before the fix when reproducing a visible defect. After the -fix, capture the same relevant state and inspect the full pane with realistic copy and surrounding -chrome. A mockup, source diff, changed assertion or screenshot of an unrelated build is not proof. -Inspect the composition as well as the link count: relabeling a redundant return as a breadcrumb -can still repeat the same context and add an unnecessary header row. Trace ancestor reachability -through the parent before deciding another visible shortcut is needed. - -- Wait for component registration, pending content, fonts and rendered frames before measuring. - Fail on missing/zero-sized targets. Record any subpixel tolerance; keep it below a full CSS pixel - for exact spines (the source example uses 0.5px). - For live overlays, verify that capture preserves the viewport and includes the full trigger/panel; - a full-page screenshot can invalidate a crop by causing responsive reflow or portal repositioning. -- Measure the defect's layer: wrapper, inner content, rendered text Range, icon viewport and - actual pointer target are different. A Range is not exact glyph ink. Account for shadow parts - through supported test seams; do not introduce production shadow queries for acceptance. -- Exercise affected reflow, wrapped translations, optional content and input paths. A static - screenshot cannot prove focus return, keyboard navigation, touch discovery, interruptibility - or async settlement. Record a continuous interaction when the defect unfolds over time. -- For a layout assertion, prove it rejects the relevant defect: move only label text while the - frame stays fixed, introduce a doubled margin/phantom helper, or expand a title target into - filler. Confirm the mutation won the cascade, observe failure, restore and observe success. - Use changes relevant to the task; do not repeat the entire example suite for a tiny fix. -- Verify raw data when changing presentation. For code, copy the source payload, not highlighted - DOM text: preserve tabs, blank lines, final newline and streamed additions. Verify clipboard - success/failure, native selection and long-line scrolling independently. Document browser - selection normalization without allowing it to alter the raw clipboard payload. +- **V-PROOF-1 · MUST** When reproducing a visible defect, capture the actual running surface before + the fix. _Proof: browser_ +- **V-PROOF-2 · MUST** After the fix, capture the same relevant state and inspect the full pane with + realistic copy and surrounding chrome. _Proof: browser_ +- **V-PROOF-3 · MUST** Do not offer a mockup, source diff, changed assertion or screenshot of an + unrelated build as proof. _Proof: review_ +- **V-PROOF-4 · SHOULD** Inspect the composition as well as the link count: relabeling a redundant + return as a breadcrumb can still repeat the same context and add an unnecessary header row. +- **V-PROOF-5 · SHOULD** Trace ancestor reachability through the parent before deciding another + visible shortcut is needed. +- **V-PROOF-6 · MUST** Wait for component registration, pending content, fonts and rendered frames + before measuring. _Proof: browser_ +- **V-PROOF-7 · MUST** Fail on missing or zero-sized targets. _Proof: browser_ +- **V-PROOF-8 · MUST** Record any subpixel tolerance, and keep it below a full CSS pixel for exact + spines (the source example uses 0.5px). _Proof: review_ +- **V-PROOF-9 · MUST** For live overlays, verify that capture preserves the viewport and includes the + full trigger/panel; a full-page screenshot can invalidate a crop by causing responsive reflow or + portal repositioning. _Proof: browser_ +- **V-PROOF-10 · MUST** Measure the defect's layer: wrapper, inner content, rendered text Range, icon + viewport and actual pointer target are different. A Range is not exact glyph ink. + _Proof: browser_ +- **V-PROOF-11 · MUST** Account for shadow parts through supported test seams; do not introduce + production shadow queries for acceptance. _Proof: review_ +- **V-PROOF-12 · SHOULD** Exercise affected reflow, wrapped translations, optional content and input + paths. +- **V-PROOF-13 · MUST** Do not use a static screenshot as proof of focus return, keyboard navigation, + touch discovery, interruptibility or async settlement. _Proof: review_ +- **V-PROOF-14 · MUST** Record a continuous interaction when the defect unfolds over time. + _Proof: recording_ +- **V-PROOF-15 · MUST** For a layout assertion, prove it rejects the relevant defect: move only label + text while the frame stays fixed, introduce a doubled margin/phantom helper, or expand a title + target into filler. _Proof: browser_ +- **V-PROOF-16 · MUST** Confirm the mutation won the cascade, observe failure, restore and observe + success. _Proof: browser_ +- **V-PROOF-17 · SHOULD** Use mutations relevant to the task rather than repeating the entire example + suite for a tiny fix. +- **V-PROOF-18 · MUST** Verify raw data when changing presentation. _Proof: browser, unit_ +- **V-PROOF-19 · MUST** For code, copy the source payload, not highlighted DOM text: preserve tabs, + blank lines, final newline and streamed additions. _Proof: unit_ +- **V-PROOF-20 · MUST** Verify clipboard success/failure, native selection and long-line scrolling + independently. _Proof: browser_ +- **V-PROOF-21 · MUST** Document browser selection normalization without allowing it to alter the raw + clipboard payload. _Proof: unit, review_ The source-only [layout example](https://github.com/chaxus/ran/blob/main/packages/ranui/test/e2e/layout-contract.md) contains runnable commands and geometry utilities. Consumers adapt its relationships to their -host; these helpers are not new ranui exports. CSS zoom stress does not establish native browser -zoom or text-size accessibility support. Say which paths remain unverified. +host; these helpers are not new ranui exports. -### Review motion over time +- **V-PROOF-22 · MUST** Do not claim native browser zoom or text-size accessibility support from CSS + zoom stress. _Proof: review_ +- **V-PROOF-23 · MUST** Say which paths remain unverified. _Proof: review_ -For changed motion, inspect a continuous action at normal speed, then replay slowly where a -transition breaks. Include activation, intermediate state, completion and the reverse path; repeat -or reverse input before completion. Verify the final state, focus, event count and reading position -independently of the animation. A slow replay helps diagnose discontinuity; it is not a frame-rate -benchmark. Reduced motion needs its own visible-state check, not just a shorter recording. +### Review motion over time -Report a temporal defect as `element → input → observed frame/state → expected continuity`. -Use one representative real task across affected siblings to detect inconsistent motion language; -check the full composition again after local fixes. Preserve failed iterations as diagnostics, -separate from accepted captures. These checks complement the existing evidence binding below. +- **V-MOTION-1 · MUST** For changed motion, inspect a continuous action at normal speed, then replay + slowly where a transition breaks. _Proof: recording_ +- **V-MOTION-2 · MUST** Include activation, intermediate state, completion and the reverse path; + repeat or reverse input before completion. _Proof: recording_ +- **V-MOTION-3 · MUST** Verify the final state, focus, event count and reading position independently + of the animation. _Proof: browser_ +- **V-MOTION-4 · MUST** Use a slow replay to diagnose discontinuity, not as a frame-rate benchmark. + _Proof: review_ +- **V-MOTION-5 · MUST** Give reduced motion its own visible-state check, not just a shorter recording. + _Proof: browser_ +- **V-MOTION-6 · SHOULD** Report a temporal defect as + `element → input → observed frame/state → expected continuity`. +- **V-MOTION-7 · SHOULD** Use one representative real task across affected siblings to detect + inconsistent motion language. +- **V-MOTION-8 · SHOULD** Check the full composition again after local fixes. +- **V-MOTION-9 · SHOULD** Preserve failed iterations as diagnostics, separate from accepted captures. + +These checks complement the existing evidence binding below. ## Walk through the task -For a changed workflow or composition, start with a realistic goal expressed without naming the -control to use. Complete it from the actual entry point through result, recovery and return, as -relevant. Record the first choice, any ambiguous label, hesitation or backtracking, and whether -the result makes the next step clear. A shorter click path is not useful if the choices are unclear. -Use realistic content, including a representative long or failing case, rather than a polished -empty state alone. Fix task structure and content hierarchy before adding decorative effects. - -Report the most consequential observed problems (up to three), with the state and evidence that -show each one; do not invent findings to fill a quota. Repeat the affected path after correction. -An agent walkthrough can expose defects but is not a human usability study. When observing a real -participant, let them attempt the goal before explaining the interface; preserve that distinction -in the results below. +- **V-WALK-1 · SHOULD** For a changed workflow or composition, start with a realistic goal expressed + without naming the control to use. +- **V-WALK-2 · SHOULD** Complete the goal from the actual entry point through result, recovery and + return, as relevant. +- **V-WALK-3 · SHOULD** Record the first choice, any ambiguous label, hesitation or backtracking, and + whether the result makes the next step clear. + +A shorter click path is not useful if the choices are unclear. + +- **V-WALK-4 · SHOULD** Use realistic content, including a representative long or failing case, + rather than a polished empty state alone. +- **V-WALK-5 · SHOULD** Fix task structure and content hierarchy before adding decorative effects. +- **V-WALK-6 · SHOULD** Report the most consequential observed problems (up to three), with the state + and evidence that show each one. +- **V-WALK-7 · MUST** Do not invent findings to fill a quota. _Proof: review_ +- **V-WALK-8 · SHOULD** Repeat the affected path after correction. +- **V-WALK-9 · MUST** Report an agent walkthrough as a defect search, not as a human usability study. + _Proof: review_ +- **V-WALK-10 · SHOULD** When observing a real participant, let them attempt the goal before + explaining the interface, and preserve that distinction in the results below. ## Bind evidence to what actually ran -For each reviewed state, retain a compact record of: - -- **Runtime:** tested source revision/build and relevant dirty inputs or their fingerprint; - include shared styles/components and incoming data when they affect the result. A Git HEAD - alone is insufficient if the running build or working tree differs. -- **Scope/environment:** affected view/group membership, route/state, viewport, theme, locale, - browser and input or reduced-motion settings where relevant. -- **Media:** capture/recording locations and content identity (for example SHA-256), tied to - that runtime and state. Keep before and after distinct; never overwrite the only before image. -- **Review:** what the automated check and agent inspection actually established, separate - from any human decision and any unverified state. - -A short Markdown record is enough; an existing manifest may supply these fields. Do not add a -new evidence service or configuration framework for an ordinary change. Keep hashes beside the -files or in a referenced manifest so the claimed binding can be checked independently. - -**A review applies only to the evidence and runtime scope it inspected.** Replacing an image, -editing a recording, changing group membership or changing relevant runtime inputs makes the -affected review stale. After a shared style/component change, refresh every affected state, -rebuild if needed, then inspect the new evidence. Old captures may remain as history, not current -proof. A docs-only edit or unrelated source change does not invalidate identical runtime evidence; -record why its dependency scope is unaffected rather than recapturing everything by HEAD alone. +Each reviewed state gets a compact record with these fields: + +- **V-FRESH-1 · MUST** Runtime: record the tested source revision/build and relevant dirty inputs or + their fingerprint, including shared styles/components and incoming data when they affect the + result. A Git HEAD alone is insufficient if the running build or working tree differs. + _Proof: review_ +- **V-FRESH-2 · MUST** Scope/environment: record affected view/group membership, route/state, + viewport, theme, locale, browser and input or reduced-motion settings where relevant. + _Proof: review_ +- **V-FRESH-3 · MUST** Media: record capture/recording locations and content identity (for example + SHA-256), tied to that runtime and state. _Proof: review_ +- **V-FRESH-4 · MUST** Keep before and after distinct; never overwrite the only before image. + _Proof: review_ +- **V-FRESH-5 · MUST** Review: record what the automated check and agent inspection actually + established, separate from any human decision and any unverified state. _Proof: review_ +- **V-FRESH-6 · MAY** Use a short Markdown record; an existing manifest may supply these fields. +- **V-FRESH-7 · SHOULD** Do not add a new evidence service or configuration framework for an ordinary + change. +- **V-FRESH-8 · MUST** Keep hashes beside the files or in a referenced manifest so the claimed binding + can be checked independently. _Proof: review_ + +**A review applies only to the evidence and runtime scope it inspected.** + +- **V-FRESH-9 · MUST** Treat the affected review as stale after replacing an image, editing a + recording, changing group membership or changing relevant runtime inputs. _Proof: review_ +- **V-FRESH-10 · MUST** After a shared style/component change, refresh every affected state, rebuild + if needed, then inspect the new evidence. _Proof: browser, review_ +- **V-FRESH-11 · MUST** Present no old capture as current proof. _Proof: review_ +- **V-FRESH-12 · MUST** When keeping runtime evidence across a docs-only edit or unrelated source + change, record why its dependency scope is unaffected. _Proof: review_ + +Old captures may remain as history. A docs-only edit or unrelated source change leaves identical +runtime evidence valid, so recapturing everything by HEAD alone is unnecessary. ## Report authority truthfully -Keep these results separate, even in a one-paragraph report: - | Result | Who/what can establish it | What it does not establish | | ------------------- | ------------------------------------------------------------------------------- | ------------------------------------ | | Automated checks | Fresh command output or browser assertions for the declared runtime | Visual quality or a human decision | | Agent visual review | Agent inspected current captures and the affected interaction | User acceptance | | Human decision | An actual user's decision, with its message/action reference and evidence scope | Acceptance of later changed evidence | -Use explicit outcomes such as `automated: passed`, `agent review: passed with limits`, -`human: not requested`. If human review was requested and is outstanding, say `pending`; -if a user rejected it, preserve `rejected` and the finding until that user reviews the updated -scope. An agent may report its correction and fresh checks, but cannot invent a user decision -or clear a human rejection. Avoid an unattributed “accepted”, “approved” or “验收通过”. +- **V-REPORT-1 · MUST** Keep these three results separate, even in a one-paragraph report. + _Proof: review_ +- **V-REPORT-2 · MUST** Claim each result only from the source the table above names, and never for + what the table says it does not establish. _Proof: review_ +- **V-REPORT-3 · SHOULD** Use explicit outcomes such as `automated: passed`, + `agent review: passed with limits`, `human: not requested`. +- **V-REPORT-4 · MUST** If human review was requested and is outstanding, say `pending`. + _Proof: review_ +- **V-REPORT-5 · MUST** If a user rejected it, preserve `rejected` and the finding until that user + reviews the updated scope. _Proof: review_ +- **V-REPORT-6 · MUST** An agent may report its correction and fresh checks, but never invents a user + decision or clears a human rejection. _Proof: review_ +- **V-REPORT-7 · MUST** Avoid an unattributed “accepted”, “approved” or “验收通过”. _Proof: review_ User authorization controls whether to push, update a PR or take another external action. -This document adds no blanket human approval gate: finish and deliver authorized work while -reporting `not requested`/`pending` truthfully. Ask only when the actual authorization or a -necessary product decision is missing. Never label the agent's own visual check “user accepted”. +This document adds no blanket human approval gate. + +- **V-REPORT-8 · SHOULD** Finish and deliver authorized work while reporting + `not requested`/`pending` truthfully. +- **V-REPORT-9 · SHOULD** Ask only when the actual authorization or a necessary product decision is + missing. +- **V-REPORT-10 · MUST** Never label the agent's own visual check “user accepted”. _Proof: review_ A minimal delivery note can be: @@ -142,15 +216,27 @@ A minimal delivery note can be: ## Regression of the guidance itself -For opt-in or task-authorized live evaluation of skill/routing/acceptance changes, use the source-only -[six agent scenarios](https://github.com/chaxus/ran/tree/main/plugins/ran/evals). -Deterministic controls check the assessor without model calls; live runs require an available agent harness -and are not a mandatory gate for every prose edit. Freeze tasks, fixtures, tools and independent rubrics before live runs. Use fresh sessions with -matching model/settings; pin baseline and candidate guidance in separate workspaces. Retain -actual agent work, prompts, outcome checks and evidence. Record baseline passes as controls and -candidate failures as failures. Do not show workers the assessor's thresholds or repair patches. - -Seeded-defect rejection/restoration checks establish the assessor's sensitivity. Only live agent -runs establish behavior on those tasks; prose matching, hand-authored answers or corrected test -expectations do not. Inspect visual judgment independently of mechanical checks. Report sample -size, tools and uncovered conditions; six successful tasks do not prove reliability across models. +Deterministic controls check the assessor without model calls; live runs need an available agent +harness and are not a mandatory gate for every prose edit. + +- **V-EVAL-1 · SHOULD** For opt-in or task-authorized live evaluation of skill/routing/acceptance + changes, use the source-only + [six agent scenarios](https://github.com/chaxus/ran/tree/main/plugins/ran/evals). +- **V-EVAL-2 · MUST** Freeze tasks, fixtures, tools and independent rubrics before live runs. + _Proof: review_ +- **V-EVAL-3 · MUST** Use fresh sessions with matching model/settings. _Proof: review_ +- **V-EVAL-4 · MUST** Pin baseline and candidate guidance in separate workspaces. _Proof: review_ +- **V-EVAL-5 · MUST** Retain actual agent work, prompts, outcome checks and evidence. _Proof: review_ +- **V-EVAL-6 · MUST** Record baseline passes as controls and candidate failures as failures. + _Proof: review_ +- **V-EVAL-7 · MUST** Do not show workers the assessor's thresholds or repair patches. + _Proof: review_ + +Seeded-defect rejection/restoration checks establish the assessor's sensitivity. + +- **V-EVAL-8 · MUST** Claim behavior on those tasks only from live agent runs; prose matching, + hand-authored answers or corrected test expectations are no evidence of it. _Proof: review_ +- **V-EVAL-9 · MUST** Inspect visual judgment independently of mechanical checks. _Proof: review_ +- **V-EVAL-10 · MUST** Report sample size, tools and uncovered conditions. _Proof: review_ +- **V-EVAL-11 · MUST** Six successful tasks are no proof of reliability across models; do not claim + it from them. _Proof: review_ diff --git a/packages/ranui/docs/DESIGN.md b/packages/ranui/docs/DESIGN.md index 8ffa0ec7b0..9f224d47ab 100644 --- a/packages/ranui/docs/DESIGN.md +++ b/packages/ranui/docs/DESIGN.md @@ -6,32 +6,47 @@ Design methods for the host product and its existing technology. Read only the r sections through [the task index](./INTERFACE_GUIDE.md). Library tokens and API mechanics belong to their implementation adapter, not this portable method. +Each rule carries an ID and a level. **MUST** is a defect when broken and names its proof; +**SHOULD** is the default, and a deviation states its reason for that case; **MAY** is optional. +The [rule index](./RULES.md) lists every graded rule with its proof. + ## 1. Color — a state ladder, not a palette -Choose semantic roles for content, surfaces, boundaries, actions and status. Define default, -hover, active, disabled and focus states together. Resolve light/dark values through the host’s -authoritative tokens; verify contrast in the actual composition. Brand accents are contextual. +- **D-COLOR-1 · SHOULD** Choose semantic roles for content, surfaces, boundaries, actions and status. +- **D-COLOR-2 · SHOULD** Define default, hover, active, disabled and focus states together. +- **D-COLOR-3 · SHOULD** Resolve light/dark values through the host’s authoritative tokens, and verify + contrast in the actual composition. + +Brand accents are contextual. ## 2. Spacing — a limited, rhythmic scale -Use the host’s existing spacing scale. Tight gaps group related parts; larger gaps separate -tasks or regions. Establish inside padding before outside gaps. Do not copy a reference -product’s dimensions without evaluating content, density and input method. +- **D-SPACE-1 · SHOULD** Use the host’s existing spacing scale. +- **D-SPACE-2 · SHOULD** Use tight gaps to group related parts and larger gaps to separate tasks or + regions. +- **D-SPACE-3 · SHOULD** Establish inside padding before outside gaps. +- **D-SPACE-4 · SHOULD** Do not copy a reference product’s dimensions without evaluating content, density + and input method. ## 3. Typography — choose a role, not a size -Choose heading, label, body, action and code roles with coherent size, weight and line height. -Verify actual scripts, fallback metrics and long content. Typography establishes hierarchy -before decorative containers. Keep a single authority for each role. +- **D-TYPE-1 · SHOULD** Choose heading, label, body, action and code roles with coherent size, weight and + line height, and keep a single authority for each role. +- **D-TYPE-2 · SHOULD** Verify actual scripts, fallback metrics and long content. +- **D-TYPE-3 · SHOULD** Establish hierarchy with typography before decorative containers. ## 4. Radius & elevation -Coordinate nested corner radii with their real inset. Elevation explains a floating layer, -not importance; use the host’s stacking roles and verify overlays against surrounding chrome. +- **D-ELEV-1 · SHOULD** Coordinate nested corner radii with their real inset. +- **D-ELEV-2 · SHOULD** Use elevation to explain a floating layer, not importance, through the host’s + stacking roles, and verify overlays against surrounding chrome. ## 5. Motion — prefer none -Timing below is a starting heuristic. Use the host system’s motion tokens and verify frequency, continuity and reduced motion in the actual task. +Timing below is a starting heuristic. + +- **D-MOTION-1 · SHOULD** Use the host system’s motion tokens and verify frequency, continuity and reduced + motion in the actual task. | Duration | Use | | -------- | ----------------------------------------------------------------------- | @@ -40,136 +55,185 @@ Timing below is a starting heuristic. Use the host system’s motion tokens and | ~200ms | Menu/popover motion when it clarifies the layer change | | ~300ms | Dialog motion when orientation benefits from it | -**Principle:** purpose and frequency determine whether motion earns time. Repeated interaction defaults -to immediate feedback; a larger change is not automatically a reason to animate. Use existing tokens -for purposeful motion, keep it quick and interruptible, and respect `prefers-reduced-motion`. +**Principle:** purpose and frequency determine whether motion earns time. -### Functional motion contract +- **D-MOTION-2 · SHOULD** Default repeated interaction to immediate feedback; a larger change is not + automatically a reason to animate. +- **D-MOTION-3 · SHOULD** Use existing tokens for purposeful motion and keep it quick and interruptible. + Reduced motion is graded under D-MOTION-10 and D-ACCESS-5. -Specify the trigger, state change and destination before choosing an effect. Preserve unrelated -content, drafts and reading position. Sibling components with the same interaction meaning should -share a motion language; consistency does not require every component to animate. +### Functional motion contract -When motion is useful, allow a new intent to interrupt or reverse it from the current presentation, -without a jump, stale completion or duplicate activation. Keep feedback immediate; animation is not -an input lock. During insertion/removal, avoid visibly squeezing text or passing exiting controls -across live actions; coordinate opacity and layout so the result stays readable. Reduced motion -must retain a visible, understandable final state. A spring is optional and need not overshoot. +- **D-MOTION-4 · SHOULD** Specify the trigger, state change and destination before choosing an effect. +- **D-MOTION-5 · MUST** Motion preserves unrelated content, drafts and reading position. _Proof: recording_ +- **D-MOTION-6 · SHOULD** Give sibling components with the same interaction meaning a shared motion + language; consistency does not require every component to animate. +- **D-MOTION-7 · MUST** When motion is used, a new intent can interrupt or reverse it from the current + presentation, without a jump, stale completion or duplicate activation. _Proof: recording_ +- **D-MOTION-8 · MUST** Feedback stays immediate; animation is not an input lock. _Proof: recording_ +- **D-MOTION-9 · SHOULD** During insertion/removal, avoid visibly squeezing text or passing exiting controls + across live actions; coordinate opacity and layout so the result stays readable. +- **D-MOTION-10 · MUST** Reduced motion retains a visible, understandable final state. _Proof: browser_ +- **D-MOTION-11 · MAY** Use a spring; it need not overshoot. See the optional [component craft research](./REFERENCE_RESEARCH.md#component-craft-设计垂点--wxaddesign) for examples and limits, and [temporal acceptance](./ACCEPTANCE.md#review-motion-over-time) for proof. -Theme palettes should settle coherently. Avoid implicit palette fades or `transition: all`; -explicitly opt in only where the host intends and verifies a coordinated effect. -Do not replay decorative title entrances or animate factual counts on each return to a working -surface. Keep readable content and actual values available immediately. A loading demonstration -may move to demonstrate loading; a decorative status pulse must not imply live activity. +- **D-MOTION-12 · SHOULD** Let theme palettes settle coherently. Avoid implicit palette fades or + `transition: all`; opt in explicitly only where the host intends and verifies a coordinated effect. +- **D-MOTION-13 · MUST** Factual values and readable content are shown immediately on return to a working + surface, without animated counts. _Proof: browser_ +- **D-MOTION-16 · SHOULD** Do not replay decorative title entrances on each return to a working surface. +- **D-MOTION-14 · MAY** A loading demonstration may move to demonstrate loading. +- **D-MOTION-15 · MUST** A status pulse appears only while the activity is live. _Proof: review_ ## 6. Content — copy is part of the system -- **Buttons:** name the outcome. Include the object when context does not establish it: "Deploy project", "Delete member". In a clearly named object or dialog, "Save" or "Delete" is sufficient; avoid generic "OK". See [contextual wording](#words-are-part-of-the-interface). -- **Errors:** say **what happened**, then **how to fix it**. ✅ "Build failed: the bundle exceeds the size limit. Reduce it or raise the limit." ❌ "Operation failed, please try again." -- **Confirmations / toasts:** state the completed **change**. ✅ "Project deleted". ❌ "Operation successful". Show completion feedback only after the operation is confirmed. -- Be specific; every sentence should remove guesswork, not add it. +- **D-COPY-1 · SHOULD** Buttons name the outcome. Include the object when context does not establish it: + "Deploy project", "Delete member". In a clearly named object or dialog, "Save" or "Delete" is sufficient; + avoid generic "OK". See [contextual wording](#words-are-part-of-the-interface). +- **D-COPY-2 · SHOULD** Errors say **what happened**, then **how to fix it**. ✅ "Build failed: the bundle + exceeds the size limit. Reduce it or raise the limit." ❌ "Operation failed, please try again." +- **D-COPY-3 · SHOULD** Confirmations and toasts state the completed **change**. ✅ "Project deleted". + ❌ "Operation successful". +- **D-COPY-4 · MUST** Show completion feedback only after the operation is confirmed. _Proof: unit, browser_ +- **D-COPY-5 · SHOULD** Be specific; every sentence removes guesswork rather than adding it. ## 7. Accessibility — the system serves everyone -- Maintain sufficient text-to-background contrast (**WCAG AA**). -- **Never signal state with color alone** — pair it with an icon or text (e.g. ✓ / ✕ labels, not just green/red). -- Every interactive element has a **visible focus ring** appropriate to its surface. Never remove it for "cleanliness". -- **Full keyboard navigation** — nothing is mouse-only. -- Respect `prefers-reduced-motion` and `prefers-color-scheme`. +- **D-ACCESS-1 · MUST** Text keeps sufficient contrast with its background (**WCAG AA**). _Proof: browser_ +- **D-ACCESS-2 · MUST** Never signal state with color alone; pair it with an icon or text (e.g. ✓ / ✕ labels, + not just green/red). _Proof: review_ +- **D-ACCESS-3 · MUST** Every interactive element has a **visible focus ring** appropriate to its surface; + never remove it for "cleanliness". _Proof: browser_ +- **D-ACCESS-4 · MUST** **Full keyboard navigation**: nothing is mouse-only. _Proof: browser_ +- **D-ACCESS-5 · MUST** Respect `prefers-reduced-motion`. _Proof: browser_ +- **D-ACCESS-6 · MUST** When the host supports both schemes, the initial render follows + `prefers-color-scheme`. _Proof: browser_ ## 8. Input & viewport — mobile and desktop, both -- Support mouse, keyboard and touch at narrow and wide widths. Reflow controls instead of - hiding the only route to an action. A hover affordance needs a click/tap alternative. -- Drags use Pointer Events including cancellation; scope `touch-action` to the actual drag - surface and pair it with a real handler. Preserve ordinary scrolling elsewhere. -- Prefer content/viewport-relative bounds (`min()`, `max()`, `clamp()`) to arbitrary - breakpoints. Use existing host breakpoints or introduce a justified shared role only when repeated needs emerge. -- A measured position becomes stale after resize, container reflow or scroll. Exercise - resizing into a narrow layout, not only loading there. Overlay placement must track its - trigger while open. The host owns lifecycle mechanics; [ranui engineering](./CODING.md#3-state-and-ownership) is one implementation adapter. -- Test actual pointer/touch gestures, hover fallback and keyboard routes where affected; - checking two color schemes does not establish input parity. +- **D-INPUT-1 · MUST** Support mouse, keyboard and touch at narrow and wide widths. _Proof: browser_ +- **D-INPUT-2 · MUST** Reflow controls instead of hiding the only route to an action. _Proof: browser_ +- **D-INPUT-3 · MUST** A hover affordance has a click/tap alternative. _Proof: browser_ +- **D-INPUT-4 · MUST** Drags use Pointer Events, including cancellation. _Proof: browser_ +- **D-INPUT-5 · MUST** Scope `touch-action` to the actual drag surface and pair it with a real handler; + ordinary scrolling keeps working elsewhere. _Proof: browser_ +- **D-INPUT-6 · SHOULD** Prefer content/viewport-relative bounds (`min()`, `max()`, `clamp()`) to arbitrary + breakpoints. +- **D-INPUT-7 · SHOULD** Use existing host breakpoints, and introduce a justified shared role only when + repeated needs emerge. + +A measured position becomes stale after resize, container reflow or scroll. + +- **D-INPUT-8 · SHOULD** Exercise resizing into a narrow layout, not only loading there. +- **D-INPUT-9 · MUST** Overlay placement tracks its trigger while open. _Proof: browser_ + +The host owns lifecycle mechanics; [ranui engineering](./CODING.md#3-state-and-ownership) is one implementation adapter. + +- **D-INPUT-10 · SHOULD** Test actual pointer/touch gestures, hover fallback and keyboard routes where + affected; checking two color schemes does not establish input parity. ## 9. Composition — the page, not the component -Choose the page structure in §10 first; then compose its components with these rules. +- **D-COMP-1 · SHOULD** Choose the page structure in §10 first; then compose its components with these + rules. ### Style coherence across components and pages -Before implementation, record a short design brief in the existing task note or product design -owner. Use the existing system; a small fix only needs the affected decisions. "Premium", "modern" -or "like Claude" is a direction to investigate, not an implementable specification. +- **D-COMP-2 · SHOULD** Before implementation, record a short design brief in the existing task note or + product design owner. Use the existing system; a small fix only needs the affected decisions. + The brief settles each applicable decision in the table below (D-COMP-2). + +"Premium", "modern" or "like Claude" is a direction to investigate, not an implementable specification. -| Decision | What the brief must settle | +| Decision | What the brief settles | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------ | | Task and hierarchy | The real content, primary action and first reading focus; what can recede or disappear. | | Proportion | Type roles/line heights, text measure, control density, icon weight and alignment spines using host tokens. | | Surface language | Which regions need containment, nested radius/inset relationships, semantic accent and elevation roles. | | State composition | A representative default plus long content, pending/failure and narrow input paths; include combined states. | | Motion | The state change it explains, trigger and frequency, interruption/reverse behavior and reduced-motion result; "none" is valid. | -| Reference decision | Where a reference is used, link its source, the principle adopted and the adaptation required by the host task. | +| Reference decision | Where a reference is used, link its source, the principle adopted and the adaptation the host task needs. | | Acceptance | The observable result and actual views/recordings that can demonstrate or reject it. | -Reuse the brief across sibling components and docs chrome; change emphasis according to the task. -Uniformity does not mean equal-sized controls or boxed sections everywhere. An expressive demo -must remain visibly distinct from its documentation chrome. Update the brief before changing the -visual direction; do not silently rewrite the standard to justify an implementation that failed review. +- **D-COMP-4 · SHOULD** Reuse the brief across sibling components and docs chrome; change emphasis + according to the task. -Compare a representative composed still with different real content before animating it. Then -inspect the state change and return path at normal speed. A shared color alone cannot reconcile -inconsistent proportions, icon weight, containment or motion. When an example looks imported from -another product, fix that relationship at its owning token/component/style rather than adding a -page-specific patch. Keep reference observations and validation limits in the optional -[research index](./REFERENCE_RESEARCH.md#style-coherence-and-transition-research). +Uniformity does not mean equal-sized controls or boxed sections everywhere. + +- **D-COMP-5 · SHOULD** Keep an expressive demo visibly distinct from its documentation chrome. +- **D-COMP-6 · SHOULD** Update the brief before changing the visual direction; do not silently rewrite the + standard to justify an implementation that failed review. +- **D-COMP-7 · SHOULD** Compare a representative composed still with different real content before + animating it, then inspect the state change and return path at normal speed. + +A shared color alone cannot reconcile inconsistent proportions, icon weight, containment or motion. + +- **D-COMP-8 · SHOULD** When an example looks imported from another product, fix that relationship at its + owning token/component/style rather than adding a page-specific patch. +- **D-COMP-9 · SHOULD** Keep reference observations and validation limits in the optional + [research index](./REFERENCE_RESEARCH.md#style-coherence-and-transition-research). ### Structure before containers -**Reach for spacing, alignment, weight and a hairline before a box.** A container is a -claim that its contents are a separate object. Most regions need a background, a hairline -and intentional spacing — nothing more. +- **D-BOX-1 · SHOULD** **Reach for spacing, alignment, weight and a hairline before a box.** + +A container is a claim that its contents are a separate object. Most regions need a background, a +hairline and intentional spacing — nothing more. + +- **D-BOX-2 · SHOULD** Reserve a box for **a genuinely separate object**: a live demo stage, an overlay, a + panel that floats. Not for a code block, a table, a list row, a previous/next link, or a cell in a + gallery. +- **D-BOX-3 · SHOULD** **Avoid redundant nested containers and doubled padding.** A card inside a padded + panel does not need another decorative frame or inset. +- **D-BOX-4 · SHOULD** Keep functionally required nested controls or surfaces, and coordinate their radii + with the actual inset; containment alone is not a reason to add a box. +- **D-BOX-5 · SHOULD** **Every persistent boundary needs a distinct job.** Start with headings, alignment + and proximity to group content. Add a border, surface fill or elevation only when that structure leaves + a real boundary unclear. +- **D-BOX-6 · SHOULD** Do not repeat the same grouping with a panel frame, a nested card, row rules and + another inset at the same time. -**A box is for a genuinely separate object** — a live demo stage, an overlay, a panel that -floats. Not for a code block, a table, a list row, a previous/next link, or a cell in a -gallery. +Faint lines are still extra hierarchy; reducing their contrast or shortening them leaves a redundant +enclosure redundant. -**Avoid redundant nested containers and doubled padding.** A card inside a padded panel -does not need another decorative frame or inset. Keep functionally required nested controls or -surfaces, and coordinate their radii with the actual inset; containment alone is not a reason to add a box. +The table below applies D-BOX-5 and D-BOX-8. -**Every persistent boundary needs a distinct job.** Start with headings, alignment and proximity -to group content. Add a border, surface fill or elevation only when that structure leaves a real -boundary unclear. Do not repeat the same grouping with a panel frame, a nested card, row rules -and another inset at the same time. Faint lines are still extra hierarchy; reducing their contrast -or shortening them does not justify a redundant enclosure. +- **D-BOX-8 · MUST** Keep keyboard focus and persistent selection cues; never remove them to make the + resting layout quieter. _Proof: browser_ -| Boundary | Keep when it communicates | Remove when it only repeats | -| ----------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------- | -| Field/control frame | Where values can be entered or a selection made | A decorative wrapper around already legible commands | -| Region frame or divider | An independent viewport, resize boundary or major change of task | A title and gap that already make the same group clear | -| Nested surface | A distinct floating layer, preview, drop target or interaction boundary | Another container inside an established settings pane | -| Focus/selection outline | Keyboard focus or persistent selection state | Never remove this state cue merely to make the resting layout quieter | +| Boundary | Keep when it communicates | Remove when it only repeats | +| ----------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------ | +| Field/control frame | Where values can be entered or a selection made | A decorative wrapper around already legible commands | +| Region frame or divider | An independent viewport, resize boundary or major change of task | A title and gap that already make the same group clear | +| Nested surface | A distinct floating layer, preview, drop target or interaction boundary | Another container inside an established settings pane | +| Focus/selection outline | Keyboard focus or persistent selection state | Keep this state cue even when the resting layout would look quieter without it | -Review the full pane or page, not only the cropped group. For every remaining stable outline, -fill or separator, name the relationship or interaction it communicates and check what becomes -ambiguous if it is removed. A frame is optional; a reachable, recognisable control is not. +- **D-BOX-9 · SHOULD** Review the full pane or page, not only the cropped group. For every remaining stable + outline, fill or separator, name the relationship or interaction it communicates and check what becomes + ambiguous if it is removed. + +A frame is optional; a reachable, recognisable control is not. ### Emphasis is a budget **Colour, weight, badges, fills and elevation are scarce.** If everything is emphasised, -nothing reads as important. Establish priority with structure and proximity first, and -spend emphasis on the one thing that deserves it. +nothing reads as important. + +- **D-COMP-10 · SHOULD** Establish priority with structure and proximity first, and spend emphasis on the + one thing that deserves it. -**Elevation explains stacking, not importance.** Shadows belong to surfaces that genuinely -float above content — popovers, menus, dialogs, notifications. Do not shadow every card. +**Elevation explains stacking, not importance.** + +- **D-ELEV-3 · SHOULD** Reserve shadows for surfaces that genuinely float above content: popovers, menus, + dialogs, notifications. Do not shadow every card. ### Spacing carries meaning -The scale in §2 is not only a rhythm; each step states a relationship. Choose the step by -what the gap _means_: +The scale in §2 is not only a rhythm; each step states a relationship. + +Choose the step by what the gap _means_ (D-SPACE-2): | Relationship | Relative spacing | | ------------------------ | ------------------------------- | @@ -180,68 +244,95 @@ what the gap _means_: | Major region boundary | Strongest deliberate separation | **Inside before outside:** a component's own padding is decided before the gap between -components. **Smaller gap means tighter relationship** — that is the only thing vertical -rhythm communicates, so do not undo it with a decorative divider. +components (D-SPACE-3). **Smaller gap means tighter relationship** — that is the only thing vertical +rhythm communicates. + +- **D-SPACE-6 · SHOULD** Do not undo a gap's relationship with a decorative divider. ### Vertical rhythm across sibling views -Repeat relationships, not fixed heights. Choose title/description, field/hint, group and region gaps -from the host scale according to their relationship. These are composition decisions, not universal -component dimensions. Translations grow naturally; missing descriptions leave no vacant row. +- **D-SPACE-7 · SHOULD** Repeat relationships, not fixed heights. Choose title/description, field/hint, + group and region gaps from the host scale according to their relationship. + +These are composition decisions, not universal component dimensions. + +- **D-SPACE-8 · SHOULD** Let translations grow naturally, and leave no vacant row for a missing + description. +- **D-SPACE-9 · SHOULD** Give each gap one owner: parent stack or explicit child margin. Reset authored + local heading/paragraph margins rather than adding them to the parent's gap. +- **D-SPACE-10 · SHOULD** Account for owned control padding, collapsing margins and inline baseline space; + do not compensate with transforms or smaller type. +- **D-SPACE-11 · SHOULD** Place hints with the field/action they explain. +- **D-SPACE-12 · MUST** Hidden helpers leave no accidental gap. _Proof: browser_ +- **D-SPACE-16 · SHOULD** A reserved async status line has a stated purpose and bounds. + Measure both axes after content settles; [ACCEPTANCE](./ACCEPTANCE.md) grades this as V-PROOF-6. -Give each gap one owner: parent stack or explicit child margin. Reset authored local heading/ -paragraph margins rather than adding them to the parent's gap. Account for owned control padding, -collapsing margins and inline baseline space; do not compensate with transforms or smaller type. -Place hints with the field/action they explain. Hidden helpers leave no accidental gap; a reserved -async status line needs a stated purpose and bounds. +`next.top - previous.bottom` measures frame separation, not glyph/line-box space. -Measure both axes after content settles. `next.top - previous.bottom` measures frame separation, -not glyph/line-box space. Keep leading, padding and hairlines distinct: 16px padding plus a 1px -rule is not a 17px spacing token. Use independent expectations and exercise wrapping, resizing, -helper/error visibility and relevant density. [ACCEPTANCE](./ACCEPTANCE.md#browser-proof) owns -settling and mutation checks for doubled margins, phantom rows and text-only drift. +- **D-SPACE-14 · SHOULD** Keep leading, padding and hairlines distinct: 16px padding plus a 1px rule is + not a 17px spacing token. +- **D-SPACE-15 · SHOULD** Use independent expectations and exercise wrapping, resizing, helper/error + visibility and relevant density. + +[ACCEPTANCE](./ACCEPTANCE.md#browser-proof) owns settling and mutation checks for doubled margins, +phantom rows and text-only drift. ### The measure governs text, not the column -**Choose a readable measure for the actual script and font.** For Latin prose, 60–75 -characters per line is a starting heuristic, not a universal range for every writing system. -Review realistic text at supported widths, zoom levels and font fallbacks; CJK and mixed-script -text need their own judgment. [WCAG visual presentation](https://www.w3.org/WAI/WCAG22/Understanding/visual-presentation.html) +- **D-TYPE-4 · SHOULD** **Choose a readable measure for the actual script and font.** + +For Latin prose, 60–75 characters per line is a starting heuristic, not a universal range for every +writing system. + +- **D-TYPE-5 · SHOULD** Review realistic text at supported widths, zoom levels and font fallbacks; CJK and + mixed-script text need their own judgment. + +[WCAG visual presentation](https://www.w3.org/WAI/WCAG22/Understanding/visual-presentation.html) also distinguishes CJK in its Level AAA criterion; it is not a mandatory library default. -A table, code fence, demo stage or image should retain the width its content needs. -Cap the text elements, not the column that holds them. -**Declare the measure in `rem`, never `em`.** `em` resolves against the element's own -font-size, so the larger the type, the longer the line — exactly backwards. +- **D-TYPE-6 · SHOULD** Let a table, code fence, demo stage or image retain the width its content needs. + Cap the text elements, not the column that holds them. +- **D-TYPE-7 · SHOULD** **Declare the measure in `rem`, never `em`.** + +`em` resolves against the element's own font-size, so the larger the type, the longer the line — +exactly backwards. -**Build hierarchy out of type before reaching for a box.** One step of size and one step -down in colour turns a title and its first sentence into a title and a deck, with no new -container and no new border. +- **D-TYPE-8 · SHOULD** **Build hierarchy out of type before reaching for a box.** + +One step of size and one step down in colour turns a title and its first sentence into a title and a +deck, with no new container and no new border. ### If the layout says two things are one object, the spacing must agree -**A shared border, a shared radius and a gap between them are a contradiction.** Decide -whether the elements are one object or two, and make every property say the same thing. +**A shared border, a shared radius and a gap between them are a contradiction.** -**When a value must equal another value, name it.** A gap that a child has to subtract is -not a literal in two places; it is one custom property referenced twice. +- **D-ALIGN-1 · SHOULD** Decide whether the elements are one object or two, and make every property say + the same thing. +- **D-ALIGN-2 · SHOULD** **When a value must equal another value, name it.** A gap that a child has to + subtract is not a literal in two places; it is one custom property referenced twice. ### Reserve a column only when its content exists **A column declared unconditionally is still a column, even on the pages that have nothing -to put in it.** Condition the track on the element actually being there (`:has()`), not on -a page type you assume implies it. +to put in it.** + +- **D-ALIGN-3 · SHOULD** Condition the track on the element actually being there (`:has()`), not on a + page type you assume implies it. ### Alignment is structure, not polish -**Name each alignment spine and the geometry it aligns.** A page content spine, navigation -column and nested form-label spine can differ intentionally. State which siblings share each one; -include headers, body copy, rows, actions and empty/loading/error content. A back button plus gap -may establish a separate title column: do not silently treat that title as aligned to body text. -A missing optional icon or badge must not move labels out of their declared column. A nested level -returns exactly to its parent's spine when it ends. +- **D-ALIGN-4 · SHOULD** **Name each alignment spine and the geometry it aligns.** State which siblings + share each one; include headers, body copy, rows, actions and empty/loading/error content. -Compare the right boxes. These are separate measurements, even when they happen to coincide: +A page content spine, navigation column and nested form-label spine can differ intentionally. + +- **D-ALIGN-5 · SHOULD** A back button plus gap may establish a separate title column: do not silently + treat that title as aligned to body text. +- **D-ALIGN-6 · MUST** A missing optional icon or badge never moves labels out of their declared column. + _Proof: browser_ +- **D-ALIGN-7 · SHOULD** A nested level returns exactly to its parent's spine when it ends. + Compare the right boxes. These are separate measurements, even when they happen to coincide; measuring the + defect's layer is graded in [ACCEPTANCE](./ACCEPTANCE.md) as V-PROOF-10. | Geometry | What to measure | | ------------- | ------------------------------------------------------------------------------------------------- | @@ -252,194 +343,259 @@ Compare the right boxes. These are separate measurements, even when they happen | Hit area | The actual event target and its hover/focus surface; verify points near its edges | A button frame aligned with a paragraph does not prove its text is aligned: internal padding can -move the label while the frame stays fixed. `Range` gives typographic geometry, not exact glyph ink; -inspect optical glyph corrections separately. For web components, measure the host, documented shadow parts and slotted text through supported -test seams; do not add production shadow queries or assume document CSS crosses the boundary. - -**Keep a header command's hit zone separate from layout filler.** A title/menu trigger should hug -its content plus deliberate padding, bounded by the available width. Let a separate flex/grid track -absorb remaining space. Define truncation/wrapping and adjacent action behavior for long translations. -Do not make an entire unused header strip clickable merely by giving the title button `flex: 1`. -A row that intentionally selects its whole width is a different role; record that contract explicitly. -Coarse-pointer targets may grow without stretching the hover fill across unrelated content. - -**Count every inset from the shared ancestor to the visible text.** Parent padding, nested panel -padding, a control's internal padding, borders and navigation columns all contribute. Remove a -redundant inset at its owner instead of cancelling it with an unexplained negative margin. Empty -states retain their region's content spine and width; extra breathing room must not introduce a -second left edge or make a compact action fill the region. Product-specific insets and target sizes -remain application aliases, not new foundation values or universal component defaults. - -**When two layout edges or gaps are meant to be equal, a one-pixel difference is a defect.** -Optical correction for an asymmetric glyph is a separate, documented asset/component decision: -reuse the correction at supported sizes while preserving exact sibling gaps, baselines and alignment -spines. It is not permission to nudge page margins until a screenshot looks balanced. +move the label while the frame stays fixed. `Range` gives typographic geometry, not exact glyph ink. + +- **D-ALIGN-9 · SHOULD** Inspect optical glyph corrections separately. + For web components, measure the host, documented shadow parts and slotted text through supported test + seams. V-PROOF-11 in [ACCEPTANCE](./ACCEPTANCE.md) owns this, including the ban on production shadow queries + and on assuming document CSS crosses the boundary. +- **D-ALIGN-11 · SHOULD** **Keep a header command's hit zone separate from layout filler.** A title/menu + trigger hugs its content plus deliberate padding, bounded by the available width. Let a separate + flex/grid track absorb remaining space. +- **D-ALIGN-12 · SHOULD** Define truncation/wrapping and adjacent action behavior for long translations. +- **D-ALIGN-13 · MUST** The title trigger's hit area does not span unused header space (for example through + `flex: 1` on the title button) unless D-ALIGN-14 applies. _Proof: browser_ +- **D-ALIGN-14 · SHOULD** A row that intentionally selects its whole width is a different role; record + that contract explicitly. +- **D-ALIGN-15 · MAY** Coarse-pointer targets may grow larger. +- **D-ALIGN-22 · SHOULD** A grown target's hover fill stays on the control's own content, not across unrelated + content. +- **D-ALIGN-16 · SHOULD** **Count every inset from the shared ancestor to the visible text.** Parent + padding, nested panel padding, a control's internal padding, borders and navigation columns all + contribute. +- **D-ALIGN-17 · SHOULD** Remove a redundant inset at its owner instead of cancelling it with an + unexplained negative margin. +- **D-ALIGN-18 · SHOULD** Empty states retain their region's content spine and width; extra breathing room + never introduces a second left edge or makes a compact action fill the region. +- **D-ALIGN-19 · SHOULD** Keep product-specific insets and target sizes as application aliases, not new + foundation values or universal component defaults. +- **D-ALIGN-20 · SHOULD** **When two layout edges or gaps are meant to be equal, a one-pixel difference is a + defect.** Optical glyph correction (D-ALIGN-21) is the only tolerance. +- **D-ALIGN-21 · SHOULD** Treat optical correction for an asymmetric glyph as a separate, documented + asset/component decision, reused at supported sizes while sibling gaps, baselines and spines stay exact. + +Optical correction is not permission to nudge page margins until a screenshot looks balanced. ### Selection, links and other semantic lies -**Selection is persistent state and must not look like hover.** Hover is transient; if the -current item is marked with the same fill hover uses, the interface has two names for one -appearance. +- **D-SEM-1 · MUST** **Selection is persistent state and never looks like hover.** _Proof: browser_ + +Hover is transient; if the current item is marked with the same fill hover uses, the interface has two +names for one appearance. + +- **D-SEM-2 · MUST** **Action and link roles remain distinguishable.** _Proof: browser_ +- **D-SEM-3 · SHOULD** Verify inline links against surrounding prose, including themes where action and + body colors happen to match. -**Action and link roles remain distinguishable.** Verify inline links against surrounding prose, -including themes where action and body colors happen to match. +**A link leaves; a button acts.** -**A link leaves; a button acts.** Anything that changes application state is a button, -whatever it looks like. Do not style a command as a link to make it quiet, and do not let -a button-shaped link keep an underline. +- **D-SEM-4 · MUST** Anything that changes application state is a button, whatever it looks like. + _Proof: review, browser_ +- **D-SEM-5 · SHOULD** A button-shaped link carries no underline. ### Command hierarchy in constrained panes -**Remove redundant group enclosures without erasing control affordance.** Headings, -alignment and proximity establish groups inside a settings pane; they do not automatically -make an icon and label look clickable. Do not require flat rows in every context. Use them -when a recognizable menu/list pattern already conveys action. Standalone commands may need -their own restrained button surface or functional stroke, visible before hover in every -supported theme. A button's own shape identifies its target; an enclosing card, muted group -fill and row rules must still pass the distinct-boundary test above. - -Do not rely on hover, pointer cursors, verb labels or keyboard focus alone for initial -discovery. Inspect the resting full pane with the pointer away, including touch use. Keep -hover, press, disabled and keyboard focus distinguishable after the control is discovered. -Long translated labels may wrap inside a single command column rather than creating a -ragged horizontal button cloud. Do not add navigation chevrons to immediate commands. - -Declare which visible columns are shared with adjacent checkboxes or controls: measure -icon/checkbox edges and text Ranges, not only button rectangles or alignment within the -command group. A hit target may extend into an owned gutter to preserve those columns; -name the extension, keep it inside its pane and verify non-overlap and focus visibility. - -Separate destructive commands from recovery and transfer actions. A danger label and icon -can communicate intent without a large red outline on every resting command; reserve the -strongest danger fill for explicit confirmation. Keep confirmation, cancellation, focus -return and busy guards. Acceptance covers resting discoverability, declared shared columns, -full-pane boundary hierarchy, target bounds, label clipping, contrast and interaction states. -An automated surface-difference check catches invisible control fills but does not prove -that users recognize the action: inspect the actual render as well. Reuse foundation roles -and coarse-input target requirements; this guidance changes no component defaults. +- **D-CMD-1 · SHOULD** **Remove redundant group enclosures without erasing control affordance.** + +Headings, alignment and proximity establish groups inside a settings pane; by themselves they leave an +icon and label without a clickable look. + +- **D-CMD-2 · SHOULD** Use flat rows when a recognizable menu/list pattern already conveys action; do not + require them in every context. +- **D-CMD-3 · SHOULD** Give a standalone command that needs it its own restrained button surface or + functional stroke, visible before hover in every supported theme. +- **D-CMD-4 · SHOULD** Let a button's own shape identify its target; an enclosing card, muted group fill + and row rules still pass the distinct-boundary test (D-BOX-5). +- **D-CMD-5 · MUST** Initial discovery does not rely on hover, pointer cursors, verb labels or keyboard + focus alone. _Proof: browser_ +- **D-CMD-6 · SHOULD** Inspect the resting full pane with the pointer away, including touch use. +- **D-CMD-7 · MUST** Hover, press, disabled and keyboard focus stay distinguishable after the control is + discovered. _Proof: browser_ +- **D-CMD-8 · SHOULD** Wrap long translated labels inside one command column instead of a ragged horizontal + button cloud. +- **D-CMD-9 · MUST** Do not add navigation chevrons to immediate commands. _Proof: review_ +- **D-CMD-10 · SHOULD** Declare which visible columns are shared with adjacent checkboxes or controls: + measure icon/checkbox edges and text Ranges, not only button rectangles or alignment within the command + group. +- **D-CMD-11 · MAY** A hit target may extend into an owned gutter to preserve those columns; name the + extension. +- **D-CMD-12 · MUST** An extended hit target stays inside its pane, does not overlap other targets, and + keeps focus visible. _Proof: browser_ +- **D-CMD-13 · SHOULD** Separate destructive commands from recovery and transfer actions. +- **D-CMD-14 · SHOULD** Let a danger label and icon communicate intent without a large red outline on + every resting command; reserve the strongest danger fill for explicit confirmation. +- **D-CMD-15 · MUST** Destructive commands keep confirmation, cancellation, focus return and busy guards. + _Proof: browser_ +- **D-CMD-16 · SHOULD** Accept a command pane against its resting state, not only its hover state. + +Acceptance covers resting discoverability, declared shared columns, full-pane boundary hierarchy, target +bounds, label clipping, contrast and interaction states. + +- **D-CMD-17 · SHOULD** Inspect the actual render as well: an automated surface-difference check catches + invisible control fills but does not prove that users recognize the action. +- **D-CMD-18 · SHOULD** Reuse foundation roles and coarse-input target requirements; this guidance changes + no component defaults. ### One product, one token set -Use the product’s authoritative semantic tokens. Aliases may improve readability, but resolve -from that source rather than inventing a competing palette. Coordinate third-party components -through supported theme APIs and verify the composed result. +- **D-TOKEN-1 · SHOULD** Use the product’s authoritative semantic tokens. Aliases may improve readability, + but resolve from that source rather than inventing a competing palette. +- **D-TOKEN-2 · SHOULD** Coordinate third-party components through supported theme APIs and verify the + composed result. ### Density follows control roles Spacing, intrinsic dimensions and hit targets solve different problems. A small glyph can have -a larger native button target. Measure actual hit bounds, focus and adjacent gaps; derive repeated -host aliases from foundation tokens, not one application's dimensions copied across the library. +a larger native button target. -Choose target bounds for the actual input method without enlarging every glyph or reserving -space for hidden controls. Use supported public theme APIs. Name icon-only controls accessibly -and document legitimate viewport, hairline, artwork and optical geometry exceptions. +- **D-TOKEN-3 · SHOULD** Measure actual hit bounds, focus and adjacent gaps. +- **D-TOKEN-4 · SHOULD** Derive repeated host aliases from foundation tokens, not one application's + dimensions copied across the library. +- **D-TOKEN-5 · SHOULD** Choose target bounds for the actual input method without enlarging every glyph or + reserving space for hidden controls. Use supported public theme APIs. +- **D-TOKEN-6 · MUST** Name icon-only controls accessibly. _Proof: browser_ +- **D-TOKEN-7 · SHOULD** Document legitimate viewport, hairline, artwork and optical geometry exceptions. ### Evaluate craft through evidence -Polish follows task clarity, not a preset of cards, blur or entrances. Choose a deliberate -visual direction using realistic objects, copy and states; a generic empty shell cannot establish -product quality. Working interfaces need sustained reading and frequent actions; marketing pages -may need a different composition. Evaluate the smallest intervention against real content, language, -device and repeated use. Primary references answer -specific questions: [Linear hierarchy](https://linear.app/now/behind-the-latest-design-refresh), +Polish follows task clarity, not a preset of cards, blur or entrances. + +- **D-CRAFT-1 · SHOULD** Choose a deliberate visual direction using realistic objects, copy and states; a + generic empty shell cannot establish product quality. + +Working interfaces need sustained reading and frequent actions; marketing pages may need a different +composition. + +- **D-CRAFT-2 · SHOULD** Evaluate the smallest intervention against real content, language, device and + repeated use. + +Primary references answer specific questions: +[Linear hierarchy](https://linear.app/now/behind-the-latest-design-refresh), [Geist typography](https://vercel.com/geist/typography), [optical alignment](https://craft.gustavofior.com/optical-alignment) and -[interruptibility](https://craft.gustavofior.com/interruptibility). Adapt their principles with -the host’s tokens and actual capabilities; do not copy a product's pixel sizes or import recipes as rules. -[Don't Make Me Think](https://sensible.com/dont-make-me-think/) informs scanning, hierarchy and -clear choices; [taste-skill](https://github.com/leonxlnx/taste-skill) informs deliberate direction -and critique; [ryugi](https://github.com/Innei/ryugi) informs executable work and fresh evidence. -These references inform decisions, not additional instruction sets. A stylistic preference needs -context; a requirement must identify the task or observable contract it protects. - -Choose references by the job they support: logo/app identity, marketing composition and frequent -working interactions solve different problems. Record the observed pattern and its adaptation; -verify the result with real content. The optional [research index](./REFERENCE_RESEARCH.md) maps -sources to questions and demonstrates this translation without adding another rule set. - -When direction is unresolved, compare two small real-host variants with the same content, width -and theme, changing one dimension at a time. Explain what each difference does for the task. +[interruptibility](https://craft.gustavofior.com/interruptibility). + +- **D-CRAFT-3 · SHOULD** Adapt their principles with the host’s tokens and actual capabilities; do not + copy a product's pixel sizes or import recipes as rules. +- **D-CRAFT-4 · SHOULD** Treat references as inputs to decisions, not additional instruction sets; for + example, [Don't Make Me Think](https://sensible.com/dont-make-me-think/) informs scanning, hierarchy and + clear choices. + +[taste-skill](https://github.com/leonxlnx/taste-skill) informs deliberate direction and critique; +[ryugi](https://github.com/Innei/ryugi) informs executable work and fresh evidence. + +- **D-CRAFT-5 · SHOULD** A stylistic preference needs context; a requirement identifies the task or + observable contract it protects. +- **D-CRAFT-6 · SHOULD** Choose references by the job they support: logo/app identity, marketing + composition and frequent working interactions solve different problems. +- **D-CRAFT-7 · SHOULD** Record the observed pattern and its adaptation; verify the result with real + content. + +The optional [research index](./REFERENCE_RESEARCH.md) maps sources to questions and demonstrates this +translation without adding another rule set. + +- **D-CRAFT-8 · SHOULD** When direction is unresolved, compare two small real-host variants with the same + content, width and theme, changing one dimension at a time. Explain what each difference does for the + task. + A clear direction or small correction proceeds directly; there is no mandatory approval ritual. -- Balance short headings progressively with `text-wrap: balance`, preserving normal wrapping. - Do not insert locale-dependent manual breaks or clip text to preserve a screenshot. Check cost - and support before using `pretty` on long live content. -- Tabular figures stabilize digit advances if the font supports them; reserve a meaningful - column for digit-count changes such as 99 → 100 without truncating values. -- Correct asymmetric glyphs in a reusable asset/component, preserving exact layout spines. - Derive nested corner geometry from actual inset; remove an unnecessary nested surface first. -- Motion needs a job: immediate frequent feedback, interruptible transitions, reduced motion and - instant themes. Optional delight must not delay typing or obscure status. Pause offscreen - decoration and profile realistic content before adopting costly effects. See §5. +- **D-CRAFT-9 · SHOULD** Balance short headings progressively with `text-wrap: balance`, preserving normal + wrapping. +- **D-CRAFT-10 · MUST** Headings contain no locale-dependent manual line breaks and are not clipped to + preserve a screenshot. _Proof: browser_ +- **D-CRAFT-11 · SHOULD** Check cost and support before using `pretty` on long live content. +- **D-CRAFT-12 · SHOULD** Use tabular figures to stabilize digit advances if the font supports them. +- **D-CRAFT-13 · SHOULD** Reserve a meaningful column for digit-count changes such as 99 → 100. +- **D-CRAFT-14 · MUST** A digit-count change never truncates the value. _Proof: browser_ +- **D-CRAFT-15 · SHOULD** Correct asymmetric glyphs in a reusable asset/component, preserving exact layout + spines. +- **D-CRAFT-16 · SHOULD** Derive nested corner geometry from actual inset; remove an unnecessary nested + surface first. +- **D-CRAFT-17 · SHOULD** Give motion a job: immediate frequent feedback, interruptible transitions, + reduced motion and instant themes. See §5. +- **D-CRAFT-18 · MUST** Optional delight never delays typing or obscures status. _Proof: recording_ +- **D-CRAFT-19 · SHOULD** Pause offscreen decoration and profile realistic content before adopting costly + effects. ### Words are part of the interface -Avoid unnecessary secondary labels and descriptions. Let the layout do the explaining. -A heading, grouping, proximity or obvious control should not need another sentence repeating -its purpose. Remove copy with no new decision, constraint or consequence; do not replace it -with a decorative badge or tooltip. Preserve accessible names, field labels, units, errors, -risk disclosures and useful first-use guidance. Test whether the task remains understandable. - -Let context carry context: `Users` rather than `User Management`; do not repeat a dialog's -question in its body. Name results (`Save`, `Move`, `Delete`), not gestures (`Click to save`). -Include the object only when context does not establish it. `Cancel` leaves without committing. -Use sentence case, no period on short labels, and a period on explanatory sentences. -Use one `…` when an action needs more input before completing; reserve ALL CAPS for short -statuses, eyebrows and acronyms. +- **D-COPY-6 · SHOULD** Avoid unnecessary secondary labels and descriptions. Let the layout do the + explaining. +- **D-COPY-7 · SHOULD** A heading, grouping, proximity or obvious control needs no other sentence + repeating its purpose. +- **D-COPY-8 · SHOULD** Remove copy with no new decision, constraint or consequence; do not replace it + with a decorative badge or tooltip. +- **D-COPY-9 · MUST** Preserve accessible names, field labels, units, errors and risk disclosures. + _Proof: browser_ +- **D-COPY-10 · SHOULD** Preserve useful first-use guidance, and test whether the task remains + understandable. +- **D-COPY-11 · SHOULD** Let context carry context: `Users` rather than `User Management`; do not repeat a + dialog's question in its body. +- **D-COPY-12 · SHOULD** Name results (`Save`, `Move`, `Delete`), not gestures (`Click to save`). Include + the object only when context does not establish it. +- **D-COPY-13 · MUST** `Cancel` leaves without committing. _Proof: browser_ +- **D-COPY-14 · SHOULD** Use sentence case, no period on short labels, and a period on explanatory + sentences. +- **D-COPY-15 · SHOULD** Use one `…` when an action needs more input before completing; reserve ALL CAPS + for short statuses, eyebrows and acronyms. ### Review checklist for a composed surface -Apply these questions to the affected surface and reachable changed states. Skip irrelevant variants; -a small patch does not require redesigning unrelated pages. A "no" within scope is a finding: +- **D-REVIEW-1 · SHOULD** Apply these questions to the affected surface and reachable changed states. + Skip irrelevant variants; a small patch does not require redesigning unrelated pages. A "no" within + scope is a finding. 1. Can a new reader recognise the purpose and the primary action without guessing? 2. Does every control's label, state and result describe one consistent outcome? -3. Is emphasis scarce — does the core thing get the weight while colour, badges and - primary buttons stay rare? +3. Is emphasis scarce — does the core thing get the weight while colour, badges and primary buttons stay rare? 4. Could this do less: any entry point, option or state removable without weakening the task? -5. Are named spines exact at the visible text/content layer, including nested and empty states, - with intentional navigation columns and no doubled padding? Do adjacent controls share their declared - icon/text columns? Are title hit zones content-hugging? Do repeated vertical relationships use - consistent gaps with one owner, including wrapped copy and hidden helper/error content? -6. Is running text comfortable in its actual scripts, fonts and zoom levels, and is - everything that is **not** running text free of that measure cap? -7. Does every element that looks like part of one object agree — border, radius **and** - spacing — and does every reserved column actually have content? Does each persistent frame, - fill or divider communicate a distinct boundary instead of nesting the same group again? -8. Does it hold in relevant changed states: empty, loading, failure, long translations, narrow - width, supported themes, reduced motion and **RTL**, where affected? -9. For layout or interaction changes, has the affected surface been checked in a real browser - at the relevant widths and themes? Are standalone actions recognizable before hover, including - touch use, without redundant group enclosures? +5. Are named spines exact at the visible text/content layer, including nested and empty states, with intentional navigation columns and no doubled padding? +6. Do adjacent controls share their declared icon/text columns? +7. Are title hit zones content-hugging? +8. Do repeated vertical relationships use consistent gaps with one owner, including wrapped copy and hidden helper/error content? +9. Is running text comfortable in its actual scripts, fonts and zoom levels, and is everything that is **not** running text free of that measure cap? +10. Does every element that looks like part of one object agree — border, radius **and** spacing — and does every reserved column actually have content? Does each persistent frame, fill or divider communicate a distinct boundary instead of nesting the same group again? +11. Does it hold in relevant changed states: empty, loading, failure, long translations, narrow width, supported themes, reduced motion and **RTL**, where affected? +12. For layout or interaction changes, has the affected surface been checked in a real browser at the relevant widths and themes? Are standalone actions recognizable before hover, including touch use, without redundant group enclosures? A happy-path screenshot does not prove keyboard behavior, focus, dynamic content, -resizing or failure recovery. Follow [ACCEPTANCE](./ACCEPTANCE.md) for independent expectations, -actual runtime/capture identity, browser measurements and separate automated, agent and human -results. The source-only [browser example](https://github.com/chaxus/ran/blob/main/packages/ranui/test/e2e/layout-contract.md) +resizing or failure recovery. + +- **D-REVIEW-2 · SHOULD** Follow [ACCEPTANCE](./ACCEPTANCE.md) for independent expectations, actual + runtime/capture identity, browser measurements and separate automated, agent and human results. + +The source-only [browser example](https://github.com/chaxus/ran/blob/main/packages/ranui/test/e2e/layout-contract.md) contains setup commands and test utility details; they are not public package APIs. ## 10. Information architecture — the shape before the composition -Choose structure before composition for information-dense consoles, dashboards and workbenches. +- **D-IA-1 · SHOULD** Choose structure before composition for information-dense consoles, dashboards and + workbenches. + Complete API data is not a page design: the reader still needs a clear order for understanding -objects, their state and relationships, then acting. Single-conversion and landing pages need -their own task structure rather than a dense-console template. +objects, their state and relationships, then acting. + +- **D-IA-2 · SHOULD** Give single-conversion and landing pages their own task structure rather than a + dense-console template. ### Answer three questions before naming a component -1. **What must the reader see on arrival?** That is the primary information. -2. **What else has to be visible for it to make sense?** Related resources, related models, - context. +- **D-IA-3 · SHOULD** Settle all three answers before choosing components. + +1. **What does the reader need to see on arrival?** That is the primary information. +2. **What else has to be visible for it to make sense?** Related resources, related models, context. 3. **What do they do next?** Judge, act, or keep thinking. -**A page has exactly one primary model.** Supporting models may help the reader understand or -operate it; they may not compete for the first screen. Prefer a clear, unambiguous next choice -over an arbitrary click budget. Test whether readers recognize the next action, not merely -whether the destination is reachable. +- **D-IA-4 · SHOULD** **A page has exactly one primary model.** Supporting models may help the reader + understand or operate it; they may not compete for the first screen. +- **D-IA-5 · SHOULD** Prefer a clear, unambiguous next choice over an arbitrary click budget. Test whether + readers recognize the next action, not merely whether the destination is reachable. ### The shape follows the task, not the payload -Choose a skeleton from the reader's question, not an API array or route ID: +- **D-IA-6 · SHOULD** Choose a skeleton from the reader's question, not an API array or route ID, using + the table below. | Task | Useful shape | | --------------------------------------------- | -------------------------------------------------------- | @@ -454,13 +610,17 @@ Choose a skeleton from the reader's question, not an API array or route ID: | Find occupancy/conflicts or location | Calendar or map/canvas | | Work on the next item / change rules | Master-detail workbench / configuration form | -Do not confuse historical events with future steps, a filter with kanban, or ordered prose -with a field grid. A date alone does not justify a calendar. Ship multiple views only when -each serves a distinct task worth its filter/state/action maintenance. +- **D-IA-7 · SHOULD** Do not confuse historical events with future steps, a filter with kanban, or ordered + prose with a field grid. A date alone does not justify a calendar. +- **D-IA-8 · SHOULD** Ship multiple views only when each serves a distinct task worth its + filter/state/action maintenance. ### Each kind of information has a place -| Information | Belongs | Must not end up | +- **D-IA-9 · SHOULD** Lead with identity and status in the title or summary region, and keep actions, + feedback and evidence beside their subject, as the table below lays out. + +| Information | Belongs | Wrong place | | ------------- | ------------------------------------------------------------ | ---------------------------------------- | | Identity | Title, object summary | The last column, or behind a tab | | Status | Title or summary region | Findable only in a detail field | @@ -471,65 +631,81 @@ each serves a distinct task worth its filter/state/action maintenance. | Actions | Primary in the title region, the rest beside their object | Buried under "more" | | Feedback | Beside the action, keeping task context | A global toast detached from its subject | -**Every fact has exactly one authoritative location.** Elsewhere shows a summary or an entry -point that links back to it. - -Default reading order — page identity → current status or exception → primary task and primary -action → the information the judgement needs → relationships, changes, evidence → secondary -information and low-frequency actions. **One primary action per task region**, and it is the -most likely next step, not the most destructive one (§9, "Emphasis is a budget", governs how -much weight it may take). +- **D-IA-10 · SHOULD** **Every fact has exactly one authoritative location.** Elsewhere shows a summary or + an entry point that links back to it. +- **D-IA-11 · SHOULD** Follow the default reading order: page identity → current status or exception → + primary task and primary action → the information the judgement needs → relationships, changes, + evidence → secondary information and low-frequency actions. +- **D-IA-12 · SHOULD** **One primary action per task region**, and it is the most likely next step, not + the most destructive one (§9, "Emphasis is a budget", governs how much weight it may take). ### Density is effective information, not controls per inch Tightening spacing raises visual density and leaves effective density where it was. Removing irrelevant fields and putting the comparison in one place raises the real thing. -**Use at most two adjacent density levels on one page** — spacious for first use and risky -confirmation, standard for most lists and details, compact for expert workbenches. Container -padding and line height may tighten; readable body text, a visible focus ring and pointer -target size may not (§8). +- **D-IA-13 · SHOULD** **Use at most two adjacent density levels on one page**: spacious for first use and + risky confirmation, standard for most lists and details, compact for expert workbenches. +- **D-IA-14 · MAY** Container padding and line height may tighten. +- **D-IA-15 · MUST** Readable body text, a visible focus ring and pointer target size do not shrink with + density (§7, §8). _Proof: browser_ ### Containers do not fix structure -**One contextual return per view.** Do not show an outer “Back to chat” and an inner -“Back to artifacts” together. The detail view returns to its immediate parent; ancestors remain -reachable through the route hierarchy or clearly named breadcrumbs. Verify the full journey, -including focus return and long translated destination names. If text does not fit, reflow or -use a meaningful shorter destination; an ellipsis or tooltip must not hide the only destination cue. -Choose one composition for the parent context. Do not add a breadcrumb merely to relabel a -redundant return shortcut, or repeat that parent as an inert crumb above its return command. +- **D-IA-16 · SHOULD** **One contextual return per view.** Do not show an outer “Back to chat” and an + inner “Back to artifacts” together. +- **D-IA-17 · SHOULD** The detail view returns to its immediate parent. +- **D-IA-18 · MUST** Ancestors remain reachable through the route hierarchy or clearly named breadcrumbs. + _Proof: browser_ +- **D-IA-19 · SHOULD** Verify the full journey, including focus return and long translated destination + names. +- **D-IA-27 · MUST** Returning to the parent restores focus to the originating item. _Proof: browser_ +- **D-IA-20 · MUST** If text does not fit, reflow or use a meaningful shorter destination; an ellipsis or + tooltip never hides the only destination cue. _Proof: browser_ +- **D-IA-21 · SHOULD** Choose one composition for the parent context. Do not add a breadcrumb merely to + relabel a redundant return shortcut, or repeat that parent as an inert crumb above its return command. + When returning through the parent already reaches the ancestor, that journey preserves access -without adding another detail-header row. Use breadcrumbs when the hierarchy itself helps the task. +without adding another detail-header row. -**A modal is not a navigation layer.** Anything needing a copyable link, history, a -side-by-side comparison, or work that survives a refresh gets a route. Reserve a modal for a -short confirmation or a single field, a popover or menu for transient context, and a disclosure -row for progressive disclosure. +- **D-IA-22 · SHOULD** Use breadcrumbs when the hierarchy itself helps the task. -**Tabs carry peer views of one object** — its conversation, its checks, its diff — never -unrelated modules; that is navigation's job. +**A modal is not a navigation layer.** -If the chosen library lacks a suitable primitive, use existing semantic elements and host tokens; -verify the required interaction rather than inventing unsupported APIs. +- **D-IA-23 · SHOULD** Give a route to anything needing a copyable link, history, a side-by-side + comparison, or work that survives a refresh. +- **D-IA-24 · SHOULD** Reserve a modal for a short confirmation or a single field, a popover or menu for + transient context, and a disclosure row for progressive disclosure. +- **D-IA-25 · SHOULD** **Tabs carry peer views of one object** — its conversation, its checks, its diff — + never unrelated modules; that is navigation's job. +- **D-IA-26 · SHOULD** If the chosen library lacks a suitable primitive, use existing semantic elements + and host tokens; verify the required interaction rather than inventing unsupported APIs. ## Verification checklist (before shipping UI) -Select checks for the affected surface, supported variants and changed behavior; do not create -unrelated flows merely to satisfy the list. Repository CI still runs its complete gates. - -- [ ] Primary task and primary action are unmistakable. -- [ ] Affected layout/theme changes work at relevant **narrow and wide** widths and supported themes. -- [ ] Works with **mouse and touch** — any drag/gesture uses Pointer Events, not mouse-only; any - `trigger="hover"` has a tap fallback. -- [ ] All changed **states** exercised (hover, active, focus, disabled, loading, empty, error). -- [ ] **Keyboard / focus** behavior verified; visible focus everywhere. -- [ ] Relevant edge cases: long text, large numbers and the host's supported scripts/locales. -- [ ] Spacing comes from the scale; type uses a role; color uses semantic tokens. -- [ ] Run the host’s applicable checks and inspect actual browser evidence. -- [ ] Copy follows §6; nothing signals state by color alone (§7). -- [ ] For a dense surface: one primary model, the skeleton chosen from the question rather - than the payload, and every fact with one authoritative location (§10). +- **D-VERIFY-1 · SHOULD** Select checks for the affected surface, supported variants and changed behavior; + do not create unrelated flows merely to satisfy the list. + +Repository CI still runs its complete gates. + +- **D-VERIFY-2 · SHOULD** Primary task and primary action are unmistakable. + +Affected layout/theme changes work at relevant **narrow and wide** widths and supported themes (D-INPUT-1). +Mouse and touch both work: drags use Pointer Events and any `trigger="hover"` has a tap fallback (D-INPUT-3, +D-INPUT-4). Keyboard and visible focus are covered by D-ACCESS-3 and D-ACCESS-4. + +- **D-VERIFY-5 · SHOULD** Exercise all changed **states** (hover, active, focus, disabled, loading, empty, + error). +- **D-VERIFY-7 · SHOULD** Cover relevant edge cases: long text, large numbers and the host's supported + scripts/locales. +- **D-VERIFY-8 · SHOULD** Spacing comes from the scale; type uses a role; color uses semantic tokens. +- **D-VERIFY-9 · SHOULD** Run the host’s applicable checks and inspect actual browser evidence. +- **D-VERIFY-10 · SHOULD** Copy follows §6. + +Color-only state signals are covered by D-ACCESS-2. + +- **D-VERIFY-12 · SHOULD** For a dense surface: one primary model, the skeleton chosen from the question + rather than the payload, and every fact with one authoritative location (§10). --- @@ -548,50 +724,66 @@ unrelated flows merely to satisfy the list. Repository CI still runs its complet > [Coding guidelines](https://ran.chaxus.com/src/ranui/coding-guides/) — update those when a > rule here changes in a way consumers can see. +Each rule carries an ID and a level, with the meanings defined in [DESIGN](./DESIGN.md); the +[rule index](./RULES.md) lists the ranui rules after the portable ones. + ## ranui default visual direction The default is a quiet, content-first working interface: neutral semantic surfaces, restrained emphasis and compact desktop controls with reachable touch targets. Distinction comes from clear typography, proportions and complete states, not a decorative effect on every component. -Use existing token roles to choose exact dimensions; do not introduce a second "premium" scale. -Glass and expressive demonstrations are explicit surface/theme choices, not default text/control -backgrounds. Preserve reading contrast and the same interaction semantics across themes. -Apply the shared [style brief](./DESIGN.md#style-coherence-across-components-and-pages) before -changing a family. Review it in a realistic composed page with neighboring controls; a polished -isolated component does not establish a coherent product. +- **R-STYLE-1 · SHOULD** Use existing token roles to choose exact dimensions; do not introduce a second + "premium" scale. +- **R-STYLE-2 · SHOULD** Treat glass and expressive demonstrations as explicit surface or theme choices, + not as default text or control backgrounds. +- **R-STYLE-3 · MUST** Preserve reading contrast and the same interaction semantics across themes. + _Proof: browser_ +- **R-STYLE-4 · SHOULD** Apply the shared [style brief](./DESIGN.md#style-coherence-across-components-and-pages) + before changing a family. +- **R-STYLE-5 · SHOULD** Review a family change in a realistic composed page with neighboring controls; a + polished isolated component does not establish a coherent product. ## How to use this file -- Prefer **semantic tokens** (`--ran-color-*`, `--ran-space-*`, …) over raw scales or hex. -- Decide by **role/state**, not by eyeballing a value. The value is chosen for you. -- When a choice is unresolved, mark it explicitly — don't bury a guess in code. -- Design **all reachable states** (default, hover, active, focus, disabled, loading, empty, error), not just the happy path. -- **Verify the rendered result**, in light and dark, at narrow and wide widths — code review alone is not enough. - -For observable behavior, read [INTERACTION.md](./INTERACTION.md). The short +- **R-USE-1 · SHOULD** Prefer **semantic tokens** (`--ran-color-*`, `--ran-space-*`, …) over raw scales or hex. +- **R-USE-2 · SHOULD** Decide by **role/state**, not by eyeballing a value. The value is chosen for you. +- **R-USE-3 · SHOULD** When a choice is unresolved, mark it explicitly instead of burying a guess in code. +- **R-USE-4 · SHOULD** Design **all reachable states** (default, hover, active, focus, disabled, loading, + empty, error), not just the happy path. +- **R-USE-5 · MUST** **Verify the rendered result** in the themes and widths the change affects; code review + alone is not enough. _Proof: browser_ +- **R-USE-6 · SHOULD** Resolve conflicts in this order: **user goals → verified evidence → this file → + repo guidance (`CLAUDE.md`) → shipped patterns → general heuristics.** + +[INTERACTION.md](./INTERACTION.md) owns observable behavior. The short [interface index](./INTERFACE_GUIDE.md) routes other decisions to their owner; optional task recipes live separately in [WORKFLOW_PATTERNS.md](./WORKFLOW_PATTERNS.md). -Conflict resolution order: **user goals → verified evidence → this file → repo guidance (`CLAUDE.md`) → shipped patterns → general heuristics.** - ## What is machine-checked `pnpm -F ranui verify:design` enforces nine source rules: dark-safe and declared token fallbacks, semantic colors, spacing/sizing scales, Pointer Events, working `hidden`, held builder references and constructor-only shadow mounting. Repository CI also checks its -configured consumer surfaces. Its per-file baseline is a debt ratchet, never permission to -add violations. Static checks do not prove composition or interaction. +configured consumer surfaces. Static checks cannot prove composition or interaction. See [CODING](./CODING.md#what-is-machine-checked) for engineering checks and [ACCEPTANCE](./ACCEPTANCE.md) for actual browser evidence and decision authority. +- **R-CHECK-1 · MUST** A change adds no new `verify:design` violations; the per-file baseline is a debt + ratchet, not permission to add violations. _Proof: review_ +- **R-CHECK-2 · MUST** A component that implements a drag drives it with Pointer Events + (`pointerdown`/`pointermove`/`pointerup`/`pointercancel`), not a `mousemove`-only loop, so touch can drive + it. _Proof: check:mouse-only-drag_ + ## Looking at the whole system at once `pnpm -F ranui design:bundle` captures live documentation components and resolved light/dark -tokens into a browsable bundle. Start the docs preview first (`pnpm -F docs build` then -`pnpm -F docs preview`). Cards are rendered ranui output, not drawings of imagined APIs. -The command reports components missing a live demo. Use the matching installed version; -a reference bundle is not evidence that the host application passed acceptance. +tokens into a browsable bundle. It needs the docs preview running first (`pnpm -F docs build` +then `pnpm -F docs preview`). Cards are rendered ranui output, not drawings of imagined APIs. +The command reports components missing a live demo. + +- **R-BUNDLE-1 · SHOULD** Use the bundle from the matching installed version; a reference bundle is not + evidence that the host application passed acceptance. ## ranui color — a state ladder, not a palette @@ -612,7 +804,7 @@ Each hue is a **10-step scale** (`100`–`1000`). Every step has **one fixed job Scales: `--ran-gray-100..1000`, `--ran-gray-alpha-100..1000` (translucent, layers over any surface), `--ran-blue-*`, `--ran-red-*`, `--ran-amber-*`, `--ran-green-*`, plus `--ran-background-100/200`. -**Use the semantic layer, not the scale, in components:** +- **R-COLOR-1 · SHOULD** **Use the semantic layer in the table below, not the scale, in components.** | Token | Maps to | Use for | | -------------------------------------------- | ------------------------------------- | -------------------------------- | @@ -633,14 +825,27 @@ Scales: `--ran-gray-100..1000`, `--ran-gray-alpha-100..1000` (translucent, layer | `--ran-color-success` / `warning` / `danger` | green-700 / amber-700 / red-700 | Status | | `--ran-color-link` | blue-700 | Links | -**Accent meaning:** the **primary action is monochrome** — `--ran-color-primary` is black-on-white in light, white-on-black in dark (matching Vercel's brand tone), and text/icons on it use `--ran-color-primary-text` (the inverse, which flips too). **Blue** is reserved for **links (`--ran-color-link`) and the focus ring** only · green = success · amber = warning · red = danger/error. There is no separate "contrast" token — primary _is_ the highest-contrast monochrome action. +**Accent meaning:** the **primary action is monochrome** — `--ran-color-primary` is black-on-white in light, white-on-black in dark (matching Vercel's brand tone). There is no separate "contrast" token — primary _is_ the highest-contrast monochrome action. -**Light & dark:** same token name, different value. `gray-1000` is `#171717` in light and `#ededed` in dark. Components reference the _semantic name_. The dark mixin redefines the base scales plus explicit surface, primary hover/active and shadow overrides; read the theme declarations rather than assuming every semantic value follows one scale step. +- **R-COLOR-2 · SHOULD** Text and icons on a primary surface use `--ran-color-primary-text` (the inverse, + which flips too). +- **R-COLOR-3 · SHOULD** Reserve **blue** for **links (`--ran-color-link`) and the focus ring** only; green + means success, amber warning, red danger/error. + +**Light & dark:** same token name, different value. `gray-1000` is `#171717` in light and `#ededed` in dark. Components reference the _semantic name_. The dark mixin redefines the base scales plus explicit surface, primary hover/active and shadow overrides. + +- **R-COLOR-4 · SHOULD** Read the theme declarations rather than assuming every semantic value follows one + scale step. **Rules** -- Never hard-code a hex/rgb in a component for a value that should follow the theme. -- A component token's fallback must point at a **token that flips** (`var(--ran-gray-alpha-100, …)`, `var(--ran-blue-100, …)`, `var(--ran-color-text, …)`), never a light-only literal — otherwise it breaks in dark mode. +- **R-COLOR-5 · MUST** Do not hard-code a hex/rgb in a component for a value that follows the theme. + _Proof: check:bare-colour_ +- **R-COLOR-6 · MUST** A component token's colour fallback starts with a **token that flips** + (`var(--ran-gray-alpha-100, …)`, `var(--ran-blue-100, …)`, `var(--ran-color-text, …)`), never a bare + light-only literal — otherwise it breaks in dark mode. A final literal behind that token, as in + `var(--ran-btn-background, var(--ran-color-primary, #171717))`, is allowed. + _Proof: check:dark-unsafe-fallback_ ## ranui spacing — a limited, rhythmic scale @@ -648,13 +853,17 @@ A **4px base unit** with **nine values** only: `--ran-space-1..24` → `4, 8, 12 [ranui spacing relationships](#ranui-spacing-relationships) map each relationship to a step. -**Rule:** pick a value from the scale. Don't invent `20px`/`28px` — a limited set is what creates the page's rhythm. +- **R-SPACE-1 · MUST** Pick spacing from the scale; do not invent `20px`/`28px` — a limited set is what + creates the page's rhythm. _Proof: check:spacing-scale_ +- **R-SPACE-2 · MUST** Intrinsic dimensions (`width`, `height`, their `min-`/`max-` forms, `font-size`, + `line-height`, `border-radius`) take `--ran-size-*` or role tokens, never `--ran-space-*`, so consumers can + retune either scale without perturbing the other. _Proof: check:sizing-scale_ ## ranui typography — choose a role, not a size Fonts: `--ran-font-family` (Geist Sans, UI & prose), `--ran-font-mono` (Geist Mono, code, data, and eyebrow/labels). Base: `--ran-font-size: 14px`, `--ran-line-height: 1.5715`. -Decide by **role**; the role fixes font, size, weight, line-height. Each row is backed by real tokens in `theme/tokens.less` — reference them (`var(--ran-{component}-…, var(--ran-text-{role}…, fallback))`), don't hand-copy the px/weight number: +The role fixes font, size, weight and line-height. Each row is backed by real tokens in `theme/tokens.less`: | Role | Use | Weight | Size tokens | Notes | | ------- | ----------------------------- | ------------------------------------------------- | ----------------------------------------- | --------------------------------------------------------------- | @@ -664,49 +873,90 @@ Decide by **role**; the role fixes font, size, weight, line-height. Each row is | button | Button text | `--ran-text-button-weight` (500) | `--ran-text-button-size` (14px) | `--ran-text-button-line-height: 1` for crisp vertical centering | | mono | Code, data, eyebrows | `--ran-text-mono-weight-regular/medium` (400/500) | borrows label/copy size tiers | `--ran-font-mono` | -**Rule:** ask "what role is this text?" (heading / label / copy / button) — the style follows. Don't pick raw px per instance. +- **R-TYPE-1 · SHOULD** Decide text style by **role**: ask "what role is this text?" (heading / label / + copy / button) and the style follows. Do not pick raw px per instance. +- **R-TYPE-2 · SHOULD** Reference the role tokens + (`var(--ran-{component}-…, var(--ran-text-{role}…, fallback))`) instead of hand-copying the px/weight + number. +- **R-TYPE-3 · SHOULD** Follow the Notes column of the role table; for example, compact action labels stay + on one line while field labels may wrap. + +**A role is a tool, not a law.** -**A role is a tool, not a law.** Transient/decorative UI that doesn't map to any role (an active-link weight bump, a player gesture-flash overlay) keeps its own one-off component token instead of being forced into the nearest role — see `r-link`'s `--ran-link-active-font-weight` and `r-player`'s `--ran-player-gesture-flash-font-weight`. +- **R-TYPE-4 · MAY** Transient/decorative UI that maps to no role (an active-link weight bump, a player + gesture-flash overlay) keeps its own one-off component token instead of being forced into the nearest + role — see `r-link`'s `--ran-link-active-font-weight` and `r-player`'s + `--ran-player-gesture-flash-font-weight`. ## ranui radius & elevation Radius: `--ran-radius-sm` 6 · `--ran-radius-md` 12 · `--ran-radius-lg` 16 · `--ran-radius-full` 9999. -- Controls (button, input, select) → `sm`. Cards / dialogs → `md`. Large surfaces → `lg`. Pills / avatars → `full`. +- **R-RADIUS-1 · SHOULD** Controls (button, input, select) → `sm`. Cards / dialogs → `md`. Large surfaces → + `lg`. Pills / avatars → `full`. + +**Elevation is a role, not decoration.** + +- **R-ELEV-1 · SHOULD** Pick the shadow tier from the table below by _what the element is_. +- **R-ELEV-2 · SHOULD** Make sure each tier is actually perceptible — a shadow you can't see fails its job. -**Elevation is a role, not decoration.** Pick the shadow by _what the element is_, and make sure the tier is actually perceptible — a shadow you can't see fails its job. +| Tier | Token | Use for | +| ------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| Raised | `--ran-shadow-elevated` | In-flow surfaces that also have a border — `r-card`, `r-section`. Subtle on purpose. | +| Overlay | `--ran-shadow-menu` | Transient layers floating **over** content — `r-dropdown`, `r-select` menu, `r-popover`, tooltips, `r-message`/toast. Clearly lifts off the page. | +| Modal | `--ran-shadow-modal` | Blocking dialogs — `r-modal`. Strongest. | -| Tier | Token | Use for | -| ------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -| Raised | `--ran-shadow-elevated` | In-flow surfaces that also have a border — `r-card`, `r-section`. Subtle on purpose. | -| Overlay | `--ran-shadow-menu` | Transient layers floating **over** content — `r-dropdown`, `r-select` menu, `r-popover`, tooltips, `r-message`/toast. Must clearly lift off the page. | -| Modal | `--ran-shadow-modal` | Blocking dialogs — `r-modal`. Strongest. | +- **R-ELEV-3 · MUST** A floating overlay does not fall back to `--ran-shadow-elevated` (the card tier) — it + would look flat. _Proof: test:packages/ranui/test/unit/theme.semantic-tokens.test.ts, review_ +- **R-ELEV-4 · MUST** An overlay's shadow clearly lifts it off the page. _Proof: browser_ -A floating overlay must **never** fall back to `--ran-shadow-elevated` (the card tier) — it will look flat. Borderless overlays (dropdown, toast) rely on the shadow alone for separation, so the overlay tiers carry real weight. +Borderless overlays (dropdown, toast) rely on the shadow alone for separation, so the overlay tiers carry real weight. -**Stacking follows the same roles (z-index ladder).** Floating overlays portal to ``, so they need an explicit stacking tier. The z-index must sit on the element that is **positioned and portaled** (the host) — a z-index on an inner shadow node is trapped in the host's stacking context and does nothing against page content. +**Stacking follows the same roles (z-index ladder).** Floating overlays portal to ``, so they need an explicit stacking tier. + +- **R-Z-1 · MUST** The z-index sits on the element that is **positioned and portaled** (the host); a + z-index on an inner shadow node is trapped in the host's stacking context and does nothing against page + content. _Proof: test:packages/ranui/test/e2e/dropdown-layer.spec.ts_ +- **R-Z-2 · MUST** Overlays use the stacking tiers in the table below, so a select opened inside a modal + stays visible. _Proof: test:packages/ranui/test/e2e/dropdown-layer.spec.ts_ +- **R-Z-3 · MUST** Toasts and notifications sit on the top tier (`--ran-z-message`), above modals and + dropdowns. _Proof: review_ | Tier | Token | Default | Use for | | -------- | ------------------ | ------- | --------------------------------------------------------------------------------------------------- | | Modal | `--ran-z-modal` | `1000` | Blocking dialogs (`r-modal`) and their mask. | | Dropdown | `--ran-z-dropdown` | `1100` | Dropdown / select menu / popover. **Above** modal, so a select opened inside a modal stays visible. | -| Message | `--ran-z-message` | `1200` | Toasts / notifications. Always on top. | +| Message | `--ran-z-message` | `1200` | Toasts / notifications. The top tier. | -Override a tier globally (set `--ran-z-dropdown` on `:root`) or per component (`--ran-dropdown-host-z-index`, `--ran-modal-root-z-index`, `--ran-message-z-index`) — never reach for `!important`. An overlay open while the page scrolls must re-run its placement on `scroll`/`resize` so it tracks its trigger (e.g. inside a sticky header). +- **R-Z-4 · MUST** Override a tier globally (set `--ran-z-dropdown` on `:root`) or per component + (`--ran-dropdown-host-z-index`, `--ran-modal-root-z-index`, `--ran-message-z-index`), never with + `!important`. _Proof: test:packages/ranui/test/e2e/dropdown-layer.spec.ts, review_ +- **R-Z-5 · MUST** An overlay open while the page scrolls re-runs its placement on `scroll`/`resize` so it + tracks its trigger (e.g. inside a sticky header). _Proof: test:packages/ranui/test/unit/utils.floating.test.ts_ **Host stacking contexts:** a body-portaled overlay compares directly with root chrome; it usually needs no wrapper elevation. A non-portaled fixed overlay remains trapped in its ancestor stacking context (`isolation`, `transform`, `opacity`, `filter`, `will-change`). -Raise an enclosing wrapper only while that overlay actually paints, including its closing -transition. A blanket elevated wrapper promotes ordinary content above sticky chrome too. -Verify reflected open/closing attributes against the component API before using selectors. - -**Never animate a theme switch.** Transitions are for _interaction_ (hover / focus / press), not for flipping light↔dark. CSS cannot tell _why_ a property changed: any palette prop listed in a `transition` will also fade when the theme flips its token — each component at its own duration, while the host page has already switched. A general-purpose library must not impose that on its consumers. Rules: - -- **Palette props never get a default transition.** `background` / `background-color`, `color`, `border-color`, `box-shadow`, `fill`, `stroke` — none of them, on any element that carries a theme-driven color. Hover/focus feedback on these props snaps. -- **Motion props may transition.** `transform`, `opacity`, and box-geometry props (`left` / `top` / `width` / `height` / `font-size`) don't follow the theme — checkbox pops, tab ink-bars, ripples all stay animated. -- `transition: all` and bare-duration shorthands (`transition: 0.2s` — which means `all`) are banned; they silently include palette props. -- Every removed default keeps its `--ran-*-transition` hook var, so a consumer who wants palette fades can opt in per component. +A blanket elevated wrapper promotes ordinary content above sticky chrome too. + +- **R-Z-6 · MUST** Raise an enclosing wrapper only while that overlay actually paints, including its + closing transition. _Proof: browser_ +- **R-Z-7 · SHOULD** Verify reflected open/closing attributes against the component API before using + selectors. + +**Theme switches are not animated.** Transitions are for _interaction_ (hover / focus / press), not for flipping light↔dark. CSS cannot tell _why_ a property changed: any palette prop listed in a `transition` will also fade when the theme flips its token — each component at its own duration, while the host page has already switched. A general-purpose library that did this would impose it on every consumer. For ranui components this adapter deliberately tightens the portable D-MOTION-12 to MUST. + +- **R-MOTION-1 · MUST** Do not animate a theme switch. _Proof: browser_ +- **R-MOTION-2 · MUST** **Palette props get no default transition.** `background` / `background-color`, + `color`, `border-color`, `box-shadow`, `fill`, `stroke` — none of them, on any element that carries a + theme-driven color. Hover/focus feedback on these props snaps. _Proof: review, browser_ +- **R-MOTION-3 · MAY** **Motion props may transition.** `transform`, `opacity`, and box-geometry props + (`left` / `top` / `width` / `height` / `font-size`) do not follow the theme — checkbox pops, tab + ink-bars, ripples all stay animated. +- **R-MOTION-4 · MUST** Do not use `transition: all` or bare-duration shorthands (`transition: 0.2s` — + which means `all`); they silently include palette props. _Proof: review_ +- **R-MOTION-5 · MUST** Every removed default keeps its `--ran-*-transition` hook var, so a consumer who + wants palette fades can opt in per component. _Proof: review_ ```less /* ✗ fades on theme switch (palette props; `all`; bare duration = all) */ @@ -725,51 +975,87 @@ transition: var(--ran-input-transition, none); ## ranui components — how to apply the system -- Reuse existing semantic native controls or supported ranui primitives; do not rebuild them merely for styling. Choose a ranui component when its contract fits; preserve native form and keyboard semantics, including supported native controls in overlay slots ([INTERACTION §2](./INTERACTION.md#2-reachable-controls-and-predictable-focus)). -- Theme through **CSS variables**, **`::part()`**, or the **`sheet`** attribute (escape hatch). CSS variables cross Shadow DOM; selectors do not. -- Component tokens default to semantic tokens: `var(--ran-btn-background, var(--ran-color-primary, #171717))`. -- Map states to the color ladder: default → `bg`/`text`; hover → `bg-hover` / `border-hover` / `primary-hover`; active → `bg-active` / `primary-active`; disabled → appropriate disabled tokens, with opacity only when needed and still legible; focus → focus ring. -- **Interactive cards opt in**: `r-card` only reacts to hover with the `hoverable` attribute (border 400 → 500 + elevated shadow). Non-interactive cards must stay inert — never add hover feedback to something that isn't clickable. -- **When the host needs ranui theme switching UI**, use `` (system / light / dark segmented pill, wired to `setTheme`/localStorage, syncs across instances and updates `theme-color` metas) instead of hand-rolling equivalent toggles. Preserve an existing host theme authority; this does not require adding a theme switch. Localize with `label` / `label-system` / `label-light` / `label-dark`. -- **Typography ships with the system**: `import 'ranui/fonts'` (or link `dist/fonts/fonts.css`) self-hosts Geist Sans + Geist Mono (variable, OFL-licensed) — the canonical faces behind `--ran-font-family` / `--ran-font-mono`. Without it the stacks fall back to system fonts. -- See [THEME_STYLE_SYSTEM_DESIGN.md](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/THEME_STYLE_SYSTEM_DESIGN.md) for the repository-only token architecture reference and [style-tokens-public.md](./style-tokens-public.md) for the generated per-component token list. +- **R-COMP-1 · SHOULD** Reuse existing semantic native controls or supported ranui primitives; do not + rebuild them merely for styling. +- **R-COMP-2 · SHOULD** Choose a ranui component when its contract fits. +- **R-COMP-3 · MUST** Preserve native form and keyboard semantics, including supported native controls in + overlay slots ([INTERACTION §2](./INTERACTION.md#2-reachable-controls-and-predictable-focus)). + _Proof: browser_ +- **R-COMP-4 · SHOULD** Theme through **CSS variables**, **`::part()`**, or the **`sheet`** attribute + (escape hatch). CSS variables cross Shadow DOM; selectors do not. +- **R-COMP-5 · SHOULD** Component tokens default to semantic tokens: + `var(--ran-btn-background, var(--ran-color-primary, #171717))`. +- **R-COMP-6 · SHOULD** Map states to the color ladder: default → `bg`/`text`; hover → `bg-hover` / + `border-hover` / `primary-hover`; active → `bg-active` / `primary-active`; disabled → appropriate + disabled tokens, with opacity only when needed and still legible; focus → focus ring. +- **R-COMP-7 · MUST** **Interactive cards opt in**: `r-card` only reacts to hover with the `hoverable` + attribute (border 400 → 500 + elevated shadow). Non-interactive cards stay inert — no hover feedback on + something that isn't clickable. _Proof: browser_ +- **R-COMP-8 · SHOULD** **When the host needs ranui theme switching UI**, use `` (system / + light / dark segmented pill, wired to `setTheme`/localStorage, syncs across instances and updates + `theme-color` metas) instead of hand-rolling equivalent toggles. +- **R-COMP-9 · SHOULD** Preserve an existing host theme authority; R-COMP-8 does not require adding a theme + switch. +- **R-COMP-10 · SHOULD** Localize `` with `label` / `label-system` / `label-light` / + `label-dark`. +- **R-COMP-11 · MAY** **Typography ships with the system**: `import 'ranui/fonts'` (or link + `dist/fonts/fonts.css`) self-hosts Geist Sans + Geist Mono (variable, OFL-licensed) — the canonical + faces behind `--ran-font-family` / `--ran-font-mono`. Without it the stacks fall back to system fonts. + +See [THEME_STYLE_SYSTEM_DESIGN.md](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/THEME_STYLE_SYSTEM_DESIGN.md) for the repository-only token architecture reference and [style-tokens-public.md](./style-tokens-public.md) for the generated per-component token list. ### A fallback must name a token that exists -Check declared tokens, including fallbacks, against the installed theme. For example, -`--ran-color-danger` exists; a plausible `--ran-color-error` does not. An undefined `var()` -can silently discard the entire declaration. [CODING §5](./CODING.md#5-styles) owns enforcement. +For example, `--ran-color-danger` exists; a plausible `--ran-color-error` does not. +[CODING §5](./CODING.md#5-styles) owns enforcement. + +- **R-TOKEN-1 · MUST** Declared tokens, including fallbacks, name tokens that exist in the installed theme; + an undefined `var()` can silently discard the entire declaration. _Proof: check:undefined-token-fallback_ ### `hidden` must keep working -An author `:host` display rule must preserve `:host([hidden]) { display: none; }`. -See [CODING §5](./CODING.md#5-styles); hiding optional content must also remove accidental gaps. +See [CODING §5](./CODING.md#5-styles). + +- **R-HOST-1 · MUST** An author `:host` display rule preserves `:host([hidden]) { display: none; }`. + _Proof: check:hidden-inert_ +- **R-HOST-2 · MUST** Hiding optional content also removes accidental gaps. _Proof: browser_ ### Hold what you build -Build shadow structure once and retain its references; do not rediscover it through selectors. [CODING §2](./CODING.md#2-the-component-skeleton) owns construction, runtime-child exceptions and SSR implementation. These are engineering requirements, not extra visual recipes. +- **R-BUILD-1 · MUST** Append shadow structure in the constructor; content that genuinely cannot exist yet + is mounted later only with the documented `// deferred mount: ` marker on the line above, which also + says how a second append is prevented. _Proof: check:shadow-mount-outside-constructor_ +- **R-BUILD-2 · MUST** Retain the references to what you built; do not rediscover them through selectors. + _Proof: check:built-then-queried_ + ## ranui component token naming -Use `--ran-{component}-{element}[-{state}]-{property}`. Keep the existing component prefix; -name the most specific distinct UI part, not its full DOM/BEM path. Keep track/fill as -separate parts; drop wrapper/position segments that add no identity. State is optional and -must correspond to an independently overridable interaction state. Property names retain -their CSS meaning. Aim for at most four semantic segments after `ran`, without collapsing -distinct override points merely to shorten a name. +- **R-NAME-1 · SHOULD** Use `--ran-{component}-{element}[-{state}]-{property}`, keeping the existing + component prefix. +- **R-NAME-2 · SHOULD** Name the most specific distinct UI part, not its full DOM/BEM path; drop + wrapper/position segments that add no identity. +- **R-NAME-3 · SHOULD** Keep track/fill as separate parts. +- **R-NAME-4 · MUST** The state segment is optional, and when present corresponds to an independently + overridable interaction state. _Proof: review_ +- **R-NAME-5 · SHOULD** Property names retain their CSS meaning. +- **R-NAME-6 · SHOULD** Aim for at most four semantic segments after `ran`, without collapsing distinct + override points merely to shorten a name. For example, `--ran-progress-fill-background` names a part; `--ran-progress-wrap-value-background` encodes implementation nesting. The generated [public catalog](./style-tokens-public.md) -owns available names. This section owns naming; do not hand-maintain another catalog. +owns available names. This section owns naming. + +- **R-NAME-7 · SHOULD** Do not hand-maintain another token catalog. ## Applying generic composition to ranui -The [navigation-layer rules](./DESIGN.md#containers-do-not-fix-structure) map to ranui as -follows: `r-modal` for a short confirmation or a single field, `r-popover` / `r-dropdown` for -transient context, `r-disclosure-row` for progressive disclosure, and `r-tabs` only for peer -views of one object. +- **R-COMPOSE-1 · SHOULD** Map the [navigation-layer rules](./DESIGN.md#containers-do-not-fix-structure) + to ranui as follows: `r-modal` for a short confirmation or a single field, `r-popover` / `r-dropdown` + for transient context, `r-disclosure-row` for progressive disclosure, and `r-tabs` only for peer views + of one object. ### ranui spacing relationships @@ -782,15 +1068,20 @@ views of one object. | Related subsections | `--ran-space-6` | Compact blocks | | Major region boundary | `--ran-space-8` | Region separation | -**Inside before outside:** these are host composition choices, not fixed component heights. -Major regions can use `--ran-space-10` when their relationship warrants it. Absent descriptions -leave no vacant row. The generic composition contract owns the relationship; this adapter owns -its mapping to the installed ranui scale. +- **R-COMPOSE-2 · SHOULD** Space each relationship with the token the table above maps it to. -For geometry proofs, measure the host, documented shadow parts and slotted text. Use -`ranui/testing` for closed-root browser tests; do not introduce production shadow queries. -Use generated public variables and parts for overrides. Consumer aliases may improve naming -while deriving their values from the existing `--ran-*` foundation. +**Inside before outside:** these are host composition choices, not fixed component heights. The +generic composition contract owns the relationship; this adapter owns its mapping to the installed +ranui scale. + +- **R-COMPOSE-3 · MAY** Major regions use `--ran-space-10` when their relationship warrants it. +- **R-COMPOSE-4 · MUST** Absent descriptions leave no vacant row. _Proof: browser_ +- **R-COMPOSE-5 · SHOULD** For geometry proofs, measure the host, documented shadow parts and slotted text. +- **R-COMPOSE-6 · SHOULD** Use `ranui/testing` for closed-root browser tests. +- **R-COMPOSE-7 · MUST** Do not introduce production shadow queries. _Proof: review_ +- **R-COMPOSE-8 · SHOULD** Use generated public variables and parts for overrides. +- **R-COMPOSE-9 · MAY** Consumer aliases may improve naming while deriving their values from the existing + `--ran-*` foundation. ```css .workspace { @@ -806,6 +1097,9 @@ while deriving their values from the existing `--ran-*` foundation. } ``` -The 32px target illustrates one host role, not a universal minimum. Verify coarse-input -bounds independently. ranui ships no table, tree, calendar, kanban or timeline; use semantic -host implementations with these same foundation roles where those views are needed. +The 32px target illustrates one host role, not a universal minimum. ranui ships no table, tree, +calendar, kanban or timeline. + +- **R-COMPOSE-10 · MUST** Verify coarse-input bounds independently. _Proof: browser_ +- **R-COMPOSE-11 · SHOULD** Where table, tree, calendar, kanban or timeline views are needed, use semantic + host implementations with these same foundation roles. diff --git a/packages/ranui/docs/INTERACTION.md b/packages/ranui/docs/INTERACTION.md index c8d3b0ec5f..1148599c31 100644 --- a/packages/ranui/docs/INTERACTION.md +++ b/packages/ranui/docs/INTERACTION.md @@ -8,79 +8,116 @@ This file owns **observable behavior**, independent of the screen's domain or vi Use [WORKFLOW_PATTERNS.md](./WORKFLOW_PATTERNS.md) only for a relevant multi-step task. No workflow infrastructure is implied by these general rules. +Each rule carries an ID and a level. **MUST** is a defect when broken and names its proof; +**SHOULD** is the default, and a deviation states its reason for that case; **MAY** is optional. +The [rule index](./RULES.md) lists every graded rule with its proof. + ## 1. Inputs, drafts and intent -Keep labels, prerequisites and validation near the affected field. Distinguish a missing value from -an unavailable service. Explain why submission is unavailable when the prerequisite is not obvious. -Use appropriate native input semantics and autocomplete; preserve platform editing conventions. +- **X-INPUT-1 · MUST** Keep labels, prerequisites and validation messages near the field they affect. + _Proof: browser_ +- **X-INPUT-2 · SHOULD** Distinguish a missing value from an unavailable service, and explain why + submission is unavailable when the prerequisite is not obvious. +- **X-INPUT-3 · SHOULD** Use the appropriate native input semantics and autocomplete, and preserve + platform editing conventions; expose keyboard shortcuts where they help. -| Transition | Required decision | +| Transition | Expected behavior | | ------------------------------- | -------------------------------------------------------------------------------- | | Empty → valid form | Enable the relevant submit action; leave context controls usable | | Submit → running | Dispatch once; expose busy state without unexpectedly moving focus | | Running → new edits | Specify whether edits affect this job, a later job or a retained draft | -| Failed → restore | Restore only repopulates fields; it must not submit them | +| Failed → restore | Restore only repopulates fields; it does not submit them | | Failed → retry | Retry dispatches again; preserve the original scope or ask about changed context | | Settings/source change → return | Keep unfinished input and the user's selection where possible | -Enter submits only where the field's documented convention calls for it, outside IME composition; -check composition state and `isComposing`. Preserve multiline entry and expose shortcuts when useful. -Dictation updates text and never submits by itself; retain the text that preceded capture. -Do not rebuild a focused input on each search response, stream update, resize or status change. - -The latest intent remains authoritative: an older query must not replace newer results, and an -import preview for one file must not appear under another. Use the host’s async identity and cancellation mechanisms; ranui contributors can consult -[CODING §3](./CODING.md#3-state-and-ownership). Retaining a draft does not prove it was saved: -promise persistence only after it succeeds. - -Search highlights literal matches in titles or snippets using safe text nodes or equivalent -escaping, never query-as-HTML. Keep enough surrounding text to explain why an item matched. +- **X-INPUT-4 · MUST** Submit dispatches once per activation, and Restore only repopulates fields without + submitting them. _Proof: unit_ +- **X-INPUT-5 · SHOULD** Follow the remaining rows of the input transition table. +- **X-INPUT-6 · MUST** Enter submits only where the field's documented convention calls for it, and never + during IME composition (check `isComposing`). Multiline entry keeps working. _Proof: browser_ +- **X-INPUT-7 · MUST** Dictation updates text and never submits by itself; the text that preceded the + capture is retained. _Proof: browser_ +- **X-INPUT-8 · MUST** Do not rebuild a focused input on a search response, stream update, resize or + status change. _Proof: browser_ +- **X-INPUT-9 · MUST** The latest intent wins: an older query never replaces newer results, and a preview + for one file never appears under another. _Proof: unit_ +- **X-INPUT-10 · SHOULD** Implement X-INPUT-9 with the host’s async identity and cancellation mechanisms; + ranui contributors can consult + [CODING §3](./CODING.md#3-state-and-ownership). +- **X-INPUT-11 · MUST** Promise persistence only after it succeeds; a retained draft is not a saved one. + _Proof: unit_ +- **X-INPUT-12 · MUST** Highlight search matches with safe text nodes or equivalent escaping, never by + inserting the query as HTML. _Proof: unit_ +- **X-INPUT-13 · SHOULD** Keep enough text around a highlighted match to explain why the item matched. ## 2. Reachable controls and predictable focus -Use native semantics and supported component contracts. Name icon-only actions accessibly. -Secondary actions may appear on hover **and keyboard focus**, with a persistent tap route on -coarse input. Keep essential recovery visible. Disabled controls remain quiet on hover and -cannot dispatch. Preserve a stable target box and focus ring as labels or status change. - -Use public attributes/events/slots/variables/parts. A keyboard-safe primitive does not prove a composed -dialog is safe: a focus trap may not discover a closed-shadow button in a footer. Verify initial focus, -Tab/Shift+Tab, Escape/Cancel, Enter and focus restoration. Fix the supported integration or use native -buttons in its supported slot; do not patch private shadow roots or add misleading host tab stops. - -A collapsed region must still identify its contents and state; disclosure must not hide an -unresolved actionable error. Collapsing a region containing focus returns focus to its toggle. -Use the collapsed summary to help readers decide whether to expand. When the expanded body -already displays that content, omit the repeated summary and keep the title and state visible. -Removing the focused object moves focus to the next meaningful action or a stable parent control. -Do not move focus merely because a background request completed. +- **X-FOCUS-1 · SHOULD** Build controls from native semantics and supported component contracts. +- **X-FOCUS-2 · MUST** Every icon-only action has an accessible name. _Proof: browser_ +- **X-FOCUS-3 · MUST** A secondary action revealed on hover is also revealed on keyboard focus and has a + persistent tap route on coarse input. _Proof: browser_ +- **X-FOCUS-4 · SHOULD** Keep essential recovery actions visible rather than revealed on hover. +- **X-FOCUS-5 · MUST** Disabled controls stay quiet on hover and cannot dispatch. _Proof: browser_ +- **X-FOCUS-6 · MUST** The target box and focus ring keep their size and position while a label or status + changes. _Proof: browser_ +- **X-FOCUS-7 · MUST** Integrate through public attributes, events, slots, variables and parts; do not + patch private shadow roots. _Proof: review_ +- **X-FOCUS-8 · MUST** Do not add host tab stops that lead nowhere or duplicate an inner control. + _Proof: browser_ +- **X-FOCUS-9 · MUST** A composed dialog places initial focus inside itself, keeps Tab and Shift+Tab + within it, closes on Escape and Cancel, activates its default action on Enter, and returns focus to + the opener when it closes. _Proof: browser_ +- **X-FOCUS-10 · SHOULD** When a focus trap cannot reach a control (for example a closed-shadow button in + a footer), fix the supported integration or place native buttons in its supported slot. +- **X-FOCUS-11 · MUST** A collapsed region still identifies its contents and state, and disclosure never + hides an unresolved actionable error. _Proof: browser_ +- **X-FOCUS-12 · MUST** Collapsing a region that contains focus returns focus to its toggle; removing the + focused object moves focus to the next meaningful action or a stable parent control. _Proof: browser_ +- **X-FOCUS-13 · MUST** Do not move focus merely because a background request completed. _Proof: browser_ +- **X-FOCUS-14 · SHOULD** Write the collapsed summary to help readers decide whether to expand; when the + expanded body already shows that content, omit the repeated summary and keep the title and state. + +A keyboard-safe primitive does not make a composed dialog safe; X-FOCUS-9 is about the composition. ## 3. Feedback and reading position -Associate feedback with the object acted on. A “Copied” acknowledgment beside Copy is easier -to associate than an unrelated global notification. Show it only after clipboard success; handle -rejection locally, preserving button geometry and focus. Use a toast for changes beyond the current -object or when local feedback would not be noticed. Do not announce every streamed character. -Bind async feedback to the operation's source snapshot. If code changes while Copy is pending, -the older completion must not mark the new content “Copied”; clear or identify stale feedback. -Verify delayed success and failure across an intervening stream update, not just an isolated delay. - -For live logs or streaming results, follow only while the reader is at the latest content. Scrolling -away suspends following; a “Jump to latest” action restores it without stealing reading position. -Use a supported follow/pin contract from the host or chosen component. Keep job and persistence -state in the application; an append-only reading log is not a job manager. +- **X-FEEDBACK-1 · SHOULD** Show feedback next to the object acted on. A “Copied” acknowledgment beside + Copy is easier to associate than an unrelated global notification; a toast suits changes beyond the + current object or feedback that would otherwise go unnoticed. +- **X-FEEDBACK-2 · MUST** Show a success acknowledgment only after the operation succeeded (for Copy, + after clipboard success), and handle rejection locally while keeping button geometry and focus. + _Proof: browser_ +- **X-FEEDBACK-3 · MUST** Async feedback belongs to the operation's source snapshot: when content changes + while Copy is pending, the older completion never marks the new content “Copied”, and stale feedback + is cleared or identified as stale. _Proof: unit, browser_ +- **X-FEEDBACK-4 · MUST** Do not announce every streamed character to assistive technology. + _Proof: browser_ +- **X-FEEDBACK-5 · MUST** Live logs and streaming results follow only while the reader is at the latest + content; scrolling away suspends following, and “Jump to latest” restores it without stealing the + reading position. _Proof: recording_ +- **X-FEEDBACK-6 · SHOULD** Use a supported follow/pin contract from the host or chosen component, and keep + job and persistence state in the application; an append-only reading log is not a job manager. + +For X-FEEDBACK-3, exercise delayed success and failure across an intervening stream update, not only an +isolated delay. ## 4. Progress, failure and recovery -Show a known phase: “Reading files”, “Checking permissions” or “Writing export”. A spinner signals -activity, not a percentage. Show percentage only with a measurable denominator; elapsed time is -secondary and must not masquerade as a completion estimate. Announce meaningful phase changes, -not every timer tick or streamed character. - -An error answers **what failed, whether anything changed, and what to do next**. For example: -“The import stopped before saving. Your selected files are still here. Retry the import.” -Only make that promise if the host can establish it. Preserve in-memory input if storage fails; -explain that persistence failed separately. Put credential-free diagnostics in optional details. +- **X-RECOVER-1 · SHOULD** Name a known phase (“Reading files”, “Checking permissions”, “Writing export”) + rather than showing only a spinner, and announce meaningful phase changes, not every timer tick. +- **X-RECOVER-2 · MUST** Show a percentage only with a measurable denominator; elapsed time is secondary + and never presented as a completion estimate. _Proof: review_ +- **X-RECOVER-3 · MUST** An error answers what failed, whether anything changed, and what to do next, and + promises only what the host can establish. _Proof: review_ +- **X-RECOVER-4 · MUST** Preserve in-memory input when storage fails, and report the persistence failure + separately. _Proof: unit_ +- **X-RECOVER-5 · SHOULD** Put credential-free diagnostics in optional details. +- **X-RECOVER-6 · SHOULD** Give one failure one coherent recovery, not duplicate rows and toasts. +- **X-RECOVER-7 · MUST** A timeout does not prove rollback: establish whether a mutating operation + committed before retrying, using the host's idempotency or deduplication contract where available. + _Proof: unit_ + +For example: “The import stopped before saving. Your selected files are still here. Retry the import.” | Condition | Useful response | Avoid | | ------------------------------------ | ----------------------------------------------------------- | ------------------------------------------------------------------ | @@ -90,24 +127,27 @@ explain that persistence failed separately. Put credential-free diagnostics in o | User cancelled | Mark cancelled; retain recoverable output | Red failure styling or restarting paused work | | Context changed | Identify stale output; refresh against current data | Applying an old result to a newer document silently | -One failure should have one coherent recovery, not duplicate rows and toasts. A timeout does not -prove rollback: establish whether a mutating operation committed before retrying, using the host's -idempotency/deduplication contract where available. Do not promise that Cancel undoes a completed write. - ## 5. Cancel, retry and completion are different outcomes -Keep Cancel and Submit independently reachable when both are supported, with clear accessible -names and stable positions. A cancellation request is not an acknowledged cancellation: show its -pending state until confirmed. If the service keeps running and the client can only stop waiting, -name that limitation instead of claiming the job stopped. Distinguish cancellation from failure. - -Retry dispatches work again; Restore only repopulates editable inputs. Do not silently broaden scope -or switch destinations on retry. Confirm a completed write before reporting saved/applied success; -retain recoverable partial output. Offer Undo only when the host can actually reverse the operation. +- **X-CANCEL-1 · MUST** Keep Cancel and Submit independently reachable when both are supported, with + clear accessible names and stable positions. _Proof: browser_ +- **X-CANCEL-2 · MUST** A cancellation request is not an acknowledged cancellation: show its pending state + until confirmed, and when the client can only stop waiting, say so instead of claiming the job stopped. + _Proof: unit_ +- **X-CANCEL-3 · MUST** Cancellation is not shown as failure. _Proof: browser_ +- **X-CANCEL-4 · MUST** Retry dispatches work again without silently broadening scope or switching + destinations; Restore only repopulates editable inputs. _Proof: unit_ +- **X-CANCEL-5 · MUST** Report saved or applied success only after the write is confirmed, and retain + recoverable partial output. _Proof: unit_ +- **X-CANCEL-6 · MUST** Offer Undo only when the host can actually reverse the operation, and never promise + that Cancel undoes a completed write. _Proof: review_ ## 6. Verify what a person can observe -Choose evidence by the defect, rather than requiring every possible test for a small change: +- **X-VERIFY-1 · SHOULD** Choose evidence by the defect, using the layers below, rather than running every + possible test for a small change. +- **X-VERIFY-2 · SHOULD** Include offline and failure paths when the task depends on external work. +- **X-VERIFY-3 · SHOULD** Report the tested commit and any remaining external prerequisites. | Evidence layer | Relevant regression | | -------------- | ------------------------------------------------------------------------------------------- | @@ -117,9 +157,8 @@ Choose evidence by the defect, rather than requiring every possible test for a s | Cross-surface | Affected forms, search, history, settings, import/export, previews and confirmations | | Variants | Relevant themes, widths, resize, keyboard/coarse input, translations/RTL and reduced motion | -Include offline/failure paths when the task depends on external work. Static audits cannot prove -focus restoration, the winning cascade or preserved reading position. Report the tested commit and -remaining external prerequisites. Library contributors use [CODING's checklist](./CODING.md#checklist-before-opening-a-pull-request); +Static audits cannot prove focus restoration, the winning cascade or a preserved reading position. +Library contributors use [CODING's checklist](./CODING.md#checklist-before-opening-a-pull-request); consumer applications use the host's relevant commands, not ranui's source-checkout commands. --- diff --git a/packages/ranui/docs/RULES.md b/packages/ranui/docs/RULES.md new file mode 100644 index 0000000000..787fadc4a9 --- /dev/null +++ b/packages/ranui/docs/RULES.md @@ -0,0 +1,491 @@ + + + + +# Rule index + +Every graded rule, with its level and proof (152 MUST, 230 SHOULD, 6 MAY). Open a rule's ID for its section. + +Proof kinds: **browser** — real browser interaction ([browser proof](./ACCEPTANCE.md#browser-proof)); +**recording** — continuous recording ([motion over time](./ACCEPTANCE.md#review-motion-over-time)); +**unit** — a unit or state test in the host; **review** — inspection of the change itself; +`check:` — a `verify:design` rule; `test:` — a named test (paths from the repository root). + +| ID | Level | Rule | Proof | +| -------------------------------------------------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | +| [D-COLOR-1](./DESIGN.md#1-color--a-state-ladder-not-a-palette) | SHOULD | Choose semantic roles for content, surfaces, boundaries, actions and status. | | +| [D-COLOR-2](./DESIGN.md#1-color--a-state-ladder-not-a-palette) | SHOULD | Define default, hover, active, disabled and focus states together. | | +| [D-COLOR-3](./DESIGN.md#1-color--a-state-ladder-not-a-palette) | SHOULD | Resolve light/dark values through the host’s authoritative tokens, and verify contrast in the actual composition. | | +| [D-SPACE-1](./DESIGN.md#2-spacing--a-limited-rhythmic-scale) | SHOULD | Use the host’s existing spacing scale. | | +| [D-SPACE-2](./DESIGN.md#2-spacing--a-limited-rhythmic-scale) | SHOULD | Use tight gaps to group related parts and larger gaps to separate tasks or regions. | | +| [D-SPACE-3](./DESIGN.md#2-spacing--a-limited-rhythmic-scale) | SHOULD | Establish inside padding before outside gaps. | | +| [D-SPACE-4](./DESIGN.md#2-spacing--a-limited-rhythmic-scale) | SHOULD | Do not copy a reference product’s dimensions without evaluating content, density and input method. | | +| [D-TYPE-1](./DESIGN.md#3-typography--choose-a-role-not-a-size) | SHOULD | Choose heading, label, body, action and code roles with coherent size, weight and line height, and keep a single authority for each role. | | +| [D-TYPE-2](./DESIGN.md#3-typography--choose-a-role-not-a-size) | SHOULD | Verify actual scripts, fallback metrics and long content. | | +| [D-TYPE-3](./DESIGN.md#3-typography--choose-a-role-not-a-size) | SHOULD | Establish hierarchy with typography before decorative containers. | | +| [D-ELEV-1](./DESIGN.md#4-radius--elevation) | SHOULD | Coordinate nested corner radii with their real inset. | | +| [D-ELEV-2](./DESIGN.md#4-radius--elevation) | SHOULD | Use elevation to explain a floating layer, not importance, through the host’s stacking roles, and verify overlays against surrounding chrome. | | +| [D-MOTION-1](./DESIGN.md#5-motion--prefer-none) | SHOULD | Use the host system’s motion tokens and verify frequency, continuity and reduced motion in the actual task. | | +| [D-MOTION-2](./DESIGN.md#5-motion--prefer-none) | SHOULD | Default repeated interaction to immediate feedback; a larger change is not automatically a reason to animate. | | +| [D-MOTION-3](./DESIGN.md#5-motion--prefer-none) | SHOULD | Use existing tokens for purposeful motion and keep it quick and interruptible. Reduced motion is graded under D-MOTION-10 and D-ACCESS-5. | | +| [D-MOTION-4](./DESIGN.md#functional-motion-contract) | SHOULD | Specify the trigger, state change and destination before choosing an effect. | | +| [D-MOTION-5](./DESIGN.md#functional-motion-contract) | MUST | Motion preserves unrelated content, drafts and reading position. | recording | +| [D-MOTION-6](./DESIGN.md#functional-motion-contract) | SHOULD | Give sibling components with the same interaction meaning a shared motion language; consistency does not require every component to animate. | | +| [D-MOTION-7](./DESIGN.md#functional-motion-contract) | MUST | When motion is used, a new intent can interrupt or reverse it from the current presentation, without a jump, stale completion or duplicate activation. | recording | +| [D-MOTION-8](./DESIGN.md#functional-motion-contract) | MUST | Feedback stays immediate; animation is not an input lock. | recording | +| [D-MOTION-9](./DESIGN.md#functional-motion-contract) | SHOULD | During insertion/removal, avoid visibly squeezing text or passing exiting controls across live actions; coordinate opacity and layout so the result stays readable. | | +| [D-MOTION-10](./DESIGN.md#functional-motion-contract) | MUST | Reduced motion retains a visible, understandable final state. | browser | +| [D-MOTION-11](./DESIGN.md#functional-motion-contract) | MAY | Use a spring; it need not overshoot. | | +| [D-MOTION-12](./DESIGN.md#functional-motion-contract) | SHOULD | Let theme palettes settle coherently. Avoid implicit palette fades or `transition: all`; opt in explicitly only where the host intends and verifies a coordinated effect. | | +| [D-MOTION-13](./DESIGN.md#functional-motion-contract) | MUST | Factual values and readable content are shown immediately on return to a working surface, without animated counts. | browser | +| [D-MOTION-16](./DESIGN.md#functional-motion-contract) | SHOULD | Do not replay decorative title entrances on each return to a working surface. | | +| [D-MOTION-14](./DESIGN.md#functional-motion-contract) | MAY | A loading demonstration may move to demonstrate loading. | | +| [D-MOTION-15](./DESIGN.md#functional-motion-contract) | MUST | A status pulse appears only while the activity is live. | review | +| [D-COPY-1](./DESIGN.md#6-content--copy-is-part-of-the-system) | SHOULD | Buttons name the outcome. Include the object when context does not establish it: "Deploy project", "Delete member". In a clearly named object or dialog, "Save" or "Delete" is sufficient; avoid generic "OK". See [contextual wording](#words-are-part-of-the-interface). | | +| [D-COPY-2](./DESIGN.md#6-content--copy-is-part-of-the-system) | SHOULD | Errors say **what happened**, then **how to fix it**. ✅ "Build failed: the bundle exceeds the size limit. Reduce it or raise the limit." ❌ "Operation failed, please try again." | | +| [D-COPY-3](./DESIGN.md#6-content--copy-is-part-of-the-system) | SHOULD | Confirmations and toasts state the completed **change**. ✅ "Project deleted". ❌ "Operation successful". | | +| [D-COPY-4](./DESIGN.md#6-content--copy-is-part-of-the-system) | MUST | Show completion feedback only after the operation is confirmed. | unit, browser | +| [D-COPY-5](./DESIGN.md#6-content--copy-is-part-of-the-system) | SHOULD | Be specific; every sentence removes guesswork rather than adding it. | | +| [D-ACCESS-1](./DESIGN.md#7-accessibility--the-system-serves-everyone) | MUST | Text keeps sufficient contrast with its background (**WCAG AA**). | browser | +| [D-ACCESS-2](./DESIGN.md#7-accessibility--the-system-serves-everyone) | MUST | Never signal state with color alone; pair it with an icon or text (e.g. ✓ / ✕ labels, not just green/red). | review | +| [D-ACCESS-3](./DESIGN.md#7-accessibility--the-system-serves-everyone) | MUST | Every interactive element has a **visible focus ring** appropriate to its surface; never remove it for "cleanliness". | browser | +| [D-ACCESS-4](./DESIGN.md#7-accessibility--the-system-serves-everyone) | MUST | **Full keyboard navigation**: nothing is mouse-only. | browser | +| [D-ACCESS-5](./DESIGN.md#7-accessibility--the-system-serves-everyone) | MUST | Respect `prefers-reduced-motion`. | browser | +| [D-ACCESS-6](./DESIGN.md#7-accessibility--the-system-serves-everyone) | MUST | When the host supports both schemes, the initial render follows `prefers-color-scheme`. | browser | +| [D-INPUT-1](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | MUST | Support mouse, keyboard and touch at narrow and wide widths. | browser | +| [D-INPUT-2](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | MUST | Reflow controls instead of hiding the only route to an action. | browser | +| [D-INPUT-3](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | MUST | A hover affordance has a click/tap alternative. | browser | +| [D-INPUT-4](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | MUST | Drags use Pointer Events, including cancellation. | browser | +| [D-INPUT-5](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | MUST | Scope `touch-action` to the actual drag surface and pair it with a real handler; ordinary scrolling keeps working elsewhere. | browser | +| [D-INPUT-6](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | SHOULD | Prefer content/viewport-relative bounds (`min()`, `max()`, `clamp()`) to arbitrary breakpoints. | | +| [D-INPUT-7](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | SHOULD | Use existing host breakpoints, and introduce a justified shared role only when repeated needs emerge. | | +| [D-INPUT-8](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | SHOULD | Exercise resizing into a narrow layout, not only loading there. | | +| [D-INPUT-9](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | MUST | Overlay placement tracks its trigger while open. | browser | +| [D-INPUT-10](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | SHOULD | Test actual pointer/touch gestures, hover fallback and keyboard routes where affected; checking two color schemes does not establish input parity. | | +| [D-COMP-1](./DESIGN.md#9-composition--the-page-not-the-component) | SHOULD | Choose the page structure in §10 first; then compose its components with these rules. | | +| [D-COMP-2](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | Before implementation, record a short design brief in the existing task note or product design owner. Use the existing system; a small fix only needs the affected decisions. The brief settles each applicable decision in the table below (D-COMP-2). | | +| [D-COMP-4](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | Reuse the brief across sibling components and docs chrome; change emphasis according to the task. | | +| [D-COMP-5](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | Keep an expressive demo visibly distinct from its documentation chrome. | | +| [D-COMP-6](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | Update the brief before changing the visual direction; do not silently rewrite the standard to justify an implementation that failed review. | | +| [D-COMP-7](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | Compare a representative composed still with different real content before animating it, then inspect the state change and return path at normal speed. | | +| [D-COMP-8](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | When an example looks imported from another product, fix that relationship at its owning token/component/style rather than adding a page-specific patch. | | +| [D-COMP-9](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | Keep reference observations and validation limits in the optional [research index](./REFERENCE_RESEARCH.md#style-coherence-and-transition-research). | | +| [D-BOX-1](./DESIGN.md#structure-before-containers) | SHOULD | **Reach for spacing, alignment, weight and a hairline before a box.** | | +| [D-BOX-2](./DESIGN.md#structure-before-containers) | SHOULD | Reserve a box for **a genuinely separate object**: a live demo stage, an overlay, a panel that floats. Not for a code block, a table, a list row, a previous/next link, or a cell in a gallery. | | +| [D-BOX-3](./DESIGN.md#structure-before-containers) | SHOULD | **Avoid redundant nested containers and doubled padding.** A card inside a padded panel does not need another decorative frame or inset. | | +| [D-BOX-4](./DESIGN.md#structure-before-containers) | SHOULD | Keep functionally required nested controls or surfaces, and coordinate their radii with the actual inset; containment alone is not a reason to add a box. | | +| [D-BOX-5](./DESIGN.md#structure-before-containers) | SHOULD | **Every persistent boundary needs a distinct job.** Start with headings, alignment and proximity to group content. Add a border, surface fill or elevation only when that structure leaves a real boundary unclear. | | +| [D-BOX-6](./DESIGN.md#structure-before-containers) | SHOULD | Do not repeat the same grouping with a panel frame, a nested card, row rules and another inset at the same time. | | +| [D-BOX-8](./DESIGN.md#structure-before-containers) | MUST | Keep keyboard focus and persistent selection cues; never remove them to make the resting layout quieter. | browser | +| [D-BOX-9](./DESIGN.md#structure-before-containers) | SHOULD | Review the full pane or page, not only the cropped group. For every remaining stable outline, fill or separator, name the relationship or interaction it communicates and check what becomes ambiguous if it is removed. | | +| [D-COMP-10](./DESIGN.md#emphasis-is-a-budget) | SHOULD | Establish priority with structure and proximity first, and spend emphasis on the one thing that deserves it. | | +| [D-ELEV-3](./DESIGN.md#emphasis-is-a-budget) | SHOULD | Reserve shadows for surfaces that genuinely float above content: popovers, menus, dialogs, notifications. Do not shadow every card. | | +| [D-SPACE-6](./DESIGN.md#spacing-carries-meaning) | SHOULD | Do not undo a gap's relationship with a decorative divider. | | +| [D-SPACE-7](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Repeat relationships, not fixed heights. Choose title/description, field/hint, group and region gaps from the host scale according to their relationship. | | +| [D-SPACE-8](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Let translations grow naturally, and leave no vacant row for a missing description. | | +| [D-SPACE-9](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Give each gap one owner: parent stack or explicit child margin. Reset authored local heading/paragraph margins rather than adding them to the parent's gap. | | +| [D-SPACE-10](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Account for owned control padding, collapsing margins and inline baseline space; do not compensate with transforms or smaller type. | | +| [D-SPACE-11](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Place hints with the field/action they explain. | | +| [D-SPACE-12](./DESIGN.md#vertical-rhythm-across-sibling-views) | MUST | Hidden helpers leave no accidental gap. | browser | +| [D-SPACE-16](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | A reserved async status line has a stated purpose and bounds. Measure both axes after content settles; [ACCEPTANCE](./ACCEPTANCE.md) grades this as V-PROOF-6. | | +| [D-SPACE-14](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Keep leading, padding and hairlines distinct: 16px padding plus a 1px rule is not a 17px spacing token. | | +| [D-SPACE-15](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Use independent expectations and exercise wrapping, resizing, helper/error visibility and relevant density. | | +| [D-TYPE-4](./DESIGN.md#the-measure-governs-text-not-the-column) | SHOULD | **Choose a readable measure for the actual script and font.** | | +| [D-TYPE-5](./DESIGN.md#the-measure-governs-text-not-the-column) | SHOULD | Review realistic text at supported widths, zoom levels and font fallbacks; CJK and mixed-script text need their own judgment. | | +| [D-TYPE-6](./DESIGN.md#the-measure-governs-text-not-the-column) | SHOULD | Let a table, code fence, demo stage or image retain the width its content needs. Cap the text elements, not the column that holds them. | | +| [D-TYPE-7](./DESIGN.md#the-measure-governs-text-not-the-column) | SHOULD | **Declare the measure in `rem`, never `em`.** | | +| [D-TYPE-8](./DESIGN.md#the-measure-governs-text-not-the-column) | SHOULD | **Build hierarchy out of type before reaching for a box.** | | +| [D-ALIGN-1](./DESIGN.md#if-the-layout-says-two-things-are-one-object-the-spacing-must-agree) | SHOULD | Decide whether the elements are one object or two, and make every property say the same thing. | | +| [D-ALIGN-2](./DESIGN.md#if-the-layout-says-two-things-are-one-object-the-spacing-must-agree) | SHOULD | **When a value must equal another value, name it.** A gap that a child has to subtract is not a literal in two places; it is one custom property referenced twice. | | +| [D-ALIGN-3](./DESIGN.md#reserve-a-column-only-when-its-content-exists) | SHOULD | Condition the track on the element actually being there (`:has()`), not on a page type you assume implies it. | | +| [D-ALIGN-4](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | **Name each alignment spine and the geometry it aligns.** State which siblings share each one; include headers, body copy, rows, actions and empty/loading/error content. | | +| [D-ALIGN-5](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | A back button plus gap may establish a separate title column: do not silently treat that title as aligned to body text. | | +| [D-ALIGN-6](./DESIGN.md#alignment-is-structure-not-polish) | MUST | A missing optional icon or badge never moves labels out of their declared column. | browser | +| [D-ALIGN-7](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | A nested level returns exactly to its parent's spine when it ends. Compare the right boxes. These are separate measurements, even when they happen to coincide; measuring the defect's layer is graded in [ACCEPTANCE](./ACCEPTANCE.md) as V-PROOF-10. | | +| [D-ALIGN-9](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | Inspect optical glyph corrections separately. For web components, measure the host, documented shadow parts and slotted text through supported test seams. V-PROOF-11 in [ACCEPTANCE](./ACCEPTANCE.md) owns this, including the ban on production shadow queries and on assuming document CSS crosses the boundary. | | +| [D-ALIGN-11](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | **Keep a header command's hit zone separate from layout filler.** A title/menu trigger hugs its content plus deliberate padding, bounded by the available width. Let a separate flex/grid track absorb remaining space. | | +| [D-ALIGN-12](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | Define truncation/wrapping and adjacent action behavior for long translations. | | +| [D-ALIGN-13](./DESIGN.md#alignment-is-structure-not-polish) | MUST | The title trigger's hit area does not span unused header space (for example through `flex: 1` on the title button) unless D-ALIGN-14 applies. | browser | +| [D-ALIGN-14](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | A row that intentionally selects its whole width is a different role; record that contract explicitly. | | +| [D-ALIGN-15](./DESIGN.md#alignment-is-structure-not-polish) | MAY | Coarse-pointer targets may grow larger. | | +| [D-ALIGN-22](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | A grown target's hover fill stays on the control's own content, not across unrelated content. | | +| [D-ALIGN-16](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | **Count every inset from the shared ancestor to the visible text.** Parent padding, nested panel padding, a control's internal padding, borders and navigation columns all contribute. | | +| [D-ALIGN-17](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | Remove a redundant inset at its owner instead of cancelling it with an unexplained negative margin. | | +| [D-ALIGN-18](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | Empty states retain their region's content spine and width; extra breathing room never introduces a second left edge or makes a compact action fill the region. | | +| [D-ALIGN-19](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | Keep product-specific insets and target sizes as application aliases, not new foundation values or universal component defaults. | | +| [D-ALIGN-20](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | **When two layout edges or gaps are meant to be equal, a one-pixel difference is a defect.** Optical glyph correction (D-ALIGN-21) is the only tolerance. | | +| [D-ALIGN-21](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | Treat optical correction for an asymmetric glyph as a separate, documented asset/component decision, reused at supported sizes while sibling gaps, baselines and spines stay exact. | | +| [D-SEM-1](./DESIGN.md#selection-links-and-other-semantic-lies) | MUST | **Selection is persistent state and never looks like hover.** | browser | +| [D-SEM-2](./DESIGN.md#selection-links-and-other-semantic-lies) | MUST | **Action and link roles remain distinguishable.** | browser | +| [D-SEM-3](./DESIGN.md#selection-links-and-other-semantic-lies) | SHOULD | Verify inline links against surrounding prose, including themes where action and body colors happen to match. | | +| [D-SEM-4](./DESIGN.md#selection-links-and-other-semantic-lies) | MUST | Anything that changes application state is a button, whatever it looks like. | review, browser | +| [D-SEM-5](./DESIGN.md#selection-links-and-other-semantic-lies) | SHOULD | A button-shaped link carries no underline. | | +| [D-CMD-1](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | **Remove redundant group enclosures without erasing control affordance.** | | +| [D-CMD-2](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Use flat rows when a recognizable menu/list pattern already conveys action; do not require them in every context. | | +| [D-CMD-3](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Give a standalone command that needs it its own restrained button surface or functional stroke, visible before hover in every supported theme. | | +| [D-CMD-4](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Let a button's own shape identify its target; an enclosing card, muted group fill and row rules still pass the distinct-boundary test (D-BOX-5). | | +| [D-CMD-5](./DESIGN.md#command-hierarchy-in-constrained-panes) | MUST | Initial discovery does not rely on hover, pointer cursors, verb labels or keyboard focus alone. | browser | +| [D-CMD-6](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Inspect the resting full pane with the pointer away, including touch use. | | +| [D-CMD-7](./DESIGN.md#command-hierarchy-in-constrained-panes) | MUST | Hover, press, disabled and keyboard focus stay distinguishable after the control is discovered. | browser | +| [D-CMD-8](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Wrap long translated labels inside one command column instead of a ragged horizontal button cloud. | | +| [D-CMD-9](./DESIGN.md#command-hierarchy-in-constrained-panes) | MUST | Do not add navigation chevrons to immediate commands. | review | +| [D-CMD-10](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Declare which visible columns are shared with adjacent checkboxes or controls: measure icon/checkbox edges and text Ranges, not only button rectangles or alignment within the command group. | | +| [D-CMD-11](./DESIGN.md#command-hierarchy-in-constrained-panes) | MAY | A hit target may extend into an owned gutter to preserve those columns; name the extension. | | +| [D-CMD-12](./DESIGN.md#command-hierarchy-in-constrained-panes) | MUST | An extended hit target stays inside its pane, does not overlap other targets, and keeps focus visible. | browser | +| [D-CMD-13](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Separate destructive commands from recovery and transfer actions. | | +| [D-CMD-14](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Let a danger label and icon communicate intent without a large red outline on every resting command; reserve the strongest danger fill for explicit confirmation. | | +| [D-CMD-15](./DESIGN.md#command-hierarchy-in-constrained-panes) | MUST | Destructive commands keep confirmation, cancellation, focus return and busy guards. | browser | +| [D-CMD-16](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Accept a command pane against its resting state, not only its hover state. | | +| [D-CMD-17](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Inspect the actual render as well: an automated surface-difference check catches invisible control fills but does not prove that users recognize the action. | | +| [D-CMD-18](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Reuse foundation roles and coarse-input target requirements; this guidance changes no component defaults. | | +| [D-TOKEN-1](./DESIGN.md#one-product-one-token-set) | SHOULD | Use the product’s authoritative semantic tokens. Aliases may improve readability, but resolve from that source rather than inventing a competing palette. | | +| [D-TOKEN-2](./DESIGN.md#one-product-one-token-set) | SHOULD | Coordinate third-party components through supported theme APIs and verify the composed result. | | +| [D-TOKEN-3](./DESIGN.md#density-follows-control-roles) | SHOULD | Measure actual hit bounds, focus and adjacent gaps. | | +| [D-TOKEN-4](./DESIGN.md#density-follows-control-roles) | SHOULD | Derive repeated host aliases from foundation tokens, not one application's dimensions copied across the library. | | +| [D-TOKEN-5](./DESIGN.md#density-follows-control-roles) | SHOULD | Choose target bounds for the actual input method without enlarging every glyph or reserving space for hidden controls. Use supported public theme APIs. | | +| [D-TOKEN-6](./DESIGN.md#density-follows-control-roles) | MUST | Name icon-only controls accessibly. | browser | +| [D-TOKEN-7](./DESIGN.md#density-follows-control-roles) | SHOULD | Document legitimate viewport, hairline, artwork and optical geometry exceptions. | | +| [D-CRAFT-1](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Choose a deliberate visual direction using realistic objects, copy and states; a generic empty shell cannot establish product quality. | | +| [D-CRAFT-2](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Evaluate the smallest intervention against real content, language, device and repeated use. | | +| [D-CRAFT-3](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Adapt their principles with the host’s tokens and actual capabilities; do not copy a product's pixel sizes or import recipes as rules. | | +| [D-CRAFT-4](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Treat references as inputs to decisions, not additional instruction sets; for example, [Don't Make Me Think](https://sensible.com/dont-make-me-think/) informs scanning, hierarchy and clear choices. | | +| [D-CRAFT-5](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | A stylistic preference needs context; a requirement identifies the task or observable contract it protects. | | +| [D-CRAFT-6](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Choose references by the job they support: logo/app identity, marketing composition and frequent working interactions solve different problems. | | +| [D-CRAFT-7](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Record the observed pattern and its adaptation; verify the result with real content. | | +| [D-CRAFT-8](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | When direction is unresolved, compare two small real-host variants with the same content, width and theme, changing one dimension at a time. Explain what each difference does for the task. | | +| [D-CRAFT-9](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Balance short headings progressively with `text-wrap: balance`, preserving normal wrapping. | | +| [D-CRAFT-10](./DESIGN.md#evaluate-craft-through-evidence) | MUST | Headings contain no locale-dependent manual line breaks and are not clipped to preserve a screenshot. | browser | +| [D-CRAFT-11](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Check cost and support before using `pretty` on long live content. | | +| [D-CRAFT-12](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Use tabular figures to stabilize digit advances if the font supports them. | | +| [D-CRAFT-13](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Reserve a meaningful column for digit-count changes such as 99 → 100. | | +| [D-CRAFT-14](./DESIGN.md#evaluate-craft-through-evidence) | MUST | A digit-count change never truncates the value. | browser | +| [D-CRAFT-15](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Correct asymmetric glyphs in a reusable asset/component, preserving exact layout spines. | | +| [D-CRAFT-16](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Derive nested corner geometry from actual inset; remove an unnecessary nested surface first. | | +| [D-CRAFT-17](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Give motion a job: immediate frequent feedback, interruptible transitions, reduced motion and instant themes. See §5. | | +| [D-CRAFT-18](./DESIGN.md#evaluate-craft-through-evidence) | MUST | Optional delight never delays typing or obscures status. | recording | +| [D-CRAFT-19](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Pause offscreen decoration and profile realistic content before adopting costly effects. | | +| [D-COPY-6](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Avoid unnecessary secondary labels and descriptions. Let the layout do the explaining. | | +| [D-COPY-7](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | A heading, grouping, proximity or obvious control needs no other sentence repeating its purpose. | | +| [D-COPY-8](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Remove copy with no new decision, constraint or consequence; do not replace it with a decorative badge or tooltip. | | +| [D-COPY-9](./DESIGN.md#words-are-part-of-the-interface) | MUST | Preserve accessible names, field labels, units, errors and risk disclosures. | browser | +| [D-COPY-10](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Preserve useful first-use guidance, and test whether the task remains understandable. | | +| [D-COPY-11](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Let context carry context: `Users` rather than `User Management`; do not repeat a dialog's question in its body. | | +| [D-COPY-12](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Name results (`Save`, `Move`, `Delete`), not gestures (`Click to save`). Include the object only when context does not establish it. | | +| [D-COPY-13](./DESIGN.md#words-are-part-of-the-interface) | MUST | `Cancel` leaves without committing. | browser | +| [D-COPY-14](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Use sentence case, no period on short labels, and a period on explanatory sentences. | | +| [D-COPY-15](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Use one `…` when an action needs more input before completing; reserve ALL CAPS for short statuses, eyebrows and acronyms. | | +| [D-REVIEW-1](./DESIGN.md#review-checklist-for-a-composed-surface) | SHOULD | Apply these questions to the affected surface and reachable changed states. Skip irrelevant variants; a small patch does not require redesigning unrelated pages. A "no" within scope is a finding. | | +| [D-REVIEW-2](./DESIGN.md#review-checklist-for-a-composed-surface) | SHOULD | Follow [ACCEPTANCE](./ACCEPTANCE.md) for independent expectations, actual runtime/capture identity, browser measurements and separate automated, agent and human results. | | +| [D-IA-1](./DESIGN.md#10-information-architecture--the-shape-before-the-composition) | SHOULD | Choose structure before composition for information-dense consoles, dashboards and workbenches. | | +| [D-IA-2](./DESIGN.md#10-information-architecture--the-shape-before-the-composition) | SHOULD | Give single-conversion and landing pages their own task structure rather than a dense-console template. | | +| [D-IA-3](./DESIGN.md#answer-three-questions-before-naming-a-component) | SHOULD | Settle all three answers before choosing components. | | +| [D-IA-4](./DESIGN.md#answer-three-questions-before-naming-a-component) | SHOULD | **A page has exactly one primary model.** Supporting models may help the reader understand or operate it; they may not compete for the first screen. | | +| [D-IA-5](./DESIGN.md#answer-three-questions-before-naming-a-component) | SHOULD | Prefer a clear, unambiguous next choice over an arbitrary click budget. Test whether readers recognize the next action, not merely whether the destination is reachable. | | +| [D-IA-6](./DESIGN.md#the-shape-follows-the-task-not-the-payload) | SHOULD | Choose a skeleton from the reader's question, not an API array or route ID, using the table below. | | +| [D-IA-7](./DESIGN.md#the-shape-follows-the-task-not-the-payload) | SHOULD | Do not confuse historical events with future steps, a filter with kanban, or ordered prose with a field grid. A date alone does not justify a calendar. | | +| [D-IA-8](./DESIGN.md#the-shape-follows-the-task-not-the-payload) | SHOULD | Ship multiple views only when each serves a distinct task worth its filter/state/action maintenance. | | +| [D-IA-9](./DESIGN.md#each-kind-of-information-has-a-place) | SHOULD | Lead with identity and status in the title or summary region, and keep actions, feedback and evidence beside their subject, as the table below lays out. | | +| [D-IA-10](./DESIGN.md#each-kind-of-information-has-a-place) | SHOULD | **Every fact has exactly one authoritative location.** Elsewhere shows a summary or an entry point that links back to it. | | +| [D-IA-11](./DESIGN.md#each-kind-of-information-has-a-place) | SHOULD | Follow the default reading order: page identity → current status or exception → primary task and primary action → the information the judgement needs → relationships, changes, evidence → secondary information and low-frequency actions. | | +| [D-IA-12](./DESIGN.md#each-kind-of-information-has-a-place) | SHOULD | **One primary action per task region**, and it is the most likely next step, not the most destructive one (§9, "Emphasis is a budget", governs how much weight it may take). | | +| [D-IA-13](./DESIGN.md#density-is-effective-information-not-controls-per-inch) | SHOULD | **Use at most two adjacent density levels on one page**: spacious for first use and risky confirmation, standard for most lists and details, compact for expert workbenches. | | +| [D-IA-14](./DESIGN.md#density-is-effective-information-not-controls-per-inch) | MAY | Container padding and line height may tighten. | | +| [D-IA-15](./DESIGN.md#density-is-effective-information-not-controls-per-inch) | MUST | Readable body text, a visible focus ring and pointer target size do not shrink with density (§7, §8). | browser | +| [D-IA-16](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | **One contextual return per view.** Do not show an outer “Back to chat” and an inner “Back to artifacts” together. | | +| [D-IA-17](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | The detail view returns to its immediate parent. | | +| [D-IA-18](./DESIGN.md#containers-do-not-fix-structure) | MUST | Ancestors remain reachable through the route hierarchy or clearly named breadcrumbs. | browser | +| [D-IA-19](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | Verify the full journey, including focus return and long translated destination names. | | +| [D-IA-27](./DESIGN.md#containers-do-not-fix-structure) | MUST | Returning to the parent restores focus to the originating item. | browser | +| [D-IA-20](./DESIGN.md#containers-do-not-fix-structure) | MUST | If text does not fit, reflow or use a meaningful shorter destination; an ellipsis or tooltip never hides the only destination cue. | browser | +| [D-IA-21](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | Choose one composition for the parent context. Do not add a breadcrumb merely to relabel a redundant return shortcut, or repeat that parent as an inert crumb above its return command. | | +| [D-IA-22](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | Use breadcrumbs when the hierarchy itself helps the task. | | +| [D-IA-23](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | Give a route to anything needing a copyable link, history, a side-by-side comparison, or work that survives a refresh. | | +| [D-IA-24](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | Reserve a modal for a short confirmation or a single field, a popover or menu for transient context, and a disclosure row for progressive disclosure. | | +| [D-IA-25](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | **Tabs carry peer views of one object** — its conversation, its checks, its diff — never unrelated modules; that is navigation's job. | | +| [D-IA-26](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | If the chosen library lacks a suitable primitive, use existing semantic elements and host tokens; verify the required interaction rather than inventing unsupported APIs. | | +| [D-VERIFY-1](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Select checks for the affected surface, supported variants and changed behavior; do not create unrelated flows merely to satisfy the list. | | +| [D-VERIFY-2](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Primary task and primary action are unmistakable. | | +| [D-VERIFY-5](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Exercise all changed **states** (hover, active, focus, disabled, loading, empty, error). | | +| [D-VERIFY-7](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Cover relevant edge cases: long text, large numbers and the host's supported scripts/locales. | | +| [D-VERIFY-8](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Spacing comes from the scale; type uses a role; color uses semantic tokens. | | +| [D-VERIFY-9](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Run the host’s applicable checks and inspect actual browser evidence. | | +| [D-VERIFY-10](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Copy follows §6. | | +| [D-VERIFY-12](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | For a dense surface: one primary model, the skeleton chosen from the question rather than the payload, and every fact with one authoritative location (§10). | | +| [X-INPUT-1](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Keep labels, prerequisites and validation messages near the field they affect. | browser | +| [X-INPUT-2](./INTERACTION.md#1-inputs-drafts-and-intent) | SHOULD | Distinguish a missing value from an unavailable service, and explain why submission is unavailable when the prerequisite is not obvious. | | +| [X-INPUT-3](./INTERACTION.md#1-inputs-drafts-and-intent) | SHOULD | Use the appropriate native input semantics and autocomplete, and preserve platform editing conventions; expose keyboard shortcuts where they help. | | +| [X-INPUT-4](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Submit dispatches once per activation, and Restore only repopulates fields without submitting them. | unit | +| [X-INPUT-5](./INTERACTION.md#1-inputs-drafts-and-intent) | SHOULD | Follow the remaining rows of the input transition table. | | +| [X-INPUT-6](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Enter submits only where the field's documented convention calls for it, and never during IME composition (check `isComposing`). Multiline entry keeps working. | browser | +| [X-INPUT-7](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Dictation updates text and never submits by itself; the text that preceded the capture is retained. | browser | +| [X-INPUT-8](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Do not rebuild a focused input on a search response, stream update, resize or status change. | browser | +| [X-INPUT-9](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | The latest intent wins: an older query never replaces newer results, and a preview for one file never appears under another. | unit | +| [X-INPUT-10](./INTERACTION.md#1-inputs-drafts-and-intent) | SHOULD | Implement X-INPUT-9 with the host’s async identity and cancellation mechanisms; ranui contributors can consult [CODING §3](./CODING.md#3-state-and-ownership). | | +| [X-INPUT-11](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Promise persistence only after it succeeds; a retained draft is not a saved one. | unit | +| [X-INPUT-12](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Highlight search matches with safe text nodes or equivalent escaping, never by inserting the query as HTML. | unit | +| [X-INPUT-13](./INTERACTION.md#1-inputs-drafts-and-intent) | SHOULD | Keep enough text around a highlighted match to explain why the item matched. | | +| [X-FOCUS-1](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | SHOULD | Build controls from native semantics and supported component contracts. | | +| [X-FOCUS-2](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | Every icon-only action has an accessible name. | browser | +| [X-FOCUS-3](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | A secondary action revealed on hover is also revealed on keyboard focus and has a persistent tap route on coarse input. | browser | +| [X-FOCUS-4](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | SHOULD | Keep essential recovery actions visible rather than revealed on hover. | | +| [X-FOCUS-5](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | Disabled controls stay quiet on hover and cannot dispatch. | browser | +| [X-FOCUS-6](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | The target box and focus ring keep their size and position while a label or status changes. | browser | +| [X-FOCUS-7](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | Integrate through public attributes, events, slots, variables and parts; do not patch private shadow roots. | review | +| [X-FOCUS-8](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | Do not add host tab stops that lead nowhere or duplicate an inner control. | browser | +| [X-FOCUS-9](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | A composed dialog places initial focus inside itself, keeps Tab and Shift+Tab within it, closes on Escape and Cancel, activates its default action on Enter, and returns focus to the opener when it closes. | browser | +| [X-FOCUS-10](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | SHOULD | When a focus trap cannot reach a control (for example a closed-shadow button in a footer), fix the supported integration or place native buttons in its supported slot. | | +| [X-FOCUS-11](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | A collapsed region still identifies its contents and state, and disclosure never hides an unresolved actionable error. | browser | +| [X-FOCUS-12](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | Collapsing a region that contains focus returns focus to its toggle; removing the focused object moves focus to the next meaningful action or a stable parent control. | browser | +| [X-FOCUS-13](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | Do not move focus merely because a background request completed. | browser | +| [X-FOCUS-14](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | SHOULD | Write the collapsed summary to help readers decide whether to expand; when the expanded body already shows that content, omit the repeated summary and keep the title and state. | | +| [X-FEEDBACK-1](./INTERACTION.md#3-feedback-and-reading-position) | SHOULD | Show feedback next to the object acted on. A “Copied” acknowledgment beside Copy is easier to associate than an unrelated global notification; a toast suits changes beyond the current object or feedback that would otherwise go unnoticed. | | +| [X-FEEDBACK-2](./INTERACTION.md#3-feedback-and-reading-position) | MUST | Show a success acknowledgment only after the operation succeeded (for Copy, after clipboard success), and handle rejection locally while keeping button geometry and focus. | browser | +| [X-FEEDBACK-3](./INTERACTION.md#3-feedback-and-reading-position) | MUST | Async feedback belongs to the operation's source snapshot: when content changes while Copy is pending, the older completion never marks the new content “Copied”, and stale feedback is cleared or identified as stale. | unit, browser | +| [X-FEEDBACK-4](./INTERACTION.md#3-feedback-and-reading-position) | MUST | Do not announce every streamed character to assistive technology. | browser | +| [X-FEEDBACK-5](./INTERACTION.md#3-feedback-and-reading-position) | MUST | Live logs and streaming results follow only while the reader is at the latest content; scrolling away suspends following, and “Jump to latest” restores it without stealing the reading position. | recording | +| [X-FEEDBACK-6](./INTERACTION.md#3-feedback-and-reading-position) | SHOULD | Use a supported follow/pin contract from the host or chosen component, and keep job and persistence state in the application; an append-only reading log is not a job manager. | | +| [X-RECOVER-1](./INTERACTION.md#4-progress-failure-and-recovery) | SHOULD | Name a known phase (“Reading files”, “Checking permissions”, “Writing export”) rather than showing only a spinner, and announce meaningful phase changes, not every timer tick. | | +| [X-RECOVER-2](./INTERACTION.md#4-progress-failure-and-recovery) | MUST | Show a percentage only with a measurable denominator; elapsed time is secondary and never presented as a completion estimate. | review | +| [X-RECOVER-3](./INTERACTION.md#4-progress-failure-and-recovery) | MUST | An error answers what failed, whether anything changed, and what to do next, and promises only what the host can establish. | review | +| [X-RECOVER-4](./INTERACTION.md#4-progress-failure-and-recovery) | MUST | Preserve in-memory input when storage fails, and report the persistence failure separately. | unit | +| [X-RECOVER-5](./INTERACTION.md#4-progress-failure-and-recovery) | SHOULD | Put credential-free diagnostics in optional details. | | +| [X-RECOVER-6](./INTERACTION.md#4-progress-failure-and-recovery) | SHOULD | Give one failure one coherent recovery, not duplicate rows and toasts. | | +| [X-RECOVER-7](./INTERACTION.md#4-progress-failure-and-recovery) | MUST | A timeout does not prove rollback: establish whether a mutating operation committed before retrying, using the host's idempotency or deduplication contract where available. | unit | +| [X-CANCEL-1](./INTERACTION.md#5-cancel-retry-and-completion-are-different-outcomes) | MUST | Keep Cancel and Submit independently reachable when both are supported, with clear accessible names and stable positions. | browser | +| [X-CANCEL-2](./INTERACTION.md#5-cancel-retry-and-completion-are-different-outcomes) | MUST | A cancellation request is not an acknowledged cancellation: show its pending state until confirmed, and when the client can only stop waiting, say so instead of claiming the job stopped. | unit | +| [X-CANCEL-3](./INTERACTION.md#5-cancel-retry-and-completion-are-different-outcomes) | MUST | Cancellation is not shown as failure. | browser | +| [X-CANCEL-4](./INTERACTION.md#5-cancel-retry-and-completion-are-different-outcomes) | MUST | Retry dispatches work again without silently broadening scope or switching destinations; Restore only repopulates editable inputs. | unit | +| [X-CANCEL-5](./INTERACTION.md#5-cancel-retry-and-completion-are-different-outcomes) | MUST | Report saved or applied success only after the write is confirmed, and retain recoverable partial output. | unit | +| [X-CANCEL-6](./INTERACTION.md#5-cancel-retry-and-completion-are-different-outcomes) | MUST | Offer Undo only when the host can actually reverse the operation, and never promise that Cancel undoes a completed write. | review | +| [X-VERIFY-1](./INTERACTION.md#6-verify-what-a-person-can-observe) | SHOULD | Choose evidence by the defect, using the layers below, rather than running every possible test for a small change. | | +| [X-VERIFY-2](./INTERACTION.md#6-verify-what-a-person-can-observe) | SHOULD | Include offline and failure paths when the task depends on external work. | | +| [X-VERIFY-3](./INTERACTION.md#6-verify-what-a-person-can-observe) | SHOULD | Report the tested commit and any remaining external prerequisites. | | +| [V-AGREE-1](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | Before editing, settle the [design brief](./DESIGN.md#style-coherence-across-components-and-pages), and state the affected task, views/states and independent expectations in that same note. | | +| [V-AGREE-2](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | Name the relevant owner, supported public API and host capability limits. | | +| [V-AGREE-3](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | Select widths, themes, languages and input paths from the actual change, without inventing unrelated work. | | +| [V-AGREE-4](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | For composition, state content/text/icon spines, intentional navigation columns, hit bounds and vertical relationships. | | +| [V-AGREE-5](./ACCEPTANCE.md#agree-on-the-observable-result) | MUST | Do not derive the expected value from the implementation under test. | review | +| [V-AGREE-6](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | Review in order: task/hierarchy, static composition, reachable combined states, then motion. | | +| [V-AGREE-7](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | Do not add animation to compensate for unresolved proportions or unclear navigation. | | +| [V-AGREE-8](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | When a review changes the intended design, update the existing brief first and identify the evidence that prompted it. | | +| [V-AGREE-9](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | Keep one rule owner. | | +| [V-AGREE-10](./ACCEPTANCE.md#agree-on-the-observable-result) | MUST | Do not let one kind of check substitute for another: an API/token check, an image comparison and an agent run each establish only their own result. | review | +| [V-AGREE-11](./ACCEPTANCE.md#agree-on-the-observable-result) | MUST | A fixture demonstrates its own result, not acceptance of a host application it did not render; do not claim it as host acceptance. | review | +| [V-PROOF-1](./ACCEPTANCE.md#browser-proof) | MUST | When reproducing a visible defect, capture the actual running surface before the fix. | browser | +| [V-PROOF-2](./ACCEPTANCE.md#browser-proof) | MUST | After the fix, capture the same relevant state and inspect the full pane with realistic copy and surrounding chrome. | browser | +| [V-PROOF-3](./ACCEPTANCE.md#browser-proof) | MUST | Do not offer a mockup, source diff, changed assertion or screenshot of an unrelated build as proof. | review | +| [V-PROOF-4](./ACCEPTANCE.md#browser-proof) | SHOULD | Inspect the composition as well as the link count: relabeling a redundant return as a breadcrumb can still repeat the same context and add an unnecessary header row. | | +| [V-PROOF-5](./ACCEPTANCE.md#browser-proof) | SHOULD | Trace ancestor reachability through the parent before deciding another visible shortcut is needed. | | +| [V-PROOF-6](./ACCEPTANCE.md#browser-proof) | MUST | Wait for component registration, pending content, fonts and rendered frames before measuring. | browser | +| [V-PROOF-7](./ACCEPTANCE.md#browser-proof) | MUST | Fail on missing or zero-sized targets. | browser | +| [V-PROOF-8](./ACCEPTANCE.md#browser-proof) | MUST | Record any subpixel tolerance, and keep it below a full CSS pixel for exact spines (the source example uses 0.5px). | review | +| [V-PROOF-9](./ACCEPTANCE.md#browser-proof) | MUST | For live overlays, verify that capture preserves the viewport and includes the full trigger/panel; a full-page screenshot can invalidate a crop by causing responsive reflow or portal repositioning. | browser | +| [V-PROOF-10](./ACCEPTANCE.md#browser-proof) | MUST | Measure the defect's layer: wrapper, inner content, rendered text Range, icon viewport and actual pointer target are different. A Range is not exact glyph ink. | browser | +| [V-PROOF-11](./ACCEPTANCE.md#browser-proof) | MUST | Account for shadow parts through supported test seams; do not introduce production shadow queries for acceptance. | review | +| [V-PROOF-12](./ACCEPTANCE.md#browser-proof) | SHOULD | Exercise affected reflow, wrapped translations, optional content and input paths. | | +| [V-PROOF-13](./ACCEPTANCE.md#browser-proof) | MUST | Do not use a static screenshot as proof of focus return, keyboard navigation, touch discovery, interruptibility or async settlement. | review | +| [V-PROOF-14](./ACCEPTANCE.md#browser-proof) | MUST | Record a continuous interaction when the defect unfolds over time. | recording | +| [V-PROOF-15](./ACCEPTANCE.md#browser-proof) | MUST | For a layout assertion, prove it rejects the relevant defect: move only label text while the frame stays fixed, introduce a doubled margin/phantom helper, or expand a title target into filler. | browser | +| [V-PROOF-16](./ACCEPTANCE.md#browser-proof) | MUST | Confirm the mutation won the cascade, observe failure, restore and observe success. | browser | +| [V-PROOF-17](./ACCEPTANCE.md#browser-proof) | SHOULD | Use mutations relevant to the task rather than repeating the entire example suite for a tiny fix. | | +| [V-PROOF-18](./ACCEPTANCE.md#browser-proof) | MUST | Verify raw data when changing presentation. | browser, unit | +| [V-PROOF-19](./ACCEPTANCE.md#browser-proof) | MUST | For code, copy the source payload, not highlighted DOM text: preserve tabs, blank lines, final newline and streamed additions. | unit | +| [V-PROOF-20](./ACCEPTANCE.md#browser-proof) | MUST | Verify clipboard success/failure, native selection and long-line scrolling independently. | browser | +| [V-PROOF-21](./ACCEPTANCE.md#browser-proof) | MUST | Document browser selection normalization without allowing it to alter the raw clipboard payload. | unit, review | +| [V-PROOF-22](./ACCEPTANCE.md#browser-proof) | MUST | Do not claim native browser zoom or text-size accessibility support from CSS zoom stress. | review | +| [V-PROOF-23](./ACCEPTANCE.md#browser-proof) | MUST | Say which paths remain unverified. | review | +| [V-MOTION-1](./ACCEPTANCE.md#review-motion-over-time) | MUST | For changed motion, inspect a continuous action at normal speed, then replay slowly where a transition breaks. | recording | +| [V-MOTION-2](./ACCEPTANCE.md#review-motion-over-time) | MUST | Include activation, intermediate state, completion and the reverse path; repeat or reverse input before completion. | recording | +| [V-MOTION-3](./ACCEPTANCE.md#review-motion-over-time) | MUST | Verify the final state, focus, event count and reading position independently of the animation. | browser | +| [V-MOTION-4](./ACCEPTANCE.md#review-motion-over-time) | MUST | Use a slow replay to diagnose discontinuity, not as a frame-rate benchmark. | review | +| [V-MOTION-5](./ACCEPTANCE.md#review-motion-over-time) | MUST | Give reduced motion its own visible-state check, not just a shorter recording. | browser | +| [V-MOTION-6](./ACCEPTANCE.md#review-motion-over-time) | SHOULD | Report a temporal defect as `element → input → observed frame/state → expected continuity`. | | +| [V-MOTION-7](./ACCEPTANCE.md#review-motion-over-time) | SHOULD | Use one representative real task across affected siblings to detect inconsistent motion language. | | +| [V-MOTION-8](./ACCEPTANCE.md#review-motion-over-time) | SHOULD | Check the full composition again after local fixes. | | +| [V-MOTION-9](./ACCEPTANCE.md#review-motion-over-time) | SHOULD | Preserve failed iterations as diagnostics, separate from accepted captures. | | +| [V-WALK-1](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | For a changed workflow or composition, start with a realistic goal expressed without naming the control to use. | | +| [V-WALK-2](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | Complete the goal from the actual entry point through result, recovery and return, as relevant. | | +| [V-WALK-3](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | Record the first choice, any ambiguous label, hesitation or backtracking, and whether the result makes the next step clear. | | +| [V-WALK-4](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | Use realistic content, including a representative long or failing case, rather than a polished empty state alone. | | +| [V-WALK-5](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | Fix task structure and content hierarchy before adding decorative effects. | | +| [V-WALK-6](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | Report the most consequential observed problems (up to three), with the state and evidence that show each one. | | +| [V-WALK-7](./ACCEPTANCE.md#walk-through-the-task) | MUST | Do not invent findings to fill a quota. | review | +| [V-WALK-8](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | Repeat the affected path after correction. | | +| [V-WALK-9](./ACCEPTANCE.md#walk-through-the-task) | MUST | Report an agent walkthrough as a defect search, not as a human usability study. | review | +| [V-WALK-10](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | When observing a real participant, let them attempt the goal before explaining the interface, and preserve that distinction in the results below. | | +| [V-FRESH-1](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Runtime: record the tested source revision/build and relevant dirty inputs or their fingerprint, including shared styles/components and incoming data when they affect the result. A Git HEAD alone is insufficient if the running build or working tree differs. | review | +| [V-FRESH-2](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Scope/environment: record affected view/group membership, route/state, viewport, theme, locale, browser and input or reduced-motion settings where relevant. | review | +| [V-FRESH-3](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Media: record capture/recording locations and content identity (for example SHA-256), tied to that runtime and state. | review | +| [V-FRESH-4](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Keep before and after distinct; never overwrite the only before image. | review | +| [V-FRESH-5](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Review: record what the automated check and agent inspection actually established, separate from any human decision and any unverified state. | review | +| [V-FRESH-6](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MAY | Use a short Markdown record; an existing manifest may supply these fields. | | +| [V-FRESH-7](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | SHOULD | Do not add a new evidence service or configuration framework for an ordinary change. | | +| [V-FRESH-8](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Keep hashes beside the files or in a referenced manifest so the claimed binding can be checked independently. | review | +| [V-FRESH-9](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Treat the affected review as stale after replacing an image, editing a recording, changing group membership or changing relevant runtime inputs. | review | +| [V-FRESH-10](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | After a shared style/component change, refresh every affected state, rebuild if needed, then inspect the new evidence. | browser, review | +| [V-FRESH-11](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Present no old capture as current proof. | review | +| [V-FRESH-12](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | When keeping runtime evidence across a docs-only edit or unrelated source change, record why its dependency scope is unaffected. | review | +| [V-REPORT-1](./ACCEPTANCE.md#report-authority-truthfully) | MUST | Keep these three results separate, even in a one-paragraph report. | review | +| [V-REPORT-2](./ACCEPTANCE.md#report-authority-truthfully) | MUST | Claim each result only from the source the table above names, and never for what the table says it does not establish. | review | +| [V-REPORT-3](./ACCEPTANCE.md#report-authority-truthfully) | SHOULD | Use explicit outcomes such as `automated: passed`, `agent review: passed with limits`, `human: not requested`. | | +| [V-REPORT-4](./ACCEPTANCE.md#report-authority-truthfully) | MUST | If human review was requested and is outstanding, say `pending`. | review | +| [V-REPORT-5](./ACCEPTANCE.md#report-authority-truthfully) | MUST | If a user rejected it, preserve `rejected` and the finding until that user reviews the updated scope. | review | +| [V-REPORT-6](./ACCEPTANCE.md#report-authority-truthfully) | MUST | An agent may report its correction and fresh checks, but never invents a user decision or clears a human rejection. | review | +| [V-REPORT-7](./ACCEPTANCE.md#report-authority-truthfully) | MUST | Avoid an unattributed “accepted”, “approved” or “验收通过”. | review | +| [V-REPORT-8](./ACCEPTANCE.md#report-authority-truthfully) | SHOULD | Finish and deliver authorized work while reporting `not requested`/`pending` truthfully. | | +| [V-REPORT-9](./ACCEPTANCE.md#report-authority-truthfully) | SHOULD | Ask only when the actual authorization or a necessary product decision is missing. | | +| [V-REPORT-10](./ACCEPTANCE.md#report-authority-truthfully) | MUST | Never label the agent's own visual check “user accepted”. | review | +| [V-EVAL-1](./ACCEPTANCE.md#regression-of-the-guidance-itself) | SHOULD | For opt-in or task-authorized live evaluation of skill/routing/acceptance changes, use the source-only [six agent scenarios](https://github.com/chaxus/ran/tree/main/plugins/ran/evals). | | +| [V-EVAL-2](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Freeze tasks, fixtures, tools and independent rubrics before live runs. | review | +| [V-EVAL-3](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Use fresh sessions with matching model/settings. | review | +| [V-EVAL-4](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Pin baseline and candidate guidance in separate workspaces. | review | +| [V-EVAL-5](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Retain actual agent work, prompts, outcome checks and evidence. | review | +| [V-EVAL-6](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Record baseline passes as controls and candidate failures as failures. | review | +| [V-EVAL-7](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Do not show workers the assessor's thresholds or repair patches. | review | +| [V-EVAL-8](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Claim behavior on those tasks only from live agent runs; prose matching, hand-authored answers or corrected test expectations are no evidence of it. | review | +| [V-EVAL-9](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Inspect visual judgment independently of mechanical checks. | review | +| [V-EVAL-10](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Report sample size, tools and uncovered conditions. | review | +| [V-EVAL-11](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Six successful tasks are no proof of reliability across models; do not claim it from them. | review | +| [W-RECIPE-1](./WORKFLOW_PATTERNS.md#optional-workflow-patterns) | SHOULD | For each selected recipe, establish what the application supports before choosing controls. | | +| [W-RECIPE-2](./WORKFLOW_PATTERNS.md#optional-workflow-patterns) | SHOULD | Keep the state for jobs, configuration, selected sources and revisions in their existing owners. Views read that state and dispatch commands instead of duplicating service clients and request flags in each panel. | | +| [W-QUEUE-1](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | SHOULD | Settle the contract for each state in the queue table above. | | +| [W-QUEUE-2](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | MUST | New work never silently replaces or cancels the active job. Without queue support, retain the new input and explain the constraint. | unit | +| [W-QUEUE-3](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | MUST | Editing a pending item in a paused queue neither dispatches it nor resumes the queue. | unit | +| [W-QUEUE-4](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | SHOULD | Summarize queued work in one line, such as “2 queued” or “Paused · 2 queued”. | | +| [W-QUEUE-5](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | SHOULD | Collapse queue details when unneeded; expand into rows rather than nested cards. | | +| [W-QUEUE-6](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | MUST | Each row has an identifiable item and offers only supported actions: Edit, Remove, or Run now. | browser | +| [W-QUEUE-7](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | SHOULD | Define whether Run now waits, preempts or runs concurrently. | | +| [W-QUEUE-8](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | SHOULD | Represent preparation within the row instead of adding a large panel full of disabled commands. | | +| [W-QUEUE-9](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | MUST | Queue editing preserves the current draft, and cancelling editing restores it. | unit | +| [W-QUEUE-10](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | MUST | Reconnection does not auto-dispatch queued work unless the user chose that policy. | unit | +| [W-QUEUE-11](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | SHOULD | Apply [general focus and cancel rules](./INTERACTION.md) when removing a row, collapsing the queue or stopping work. | | +| [W-SETTINGS-1](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | Saving configuration and checking availability are separate actions with separate outcomes, and each shows what it accomplished. | unit, browser | +| [W-SETTINGS-2](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Derive controls from capability and state, not a single `connected` boolean. | | +| [W-SETTINGS-3](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Make each state in the settings table above distinguishable to the user. | | +| [W-SETTINGS-4](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | Reconnecting an account whose authorization is required or expired keeps saved work. | unit | +| [W-SETTINGS-5](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | Invalidate verification when relevant endpoint, account or credential settings change. | unit | +| [W-SETTINGS-6](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Treat browser online/offline events as hints, not as proof that a particular endpoint works. | | +| [W-SETTINGS-7](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Reveal settings for the chosen path. A local conversion path may need cache/download/device controls; a remote path may need endpoint/account controls. | | +| [W-SETTINGS-8](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | Hide irrelevant fields **and their stale status messages**. | browser | +| [W-SETTINGS-9](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Settings headers, field groups, account rows and unavailable/empty states retain the named content and navigation spines from [DESIGN's alignment contract](./DESIGN.md#alignment-is-structure-not-polish). | | +| [W-SETTINGS-10](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Keep a title/menu command content-hugging; a separate track absorbs header filler. | | +| [W-SETTINGS-11](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Count nested panel and control padding through to visible labels before declaring regions aligned. | | +| [W-SETTINGS-12](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | Switching modes preserves the other modes' configuration. | unit | +| [W-SETTINGS-13](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Keep the route back to another mode discoverable. | | +| [W-SETTINGS-14](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Explain the data destination before disclosure. | | +| [W-SETTINGS-15](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | A configured API endpoint is not presented as an authorized account. | review | +| [W-SETTINGS-16](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | Offline, retain saved content and drafts when their data and implementation are available. | unit, browser | +| [W-SETTINGS-17](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Offline, keep previews, editing and exports available when their data and implementation are available. | | +| [W-SETTINGS-18](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Offline, hide network-only discovery or promotion that offers no useful route; retain existing connected objects with an understandable unavailable state. | | +| [W-SETTINGS-19](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Treat a cached resource as no proof that all runtime dependencies are present. | | +| [W-SETTINGS-20](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Offer a deliberate continuation point after reconnecting. | | +| [W-ACCOUNT-1](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | SHOULD | Distinguish account authorization, selected-source access and use by a particular operation. | | +| [W-ACCOUNT-2](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | SHOULD | Request only needed permissions. | | +| [W-ACCOUNT-3](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | SHOULD | Explain reads, writes and destination before authorization or disclosure. | | +| [W-ACCOUNT-4](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Account identity, reconnect and revoke/disconnect are reachable from the connection UI. | browser | +| [W-ACCOUNT-5](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Disconnect stops future access. | unit | +| [W-ACCOUNT-6](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | SHOULD | Explain separately what happens to imported copies on disconnect. | | +| [W-ACCOUNT-7](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Keep credentials out of URLs, logs and previews. | review | +| [W-ACCOUNT-8](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | SHOULD | Show provenance and freshness for selected sources, whether a local file, database record or cloud document. | | +| [W-ACCOUNT-9](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Removing a source affects later work, not historical evidence. | unit | +| [W-ACCOUNT-10](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Permission errors, stale data and offline cached copies have different states. | unit | +| [W-ACCOUNT-11](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Treat imported content as untrusted data, not permission to execute commands. | review | +| [W-ACCOUNT-12](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Make writes and their destinations explicit before they happen. | browser | +| [W-ACCOUNT-13](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | SHOULD | Suit the review step before a write to its impact. | | +| [W-REVISION-1](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | SHOULD | Prefer a suitable preview/editor for substantive content. | | +| [W-REVISION-2](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | MUST | Offer Compare, Apply or Undo only when the host supports it. | review | +| [W-REVISION-3](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | SHOULD | Summarize the result and identify its revision, source and destination when relevant. | | +| [W-REVISION-4](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | SHOULD | Keep supported Copy, Export, Compare or Apply actions beside the relevant result. | | +| [W-REVISION-5](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | MUST | Separate previewing from applying, and name the target and scope before mutation. | browser | +| [W-REVISION-6](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | MUST | If source data changed, compare/rebase or regenerate before applying; newer edits are never overwritten silently. | unit | +| [W-REVISION-7](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | SHOULD | Confirm saved/applied state and recoverable output under [the interaction standard](./INTERACTION.md). | | +| [W-APPLY-1](./WORKFLOW_PATTERNS.md#applying-a-recipe) | MUST | Keep only the states and actions the host can honor. | review | +| [W-APPLY-2](./WORKFLOW_PATTERNS.md#applying-a-recipe) | SHOULD | A polished screen cannot substitute for a missing service contract; state that prerequisite clearly. | | + +--- + + + +## ranui adapter rules + +ranui-specific rules (31 MUST, 41 SHOULD, 5 MAY). + +| ID | Level | Rule | Proof | +| ------------------------------------------------------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | +| [R-STYLE-1](./DESIGN.md#ranui-default-visual-direction) | SHOULD | Use existing token roles to choose exact dimensions; do not introduce a second "premium" scale. | | +| [R-STYLE-2](./DESIGN.md#ranui-default-visual-direction) | SHOULD | Treat glass and expressive demonstrations as explicit surface or theme choices, not as default text or control backgrounds. | | +| [R-STYLE-3](./DESIGN.md#ranui-default-visual-direction) | MUST | Preserve reading contrast and the same interaction semantics across themes. | browser | +| [R-STYLE-4](./DESIGN.md#ranui-default-visual-direction) | SHOULD | Apply the shared [style brief](./DESIGN.md#style-coherence-across-components-and-pages) before changing a family. | | +| [R-STYLE-5](./DESIGN.md#ranui-default-visual-direction) | SHOULD | Review a family change in a realistic composed page with neighboring controls; a polished isolated component does not establish a coherent product. | | +| [R-USE-1](./DESIGN.md#how-to-use-this-file) | SHOULD | Prefer **semantic tokens** (`--ran-color-*`, `--ran-space-*`, …) over raw scales or hex. | | +| [R-USE-2](./DESIGN.md#how-to-use-this-file) | SHOULD | Decide by **role/state**, not by eyeballing a value. The value is chosen for you. | | +| [R-USE-3](./DESIGN.md#how-to-use-this-file) | SHOULD | When a choice is unresolved, mark it explicitly instead of burying a guess in code. | | +| [R-USE-4](./DESIGN.md#how-to-use-this-file) | SHOULD | Design **all reachable states** (default, hover, active, focus, disabled, loading, empty, error), not just the happy path. | | +| [R-USE-5](./DESIGN.md#how-to-use-this-file) | MUST | **Verify the rendered result** in the themes and widths the change affects; code review alone is not enough. | browser | +| [R-USE-6](./DESIGN.md#how-to-use-this-file) | SHOULD | Resolve conflicts in this order: **user goals → verified evidence → this file → repo guidance (`CLAUDE.md`) → shipped patterns → general heuristics.** | | +| [R-CHECK-1](./DESIGN.md#what-is-machine-checked) | MUST | A change adds no new `verify:design` violations; the per-file baseline is a debt ratchet, not permission to add violations. | review | +| [R-CHECK-2](./DESIGN.md#what-is-machine-checked) | MUST | A component that implements a drag drives it with Pointer Events (`pointerdown`/`pointermove`/`pointerup`/`pointercancel`), not a `mousemove`-only loop, so touch can drive it. | check:mouse-only-drag | +| [R-BUNDLE-1](./DESIGN.md#looking-at-the-whole-system-at-once) | SHOULD | Use the bundle from the matching installed version; a reference bundle is not evidence that the host application passed acceptance. | | +| [R-COLOR-1](./DESIGN.md#ranui-color--a-state-ladder-not-a-palette) | SHOULD | **Use the semantic layer in the table below, not the scale, in components.** | | +| [R-COLOR-2](./DESIGN.md#ranui-color--a-state-ladder-not-a-palette) | SHOULD | Text and icons on a primary surface use `--ran-color-primary-text` (the inverse, which flips too). | | +| [R-COLOR-3](./DESIGN.md#ranui-color--a-state-ladder-not-a-palette) | SHOULD | Reserve **blue** for **links (`--ran-color-link`) and the focus ring** only; green means success, amber warning, red danger/error. | | +| [R-COLOR-4](./DESIGN.md#ranui-color--a-state-ladder-not-a-palette) | SHOULD | Read the theme declarations rather than assuming every semantic value follows one scale step. | | +| [R-COLOR-5](./DESIGN.md#ranui-color--a-state-ladder-not-a-palette) | MUST | Do not hard-code a hex/rgb in a component for a value that follows the theme. | check:bare-colour | +| [R-COLOR-6](./DESIGN.md#ranui-color--a-state-ladder-not-a-palette) | MUST | A component token's colour fallback starts with a **token that flips** (`var(--ran-gray-alpha-100, …)`, `var(--ran-blue-100, …)`, `var(--ran-color-text, …)`), never a bare light-only literal — otherwise it breaks in dark mode. A final literal behind that token, as in `var(--ran-btn-background, var(--ran-color-primary, #171717))`, is allowed. | check:dark-unsafe-fallback | +| [R-SPACE-1](./DESIGN.md#ranui-spacing--a-limited-rhythmic-scale) | MUST | Pick spacing from the scale; do not invent `20px`/`28px` — a limited set is what creates the page's rhythm. | check:spacing-scale | +| [R-SPACE-2](./DESIGN.md#ranui-spacing--a-limited-rhythmic-scale) | MUST | Intrinsic dimensions (`width`, `height`, their `min-`/`max-` forms, `font-size`, `line-height`, `border-radius`) take `--ran-size-*` or role tokens, never `--ran-space-*`, so consumers can retune either scale without perturbing the other. | check:sizing-scale | +| [R-TYPE-1](./DESIGN.md#ranui-typography--choose-a-role-not-a-size) | SHOULD | Decide text style by **role**: ask "what role is this text?" (heading / label / copy / button) and the style follows. Do not pick raw px per instance. | | +| [R-TYPE-2](./DESIGN.md#ranui-typography--choose-a-role-not-a-size) | SHOULD | Reference the role tokens (`var(--ran-{component}-…, var(--ran-text-{role}…, fallback))`) instead of hand-copying the px/weight number. | | +| [R-TYPE-3](./DESIGN.md#ranui-typography--choose-a-role-not-a-size) | SHOULD | Follow the Notes column of the role table; for example, compact action labels stay on one line while field labels may wrap. | | +| [R-TYPE-4](./DESIGN.md#ranui-typography--choose-a-role-not-a-size) | MAY | Transient/decorative UI that maps to no role (an active-link weight bump, a player gesture-flash overlay) keeps its own one-off component token instead of being forced into the nearest role — see `r-link`'s `--ran-link-active-font-weight` and `r-player`'s `--ran-player-gesture-flash-font-weight`. | | +| [R-RADIUS-1](./DESIGN.md#ranui-radius--elevation) | SHOULD | Controls (button, input, select) → `sm`. Cards / dialogs → `md`. Large surfaces → `lg`. Pills / avatars → `full`. | | +| [R-ELEV-1](./DESIGN.md#ranui-radius--elevation) | SHOULD | Pick the shadow tier from the table below by _what the element is_. | | +| [R-ELEV-2](./DESIGN.md#ranui-radius--elevation) | SHOULD | Make sure each tier is actually perceptible — a shadow you can't see fails its job. | | +| [R-ELEV-3](./DESIGN.md#ranui-radius--elevation) | MUST | A floating overlay does not fall back to `--ran-shadow-elevated` (the card tier) — it would look flat. | test:packages/ranui/test/unit/theme.semantic-tokens.test.ts, review | +| [R-ELEV-4](./DESIGN.md#ranui-radius--elevation) | MUST | An overlay's shadow clearly lifts it off the page. | browser | +| [R-Z-1](./DESIGN.md#ranui-radius--elevation) | MUST | The z-index sits on the element that is **positioned and portaled** (the host); a z-index on an inner shadow node is trapped in the host's stacking context and does nothing against page content. | test:packages/ranui/test/e2e/dropdown-layer.spec.ts | +| [R-Z-2](./DESIGN.md#ranui-radius--elevation) | MUST | Overlays use the stacking tiers in the table below, so a select opened inside a modal stays visible. | test:packages/ranui/test/e2e/dropdown-layer.spec.ts | +| [R-Z-3](./DESIGN.md#ranui-radius--elevation) | MUST | Toasts and notifications sit on the top tier (`--ran-z-message`), above modals and dropdowns. | review | +| [R-Z-4](./DESIGN.md#ranui-radius--elevation) | MUST | Override a tier globally (set `--ran-z-dropdown` on `:root`) or per component (`--ran-dropdown-host-z-index`, `--ran-modal-root-z-index`, `--ran-message-z-index`), never with `!important`. | test:packages/ranui/test/e2e/dropdown-layer.spec.ts, review | +| [R-Z-5](./DESIGN.md#ranui-radius--elevation) | MUST | An overlay open while the page scrolls re-runs its placement on `scroll`/`resize` so it tracks its trigger (e.g. inside a sticky header). | test:packages/ranui/test/unit/utils.floating.test.ts | +| [R-Z-6](./DESIGN.md#ranui-radius--elevation) | MUST | Raise an enclosing wrapper only while that overlay actually paints, including its closing transition. | browser | +| [R-Z-7](./DESIGN.md#ranui-radius--elevation) | SHOULD | Verify reflected open/closing attributes against the component API before using selectors. | | +| [R-MOTION-1](./DESIGN.md#ranui-radius--elevation) | MUST | Do not animate a theme switch. | browser | +| [R-MOTION-2](./DESIGN.md#ranui-radius--elevation) | MUST | **Palette props get no default transition.** `background` / `background-color`, `color`, `border-color`, `box-shadow`, `fill`, `stroke` — none of them, on any element that carries a theme-driven color. Hover/focus feedback on these props snaps. | review, browser | +| [R-MOTION-3](./DESIGN.md#ranui-radius--elevation) | MAY | **Motion props may transition.** `transform`, `opacity`, and box-geometry props (`left` / `top` / `width` / `height` / `font-size`) do not follow the theme — checkbox pops, tab ink-bars, ripples all stay animated. | | +| [R-MOTION-4](./DESIGN.md#ranui-radius--elevation) | MUST | Do not use `transition: all` or bare-duration shorthands (`transition: 0.2s` — which means `all`); they silently include palette props. | review | +| [R-MOTION-5](./DESIGN.md#ranui-radius--elevation) | MUST | Every removed default keeps its `--ran-*-transition` hook var, so a consumer who wants palette fades can opt in per component. | review | +| [R-COMP-1](./DESIGN.md#ranui-components--how-to-apply-the-system) | SHOULD | Reuse existing semantic native controls or supported ranui primitives; do not rebuild them merely for styling. | | +| [R-COMP-2](./DESIGN.md#ranui-components--how-to-apply-the-system) | SHOULD | Choose a ranui component when its contract fits. | | +| [R-COMP-3](./DESIGN.md#ranui-components--how-to-apply-the-system) | MUST | Preserve native form and keyboard semantics, including supported native controls in overlay slots ([INTERACTION §2](./INTERACTION.md#2-reachable-controls-and-predictable-focus)). | browser | +| [R-COMP-4](./DESIGN.md#ranui-components--how-to-apply-the-system) | SHOULD | Theme through **CSS variables**, **`::part()`**, or the **`sheet`** attribute (escape hatch). CSS variables cross Shadow DOM; selectors do not. | | +| [R-COMP-5](./DESIGN.md#ranui-components--how-to-apply-the-system) | SHOULD | Component tokens default to semantic tokens: `var(--ran-btn-background, var(--ran-color-primary, #171717))`. | | +| [R-COMP-6](./DESIGN.md#ranui-components--how-to-apply-the-system) | SHOULD | Map states to the color ladder: default → `bg`/`text`; hover → `bg-hover` / `border-hover` / `primary-hover`; active → `bg-active` / `primary-active`; disabled → appropriate disabled tokens, with opacity only when needed and still legible; focus → focus ring. | | +| [R-COMP-7](./DESIGN.md#ranui-components--how-to-apply-the-system) | MUST | **Interactive cards opt in**: `r-card` only reacts to hover with the `hoverable` attribute (border 400 → 500 + elevated shadow). Non-interactive cards stay inert — no hover feedback on something that isn't clickable. | browser | +| [R-COMP-8](./DESIGN.md#ranui-components--how-to-apply-the-system) | SHOULD | **When the host needs ranui theme switching UI**, use `` (system / light / dark segmented pill, wired to `setTheme`/localStorage, syncs across instances and updates `theme-color` metas) instead of hand-rolling equivalent toggles. | | +| [R-COMP-9](./DESIGN.md#ranui-components--how-to-apply-the-system) | SHOULD | Preserve an existing host theme authority; R-COMP-8 does not require adding a theme switch. | | +| [R-COMP-10](./DESIGN.md#ranui-components--how-to-apply-the-system) | SHOULD | Localize `` with `label` / `label-system` / `label-light` / `label-dark`. | | +| [R-COMP-11](./DESIGN.md#ranui-components--how-to-apply-the-system) | MAY | **Typography ships with the system**: `import 'ranui/fonts'` (or link `dist/fonts/fonts.css`) self-hosts Geist Sans + Geist Mono (variable, OFL-licensed) — the canonical faces behind `--ran-font-family` / `--ran-font-mono`. Without it the stacks fall back to system fonts. | | +| [R-TOKEN-1](./DESIGN.md#a-fallback-must-name-a-token-that-exists) | MUST | Declared tokens, including fallbacks, name tokens that exist in the installed theme; an undefined `var()` can silently discard the entire declaration. | check:undefined-token-fallback | +| [R-HOST-1](./DESIGN.md#hidden-must-keep-working) | MUST | An author `:host` display rule preserves `:host([hidden]) { display: none; }`. | check:hidden-inert | +| [R-HOST-2](./DESIGN.md#hidden-must-keep-working) | MUST | Hiding optional content also removes accidental gaps. | browser | +| [R-BUILD-1](./DESIGN.md#hold-what-you-build) | MUST | Append shadow structure in the constructor; content that genuinely cannot exist yet is mounted later only with the documented `// deferred mount: ` marker on the line above, which also says how a second append is prevented. | check:shadow-mount-outside-constructor | +| [R-BUILD-2](./DESIGN.md#hold-what-you-build) | MUST | Retain the references to what you built; do not rediscover them through selectors. | check:built-then-queried | +| [R-NAME-1](./DESIGN.md#ranui-component-token-naming) | SHOULD | Use `--ran-{component}-{element}[-{state}]-{property}`, keeping the existing component prefix. | | +| [R-NAME-2](./DESIGN.md#ranui-component-token-naming) | SHOULD | Name the most specific distinct UI part, not its full DOM/BEM path; drop wrapper/position segments that add no identity. | | +| [R-NAME-3](./DESIGN.md#ranui-component-token-naming) | SHOULD | Keep track/fill as separate parts. | | +| [R-NAME-4](./DESIGN.md#ranui-component-token-naming) | MUST | The state segment is optional, and when present corresponds to an independently overridable interaction state. | review | +| [R-NAME-5](./DESIGN.md#ranui-component-token-naming) | SHOULD | Property names retain their CSS meaning. | | +| [R-NAME-6](./DESIGN.md#ranui-component-token-naming) | SHOULD | Aim for at most four semantic segments after `ran`, without collapsing distinct override points merely to shorten a name. | | +| [R-NAME-7](./DESIGN.md#ranui-component-token-naming) | SHOULD | Do not hand-maintain another token catalog. | | +| [R-COMPOSE-1](./DESIGN.md#applying-generic-composition-to-ranui) | SHOULD | Map the [navigation-layer rules](./DESIGN.md#containers-do-not-fix-structure) to ranui as follows: `r-modal` for a short confirmation or a single field, `r-popover` / `r-dropdown` for transient context, `r-disclosure-row` for progressive disclosure, and `r-tabs` only for peer views of one object. | | +| [R-COMPOSE-2](./DESIGN.md#ranui-spacing-relationships) | SHOULD | Space each relationship with the token the table above maps it to. | | +| [R-COMPOSE-3](./DESIGN.md#ranui-spacing-relationships) | MAY | Major regions use `--ran-space-10` when their relationship warrants it. | | +| [R-COMPOSE-4](./DESIGN.md#ranui-spacing-relationships) | MUST | Absent descriptions leave no vacant row. | browser | +| [R-COMPOSE-5](./DESIGN.md#ranui-spacing-relationships) | SHOULD | For geometry proofs, measure the host, documented shadow parts and slotted text. | | +| [R-COMPOSE-6](./DESIGN.md#ranui-spacing-relationships) | SHOULD | Use `ranui/testing` for closed-root browser tests. | | +| [R-COMPOSE-7](./DESIGN.md#ranui-spacing-relationships) | MUST | Do not introduce production shadow queries. | review | +| [R-COMPOSE-8](./DESIGN.md#ranui-spacing-relationships) | SHOULD | Use generated public variables and parts for overrides. | | +| [R-COMPOSE-9](./DESIGN.md#ranui-spacing-relationships) | MAY | Consumer aliases may improve naming while deriving their values from the existing `--ran-*` foundation. | | +| [R-COMPOSE-10](./DESIGN.md#ranui-spacing-relationships) | MUST | Verify coarse-input bounds independently. | browser | +| [R-COMPOSE-11](./DESIGN.md#ranui-spacing-relationships) | SHOULD | Where table, tree, calendar, kanban or timeline views are needed, use semantic host implementations with these same foundation roles. | | diff --git a/packages/ranui/docs/WORKFLOW_PATTERNS.md b/packages/ranui/docs/WORKFLOW_PATTERNS.md index d4dc99e9f1..483c3e5ba9 100644 --- a/packages/ranui/docs/WORKFLOW_PATTERNS.md +++ b/packages/ranui/docs/WORKFLOW_PATTERNS.md @@ -9,45 +9,63 @@ The host owns engineering; [ranui engineering](./CODING.md) is an optional library adapter. These are host application decisions, not additional component APIs, storage guarantees or bundled services. -For each selected recipe, establish what the application supports before choosing controls. Keep -the state for jobs, configuration, selected sources and revisions in their existing owners. Views -read that state and dispatch commands instead of duplicating service clients and request flags -in each panel. +Each rule carries an ID and a level. **MUST** is a defect when broken and names its proof; +**SHOULD** is the default, and a deviation states its reason for that case; **MAY** is optional. +The [rule index](./RULES.md) lists every graded rule with its proof. + +- **W-RECIPE-1 · SHOULD** For each selected recipe, establish what the application supports before + choosing controls. +- **W-RECIPE-2 · SHOULD** Keep the state for jobs, configuration, selected sources and revisions in + their existing owners. Views read that state and dispatch commands instead of duplicating service + clients and request flags in each panel. ## 1. Batch work and queues **Use when:** people benefit from staging another import, upload or export while work runs. **Host contract:** dispatch, preparation, concurrency/preemption, cancellation acknowledgment, -partial writes, duplicate prevention and persistence. Without queue support, retain new input -and explain the constraint instead of silently replacing the active job. - -| State | Main action | Contract to settle | -| --------------------- | ---------------------------- | ------------------------------------------------------ | -| Ready | Upload files / Export report | Scope, destination and duplicate-submit prevention | -| Running | Cancel, if supported | What stops, what remains and whether a write committed | -| Running with new work | Add to queue, if supported | Never silently replace or cancel the active job | -| Queue paused | Resume queue | Editing a pending item does not dispatch or resume it | -| Failed | Retry / Restore inputs | Use the distinct outcomes in INTERACTION §5 | - -Summarize queued work in one line, such as “2 queued” or “Paused · 2 queued”. Collapse details when -unneeded; expand into rows rather than nested cards. Each row has an identifiable item and only -supported actions: Edit, Remove, or Run now. Define whether Run now waits, preempts or runs concurrently. -Represent preparation within the row instead of adding a large panel full of disabled commands. - -Queue editing preserves the current draft; cancelling editing restores it. Reconnection does not -auto-dispatch unless the user chose that policy. Apply [general focus and cancel rules](./INTERACTION.md) -when removing a row, collapsing the queue or stopping work. +partial writes, duplicate prevention and persistence. + +| State | Main action | Contract to settle | +| --------------------- | ---------------------------- | ----------------------------------------------------------- | +| Ready | Upload files / Export report | Scope, destination and duplicate-submit prevention | +| Running | Cancel, if supported | What stops, what remains and whether a write committed | +| Running with new work | Add to queue, if supported | The active job keeps running; new input is kept (W-QUEUE-2) | +| Queue paused | Resume queue | Editing a pending item does not dispatch or resume it | +| Failed | Retry / Restore inputs | Use the distinct outcomes in INTERACTION §5 | + +- **W-QUEUE-1 · SHOULD** Settle the contract for each state in the queue table above. +- **W-QUEUE-2 · MUST** New work never silently replaces or cancels the active job. Without queue + support, retain the new input and explain the constraint. _Proof: unit_ +- **W-QUEUE-3 · MUST** Editing a pending item in a paused queue neither dispatches it nor resumes the + queue. _Proof: unit_ +- **W-QUEUE-4 · SHOULD** Summarize queued work in one line, such as “2 queued” or “Paused · 2 queued”. +- **W-QUEUE-5 · SHOULD** Collapse queue details when unneeded; expand into rows rather than nested cards. +- **W-QUEUE-6 · MUST** Each row has an identifiable item and offers only supported actions: Edit, Remove, + or Run now. _Proof: browser_ +- **W-QUEUE-7 · SHOULD** Define whether Run now waits, preempts or runs concurrently. +- **W-QUEUE-8 · SHOULD** Represent preparation within the row instead of adding a large panel full of + disabled commands. +- **W-QUEUE-9 · MUST** Queue editing preserves the current draft, and cancelling editing restores it. + _Proof: unit_ +- **W-QUEUE-10 · MUST** Reconnection does not auto-dispatch queued work unless the user chose that + policy. _Proof: unit_ +- **W-QUEUE-11 · SHOULD** Apply [general focus and cancel rules](./INTERACTION.md) when removing a row, + collapsing the queue or stopping work. ## 2. Capability-aware settings and offline work **Use when:** a task offers distinct providers, modes, devices or network-dependent capabilities. **Host contract:** configuration storage, a real verification operation, reachability and local -runtime dependencies. Saving configuration and checking availability must be separate outcomes. +runtime dependencies. + +- **W-SETTINGS-1 · MUST** Saving configuration and checking availability are separate actions with + separate outcomes, and each shows what it accomplished. _Proof: unit, browser_ +- **W-SETTINGS-2 · SHOULD** Derive controls from capability and state, not a single `connected` boolean. -Derive controls from capability and state, not a single `connected` boolean. A remote storage account, -a database service or a local converter can be configured without currently being usable. +A remote storage account, a database service or a local converter can be configured without +currently being usable. -| State | What the user should understand | +| State | What the user understands | | ------------------------------ | --------------------------------------------------------------------- | | Unconfigured | A required choice or credential is missing | | Configured, unverified | Settings are saved; availability has not been established | @@ -56,22 +74,37 @@ a database service or a local converter can be configured without currently bein | Service unreachable | Saved configuration does not prove present reachability | | Device offline | Network operations are unavailable; available local work may continue | -Invalidate verification when relevant endpoint, account or credential settings change. Browser -online/offline events are hints, not proof that a particular endpoint works. Separate saving from -testing configuration and show what each action accomplished. - -Reveal settings for the chosen path. A local conversion path may need cache/download/device controls; -a remote path may need endpoint/account controls. Hide irrelevant fields **and their stale status -messages**. Settings headers, field groups, account rows and unavailable/empty states retain the -named content and navigation spines from [DESIGN's alignment contract](./DESIGN.md#alignment-is-structure-not-polish). -Keep a title/menu command content-hugging; a separate track absorbs header filler. Count nested -panel and control padding through to visible labels before declaring regions aligned. Preserve other modes' configuration and keep the route back discoverable. Explain the -data destination before disclosure; do not confuse an API endpoint with an authorized account. - -Offline, retain saved content, drafts, previews, editing and exports when their data and implementation -are available. Hide network-only discovery or promotion that offers no useful route; retain existing -connected objects with an understandable unavailable state. A cached resource is not proof that all -runtime dependencies are present. Offer a deliberate continuation point after reconnecting. +- **W-SETTINGS-3 · SHOULD** Make each state in the settings table above distinguishable to the user. +- **W-SETTINGS-4 · MUST** Reconnecting an account whose authorization is required or expired keeps saved + work. _Proof: unit_ +- **W-SETTINGS-5 · MUST** Invalidate verification when relevant endpoint, account or credential settings + change. _Proof: unit_ +- **W-SETTINGS-6 · SHOULD** Treat browser online/offline events as hints, not as proof that a particular + endpoint works. +- **W-SETTINGS-7 · SHOULD** Reveal settings for the chosen path. A local conversion path may need + cache/download/device controls; a remote path may need endpoint/account controls. +- **W-SETTINGS-8 · MUST** Hide irrelevant fields **and their stale status messages**. _Proof: browser_ +- **W-SETTINGS-9 · SHOULD** Settings headers, field groups, account rows and unavailable/empty states + retain the named content and navigation spines from + [DESIGN's alignment contract](./DESIGN.md#alignment-is-structure-not-polish). +- **W-SETTINGS-10 · SHOULD** Keep a title/menu command content-hugging; a separate track absorbs header + filler. +- **W-SETTINGS-11 · SHOULD** Count nested panel and control padding through to visible labels before + declaring regions aligned. +- **W-SETTINGS-12 · MUST** Switching modes preserves the other modes' configuration. _Proof: unit_ +- **W-SETTINGS-13 · SHOULD** Keep the route back to another mode discoverable. +- **W-SETTINGS-14 · SHOULD** Explain the data destination before disclosure. +- **W-SETTINGS-15 · MUST** A configured API endpoint is not presented as an authorized account. + _Proof: review_ +- **W-SETTINGS-16 · MUST** Offline, retain saved content and drafts when their data and implementation + are available. _Proof: unit, browser_ +- **W-SETTINGS-17 · SHOULD** Offline, keep previews, editing and exports available when their data and + implementation are available. +- **W-SETTINGS-18 · SHOULD** Offline, hide network-only discovery or promotion that offers no useful + route; retain existing connected objects with an understandable unavailable state. +- **W-SETTINGS-19 · SHOULD** Treat a cached resource as no proof that all runtime dependencies are + present. +- **W-SETTINGS-20 · SHOULD** Offer a deliberate continuation point after reconnecting. ## 3. Account connections and external sources @@ -79,35 +112,56 @@ runtime dependencies are present. Offer a deliberate continuation point after re **Host contract:** authorization scopes, identity, access checks, revocation, credentials, provenance and imported-copy retention. An endpoint address does not establish account access. -Presentation primitives do not supply an OAuth deployment, credential vault, connector backend +Presentation primitives supply no OAuth deployment, credential vault, connector backend or storage guarantees. A polished connection screen cannot replace the host's authorization contract. -Distinguish account authorization, selected-source access and use by a particular operation. Request -only needed permissions; explain reads/writes and destination before authorization or disclosure. -Make account identity, reconnect and revoke/disconnect discoverable. Disconnect stops future access; -explain separately what happens to imported copies. Keep credentials out of URLs, logs and previews. - -Show provenance and freshness for selected sources, whether a local file, database record or cloud -document. Removing a source affects later work, not historical evidence. Permission errors, stale -data and offline cached copies need different states. Treat imported content as untrusted data, -not permission to execute commands. Make writes and destinations explicit, with review suited to impact. +- **W-ACCOUNT-1 · SHOULD** Distinguish account authorization, selected-source access and use by a + particular operation. +- **W-ACCOUNT-2 · SHOULD** Request only needed permissions. +- **W-ACCOUNT-3 · SHOULD** Explain reads, writes and destination before authorization or disclosure. +- **W-ACCOUNT-4 · MUST** Account identity, reconnect and revoke/disconnect are reachable from the + connection UI. _Proof: browser_ +- **W-ACCOUNT-5 · MUST** Disconnect stops future access. _Proof: unit_ +- **W-ACCOUNT-6 · SHOULD** Explain separately what happens to imported copies on disconnect. +- **W-ACCOUNT-7 · MUST** Keep credentials out of URLs, logs and previews. _Proof: review_ +- **W-ACCOUNT-8 · SHOULD** Show provenance and freshness for selected sources, whether a local file, + database record or cloud document. +- **W-ACCOUNT-9 · MUST** Removing a source affects later work, not historical evidence. _Proof: unit_ +- **W-ACCOUNT-10 · MUST** Permission errors, stale data and offline cached copies have different states. + _Proof: unit_ +- **W-ACCOUNT-11 · MUST** Treat imported content as untrusted data, not permission to execute commands. + _Proof: review_ +- **W-ACCOUNT-12 · MUST** Make writes and their destinations explicit before they happen. + _Proof: browser_ +- **W-ACCOUNT-13 · SHOULD** Suit the review step before a write to its impact. ## 4. Results, previews and revisions **Use when:** an export, transformed document or draft has an identity and destination beyond a -transient status message. Prefer a suitable preview/editor for substantive content. +transient status message. **Host contract:** result/revision identity, source version, target scope, commit acknowledgment, -conflict handling and any reversible history. Do not offer Compare, Apply or Undo without support. - -1. Summarize the result and identify its revision, source and destination when relevant. -2. Keep supported Copy, Export, Compare or Apply actions beside the relevant result. -3. Separate previewing from applying. Name the target and scope before mutation. -4. If source data changed, compare/rebase or regenerate before applying; never overwrite newer edits silently. -5. Confirm saved/applied state and recoverable output under [the interaction standard](./INTERACTION.md). +conflict handling and any reversible history. + +- **W-REVISION-1 · SHOULD** Prefer a suitable preview/editor for substantive content. +- **W-REVISION-2 · MUST** Offer Compare, Apply or Undo only when the host supports it. _Proof: review_ +- **W-REVISION-3 · SHOULD** Summarize the result and identify its revision, source and destination when + relevant. +- **W-REVISION-4 · SHOULD** Keep supported Copy, Export, Compare or Apply actions beside the relevant + result. +- **W-REVISION-5 · MUST** Separate previewing from applying, and name the target and scope before + mutation. _Proof: browser_ +- **W-REVISION-6 · MUST** If source data changed, compare/rebase or regenerate before applying; newer + edits are never overwritten silently. _Proof: unit_ +- **W-REVISION-7 · SHOULD** Confirm saved/applied state and recoverable output under + [the interaction standard](./INTERACTION.md). ## Applying a recipe -Keep only the states and actions the host can honor. Verify its relevant transitions: paused work -stays paused, changed configuration invalidates verification, source removal affects future work, -and stale revisions cannot overwrite new edits. Include interrupted/offline work when relevant. -A polished screen cannot substitute for a missing service contract; state that prerequisite clearly. +- **W-APPLY-1 · MUST** Keep only the states and actions the host can honor. _Proof: review_ +- **W-APPLY-2 · SHOULD** A polished screen cannot substitute for a missing service contract; state that + prerequisite clearly. + +The key transitions already carry MUST rules with unit proof: paused work stays paused (W-QUEUE-3), +changed configuration invalidates verification (W-SETTINGS-5), source removal affects only future +work (W-ACCOUNT-9), and stale revisions cannot overwrite new edits (W-REVISION-6). Their tests cover +interrupted and offline work where relevant. diff --git a/packages/ranui/docs/adapters/DESIGN.md b/packages/ranui/docs/adapters/DESIGN.md index 86cd275965..a5b11ca7d0 100644 --- a/packages/ranui/docs/adapters/DESIGN.md +++ b/packages/ranui/docs/adapters/DESIGN.md @@ -13,50 +13,66 @@ > [Coding guidelines](https://ran.chaxus.com/src/ranui/coding-guides/) — update those when a > rule here changes in a way consumers can see. +Each rule carries an ID and a level, with the meanings defined in [DESIGN](../DESIGN.md); the +[rule index](../RULES.md) lists the ranui rules after the portable ones. + ## ranui default visual direction The default is a quiet, content-first working interface: neutral semantic surfaces, restrained emphasis and compact desktop controls with reachable touch targets. Distinction comes from clear typography, proportions and complete states, not a decorative effect on every component. -Use existing token roles to choose exact dimensions; do not introduce a second "premium" scale. -Glass and expressive demonstrations are explicit surface/theme choices, not default text/control -backgrounds. Preserve reading contrast and the same interaction semantics across themes. -Apply the shared [style brief](../DESIGN.md#style-coherence-across-components-and-pages) before -changing a family. Review it in a realistic composed page with neighboring controls; a polished -isolated component does not establish a coherent product. +- **R-STYLE-1 · SHOULD** Use existing token roles to choose exact dimensions; do not introduce a second + "premium" scale. +- **R-STYLE-2 · SHOULD** Treat glass and expressive demonstrations as explicit surface or theme choices, + not as default text or control backgrounds. +- **R-STYLE-3 · MUST** Preserve reading contrast and the same interaction semantics across themes. + _Proof: browser_ +- **R-STYLE-4 · SHOULD** Apply the shared [style brief](../DESIGN.md#style-coherence-across-components-and-pages) + before changing a family. +- **R-STYLE-5 · SHOULD** Review a family change in a realistic composed page with neighboring controls; a + polished isolated component does not establish a coherent product. ## How to use this file -- Prefer **semantic tokens** (`--ran-color-*`, `--ran-space-*`, …) over raw scales or hex. -- Decide by **role/state**, not by eyeballing a value. The value is chosen for you. -- When a choice is unresolved, mark it explicitly — don't bury a guess in code. -- Design **all reachable states** (default, hover, active, focus, disabled, loading, empty, error), not just the happy path. -- **Verify the rendered result**, in light and dark, at narrow and wide widths — code review alone is not enough. - -For observable behavior, read [INTERACTION.md](../INTERACTION.md). The short +- **R-USE-1 · SHOULD** Prefer **semantic tokens** (`--ran-color-*`, `--ran-space-*`, …) over raw scales or hex. +- **R-USE-2 · SHOULD** Decide by **role/state**, not by eyeballing a value. The value is chosen for you. +- **R-USE-3 · SHOULD** When a choice is unresolved, mark it explicitly instead of burying a guess in code. +- **R-USE-4 · SHOULD** Design **all reachable states** (default, hover, active, focus, disabled, loading, + empty, error), not just the happy path. +- **R-USE-5 · MUST** **Verify the rendered result** in the themes and widths the change affects; code review + alone is not enough. _Proof: browser_ +- **R-USE-6 · SHOULD** Resolve conflicts in this order: **user goals → verified evidence → this file → + repo guidance (`CLAUDE.md`) → shipped patterns → general heuristics.** + +[INTERACTION.md](../INTERACTION.md) owns observable behavior. The short [interface index](../INTERFACE_GUIDE.md) routes other decisions to their owner; optional task recipes live separately in [WORKFLOW_PATTERNS.md](../WORKFLOW_PATTERNS.md). -Conflict resolution order: **user goals → verified evidence → this file → repo guidance (`CLAUDE.md`) → shipped patterns → general heuristics.** - ## What is machine-checked `pnpm -F ranui verify:design` enforces nine source rules: dark-safe and declared token fallbacks, semantic colors, spacing/sizing scales, Pointer Events, working `hidden`, held builder references and constructor-only shadow mounting. Repository CI also checks its -configured consumer surfaces. Its per-file baseline is a debt ratchet, never permission to -add violations. Static checks do not prove composition or interaction. +configured consumer surfaces. Static checks cannot prove composition or interaction. See [CODING](../CODING.md#what-is-machine-checked) for engineering checks and [ACCEPTANCE](../ACCEPTANCE.md) for actual browser evidence and decision authority. +- **R-CHECK-1 · MUST** A change adds no new `verify:design` violations; the per-file baseline is a debt + ratchet, not permission to add violations. _Proof: review_ +- **R-CHECK-2 · MUST** A component that implements a drag drives it with Pointer Events + (`pointerdown`/`pointermove`/`pointerup`/`pointercancel`), not a `mousemove`-only loop, so touch can drive + it. _Proof: check:mouse-only-drag_ + ## Looking at the whole system at once `pnpm -F ranui design:bundle` captures live documentation components and resolved light/dark -tokens into a browsable bundle. Start the docs preview first (`pnpm -F docs build` then -`pnpm -F docs preview`). Cards are rendered ranui output, not drawings of imagined APIs. -The command reports components missing a live demo. Use the matching installed version; -a reference bundle is not evidence that the host application passed acceptance. +tokens into a browsable bundle. It needs the docs preview running first (`pnpm -F docs build` +then `pnpm -F docs preview`). Cards are rendered ranui output, not drawings of imagined APIs. +The command reports components missing a live demo. + +- **R-BUNDLE-1 · SHOULD** Use the bundle from the matching installed version; a reference bundle is not + evidence that the host application passed acceptance. ## ranui color — a state ladder, not a palette @@ -77,7 +93,7 @@ Each hue is a **10-step scale** (`100`–`1000`). Every step has **one fixed job Scales: `--ran-gray-100..1000`, `--ran-gray-alpha-100..1000` (translucent, layers over any surface), `--ran-blue-*`, `--ran-red-*`, `--ran-amber-*`, `--ran-green-*`, plus `--ran-background-100/200`. -**Use the semantic layer, not the scale, in components:** +- **R-COLOR-1 · SHOULD** **Use the semantic layer in the table below, not the scale, in components.** | Token | Maps to | Use for | | -------------------------------------------- | ------------------------------------- | -------------------------------- | @@ -98,14 +114,27 @@ Scales: `--ran-gray-100..1000`, `--ran-gray-alpha-100..1000` (translucent, layer | `--ran-color-success` / `warning` / `danger` | green-700 / amber-700 / red-700 | Status | | `--ran-color-link` | blue-700 | Links | -**Accent meaning:** the **primary action is monochrome** — `--ran-color-primary` is black-on-white in light, white-on-black in dark (matching Vercel's brand tone), and text/icons on it use `--ran-color-primary-text` (the inverse, which flips too). **Blue** is reserved for **links (`--ran-color-link`) and the focus ring** only · green = success · amber = warning · red = danger/error. There is no separate "contrast" token — primary _is_ the highest-contrast monochrome action. +**Accent meaning:** the **primary action is monochrome** — `--ran-color-primary` is black-on-white in light, white-on-black in dark (matching Vercel's brand tone). There is no separate "contrast" token — primary _is_ the highest-contrast monochrome action. -**Light & dark:** same token name, different value. `gray-1000` is `#171717` in light and `#ededed` in dark. Components reference the _semantic name_. The dark mixin redefines the base scales plus explicit surface, primary hover/active and shadow overrides; read the theme declarations rather than assuming every semantic value follows one scale step. +- **R-COLOR-2 · SHOULD** Text and icons on a primary surface use `--ran-color-primary-text` (the inverse, + which flips too). +- **R-COLOR-3 · SHOULD** Reserve **blue** for **links (`--ran-color-link`) and the focus ring** only; green + means success, amber warning, red danger/error. + +**Light & dark:** same token name, different value. `gray-1000` is `#171717` in light and `#ededed` in dark. Components reference the _semantic name_. The dark mixin redefines the base scales plus explicit surface, primary hover/active and shadow overrides. + +- **R-COLOR-4 · SHOULD** Read the theme declarations rather than assuming every semantic value follows one + scale step. **Rules** -- Never hard-code a hex/rgb in a component for a value that should follow the theme. -- A component token's fallback must point at a **token that flips** (`var(--ran-gray-alpha-100, …)`, `var(--ran-blue-100, …)`, `var(--ran-color-text, …)`), never a light-only literal — otherwise it breaks in dark mode. +- **R-COLOR-5 · MUST** Do not hard-code a hex/rgb in a component for a value that follows the theme. + _Proof: check:bare-colour_ +- **R-COLOR-6 · MUST** A component token's colour fallback starts with a **token that flips** + (`var(--ran-gray-alpha-100, …)`, `var(--ran-blue-100, …)`, `var(--ran-color-text, …)`), never a bare + light-only literal — otherwise it breaks in dark mode. A final literal behind that token, as in + `var(--ran-btn-background, var(--ran-color-primary, #171717))`, is allowed. + _Proof: check:dark-unsafe-fallback_ ## ranui spacing — a limited, rhythmic scale @@ -113,13 +142,17 @@ A **4px base unit** with **nine values** only: `--ran-space-1..24` → `4, 8, 12 [ranui spacing relationships](#ranui-spacing-relationships) map each relationship to a step. -**Rule:** pick a value from the scale. Don't invent `20px`/`28px` — a limited set is what creates the page's rhythm. +- **R-SPACE-1 · MUST** Pick spacing from the scale; do not invent `20px`/`28px` — a limited set is what + creates the page's rhythm. _Proof: check:spacing-scale_ +- **R-SPACE-2 · MUST** Intrinsic dimensions (`width`, `height`, their `min-`/`max-` forms, `font-size`, + `line-height`, `border-radius`) take `--ran-size-*` or role tokens, never `--ran-space-*`, so consumers can + retune either scale without perturbing the other. _Proof: check:sizing-scale_ ## ranui typography — choose a role, not a size Fonts: `--ran-font-family` (Geist Sans, UI & prose), `--ran-font-mono` (Geist Mono, code, data, and eyebrow/labels). Base: `--ran-font-size: 14px`, `--ran-line-height: 1.5715`. -Decide by **role**; the role fixes font, size, weight, line-height. Each row is backed by real tokens in `theme/tokens.less` — reference them (`var(--ran-{component}-…, var(--ran-text-{role}…, fallback))`), don't hand-copy the px/weight number: +The role fixes font, size, weight and line-height. Each row is backed by real tokens in `theme/tokens.less`: | Role | Use | Weight | Size tokens | Notes | | ------- | ----------------------------- | ------------------------------------------------- | ----------------------------------------- | --------------------------------------------------------------- | @@ -129,49 +162,90 @@ Decide by **role**; the role fixes font, size, weight, line-height. Each row is | button | Button text | `--ran-text-button-weight` (500) | `--ran-text-button-size` (14px) | `--ran-text-button-line-height: 1` for crisp vertical centering | | mono | Code, data, eyebrows | `--ran-text-mono-weight-regular/medium` (400/500) | borrows label/copy size tiers | `--ran-font-mono` | -**Rule:** ask "what role is this text?" (heading / label / copy / button) — the style follows. Don't pick raw px per instance. +- **R-TYPE-1 · SHOULD** Decide text style by **role**: ask "what role is this text?" (heading / label / + copy / button) and the style follows. Do not pick raw px per instance. +- **R-TYPE-2 · SHOULD** Reference the role tokens + (`var(--ran-{component}-…, var(--ran-text-{role}…, fallback))`) instead of hand-copying the px/weight + number. +- **R-TYPE-3 · SHOULD** Follow the Notes column of the role table; for example, compact action labels stay + on one line while field labels may wrap. + +**A role is a tool, not a law.** -**A role is a tool, not a law.** Transient/decorative UI that doesn't map to any role (an active-link weight bump, a player gesture-flash overlay) keeps its own one-off component token instead of being forced into the nearest role — see `r-link`'s `--ran-link-active-font-weight` and `r-player`'s `--ran-player-gesture-flash-font-weight`. +- **R-TYPE-4 · MAY** Transient/decorative UI that maps to no role (an active-link weight bump, a player + gesture-flash overlay) keeps its own one-off component token instead of being forced into the nearest + role — see `r-link`'s `--ran-link-active-font-weight` and `r-player`'s + `--ran-player-gesture-flash-font-weight`. ## ranui radius & elevation Radius: `--ran-radius-sm` 6 · `--ran-radius-md` 12 · `--ran-radius-lg` 16 · `--ran-radius-full` 9999. -- Controls (button, input, select) → `sm`. Cards / dialogs → `md`. Large surfaces → `lg`. Pills / avatars → `full`. +- **R-RADIUS-1 · SHOULD** Controls (button, input, select) → `sm`. Cards / dialogs → `md`. Large surfaces → + `lg`. Pills / avatars → `full`. + +**Elevation is a role, not decoration.** + +- **R-ELEV-1 · SHOULD** Pick the shadow tier from the table below by _what the element is_. +- **R-ELEV-2 · SHOULD** Make sure each tier is actually perceptible — a shadow you can't see fails its job. -**Elevation is a role, not decoration.** Pick the shadow by _what the element is_, and make sure the tier is actually perceptible — a shadow you can't see fails its job. +| Tier | Token | Use for | +| ------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| Raised | `--ran-shadow-elevated` | In-flow surfaces that also have a border — `r-card`, `r-section`. Subtle on purpose. | +| Overlay | `--ran-shadow-menu` | Transient layers floating **over** content — `r-dropdown`, `r-select` menu, `r-popover`, tooltips, `r-message`/toast. Clearly lifts off the page. | +| Modal | `--ran-shadow-modal` | Blocking dialogs — `r-modal`. Strongest. | -| Tier | Token | Use for | -| ------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -| Raised | `--ran-shadow-elevated` | In-flow surfaces that also have a border — `r-card`, `r-section`. Subtle on purpose. | -| Overlay | `--ran-shadow-menu` | Transient layers floating **over** content — `r-dropdown`, `r-select` menu, `r-popover`, tooltips, `r-message`/toast. Must clearly lift off the page. | -| Modal | `--ran-shadow-modal` | Blocking dialogs — `r-modal`. Strongest. | +- **R-ELEV-3 · MUST** A floating overlay does not fall back to `--ran-shadow-elevated` (the card tier) — it + would look flat. _Proof: test:packages/ranui/test/unit/theme.semantic-tokens.test.ts, review_ +- **R-ELEV-4 · MUST** An overlay's shadow clearly lifts it off the page. _Proof: browser_ -A floating overlay must **never** fall back to `--ran-shadow-elevated` (the card tier) — it will look flat. Borderless overlays (dropdown, toast) rely on the shadow alone for separation, so the overlay tiers carry real weight. +Borderless overlays (dropdown, toast) rely on the shadow alone for separation, so the overlay tiers carry real weight. -**Stacking follows the same roles (z-index ladder).** Floating overlays portal to ``, so they need an explicit stacking tier. The z-index must sit on the element that is **positioned and portaled** (the host) — a z-index on an inner shadow node is trapped in the host's stacking context and does nothing against page content. +**Stacking follows the same roles (z-index ladder).** Floating overlays portal to ``, so they need an explicit stacking tier. + +- **R-Z-1 · MUST** The z-index sits on the element that is **positioned and portaled** (the host); a + z-index on an inner shadow node is trapped in the host's stacking context and does nothing against page + content. _Proof: test:packages/ranui/test/e2e/dropdown-layer.spec.ts_ +- **R-Z-2 · MUST** Overlays use the stacking tiers in the table below, so a select opened inside a modal + stays visible. _Proof: test:packages/ranui/test/e2e/dropdown-layer.spec.ts_ +- **R-Z-3 · MUST** Toasts and notifications sit on the top tier (`--ran-z-message`), above modals and + dropdowns. _Proof: review_ | Tier | Token | Default | Use for | | -------- | ------------------ | ------- | --------------------------------------------------------------------------------------------------- | | Modal | `--ran-z-modal` | `1000` | Blocking dialogs (`r-modal`) and their mask. | | Dropdown | `--ran-z-dropdown` | `1100` | Dropdown / select menu / popover. **Above** modal, so a select opened inside a modal stays visible. | -| Message | `--ran-z-message` | `1200` | Toasts / notifications. Always on top. | +| Message | `--ran-z-message` | `1200` | Toasts / notifications. The top tier. | -Override a tier globally (set `--ran-z-dropdown` on `:root`) or per component (`--ran-dropdown-host-z-index`, `--ran-modal-root-z-index`, `--ran-message-z-index`) — never reach for `!important`. An overlay open while the page scrolls must re-run its placement on `scroll`/`resize` so it tracks its trigger (e.g. inside a sticky header). +- **R-Z-4 · MUST** Override a tier globally (set `--ran-z-dropdown` on `:root`) or per component + (`--ran-dropdown-host-z-index`, `--ran-modal-root-z-index`, `--ran-message-z-index`), never with + `!important`. _Proof: test:packages/ranui/test/e2e/dropdown-layer.spec.ts, review_ +- **R-Z-5 · MUST** An overlay open while the page scrolls re-runs its placement on `scroll`/`resize` so it + tracks its trigger (e.g. inside a sticky header). _Proof: test:packages/ranui/test/unit/utils.floating.test.ts_ **Host stacking contexts:** a body-portaled overlay compares directly with root chrome; it usually needs no wrapper elevation. A non-portaled fixed overlay remains trapped in its ancestor stacking context (`isolation`, `transform`, `opacity`, `filter`, `will-change`). -Raise an enclosing wrapper only while that overlay actually paints, including its closing -transition. A blanket elevated wrapper promotes ordinary content above sticky chrome too. -Verify reflected open/closing attributes against the component API before using selectors. - -**Never animate a theme switch.** Transitions are for _interaction_ (hover / focus / press), not for flipping light↔dark. CSS cannot tell _why_ a property changed: any palette prop listed in a `transition` will also fade when the theme flips its token — each component at its own duration, while the host page has already switched. A general-purpose library must not impose that on its consumers. Rules: - -- **Palette props never get a default transition.** `background` / `background-color`, `color`, `border-color`, `box-shadow`, `fill`, `stroke` — none of them, on any element that carries a theme-driven color. Hover/focus feedback on these props snaps. -- **Motion props may transition.** `transform`, `opacity`, and box-geometry props (`left` / `top` / `width` / `height` / `font-size`) don't follow the theme — checkbox pops, tab ink-bars, ripples all stay animated. -- `transition: all` and bare-duration shorthands (`transition: 0.2s` — which means `all`) are banned; they silently include palette props. -- Every removed default keeps its `--ran-*-transition` hook var, so a consumer who wants palette fades can opt in per component. +A blanket elevated wrapper promotes ordinary content above sticky chrome too. + +- **R-Z-6 · MUST** Raise an enclosing wrapper only while that overlay actually paints, including its + closing transition. _Proof: browser_ +- **R-Z-7 · SHOULD** Verify reflected open/closing attributes against the component API before using + selectors. + +**Theme switches are not animated.** Transitions are for _interaction_ (hover / focus / press), not for flipping light↔dark. CSS cannot tell _why_ a property changed: any palette prop listed in a `transition` will also fade when the theme flips its token — each component at its own duration, while the host page has already switched. A general-purpose library that did this would impose it on every consumer. For ranui components this adapter deliberately tightens the portable D-MOTION-12 to MUST. + +- **R-MOTION-1 · MUST** Do not animate a theme switch. _Proof: browser_ +- **R-MOTION-2 · MUST** **Palette props get no default transition.** `background` / `background-color`, + `color`, `border-color`, `box-shadow`, `fill`, `stroke` — none of them, on any element that carries a + theme-driven color. Hover/focus feedback on these props snaps. _Proof: review, browser_ +- **R-MOTION-3 · MAY** **Motion props may transition.** `transform`, `opacity`, and box-geometry props + (`left` / `top` / `width` / `height` / `font-size`) do not follow the theme — checkbox pops, tab + ink-bars, ripples all stay animated. +- **R-MOTION-4 · MUST** Do not use `transition: all` or bare-duration shorthands (`transition: 0.2s` — + which means `all`); they silently include palette props. _Proof: review_ +- **R-MOTION-5 · MUST** Every removed default keeps its `--ran-*-transition` hook var, so a consumer who + wants palette fades can opt in per component. _Proof: review_ ```less /* ✗ fades on theme switch (palette props; `all`; bare duration = all) */ @@ -190,51 +264,87 @@ transition: var(--ran-input-transition, none); ## ranui components — how to apply the system -- Reuse existing semantic native controls or supported ranui primitives; do not rebuild them merely for styling. Choose a ranui component when its contract fits; preserve native form and keyboard semantics, including supported native controls in overlay slots ([INTERACTION §2](../INTERACTION.md#2-reachable-controls-and-predictable-focus)). -- Theme through **CSS variables**, **`::part()`**, or the **`sheet`** attribute (escape hatch). CSS variables cross Shadow DOM; selectors do not. -- Component tokens default to semantic tokens: `var(--ran-btn-background, var(--ran-color-primary, #171717))`. -- Map states to the color ladder: default → `bg`/`text`; hover → `bg-hover` / `border-hover` / `primary-hover`; active → `bg-active` / `primary-active`; disabled → appropriate disabled tokens, with opacity only when needed and still legible; focus → focus ring. -- **Interactive cards opt in**: `r-card` only reacts to hover with the `hoverable` attribute (border 400 → 500 + elevated shadow). Non-interactive cards must stay inert — never add hover feedback to something that isn't clickable. -- **When the host needs ranui theme switching UI**, use `` (system / light / dark segmented pill, wired to `setTheme`/localStorage, syncs across instances and updates `theme-color` metas) instead of hand-rolling equivalent toggles. Preserve an existing host theme authority; this does not require adding a theme switch. Localize with `label` / `label-system` / `label-light` / `label-dark`. -- **Typography ships with the system**: `import 'ranui/fonts'` (or link `dist/fonts/fonts.css`) self-hosts Geist Sans + Geist Mono (variable, OFL-licensed) — the canonical faces behind `--ran-font-family` / `--ran-font-mono`. Without it the stacks fall back to system fonts. -- See [THEME_STYLE_SYSTEM_DESIGN.md](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/THEME_STYLE_SYSTEM_DESIGN.md) for the repository-only token architecture reference and [style-tokens-public.md](../style-tokens-public.md) for the generated per-component token list. +- **R-COMP-1 · SHOULD** Reuse existing semantic native controls or supported ranui primitives; do not + rebuild them merely for styling. +- **R-COMP-2 · SHOULD** Choose a ranui component when its contract fits. +- **R-COMP-3 · MUST** Preserve native form and keyboard semantics, including supported native controls in + overlay slots ([INTERACTION §2](../INTERACTION.md#2-reachable-controls-and-predictable-focus)). + _Proof: browser_ +- **R-COMP-4 · SHOULD** Theme through **CSS variables**, **`::part()`**, or the **`sheet`** attribute + (escape hatch). CSS variables cross Shadow DOM; selectors do not. +- **R-COMP-5 · SHOULD** Component tokens default to semantic tokens: + `var(--ran-btn-background, var(--ran-color-primary, #171717))`. +- **R-COMP-6 · SHOULD** Map states to the color ladder: default → `bg`/`text`; hover → `bg-hover` / + `border-hover` / `primary-hover`; active → `bg-active` / `primary-active`; disabled → appropriate + disabled tokens, with opacity only when needed and still legible; focus → focus ring. +- **R-COMP-7 · MUST** **Interactive cards opt in**: `r-card` only reacts to hover with the `hoverable` + attribute (border 400 → 500 + elevated shadow). Non-interactive cards stay inert — no hover feedback on + something that isn't clickable. _Proof: browser_ +- **R-COMP-8 · SHOULD** **When the host needs ranui theme switching UI**, use `` (system / + light / dark segmented pill, wired to `setTheme`/localStorage, syncs across instances and updates + `theme-color` metas) instead of hand-rolling equivalent toggles. +- **R-COMP-9 · SHOULD** Preserve an existing host theme authority; R-COMP-8 does not require adding a theme + switch. +- **R-COMP-10 · SHOULD** Localize `` with `label` / `label-system` / `label-light` / + `label-dark`. +- **R-COMP-11 · MAY** **Typography ships with the system**: `import 'ranui/fonts'` (or link + `dist/fonts/fonts.css`) self-hosts Geist Sans + Geist Mono (variable, OFL-licensed) — the canonical + faces behind `--ran-font-family` / `--ran-font-mono`. Without it the stacks fall back to system fonts. + +See [THEME_STYLE_SYSTEM_DESIGN.md](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/THEME_STYLE_SYSTEM_DESIGN.md) for the repository-only token architecture reference and [style-tokens-public.md](../style-tokens-public.md) for the generated per-component token list. ### A fallback must name a token that exists -Check declared tokens, including fallbacks, against the installed theme. For example, -`--ran-color-danger` exists; a plausible `--ran-color-error` does not. An undefined `var()` -can silently discard the entire declaration. [CODING §5](../CODING.md#5-styles) owns enforcement. +For example, `--ran-color-danger` exists; a plausible `--ran-color-error` does not. +[CODING §5](../CODING.md#5-styles) owns enforcement. + +- **R-TOKEN-1 · MUST** Declared tokens, including fallbacks, name tokens that exist in the installed theme; + an undefined `var()` can silently discard the entire declaration. _Proof: check:undefined-token-fallback_ ### `hidden` must keep working -An author `:host` display rule must preserve `:host([hidden]) { display: none; }`. -See [CODING §5](../CODING.md#5-styles); hiding optional content must also remove accidental gaps. +See [CODING §5](../CODING.md#5-styles). + +- **R-HOST-1 · MUST** An author `:host` display rule preserves `:host([hidden]) { display: none; }`. + _Proof: check:hidden-inert_ +- **R-HOST-2 · MUST** Hiding optional content also removes accidental gaps. _Proof: browser_ ### Hold what you build -Build shadow structure once and retain its references; do not rediscover it through selectors. [CODING §2](../CODING.md#2-the-component-skeleton) owns construction, runtime-child exceptions and SSR implementation. These are engineering requirements, not extra visual recipes. +- **R-BUILD-1 · MUST** Append shadow structure in the constructor; content that genuinely cannot exist yet + is mounted later only with the documented `// deferred mount: ` marker on the line above, which also + says how a second append is prevented. _Proof: check:shadow-mount-outside-constructor_ +- **R-BUILD-2 · MUST** Retain the references to what you built; do not rediscover them through selectors. + _Proof: check:built-then-queried_ + ## ranui component token naming -Use `--ran-{component}-{element}[-{state}]-{property}`. Keep the existing component prefix; -name the most specific distinct UI part, not its full DOM/BEM path. Keep track/fill as -separate parts; drop wrapper/position segments that add no identity. State is optional and -must correspond to an independently overridable interaction state. Property names retain -their CSS meaning. Aim for at most four semantic segments after `ran`, without collapsing -distinct override points merely to shorten a name. +- **R-NAME-1 · SHOULD** Use `--ran-{component}-{element}[-{state}]-{property}`, keeping the existing + component prefix. +- **R-NAME-2 · SHOULD** Name the most specific distinct UI part, not its full DOM/BEM path; drop + wrapper/position segments that add no identity. +- **R-NAME-3 · SHOULD** Keep track/fill as separate parts. +- **R-NAME-4 · MUST** The state segment is optional, and when present corresponds to an independently + overridable interaction state. _Proof: review_ +- **R-NAME-5 · SHOULD** Property names retain their CSS meaning. +- **R-NAME-6 · SHOULD** Aim for at most four semantic segments after `ran`, without collapsing distinct + override points merely to shorten a name. For example, `--ran-progress-fill-background` names a part; `--ran-progress-wrap-value-background` encodes implementation nesting. The generated [public catalog](../style-tokens-public.md) -owns available names. This section owns naming; do not hand-maintain another catalog. +owns available names. This section owns naming. + +- **R-NAME-7 · SHOULD** Do not hand-maintain another token catalog. ## Applying generic composition to ranui -The [navigation-layer rules](../DESIGN.md#containers-do-not-fix-structure) map to ranui as -follows: `r-modal` for a short confirmation or a single field, `r-popover` / `r-dropdown` for -transient context, `r-disclosure-row` for progressive disclosure, and `r-tabs` only for peer -views of one object. +- **R-COMPOSE-1 · SHOULD** Map the [navigation-layer rules](../DESIGN.md#containers-do-not-fix-structure) + to ranui as follows: `r-modal` for a short confirmation or a single field, `r-popover` / `r-dropdown` + for transient context, `r-disclosure-row` for progressive disclosure, and `r-tabs` only for peer views + of one object. ### ranui spacing relationships @@ -247,15 +357,20 @@ views of one object. | Related subsections | `--ran-space-6` | Compact blocks | | Major region boundary | `--ran-space-8` | Region separation | -**Inside before outside:** these are host composition choices, not fixed component heights. -Major regions can use `--ran-space-10` when their relationship warrants it. Absent descriptions -leave no vacant row. The generic composition contract owns the relationship; this adapter owns -its mapping to the installed ranui scale. +- **R-COMPOSE-2 · SHOULD** Space each relationship with the token the table above maps it to. -For geometry proofs, measure the host, documented shadow parts and slotted text. Use -`ranui/testing` for closed-root browser tests; do not introduce production shadow queries. -Use generated public variables and parts for overrides. Consumer aliases may improve naming -while deriving their values from the existing `--ran-*` foundation. +**Inside before outside:** these are host composition choices, not fixed component heights. The +generic composition contract owns the relationship; this adapter owns its mapping to the installed +ranui scale. + +- **R-COMPOSE-3 · MAY** Major regions use `--ran-space-10` when their relationship warrants it. +- **R-COMPOSE-4 · MUST** Absent descriptions leave no vacant row. _Proof: browser_ +- **R-COMPOSE-5 · SHOULD** For geometry proofs, measure the host, documented shadow parts and slotted text. +- **R-COMPOSE-6 · SHOULD** Use `ranui/testing` for closed-root browser tests. +- **R-COMPOSE-7 · MUST** Do not introduce production shadow queries. _Proof: review_ +- **R-COMPOSE-8 · SHOULD** Use generated public variables and parts for overrides. +- **R-COMPOSE-9 · MAY** Consumer aliases may improve naming while deriving their values from the existing + `--ran-*` foundation. ```css .workspace { @@ -271,6 +386,9 @@ while deriving their values from the existing `--ran-*` foundation. } ``` -The 32px target illustrates one host role, not a universal minimum. Verify coarse-input -bounds independently. ranui ships no table, tree, calendar, kanban or timeline; use semantic -host implementations with these same foundation roles where those views are needed. +The 32px target illustrates one host role, not a universal minimum. ranui ships no table, tree, +calendar, kanban or timeline. + +- **R-COMPOSE-10 · MUST** Verify coarse-input bounds independently. _Proof: browser_ +- **R-COMPOSE-11 · SHOULD** Where table, tree, calendar, kanban or timeline views are needed, use semantic + host implementations with these same foundation roles. diff --git a/packages/ranui/docs/adapters/RULES.md b/packages/ranui/docs/adapters/RULES.md new file mode 100644 index 0000000000..4469622781 --- /dev/null +++ b/packages/ranui/docs/adapters/RULES.md @@ -0,0 +1,85 @@ + + +## ranui adapter rules + +ranui-specific rules (31 MUST, 41 SHOULD, 5 MAY). + +| ID | Level | Rule | Proof | +| ------------------------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | +| [R-STYLE-1](../DESIGN.md#ranui-default-visual-direction) | SHOULD | Use existing token roles to choose exact dimensions; do not introduce a second "premium" scale. | | +| [R-STYLE-2](../DESIGN.md#ranui-default-visual-direction) | SHOULD | Treat glass and expressive demonstrations as explicit surface or theme choices, not as default text or control backgrounds. | | +| [R-STYLE-3](../DESIGN.md#ranui-default-visual-direction) | MUST | Preserve reading contrast and the same interaction semantics across themes. | browser | +| [R-STYLE-4](../DESIGN.md#ranui-default-visual-direction) | SHOULD | Apply the shared [style brief](../DESIGN.md#style-coherence-across-components-and-pages) before changing a family. | | +| [R-STYLE-5](../DESIGN.md#ranui-default-visual-direction) | SHOULD | Review a family change in a realistic composed page with neighboring controls; a polished isolated component does not establish a coherent product. | | +| [R-USE-1](../DESIGN.md#how-to-use-this-file) | SHOULD | Prefer **semantic tokens** (`--ran-color-*`, `--ran-space-*`, …) over raw scales or hex. | | +| [R-USE-2](../DESIGN.md#how-to-use-this-file) | SHOULD | Decide by **role/state**, not by eyeballing a value. The value is chosen for you. | | +| [R-USE-3](../DESIGN.md#how-to-use-this-file) | SHOULD | When a choice is unresolved, mark it explicitly instead of burying a guess in code. | | +| [R-USE-4](../DESIGN.md#how-to-use-this-file) | SHOULD | Design **all reachable states** (default, hover, active, focus, disabled, loading, empty, error), not just the happy path. | | +| [R-USE-5](../DESIGN.md#how-to-use-this-file) | MUST | **Verify the rendered result** in the themes and widths the change affects; code review alone is not enough. | browser | +| [R-USE-6](../DESIGN.md#how-to-use-this-file) | SHOULD | Resolve conflicts in this order: **user goals → verified evidence → this file → repo guidance (`CLAUDE.md`) → shipped patterns → general heuristics.** | | +| [R-CHECK-1](../DESIGN.md#what-is-machine-checked) | MUST | A change adds no new `verify:design` violations; the per-file baseline is a debt ratchet, not permission to add violations. | review | +| [R-CHECK-2](../DESIGN.md#what-is-machine-checked) | MUST | A component that implements a drag drives it with Pointer Events (`pointerdown`/`pointermove`/`pointerup`/`pointercancel`), not a `mousemove`-only loop, so touch can drive it. | check:mouse-only-drag | +| [R-BUNDLE-1](../DESIGN.md#looking-at-the-whole-system-at-once) | SHOULD | Use the bundle from the matching installed version; a reference bundle is not evidence that the host application passed acceptance. | | +| [R-COLOR-1](../DESIGN.md#ranui-color--a-state-ladder-not-a-palette) | SHOULD | **Use the semantic layer in the table below, not the scale, in components.** | | +| [R-COLOR-2](../DESIGN.md#ranui-color--a-state-ladder-not-a-palette) | SHOULD | Text and icons on a primary surface use `--ran-color-primary-text` (the inverse, which flips too). | | +| [R-COLOR-3](../DESIGN.md#ranui-color--a-state-ladder-not-a-palette) | SHOULD | Reserve **blue** for **links (`--ran-color-link`) and the focus ring** only; green means success, amber warning, red danger/error. | | +| [R-COLOR-4](../DESIGN.md#ranui-color--a-state-ladder-not-a-palette) | SHOULD | Read the theme declarations rather than assuming every semantic value follows one scale step. | | +| [R-COLOR-5](../DESIGN.md#ranui-color--a-state-ladder-not-a-palette) | MUST | Do not hard-code a hex/rgb in a component for a value that follows the theme. | check:bare-colour | +| [R-COLOR-6](../DESIGN.md#ranui-color--a-state-ladder-not-a-palette) | MUST | A component token's colour fallback starts with a **token that flips** (`var(--ran-gray-alpha-100, …)`, `var(--ran-blue-100, …)`, `var(--ran-color-text, …)`), never a bare light-only literal — otherwise it breaks in dark mode. A final literal behind that token, as in `var(--ran-btn-background, var(--ran-color-primary, #171717))`, is allowed. | check:dark-unsafe-fallback | +| [R-SPACE-1](../DESIGN.md#ranui-spacing--a-limited-rhythmic-scale) | MUST | Pick spacing from the scale; do not invent `20px`/`28px` — a limited set is what creates the page's rhythm. | check:spacing-scale | +| [R-SPACE-2](../DESIGN.md#ranui-spacing--a-limited-rhythmic-scale) | MUST | Intrinsic dimensions (`width`, `height`, their `min-`/`max-` forms, `font-size`, `line-height`, `border-radius`) take `--ran-size-*` or role tokens, never `--ran-space-*`, so consumers can retune either scale without perturbing the other. | check:sizing-scale | +| [R-TYPE-1](../DESIGN.md#ranui-typography--choose-a-role-not-a-size) | SHOULD | Decide text style by **role**: ask "what role is this text?" (heading / label / copy / button) and the style follows. Do not pick raw px per instance. | | +| [R-TYPE-2](../DESIGN.md#ranui-typography--choose-a-role-not-a-size) | SHOULD | Reference the role tokens (`var(--ran-{component}-…, var(--ran-text-{role}…, fallback))`) instead of hand-copying the px/weight number. | | +| [R-TYPE-3](../DESIGN.md#ranui-typography--choose-a-role-not-a-size) | SHOULD | Follow the Notes column of the role table; for example, compact action labels stay on one line while field labels may wrap. | | +| [R-TYPE-4](../DESIGN.md#ranui-typography--choose-a-role-not-a-size) | MAY | Transient/decorative UI that maps to no role (an active-link weight bump, a player gesture-flash overlay) keeps its own one-off component token instead of being forced into the nearest role — see `r-link`'s `--ran-link-active-font-weight` and `r-player`'s `--ran-player-gesture-flash-font-weight`. | | +| [R-RADIUS-1](../DESIGN.md#ranui-radius--elevation) | SHOULD | Controls (button, input, select) → `sm`. Cards / dialogs → `md`. Large surfaces → `lg`. Pills / avatars → `full`. | | +| [R-ELEV-1](../DESIGN.md#ranui-radius--elevation) | SHOULD | Pick the shadow tier from the table below by _what the element is_. | | +| [R-ELEV-2](../DESIGN.md#ranui-radius--elevation) | SHOULD | Make sure each tier is actually perceptible — a shadow you can't see fails its job. | | +| [R-ELEV-3](../DESIGN.md#ranui-radius--elevation) | MUST | A floating overlay does not fall back to `--ran-shadow-elevated` (the card tier) — it would look flat. | test:packages/ranui/test/unit/theme.semantic-tokens.test.ts, review | +| [R-ELEV-4](../DESIGN.md#ranui-radius--elevation) | MUST | An overlay's shadow clearly lifts it off the page. | browser | +| [R-Z-1](../DESIGN.md#ranui-radius--elevation) | MUST | The z-index sits on the element that is **positioned and portaled** (the host); a z-index on an inner shadow node is trapped in the host's stacking context and does nothing against page content. | test:packages/ranui/test/e2e/dropdown-layer.spec.ts | +| [R-Z-2](../DESIGN.md#ranui-radius--elevation) | MUST | Overlays use the stacking tiers in the table below, so a select opened inside a modal stays visible. | test:packages/ranui/test/e2e/dropdown-layer.spec.ts | +| [R-Z-3](../DESIGN.md#ranui-radius--elevation) | MUST | Toasts and notifications sit on the top tier (`--ran-z-message`), above modals and dropdowns. | review | +| [R-Z-4](../DESIGN.md#ranui-radius--elevation) | MUST | Override a tier globally (set `--ran-z-dropdown` on `:root`) or per component (`--ran-dropdown-host-z-index`, `--ran-modal-root-z-index`, `--ran-message-z-index`), never with `!important`. | test:packages/ranui/test/e2e/dropdown-layer.spec.ts, review | +| [R-Z-5](../DESIGN.md#ranui-radius--elevation) | MUST | An overlay open while the page scrolls re-runs its placement on `scroll`/`resize` so it tracks its trigger (e.g. inside a sticky header). | test:packages/ranui/test/unit/utils.floating.test.ts | +| [R-Z-6](../DESIGN.md#ranui-radius--elevation) | MUST | Raise an enclosing wrapper only while that overlay actually paints, including its closing transition. | browser | +| [R-Z-7](../DESIGN.md#ranui-radius--elevation) | SHOULD | Verify reflected open/closing attributes against the component API before using selectors. | | +| [R-MOTION-1](../DESIGN.md#ranui-radius--elevation) | MUST | Do not animate a theme switch. | browser | +| [R-MOTION-2](../DESIGN.md#ranui-radius--elevation) | MUST | **Palette props get no default transition.** `background` / `background-color`, `color`, `border-color`, `box-shadow`, `fill`, `stroke` — none of them, on any element that carries a theme-driven color. Hover/focus feedback on these props snaps. | review, browser | +| [R-MOTION-3](../DESIGN.md#ranui-radius--elevation) | MAY | **Motion props may transition.** `transform`, `opacity`, and box-geometry props (`left` / `top` / `width` / `height` / `font-size`) do not follow the theme — checkbox pops, tab ink-bars, ripples all stay animated. | | +| [R-MOTION-4](../DESIGN.md#ranui-radius--elevation) | MUST | Do not use `transition: all` or bare-duration shorthands (`transition: 0.2s` — which means `all`); they silently include palette props. | review | +| [R-MOTION-5](../DESIGN.md#ranui-radius--elevation) | MUST | Every removed default keeps its `--ran-*-transition` hook var, so a consumer who wants palette fades can opt in per component. | review | +| [R-COMP-1](../DESIGN.md#ranui-components--how-to-apply-the-system) | SHOULD | Reuse existing semantic native controls or supported ranui primitives; do not rebuild them merely for styling. | | +| [R-COMP-2](../DESIGN.md#ranui-components--how-to-apply-the-system) | SHOULD | Choose a ranui component when its contract fits. | | +| [R-COMP-3](../DESIGN.md#ranui-components--how-to-apply-the-system) | MUST | Preserve native form and keyboard semantics, including supported native controls in overlay slots ([INTERACTION §2](../INTERACTION.md#2-reachable-controls-and-predictable-focus)). | browser | +| [R-COMP-4](../DESIGN.md#ranui-components--how-to-apply-the-system) | SHOULD | Theme through **CSS variables**, **`::part()`**, or the **`sheet`** attribute (escape hatch). CSS variables cross Shadow DOM; selectors do not. | | +| [R-COMP-5](../DESIGN.md#ranui-components--how-to-apply-the-system) | SHOULD | Component tokens default to semantic tokens: `var(--ran-btn-background, var(--ran-color-primary, #171717))`. | | +| [R-COMP-6](../DESIGN.md#ranui-components--how-to-apply-the-system) | SHOULD | Map states to the color ladder: default → `bg`/`text`; hover → `bg-hover` / `border-hover` / `primary-hover`; active → `bg-active` / `primary-active`; disabled → appropriate disabled tokens, with opacity only when needed and still legible; focus → focus ring. | | +| [R-COMP-7](../DESIGN.md#ranui-components--how-to-apply-the-system) | MUST | **Interactive cards opt in**: `r-card` only reacts to hover with the `hoverable` attribute (border 400 → 500 + elevated shadow). Non-interactive cards stay inert — no hover feedback on something that isn't clickable. | browser | +| [R-COMP-8](../DESIGN.md#ranui-components--how-to-apply-the-system) | SHOULD | **When the host needs ranui theme switching UI**, use `` (system / light / dark segmented pill, wired to `setTheme`/localStorage, syncs across instances and updates `theme-color` metas) instead of hand-rolling equivalent toggles. | | +| [R-COMP-9](../DESIGN.md#ranui-components--how-to-apply-the-system) | SHOULD | Preserve an existing host theme authority; R-COMP-8 does not require adding a theme switch. | | +| [R-COMP-10](../DESIGN.md#ranui-components--how-to-apply-the-system) | SHOULD | Localize `` with `label` / `label-system` / `label-light` / `label-dark`. | | +| [R-COMP-11](../DESIGN.md#ranui-components--how-to-apply-the-system) | MAY | **Typography ships with the system**: `import 'ranui/fonts'` (or link `dist/fonts/fonts.css`) self-hosts Geist Sans + Geist Mono (variable, OFL-licensed) — the canonical faces behind `--ran-font-family` / `--ran-font-mono`. Without it the stacks fall back to system fonts. | | +| [R-TOKEN-1](../DESIGN.md#a-fallback-must-name-a-token-that-exists) | MUST | Declared tokens, including fallbacks, name tokens that exist in the installed theme; an undefined `var()` can silently discard the entire declaration. | check:undefined-token-fallback | +| [R-HOST-1](../DESIGN.md#hidden-must-keep-working) | MUST | An author `:host` display rule preserves `:host([hidden]) { display: none; }`. | check:hidden-inert | +| [R-HOST-2](../DESIGN.md#hidden-must-keep-working) | MUST | Hiding optional content also removes accidental gaps. | browser | +| [R-BUILD-1](../DESIGN.md#hold-what-you-build) | MUST | Append shadow structure in the constructor; content that genuinely cannot exist yet is mounted later only with the documented `// deferred mount: ` marker on the line above, which also says how a second append is prevented. | check:shadow-mount-outside-constructor | +| [R-BUILD-2](../DESIGN.md#hold-what-you-build) | MUST | Retain the references to what you built; do not rediscover them through selectors. | check:built-then-queried | +| [R-NAME-1](../DESIGN.md#ranui-component-token-naming) | SHOULD | Use `--ran-{component}-{element}[-{state}]-{property}`, keeping the existing component prefix. | | +| [R-NAME-2](../DESIGN.md#ranui-component-token-naming) | SHOULD | Name the most specific distinct UI part, not its full DOM/BEM path; drop wrapper/position segments that add no identity. | | +| [R-NAME-3](../DESIGN.md#ranui-component-token-naming) | SHOULD | Keep track/fill as separate parts. | | +| [R-NAME-4](../DESIGN.md#ranui-component-token-naming) | MUST | The state segment is optional, and when present corresponds to an independently overridable interaction state. | review | +| [R-NAME-5](../DESIGN.md#ranui-component-token-naming) | SHOULD | Property names retain their CSS meaning. | | +| [R-NAME-6](../DESIGN.md#ranui-component-token-naming) | SHOULD | Aim for at most four semantic segments after `ran`, without collapsing distinct override points merely to shorten a name. | | +| [R-NAME-7](../DESIGN.md#ranui-component-token-naming) | SHOULD | Do not hand-maintain another token catalog. | | +| [R-COMPOSE-1](../DESIGN.md#applying-generic-composition-to-ranui) | SHOULD | Map the [navigation-layer rules](../DESIGN.md#containers-do-not-fix-structure) to ranui as follows: `r-modal` for a short confirmation or a single field, `r-popover` / `r-dropdown` for transient context, `r-disclosure-row` for progressive disclosure, and `r-tabs` only for peer views of one object. | | +| [R-COMPOSE-2](../DESIGN.md#ranui-spacing-relationships) | SHOULD | Space each relationship with the token the table above maps it to. | | +| [R-COMPOSE-3](../DESIGN.md#ranui-spacing-relationships) | MAY | Major regions use `--ran-space-10` when their relationship warrants it. | | +| [R-COMPOSE-4](../DESIGN.md#ranui-spacing-relationships) | MUST | Absent descriptions leave no vacant row. | browser | +| [R-COMPOSE-5](../DESIGN.md#ranui-spacing-relationships) | SHOULD | For geometry proofs, measure the host, documented shadow parts and slotted text. | | +| [R-COMPOSE-6](../DESIGN.md#ranui-spacing-relationships) | SHOULD | Use `ranui/testing` for closed-root browser tests. | | +| [R-COMPOSE-7](../DESIGN.md#ranui-spacing-relationships) | MUST | Do not introduce production shadow queries. | review | +| [R-COMPOSE-8](../DESIGN.md#ranui-spacing-relationships) | SHOULD | Use generated public variables and parts for overrides. | | +| [R-COMPOSE-9](../DESIGN.md#ranui-spacing-relationships) | MAY | Consumer aliases may improve naming while deriving their values from the existing `--ran-*` foundation. | | +| [R-COMPOSE-10](../DESIGN.md#ranui-spacing-relationships) | MUST | Verify coarse-input bounds independently. | browser | +| [R-COMPOSE-11](../DESIGN.md#ranui-spacing-relationships) | SHOULD | Where table, tree, calendar, kanban or timeline views are needed, use semantic host implementations with these same foundation roles. | | diff --git a/packages/ranui/package.json b/packages/ranui/package.json index 16fe5a4212..c469db5ccb 100644 --- a/packages/ranui/package.json +++ b/packages/ranui/package.json @@ -206,6 +206,7 @@ "docs/REFERENCE_RESEARCH.md", "docs/ACCEPTANCE.md", "docs/INTERACTION.md", + "docs/RULES.md", "docs/WORKFLOW_PATTERNS.md", "docs/CODING.md", "docs/COMPONENTS.md", diff --git a/plugins/ran/skills/design/references/ACCEPTANCE.md b/plugins/ran/skills/design/references/ACCEPTANCE.md index ae1f96fc5b..698fe55f8c 100644 --- a/plugins/ran/skills/design/references/ACCEPTANCE.md +++ b/plugins/ran/skills/design/references/ACCEPTANCE.md @@ -6,130 +6,204 @@ Use this contract for a changed visual or interaction outcome. [DESIGN](./DESIGN appearance and composition; [INTERACTION](./INTERACTION.md) owns observable behavior. The [interface index](./INTERFACE_GUIDE.md) routes the task. Read only applicable sections. +Each rule carries an ID and a level. **MUST** is a defect when broken and names its proof; +**SHOULD** is the default, and a deviation states its reason for that case; **MAY** is optional. +The [rule index](./RULES.md) lists every graded rule with its proof. + ## Agree on the observable result -Before editing, settle the [design brief](./DESIGN.md#style-coherence-across-components-and-pages), -and state the affected task, views/states and independent expectations in that same note. Name the relevant owner, supported public API and host capability limits. Select -widths, themes, languages and input paths from the actual change; do not invent unrelated work. -For composition, state content/text/icon spines, intentional navigation columns, hit bounds -and vertical relationships. Do not derive the expected value from the implementation under test. +- **V-AGREE-1 · SHOULD** Before editing, settle the + [design brief](./DESIGN.md#style-coherence-across-components-and-pages), and state the affected + task, views/states and independent expectations in that same note. +- **V-AGREE-2 · SHOULD** Name the relevant owner, supported public API and host capability limits. +- **V-AGREE-3 · SHOULD** Select widths, themes, languages and input paths from the actual change, + without inventing unrelated work. +- **V-AGREE-4 · SHOULD** For composition, state content/text/icon spines, intentional navigation + columns, hit bounds and vertical relationships. +- **V-AGREE-5 · MUST** Do not derive the expected value from the implementation under test. + _Proof: review_ +- **V-AGREE-6 · SHOULD** Review in order: task/hierarchy, static composition, reachable combined + states, then motion. +- **V-AGREE-7 · SHOULD** Do not add animation to compensate for unresolved proportions or unclear + navigation. +- **V-AGREE-8 · SHOULD** When a review changes the intended design, update the existing brief first + and identify the evidence that prompted it. -Review in order: task/hierarchy, static composition, reachable combined states, then motion. -Do not add animation to compensate for unresolved proportions or unclear navigation. When a review -changes the intended design, update the existing brief first and identify the evidence that prompted it. This is a decision record, not a new approval ceremony for work the user already authorized. -Keep one rule owner. An API/token check verifies source contracts; an image comparison verifies -pixels; an agent run verifies whether guidance was followed. None substitutes for the others. -A fixture demonstrates its own result, not acceptance of a host application it did not render. +An API/token check verifies source contracts; an image comparison verifies pixels; an agent run +verifies whether guidance was followed. + +- **V-AGREE-9 · SHOULD** Keep one rule owner. +- **V-AGREE-10 · MUST** Do not let one kind of check substitute for another: an API/token check, an + image comparison and an agent run each establish only their own result. _Proof: review_ +- **V-AGREE-11 · MUST** A fixture demonstrates its own result, not acceptance of a host application + it did not render; do not claim it as host acceptance. _Proof: review_ ## Browser proof -Capture the actual running surface before the fix when reproducing a visible defect. After the -fix, capture the same relevant state and inspect the full pane with realistic copy and surrounding -chrome. A mockup, source diff, changed assertion or screenshot of an unrelated build is not proof. -Inspect the composition as well as the link count: relabeling a redundant return as a breadcrumb -can still repeat the same context and add an unnecessary header row. Trace ancestor reachability -through the parent before deciding another visible shortcut is needed. - -- Wait for component registration, pending content, fonts and rendered frames before measuring. - Fail on missing/zero-sized targets. Record any subpixel tolerance; keep it below a full CSS pixel - for exact spines (the source example uses 0.5px). - For live overlays, verify that capture preserves the viewport and includes the full trigger/panel; - a full-page screenshot can invalidate a crop by causing responsive reflow or portal repositioning. -- Measure the defect's layer: wrapper, inner content, rendered text Range, icon viewport and - actual pointer target are different. A Range is not exact glyph ink. Account for shadow parts - through supported test seams; do not introduce production shadow queries for acceptance. -- Exercise affected reflow, wrapped translations, optional content and input paths. A static - screenshot cannot prove focus return, keyboard navigation, touch discovery, interruptibility - or async settlement. Record a continuous interaction when the defect unfolds over time. -- For a layout assertion, prove it rejects the relevant defect: move only label text while the - frame stays fixed, introduce a doubled margin/phantom helper, or expand a title target into - filler. Confirm the mutation won the cascade, observe failure, restore and observe success. - Use changes relevant to the task; do not repeat the entire example suite for a tiny fix. -- Verify raw data when changing presentation. For code, copy the source payload, not highlighted - DOM text: preserve tabs, blank lines, final newline and streamed additions. Verify clipboard - success/failure, native selection and long-line scrolling independently. Document browser - selection normalization without allowing it to alter the raw clipboard payload. +- **V-PROOF-1 · MUST** When reproducing a visible defect, capture the actual running surface before + the fix. _Proof: browser_ +- **V-PROOF-2 · MUST** After the fix, capture the same relevant state and inspect the full pane with + realistic copy and surrounding chrome. _Proof: browser_ +- **V-PROOF-3 · MUST** Do not offer a mockup, source diff, changed assertion or screenshot of an + unrelated build as proof. _Proof: review_ +- **V-PROOF-4 · SHOULD** Inspect the composition as well as the link count: relabeling a redundant + return as a breadcrumb can still repeat the same context and add an unnecessary header row. +- **V-PROOF-5 · SHOULD** Trace ancestor reachability through the parent before deciding another + visible shortcut is needed. +- **V-PROOF-6 · MUST** Wait for component registration, pending content, fonts and rendered frames + before measuring. _Proof: browser_ +- **V-PROOF-7 · MUST** Fail on missing or zero-sized targets. _Proof: browser_ +- **V-PROOF-8 · MUST** Record any subpixel tolerance, and keep it below a full CSS pixel for exact + spines (the source example uses 0.5px). _Proof: review_ +- **V-PROOF-9 · MUST** For live overlays, verify that capture preserves the viewport and includes the + full trigger/panel; a full-page screenshot can invalidate a crop by causing responsive reflow or + portal repositioning. _Proof: browser_ +- **V-PROOF-10 · MUST** Measure the defect's layer: wrapper, inner content, rendered text Range, icon + viewport and actual pointer target are different. A Range is not exact glyph ink. + _Proof: browser_ +- **V-PROOF-11 · MUST** Account for shadow parts through supported test seams; do not introduce + production shadow queries for acceptance. _Proof: review_ +- **V-PROOF-12 · SHOULD** Exercise affected reflow, wrapped translations, optional content and input + paths. +- **V-PROOF-13 · MUST** Do not use a static screenshot as proof of focus return, keyboard navigation, + touch discovery, interruptibility or async settlement. _Proof: review_ +- **V-PROOF-14 · MUST** Record a continuous interaction when the defect unfolds over time. + _Proof: recording_ +- **V-PROOF-15 · MUST** For a layout assertion, prove it rejects the relevant defect: move only label + text while the frame stays fixed, introduce a doubled margin/phantom helper, or expand a title + target into filler. _Proof: browser_ +- **V-PROOF-16 · MUST** Confirm the mutation won the cascade, observe failure, restore and observe + success. _Proof: browser_ +- **V-PROOF-17 · SHOULD** Use mutations relevant to the task rather than repeating the entire example + suite for a tiny fix. +- **V-PROOF-18 · MUST** Verify raw data when changing presentation. _Proof: browser, unit_ +- **V-PROOF-19 · MUST** For code, copy the source payload, not highlighted DOM text: preserve tabs, + blank lines, final newline and streamed additions. _Proof: unit_ +- **V-PROOF-20 · MUST** Verify clipboard success/failure, native selection and long-line scrolling + independently. _Proof: browser_ +- **V-PROOF-21 · MUST** Document browser selection normalization without allowing it to alter the raw + clipboard payload. _Proof: unit, review_ The source-only [layout example](https://github.com/chaxus/ran/blob/main/packages/ranui/test/e2e/layout-contract.md) contains runnable commands and geometry utilities. Consumers adapt its relationships to their -host; these helpers are not new ranui exports. CSS zoom stress does not establish native browser -zoom or text-size accessibility support. Say which paths remain unverified. +host; these helpers are not new ranui exports. -### Review motion over time +- **V-PROOF-22 · MUST** Do not claim native browser zoom or text-size accessibility support from CSS + zoom stress. _Proof: review_ +- **V-PROOF-23 · MUST** Say which paths remain unverified. _Proof: review_ -For changed motion, inspect a continuous action at normal speed, then replay slowly where a -transition breaks. Include activation, intermediate state, completion and the reverse path; repeat -or reverse input before completion. Verify the final state, focus, event count and reading position -independently of the animation. A slow replay helps diagnose discontinuity; it is not a frame-rate -benchmark. Reduced motion needs its own visible-state check, not just a shorter recording. +### Review motion over time -Report a temporal defect as `element → input → observed frame/state → expected continuity`. -Use one representative real task across affected siblings to detect inconsistent motion language; -check the full composition again after local fixes. Preserve failed iterations as diagnostics, -separate from accepted captures. These checks complement the existing evidence binding below. +- **V-MOTION-1 · MUST** For changed motion, inspect a continuous action at normal speed, then replay + slowly where a transition breaks. _Proof: recording_ +- **V-MOTION-2 · MUST** Include activation, intermediate state, completion and the reverse path; + repeat or reverse input before completion. _Proof: recording_ +- **V-MOTION-3 · MUST** Verify the final state, focus, event count and reading position independently + of the animation. _Proof: browser_ +- **V-MOTION-4 · MUST** Use a slow replay to diagnose discontinuity, not as a frame-rate benchmark. + _Proof: review_ +- **V-MOTION-5 · MUST** Give reduced motion its own visible-state check, not just a shorter recording. + _Proof: browser_ +- **V-MOTION-6 · SHOULD** Report a temporal defect as + `element → input → observed frame/state → expected continuity`. +- **V-MOTION-7 · SHOULD** Use one representative real task across affected siblings to detect + inconsistent motion language. +- **V-MOTION-8 · SHOULD** Check the full composition again after local fixes. +- **V-MOTION-9 · SHOULD** Preserve failed iterations as diagnostics, separate from accepted captures. + +These checks complement the existing evidence binding below. ## Walk through the task -For a changed workflow or composition, start with a realistic goal expressed without naming the -control to use. Complete it from the actual entry point through result, recovery and return, as -relevant. Record the first choice, any ambiguous label, hesitation or backtracking, and whether -the result makes the next step clear. A shorter click path is not useful if the choices are unclear. -Use realistic content, including a representative long or failing case, rather than a polished -empty state alone. Fix task structure and content hierarchy before adding decorative effects. - -Report the most consequential observed problems (up to three), with the state and evidence that -show each one; do not invent findings to fill a quota. Repeat the affected path after correction. -An agent walkthrough can expose defects but is not a human usability study. When observing a real -participant, let them attempt the goal before explaining the interface; preserve that distinction -in the results below. +- **V-WALK-1 · SHOULD** For a changed workflow or composition, start with a realistic goal expressed + without naming the control to use. +- **V-WALK-2 · SHOULD** Complete the goal from the actual entry point through result, recovery and + return, as relevant. +- **V-WALK-3 · SHOULD** Record the first choice, any ambiguous label, hesitation or backtracking, and + whether the result makes the next step clear. + +A shorter click path is not useful if the choices are unclear. + +- **V-WALK-4 · SHOULD** Use realistic content, including a representative long or failing case, + rather than a polished empty state alone. +- **V-WALK-5 · SHOULD** Fix task structure and content hierarchy before adding decorative effects. +- **V-WALK-6 · SHOULD** Report the most consequential observed problems (up to three), with the state + and evidence that show each one. +- **V-WALK-7 · MUST** Do not invent findings to fill a quota. _Proof: review_ +- **V-WALK-8 · SHOULD** Repeat the affected path after correction. +- **V-WALK-9 · MUST** Report an agent walkthrough as a defect search, not as a human usability study. + _Proof: review_ +- **V-WALK-10 · SHOULD** When observing a real participant, let them attempt the goal before + explaining the interface, and preserve that distinction in the results below. ## Bind evidence to what actually ran -For each reviewed state, retain a compact record of: - -- **Runtime:** tested source revision/build and relevant dirty inputs or their fingerprint; - include shared styles/components and incoming data when they affect the result. A Git HEAD - alone is insufficient if the running build or working tree differs. -- **Scope/environment:** affected view/group membership, route/state, viewport, theme, locale, - browser and input or reduced-motion settings where relevant. -- **Media:** capture/recording locations and content identity (for example SHA-256), tied to - that runtime and state. Keep before and after distinct; never overwrite the only before image. -- **Review:** what the automated check and agent inspection actually established, separate - from any human decision and any unverified state. - -A short Markdown record is enough; an existing manifest may supply these fields. Do not add a -new evidence service or configuration framework for an ordinary change. Keep hashes beside the -files or in a referenced manifest so the claimed binding can be checked independently. - -**A review applies only to the evidence and runtime scope it inspected.** Replacing an image, -editing a recording, changing group membership or changing relevant runtime inputs makes the -affected review stale. After a shared style/component change, refresh every affected state, -rebuild if needed, then inspect the new evidence. Old captures may remain as history, not current -proof. A docs-only edit or unrelated source change does not invalidate identical runtime evidence; -record why its dependency scope is unaffected rather than recapturing everything by HEAD alone. +Each reviewed state gets a compact record with these fields: + +- **V-FRESH-1 · MUST** Runtime: record the tested source revision/build and relevant dirty inputs or + their fingerprint, including shared styles/components and incoming data when they affect the + result. A Git HEAD alone is insufficient if the running build or working tree differs. + _Proof: review_ +- **V-FRESH-2 · MUST** Scope/environment: record affected view/group membership, route/state, + viewport, theme, locale, browser and input or reduced-motion settings where relevant. + _Proof: review_ +- **V-FRESH-3 · MUST** Media: record capture/recording locations and content identity (for example + SHA-256), tied to that runtime and state. _Proof: review_ +- **V-FRESH-4 · MUST** Keep before and after distinct; never overwrite the only before image. + _Proof: review_ +- **V-FRESH-5 · MUST** Review: record what the automated check and agent inspection actually + established, separate from any human decision and any unverified state. _Proof: review_ +- **V-FRESH-6 · MAY** Use a short Markdown record; an existing manifest may supply these fields. +- **V-FRESH-7 · SHOULD** Do not add a new evidence service or configuration framework for an ordinary + change. +- **V-FRESH-8 · MUST** Keep hashes beside the files or in a referenced manifest so the claimed binding + can be checked independently. _Proof: review_ + +**A review applies only to the evidence and runtime scope it inspected.** + +- **V-FRESH-9 · MUST** Treat the affected review as stale after replacing an image, editing a + recording, changing group membership or changing relevant runtime inputs. _Proof: review_ +- **V-FRESH-10 · MUST** After a shared style/component change, refresh every affected state, rebuild + if needed, then inspect the new evidence. _Proof: browser, review_ +- **V-FRESH-11 · MUST** Present no old capture as current proof. _Proof: review_ +- **V-FRESH-12 · MUST** When keeping runtime evidence across a docs-only edit or unrelated source + change, record why its dependency scope is unaffected. _Proof: review_ + +Old captures may remain as history. A docs-only edit or unrelated source change leaves identical +runtime evidence valid, so recapturing everything by HEAD alone is unnecessary. ## Report authority truthfully -Keep these results separate, even in a one-paragraph report: - | Result | Who/what can establish it | What it does not establish | | ------------------- | ------------------------------------------------------------------------------- | ------------------------------------ | | Automated checks | Fresh command output or browser assertions for the declared runtime | Visual quality or a human decision | | Agent visual review | Agent inspected current captures and the affected interaction | User acceptance | | Human decision | An actual user's decision, with its message/action reference and evidence scope | Acceptance of later changed evidence | -Use explicit outcomes such as `automated: passed`, `agent review: passed with limits`, -`human: not requested`. If human review was requested and is outstanding, say `pending`; -if a user rejected it, preserve `rejected` and the finding until that user reviews the updated -scope. An agent may report its correction and fresh checks, but cannot invent a user decision -or clear a human rejection. Avoid an unattributed “accepted”, “approved” or “验收通过”. +- **V-REPORT-1 · MUST** Keep these three results separate, even in a one-paragraph report. + _Proof: review_ +- **V-REPORT-2 · MUST** Claim each result only from the source the table above names, and never for + what the table says it does not establish. _Proof: review_ +- **V-REPORT-3 · SHOULD** Use explicit outcomes such as `automated: passed`, + `agent review: passed with limits`, `human: not requested`. +- **V-REPORT-4 · MUST** If human review was requested and is outstanding, say `pending`. + _Proof: review_ +- **V-REPORT-5 · MUST** If a user rejected it, preserve `rejected` and the finding until that user + reviews the updated scope. _Proof: review_ +- **V-REPORT-6 · MUST** An agent may report its correction and fresh checks, but never invents a user + decision or clears a human rejection. _Proof: review_ +- **V-REPORT-7 · MUST** Avoid an unattributed “accepted”, “approved” or “验收通过”. _Proof: review_ User authorization controls whether to push, update a PR or take another external action. -This document adds no blanket human approval gate: finish and deliver authorized work while -reporting `not requested`/`pending` truthfully. Ask only when the actual authorization or a -necessary product decision is missing. Never label the agent's own visual check “user accepted”. +This document adds no blanket human approval gate. + +- **V-REPORT-8 · SHOULD** Finish and deliver authorized work while reporting + `not requested`/`pending` truthfully. +- **V-REPORT-9 · SHOULD** Ask only when the actual authorization or a necessary product decision is + missing. +- **V-REPORT-10 · MUST** Never label the agent's own visual check “user accepted”. _Proof: review_ A minimal delivery note can be: @@ -142,15 +216,27 @@ A minimal delivery note can be: ## Regression of the guidance itself -For opt-in or task-authorized live evaluation of skill/routing/acceptance changes, use the source-only -[six agent scenarios](https://github.com/chaxus/ran/tree/main/plugins/ran/evals). -Deterministic controls check the assessor without model calls; live runs require an available agent harness -and are not a mandatory gate for every prose edit. Freeze tasks, fixtures, tools and independent rubrics before live runs. Use fresh sessions with -matching model/settings; pin baseline and candidate guidance in separate workspaces. Retain -actual agent work, prompts, outcome checks and evidence. Record baseline passes as controls and -candidate failures as failures. Do not show workers the assessor's thresholds or repair patches. - -Seeded-defect rejection/restoration checks establish the assessor's sensitivity. Only live agent -runs establish behavior on those tasks; prose matching, hand-authored answers or corrected test -expectations do not. Inspect visual judgment independently of mechanical checks. Report sample -size, tools and uncovered conditions; six successful tasks do not prove reliability across models. +Deterministic controls check the assessor without model calls; live runs need an available agent +harness and are not a mandatory gate for every prose edit. + +- **V-EVAL-1 · SHOULD** For opt-in or task-authorized live evaluation of skill/routing/acceptance + changes, use the source-only + [six agent scenarios](https://github.com/chaxus/ran/tree/main/plugins/ran/evals). +- **V-EVAL-2 · MUST** Freeze tasks, fixtures, tools and independent rubrics before live runs. + _Proof: review_ +- **V-EVAL-3 · MUST** Use fresh sessions with matching model/settings. _Proof: review_ +- **V-EVAL-4 · MUST** Pin baseline and candidate guidance in separate workspaces. _Proof: review_ +- **V-EVAL-5 · MUST** Retain actual agent work, prompts, outcome checks and evidence. _Proof: review_ +- **V-EVAL-6 · MUST** Record baseline passes as controls and candidate failures as failures. + _Proof: review_ +- **V-EVAL-7 · MUST** Do not show workers the assessor's thresholds or repair patches. + _Proof: review_ + +Seeded-defect rejection/restoration checks establish the assessor's sensitivity. + +- **V-EVAL-8 · MUST** Claim behavior on those tasks only from live agent runs; prose matching, + hand-authored answers or corrected test expectations are no evidence of it. _Proof: review_ +- **V-EVAL-9 · MUST** Inspect visual judgment independently of mechanical checks. _Proof: review_ +- **V-EVAL-10 · MUST** Report sample size, tools and uncovered conditions. _Proof: review_ +- **V-EVAL-11 · MUST** Six successful tasks are no proof of reliability across models; do not claim + it from them. _Proof: review_ diff --git a/plugins/ran/skills/design/references/DESIGN.md b/plugins/ran/skills/design/references/DESIGN.md index d2c28a6474..ebfbae9268 100644 --- a/plugins/ran/skills/design/references/DESIGN.md +++ b/plugins/ran/skills/design/references/DESIGN.md @@ -6,32 +6,47 @@ Design methods for the host product and its existing technology. Read only the r sections through [the task index](./INTERFACE_GUIDE.md). Library tokens and API mechanics belong to their implementation adapter, not this portable method. +Each rule carries an ID and a level. **MUST** is a defect when broken and names its proof; +**SHOULD** is the default, and a deviation states its reason for that case; **MAY** is optional. +The [rule index](./RULES.md) lists every graded rule with its proof. + ## 1. Color — a state ladder, not a palette -Choose semantic roles for content, surfaces, boundaries, actions and status. Define default, -hover, active, disabled and focus states together. Resolve light/dark values through the host’s -authoritative tokens; verify contrast in the actual composition. Brand accents are contextual. +- **D-COLOR-1 · SHOULD** Choose semantic roles for content, surfaces, boundaries, actions and status. +- **D-COLOR-2 · SHOULD** Define default, hover, active, disabled and focus states together. +- **D-COLOR-3 · SHOULD** Resolve light/dark values through the host’s authoritative tokens, and verify + contrast in the actual composition. + +Brand accents are contextual. ## 2. Spacing — a limited, rhythmic scale -Use the host’s existing spacing scale. Tight gaps group related parts; larger gaps separate -tasks or regions. Establish inside padding before outside gaps. Do not copy a reference -product’s dimensions without evaluating content, density and input method. +- **D-SPACE-1 · SHOULD** Use the host’s existing spacing scale. +- **D-SPACE-2 · SHOULD** Use tight gaps to group related parts and larger gaps to separate tasks or + regions. +- **D-SPACE-3 · SHOULD** Establish inside padding before outside gaps. +- **D-SPACE-4 · SHOULD** Do not copy a reference product’s dimensions without evaluating content, density + and input method. ## 3. Typography — choose a role, not a size -Choose heading, label, body, action and code roles with coherent size, weight and line height. -Verify actual scripts, fallback metrics and long content. Typography establishes hierarchy -before decorative containers. Keep a single authority for each role. +- **D-TYPE-1 · SHOULD** Choose heading, label, body, action and code roles with coherent size, weight and + line height, and keep a single authority for each role. +- **D-TYPE-2 · SHOULD** Verify actual scripts, fallback metrics and long content. +- **D-TYPE-3 · SHOULD** Establish hierarchy with typography before decorative containers. ## 4. Radius & elevation -Coordinate nested corner radii with their real inset. Elevation explains a floating layer, -not importance; use the host’s stacking roles and verify overlays against surrounding chrome. +- **D-ELEV-1 · SHOULD** Coordinate nested corner radii with their real inset. +- **D-ELEV-2 · SHOULD** Use elevation to explain a floating layer, not importance, through the host’s + stacking roles, and verify overlays against surrounding chrome. ## 5. Motion — prefer none -Timing below is a starting heuristic. Use the host system’s motion tokens and verify frequency, continuity and reduced motion in the actual task. +Timing below is a starting heuristic. + +- **D-MOTION-1 · SHOULD** Use the host system’s motion tokens and verify frequency, continuity and reduced + motion in the actual task. | Duration | Use | | -------- | ----------------------------------------------------------------------- | @@ -40,136 +55,185 @@ Timing below is a starting heuristic. Use the host system’s motion tokens and | ~200ms | Menu/popover motion when it clarifies the layer change | | ~300ms | Dialog motion when orientation benefits from it | -**Principle:** purpose and frequency determine whether motion earns time. Repeated interaction defaults -to immediate feedback; a larger change is not automatically a reason to animate. Use existing tokens -for purposeful motion, keep it quick and interruptible, and respect `prefers-reduced-motion`. +**Principle:** purpose and frequency determine whether motion earns time. -### Functional motion contract +- **D-MOTION-2 · SHOULD** Default repeated interaction to immediate feedback; a larger change is not + automatically a reason to animate. +- **D-MOTION-3 · SHOULD** Use existing tokens for purposeful motion and keep it quick and interruptible. + Reduced motion is graded under D-MOTION-10 and D-ACCESS-5. -Specify the trigger, state change and destination before choosing an effect. Preserve unrelated -content, drafts and reading position. Sibling components with the same interaction meaning should -share a motion language; consistency does not require every component to animate. +### Functional motion contract -When motion is useful, allow a new intent to interrupt or reverse it from the current presentation, -without a jump, stale completion or duplicate activation. Keep feedback immediate; animation is not -an input lock. During insertion/removal, avoid visibly squeezing text or passing exiting controls -across live actions; coordinate opacity and layout so the result stays readable. Reduced motion -must retain a visible, understandable final state. A spring is optional and need not overshoot. +- **D-MOTION-4 · SHOULD** Specify the trigger, state change and destination before choosing an effect. +- **D-MOTION-5 · MUST** Motion preserves unrelated content, drafts and reading position. _Proof: recording_ +- **D-MOTION-6 · SHOULD** Give sibling components with the same interaction meaning a shared motion + language; consistency does not require every component to animate. +- **D-MOTION-7 · MUST** When motion is used, a new intent can interrupt or reverse it from the current + presentation, without a jump, stale completion or duplicate activation. _Proof: recording_ +- **D-MOTION-8 · MUST** Feedback stays immediate; animation is not an input lock. _Proof: recording_ +- **D-MOTION-9 · SHOULD** During insertion/removal, avoid visibly squeezing text or passing exiting controls + across live actions; coordinate opacity and layout so the result stays readable. +- **D-MOTION-10 · MUST** Reduced motion retains a visible, understandable final state. _Proof: browser_ +- **D-MOTION-11 · MAY** Use a spring; it need not overshoot. See the optional [component craft research](./REFERENCE_RESEARCH.md#component-craft-设计垂点--wxaddesign) for examples and limits, and [temporal acceptance](./ACCEPTANCE.md#review-motion-over-time) for proof. -Theme palettes should settle coherently. Avoid implicit palette fades or `transition: all`; -explicitly opt in only where the host intends and verifies a coordinated effect. -Do not replay decorative title entrances or animate factual counts on each return to a working -surface. Keep readable content and actual values available immediately. A loading demonstration -may move to demonstrate loading; a decorative status pulse must not imply live activity. +- **D-MOTION-12 · SHOULD** Let theme palettes settle coherently. Avoid implicit palette fades or + `transition: all`; opt in explicitly only where the host intends and verifies a coordinated effect. +- **D-MOTION-13 · MUST** Factual values and readable content are shown immediately on return to a working + surface, without animated counts. _Proof: browser_ +- **D-MOTION-16 · SHOULD** Do not replay decorative title entrances on each return to a working surface. +- **D-MOTION-14 · MAY** A loading demonstration may move to demonstrate loading. +- **D-MOTION-15 · MUST** A status pulse appears only while the activity is live. _Proof: review_ ## 6. Content — copy is part of the system -- **Buttons:** name the outcome. Include the object when context does not establish it: "Deploy project", "Delete member". In a clearly named object or dialog, "Save" or "Delete" is sufficient; avoid generic "OK". See [contextual wording](#words-are-part-of-the-interface). -- **Errors:** say **what happened**, then **how to fix it**. ✅ "Build failed: the bundle exceeds the size limit. Reduce it or raise the limit." ❌ "Operation failed, please try again." -- **Confirmations / toasts:** state the completed **change**. ✅ "Project deleted". ❌ "Operation successful". Show completion feedback only after the operation is confirmed. -- Be specific; every sentence should remove guesswork, not add it. +- **D-COPY-1 · SHOULD** Buttons name the outcome. Include the object when context does not establish it: + "Deploy project", "Delete member". In a clearly named object or dialog, "Save" or "Delete" is sufficient; + avoid generic "OK". See [contextual wording](#words-are-part-of-the-interface). +- **D-COPY-2 · SHOULD** Errors say **what happened**, then **how to fix it**. ✅ "Build failed: the bundle + exceeds the size limit. Reduce it or raise the limit." ❌ "Operation failed, please try again." +- **D-COPY-3 · SHOULD** Confirmations and toasts state the completed **change**. ✅ "Project deleted". + ❌ "Operation successful". +- **D-COPY-4 · MUST** Show completion feedback only after the operation is confirmed. _Proof: unit, browser_ +- **D-COPY-5 · SHOULD** Be specific; every sentence removes guesswork rather than adding it. ## 7. Accessibility — the system serves everyone -- Maintain sufficient text-to-background contrast (**WCAG AA**). -- **Never signal state with color alone** — pair it with an icon or text (e.g. ✓ / ✕ labels, not just green/red). -- Every interactive element has a **visible focus ring** appropriate to its surface. Never remove it for "cleanliness". -- **Full keyboard navigation** — nothing is mouse-only. -- Respect `prefers-reduced-motion` and `prefers-color-scheme`. +- **D-ACCESS-1 · MUST** Text keeps sufficient contrast with its background (**WCAG AA**). _Proof: browser_ +- **D-ACCESS-2 · MUST** Never signal state with color alone; pair it with an icon or text (e.g. ✓ / ✕ labels, + not just green/red). _Proof: review_ +- **D-ACCESS-3 · MUST** Every interactive element has a **visible focus ring** appropriate to its surface; + never remove it for "cleanliness". _Proof: browser_ +- **D-ACCESS-4 · MUST** **Full keyboard navigation**: nothing is mouse-only. _Proof: browser_ +- **D-ACCESS-5 · MUST** Respect `prefers-reduced-motion`. _Proof: browser_ +- **D-ACCESS-6 · MUST** When the host supports both schemes, the initial render follows + `prefers-color-scheme`. _Proof: browser_ ## 8. Input & viewport — mobile and desktop, both -- Support mouse, keyboard and touch at narrow and wide widths. Reflow controls instead of - hiding the only route to an action. A hover affordance needs a click/tap alternative. -- Drags use Pointer Events including cancellation; scope `touch-action` to the actual drag - surface and pair it with a real handler. Preserve ordinary scrolling elsewhere. -- Prefer content/viewport-relative bounds (`min()`, `max()`, `clamp()`) to arbitrary - breakpoints. Use existing host breakpoints or introduce a justified shared role only when repeated needs emerge. -- A measured position becomes stale after resize, container reflow or scroll. Exercise - resizing into a narrow layout, not only loading there. Overlay placement must track its - trigger while open. The host owns lifecycle mechanics; [ranui engineering](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/CODING.md#3-state-and-ownership) is one implementation adapter. -- Test actual pointer/touch gestures, hover fallback and keyboard routes where affected; - checking two color schemes does not establish input parity. +- **D-INPUT-1 · MUST** Support mouse, keyboard and touch at narrow and wide widths. _Proof: browser_ +- **D-INPUT-2 · MUST** Reflow controls instead of hiding the only route to an action. _Proof: browser_ +- **D-INPUT-3 · MUST** A hover affordance has a click/tap alternative. _Proof: browser_ +- **D-INPUT-4 · MUST** Drags use Pointer Events, including cancellation. _Proof: browser_ +- **D-INPUT-5 · MUST** Scope `touch-action` to the actual drag surface and pair it with a real handler; + ordinary scrolling keeps working elsewhere. _Proof: browser_ +- **D-INPUT-6 · SHOULD** Prefer content/viewport-relative bounds (`min()`, `max()`, `clamp()`) to arbitrary + breakpoints. +- **D-INPUT-7 · SHOULD** Use existing host breakpoints, and introduce a justified shared role only when + repeated needs emerge. + +A measured position becomes stale after resize, container reflow or scroll. + +- **D-INPUT-8 · SHOULD** Exercise resizing into a narrow layout, not only loading there. +- **D-INPUT-9 · MUST** Overlay placement tracks its trigger while open. _Proof: browser_ + +The host owns lifecycle mechanics; [ranui engineering](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/CODING.md#3-state-and-ownership) is one implementation adapter. + +- **D-INPUT-10 · SHOULD** Test actual pointer/touch gestures, hover fallback and keyboard routes where + affected; checking two color schemes does not establish input parity. ## 9. Composition — the page, not the component -Choose the page structure in §10 first; then compose its components with these rules. +- **D-COMP-1 · SHOULD** Choose the page structure in §10 first; then compose its components with these + rules. ### Style coherence across components and pages -Before implementation, record a short design brief in the existing task note or product design -owner. Use the existing system; a small fix only needs the affected decisions. "Premium", "modern" -or "like Claude" is a direction to investigate, not an implementable specification. +- **D-COMP-2 · SHOULD** Before implementation, record a short design brief in the existing task note or + product design owner. Use the existing system; a small fix only needs the affected decisions. + The brief settles each applicable decision in the table below (D-COMP-2). + +"Premium", "modern" or "like Claude" is a direction to investigate, not an implementable specification. -| Decision | What the brief must settle | +| Decision | What the brief settles | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------ | | Task and hierarchy | The real content, primary action and first reading focus; what can recede or disappear. | | Proportion | Type roles/line heights, text measure, control density, icon weight and alignment spines using host tokens. | | Surface language | Which regions need containment, nested radius/inset relationships, semantic accent and elevation roles. | | State composition | A representative default plus long content, pending/failure and narrow input paths; include combined states. | | Motion | The state change it explains, trigger and frequency, interruption/reverse behavior and reduced-motion result; "none" is valid. | -| Reference decision | Where a reference is used, link its source, the principle adopted and the adaptation required by the host task. | +| Reference decision | Where a reference is used, link its source, the principle adopted and the adaptation the host task needs. | | Acceptance | The observable result and actual views/recordings that can demonstrate or reject it. | -Reuse the brief across sibling components and docs chrome; change emphasis according to the task. -Uniformity does not mean equal-sized controls or boxed sections everywhere. An expressive demo -must remain visibly distinct from its documentation chrome. Update the brief before changing the -visual direction; do not silently rewrite the standard to justify an implementation that failed review. +- **D-COMP-4 · SHOULD** Reuse the brief across sibling components and docs chrome; change emphasis + according to the task. + +Uniformity does not mean equal-sized controls or boxed sections everywhere. + +- **D-COMP-5 · SHOULD** Keep an expressive demo visibly distinct from its documentation chrome. +- **D-COMP-6 · SHOULD** Update the brief before changing the visual direction; do not silently rewrite the + standard to justify an implementation that failed review. +- **D-COMP-7 · SHOULD** Compare a representative composed still with different real content before + animating it, then inspect the state change and return path at normal speed. -Compare a representative composed still with different real content before animating it. Then -inspect the state change and return path at normal speed. A shared color alone cannot reconcile -inconsistent proportions, icon weight, containment or motion. When an example looks imported from -another product, fix that relationship at its owning token/component/style rather than adding a -page-specific patch. Keep reference observations and validation limits in the optional -[research index](./REFERENCE_RESEARCH.md#style-coherence-and-transition-research). +A shared color alone cannot reconcile inconsistent proportions, icon weight, containment or motion. + +- **D-COMP-8 · SHOULD** When an example looks imported from another product, fix that relationship at its + owning token/component/style rather than adding a page-specific patch. +- **D-COMP-9 · SHOULD** Keep reference observations and validation limits in the optional + [research index](./REFERENCE_RESEARCH.md#style-coherence-and-transition-research). ### Structure before containers -**Reach for spacing, alignment, weight and a hairline before a box.** A container is a -claim that its contents are a separate object. Most regions need a background, a hairline -and intentional spacing — nothing more. +- **D-BOX-1 · SHOULD** **Reach for spacing, alignment, weight and a hairline before a box.** + +A container is a claim that its contents are a separate object. Most regions need a background, a +hairline and intentional spacing — nothing more. + +- **D-BOX-2 · SHOULD** Reserve a box for **a genuinely separate object**: a live demo stage, an overlay, a + panel that floats. Not for a code block, a table, a list row, a previous/next link, or a cell in a + gallery. +- **D-BOX-3 · SHOULD** **Avoid redundant nested containers and doubled padding.** A card inside a padded + panel does not need another decorative frame or inset. +- **D-BOX-4 · SHOULD** Keep functionally required nested controls or surfaces, and coordinate their radii + with the actual inset; containment alone is not a reason to add a box. +- **D-BOX-5 · SHOULD** **Every persistent boundary needs a distinct job.** Start with headings, alignment + and proximity to group content. Add a border, surface fill or elevation only when that structure leaves + a real boundary unclear. +- **D-BOX-6 · SHOULD** Do not repeat the same grouping with a panel frame, a nested card, row rules and + another inset at the same time. -**A box is for a genuinely separate object** — a live demo stage, an overlay, a panel that -floats. Not for a code block, a table, a list row, a previous/next link, or a cell in a -gallery. +Faint lines are still extra hierarchy; reducing their contrast or shortening them leaves a redundant +enclosure redundant. -**Avoid redundant nested containers and doubled padding.** A card inside a padded panel -does not need another decorative frame or inset. Keep functionally required nested controls or -surfaces, and coordinate their radii with the actual inset; containment alone is not a reason to add a box. +The table below applies D-BOX-5 and D-BOX-8. -**Every persistent boundary needs a distinct job.** Start with headings, alignment and proximity -to group content. Add a border, surface fill or elevation only when that structure leaves a real -boundary unclear. Do not repeat the same grouping with a panel frame, a nested card, row rules -and another inset at the same time. Faint lines are still extra hierarchy; reducing their contrast -or shortening them does not justify a redundant enclosure. +- **D-BOX-8 · MUST** Keep keyboard focus and persistent selection cues; never remove them to make the + resting layout quieter. _Proof: browser_ -| Boundary | Keep when it communicates | Remove when it only repeats | -| ----------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------- | -| Field/control frame | Where values can be entered or a selection made | A decorative wrapper around already legible commands | -| Region frame or divider | An independent viewport, resize boundary or major change of task | A title and gap that already make the same group clear | -| Nested surface | A distinct floating layer, preview, drop target or interaction boundary | Another container inside an established settings pane | -| Focus/selection outline | Keyboard focus or persistent selection state | Never remove this state cue merely to make the resting layout quieter | +| Boundary | Keep when it communicates | Remove when it only repeats | +| ----------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------ | +| Field/control frame | Where values can be entered or a selection made | A decorative wrapper around already legible commands | +| Region frame or divider | An independent viewport, resize boundary or major change of task | A title and gap that already make the same group clear | +| Nested surface | A distinct floating layer, preview, drop target or interaction boundary | Another container inside an established settings pane | +| Focus/selection outline | Keyboard focus or persistent selection state | Keep this state cue even when the resting layout would look quieter without it | -Review the full pane or page, not only the cropped group. For every remaining stable outline, -fill or separator, name the relationship or interaction it communicates and check what becomes -ambiguous if it is removed. A frame is optional; a reachable, recognisable control is not. +- **D-BOX-9 · SHOULD** Review the full pane or page, not only the cropped group. For every remaining stable + outline, fill or separator, name the relationship or interaction it communicates and check what becomes + ambiguous if it is removed. + +A frame is optional; a reachable, recognisable control is not. ### Emphasis is a budget **Colour, weight, badges, fills and elevation are scarce.** If everything is emphasised, -nothing reads as important. Establish priority with structure and proximity first, and -spend emphasis on the one thing that deserves it. +nothing reads as important. + +- **D-COMP-10 · SHOULD** Establish priority with structure and proximity first, and spend emphasis on the + one thing that deserves it. -**Elevation explains stacking, not importance.** Shadows belong to surfaces that genuinely -float above content — popovers, menus, dialogs, notifications. Do not shadow every card. +**Elevation explains stacking, not importance.** + +- **D-ELEV-3 · SHOULD** Reserve shadows for surfaces that genuinely float above content: popovers, menus, + dialogs, notifications. Do not shadow every card. ### Spacing carries meaning -The scale in §2 is not only a rhythm; each step states a relationship. Choose the step by -what the gap _means_: +The scale in §2 is not only a rhythm; each step states a relationship. + +Choose the step by what the gap _means_ (D-SPACE-2): | Relationship | Relative spacing | | ------------------------ | ------------------------------- | @@ -180,68 +244,95 @@ what the gap _means_: | Major region boundary | Strongest deliberate separation | **Inside before outside:** a component's own padding is decided before the gap between -components. **Smaller gap means tighter relationship** — that is the only thing vertical -rhythm communicates, so do not undo it with a decorative divider. +components (D-SPACE-3). **Smaller gap means tighter relationship** — that is the only thing vertical +rhythm communicates. + +- **D-SPACE-6 · SHOULD** Do not undo a gap's relationship with a decorative divider. ### Vertical rhythm across sibling views -Repeat relationships, not fixed heights. Choose title/description, field/hint, group and region gaps -from the host scale according to their relationship. These are composition decisions, not universal -component dimensions. Translations grow naturally; missing descriptions leave no vacant row. +- **D-SPACE-7 · SHOULD** Repeat relationships, not fixed heights. Choose title/description, field/hint, + group and region gaps from the host scale according to their relationship. + +These are composition decisions, not universal component dimensions. + +- **D-SPACE-8 · SHOULD** Let translations grow naturally, and leave no vacant row for a missing + description. +- **D-SPACE-9 · SHOULD** Give each gap one owner: parent stack or explicit child margin. Reset authored + local heading/paragraph margins rather than adding them to the parent's gap. +- **D-SPACE-10 · SHOULD** Account for owned control padding, collapsing margins and inline baseline space; + do not compensate with transforms or smaller type. +- **D-SPACE-11 · SHOULD** Place hints with the field/action they explain. +- **D-SPACE-12 · MUST** Hidden helpers leave no accidental gap. _Proof: browser_ +- **D-SPACE-16 · SHOULD** A reserved async status line has a stated purpose and bounds. + Measure both axes after content settles; [ACCEPTANCE](./ACCEPTANCE.md) grades this as V-PROOF-6. -Give each gap one owner: parent stack or explicit child margin. Reset authored local heading/ -paragraph margins rather than adding them to the parent's gap. Account for owned control padding, -collapsing margins and inline baseline space; do not compensate with transforms or smaller type. -Place hints with the field/action they explain. Hidden helpers leave no accidental gap; a reserved -async status line needs a stated purpose and bounds. +`next.top - previous.bottom` measures frame separation, not glyph/line-box space. -Measure both axes after content settles. `next.top - previous.bottom` measures frame separation, -not glyph/line-box space. Keep leading, padding and hairlines distinct: 16px padding plus a 1px -rule is not a 17px spacing token. Use independent expectations and exercise wrapping, resizing, -helper/error visibility and relevant density. [ACCEPTANCE](./ACCEPTANCE.md#browser-proof) owns -settling and mutation checks for doubled margins, phantom rows and text-only drift. +- **D-SPACE-14 · SHOULD** Keep leading, padding and hairlines distinct: 16px padding plus a 1px rule is + not a 17px spacing token. +- **D-SPACE-15 · SHOULD** Use independent expectations and exercise wrapping, resizing, helper/error + visibility and relevant density. + +[ACCEPTANCE](./ACCEPTANCE.md#browser-proof) owns settling and mutation checks for doubled margins, +phantom rows and text-only drift. ### The measure governs text, not the column -**Choose a readable measure for the actual script and font.** For Latin prose, 60–75 -characters per line is a starting heuristic, not a universal range for every writing system. -Review realistic text at supported widths, zoom levels and font fallbacks; CJK and mixed-script -text need their own judgment. [WCAG visual presentation](https://www.w3.org/WAI/WCAG22/Understanding/visual-presentation.html) +- **D-TYPE-4 · SHOULD** **Choose a readable measure for the actual script and font.** + +For Latin prose, 60–75 characters per line is a starting heuristic, not a universal range for every +writing system. + +- **D-TYPE-5 · SHOULD** Review realistic text at supported widths, zoom levels and font fallbacks; CJK and + mixed-script text need their own judgment. + +[WCAG visual presentation](https://www.w3.org/WAI/WCAG22/Understanding/visual-presentation.html) also distinguishes CJK in its Level AAA criterion; it is not a mandatory library default. -A table, code fence, demo stage or image should retain the width its content needs. -Cap the text elements, not the column that holds them. -**Declare the measure in `rem`, never `em`.** `em` resolves against the element's own -font-size, so the larger the type, the longer the line — exactly backwards. +- **D-TYPE-6 · SHOULD** Let a table, code fence, demo stage or image retain the width its content needs. + Cap the text elements, not the column that holds them. +- **D-TYPE-7 · SHOULD** **Declare the measure in `rem`, never `em`.** -**Build hierarchy out of type before reaching for a box.** One step of size and one step -down in colour turns a title and its first sentence into a title and a deck, with no new -container and no new border. +`em` resolves against the element's own font-size, so the larger the type, the longer the line — +exactly backwards. + +- **D-TYPE-8 · SHOULD** **Build hierarchy out of type before reaching for a box.** + +One step of size and one step down in colour turns a title and its first sentence into a title and a +deck, with no new container and no new border. ### If the layout says two things are one object, the spacing must agree -**A shared border, a shared radius and a gap between them are a contradiction.** Decide -whether the elements are one object or two, and make every property say the same thing. +**A shared border, a shared radius and a gap between them are a contradiction.** -**When a value must equal another value, name it.** A gap that a child has to subtract is -not a literal in two places; it is one custom property referenced twice. +- **D-ALIGN-1 · SHOULD** Decide whether the elements are one object or two, and make every property say + the same thing. +- **D-ALIGN-2 · SHOULD** **When a value must equal another value, name it.** A gap that a child has to + subtract is not a literal in two places; it is one custom property referenced twice. ### Reserve a column only when its content exists **A column declared unconditionally is still a column, even on the pages that have nothing -to put in it.** Condition the track on the element actually being there (`:has()`), not on -a page type you assume implies it. +to put in it.** + +- **D-ALIGN-3 · SHOULD** Condition the track on the element actually being there (`:has()`), not on a + page type you assume implies it. ### Alignment is structure, not polish -**Name each alignment spine and the geometry it aligns.** A page content spine, navigation -column and nested form-label spine can differ intentionally. State which siblings share each one; -include headers, body copy, rows, actions and empty/loading/error content. A back button plus gap -may establish a separate title column: do not silently treat that title as aligned to body text. -A missing optional icon or badge must not move labels out of their declared column. A nested level -returns exactly to its parent's spine when it ends. +- **D-ALIGN-4 · SHOULD** **Name each alignment spine and the geometry it aligns.** State which siblings + share each one; include headers, body copy, rows, actions and empty/loading/error content. -Compare the right boxes. These are separate measurements, even when they happen to coincide: +A page content spine, navigation column and nested form-label spine can differ intentionally. + +- **D-ALIGN-5 · SHOULD** A back button plus gap may establish a separate title column: do not silently + treat that title as aligned to body text. +- **D-ALIGN-6 · MUST** A missing optional icon or badge never moves labels out of their declared column. + _Proof: browser_ +- **D-ALIGN-7 · SHOULD** A nested level returns exactly to its parent's spine when it ends. + Compare the right boxes. These are separate measurements, even when they happen to coincide; measuring the + defect's layer is graded in [ACCEPTANCE](./ACCEPTANCE.md) as V-PROOF-10. | Geometry | What to measure | | ------------- | ------------------------------------------------------------------------------------------------- | @@ -252,194 +343,259 @@ Compare the right boxes. These are separate measurements, even when they happen | Hit area | The actual event target and its hover/focus surface; verify points near its edges | A button frame aligned with a paragraph does not prove its text is aligned: internal padding can -move the label while the frame stays fixed. `Range` gives typographic geometry, not exact glyph ink; -inspect optical glyph corrections separately. For web components, measure the host, documented shadow parts and slotted text through supported -test seams; do not add production shadow queries or assume document CSS crosses the boundary. - -**Keep a header command's hit zone separate from layout filler.** A title/menu trigger should hug -its content plus deliberate padding, bounded by the available width. Let a separate flex/grid track -absorb remaining space. Define truncation/wrapping and adjacent action behavior for long translations. -Do not make an entire unused header strip clickable merely by giving the title button `flex: 1`. -A row that intentionally selects its whole width is a different role; record that contract explicitly. -Coarse-pointer targets may grow without stretching the hover fill across unrelated content. - -**Count every inset from the shared ancestor to the visible text.** Parent padding, nested panel -padding, a control's internal padding, borders and navigation columns all contribute. Remove a -redundant inset at its owner instead of cancelling it with an unexplained negative margin. Empty -states retain their region's content spine and width; extra breathing room must not introduce a -second left edge or make a compact action fill the region. Product-specific insets and target sizes -remain application aliases, not new foundation values or universal component defaults. - -**When two layout edges or gaps are meant to be equal, a one-pixel difference is a defect.** -Optical correction for an asymmetric glyph is a separate, documented asset/component decision: -reuse the correction at supported sizes while preserving exact sibling gaps, baselines and alignment -spines. It is not permission to nudge page margins until a screenshot looks balanced. +move the label while the frame stays fixed. `Range` gives typographic geometry, not exact glyph ink. + +- **D-ALIGN-9 · SHOULD** Inspect optical glyph corrections separately. + For web components, measure the host, documented shadow parts and slotted text through supported test + seams. V-PROOF-11 in [ACCEPTANCE](./ACCEPTANCE.md) owns this, including the ban on production shadow queries + and on assuming document CSS crosses the boundary. +- **D-ALIGN-11 · SHOULD** **Keep a header command's hit zone separate from layout filler.** A title/menu + trigger hugs its content plus deliberate padding, bounded by the available width. Let a separate + flex/grid track absorb remaining space. +- **D-ALIGN-12 · SHOULD** Define truncation/wrapping and adjacent action behavior for long translations. +- **D-ALIGN-13 · MUST** The title trigger's hit area does not span unused header space (for example through + `flex: 1` on the title button) unless D-ALIGN-14 applies. _Proof: browser_ +- **D-ALIGN-14 · SHOULD** A row that intentionally selects its whole width is a different role; record + that contract explicitly. +- **D-ALIGN-15 · MAY** Coarse-pointer targets may grow larger. +- **D-ALIGN-22 · SHOULD** A grown target's hover fill stays on the control's own content, not across unrelated + content. +- **D-ALIGN-16 · SHOULD** **Count every inset from the shared ancestor to the visible text.** Parent + padding, nested panel padding, a control's internal padding, borders and navigation columns all + contribute. +- **D-ALIGN-17 · SHOULD** Remove a redundant inset at its owner instead of cancelling it with an + unexplained negative margin. +- **D-ALIGN-18 · SHOULD** Empty states retain their region's content spine and width; extra breathing room + never introduces a second left edge or makes a compact action fill the region. +- **D-ALIGN-19 · SHOULD** Keep product-specific insets and target sizes as application aliases, not new + foundation values or universal component defaults. +- **D-ALIGN-20 · SHOULD** **When two layout edges or gaps are meant to be equal, a one-pixel difference is a + defect.** Optical glyph correction (D-ALIGN-21) is the only tolerance. +- **D-ALIGN-21 · SHOULD** Treat optical correction for an asymmetric glyph as a separate, documented + asset/component decision, reused at supported sizes while sibling gaps, baselines and spines stay exact. + +Optical correction is not permission to nudge page margins until a screenshot looks balanced. ### Selection, links and other semantic lies -**Selection is persistent state and must not look like hover.** Hover is transient; if the -current item is marked with the same fill hover uses, the interface has two names for one -appearance. +- **D-SEM-1 · MUST** **Selection is persistent state and never looks like hover.** _Proof: browser_ + +Hover is transient; if the current item is marked with the same fill hover uses, the interface has two +names for one appearance. -**Action and link roles remain distinguishable.** Verify inline links against surrounding prose, -including themes where action and body colors happen to match. +- **D-SEM-2 · MUST** **Action and link roles remain distinguishable.** _Proof: browser_ +- **D-SEM-3 · SHOULD** Verify inline links against surrounding prose, including themes where action and + body colors happen to match. -**A link leaves; a button acts.** Anything that changes application state is a button, -whatever it looks like. Do not style a command as a link to make it quiet, and do not let -a button-shaped link keep an underline. +**A link leaves; a button acts.** + +- **D-SEM-4 · MUST** Anything that changes application state is a button, whatever it looks like. + _Proof: review, browser_ +- **D-SEM-5 · SHOULD** A button-shaped link carries no underline. ### Command hierarchy in constrained panes -**Remove redundant group enclosures without erasing control affordance.** Headings, -alignment and proximity establish groups inside a settings pane; they do not automatically -make an icon and label look clickable. Do not require flat rows in every context. Use them -when a recognizable menu/list pattern already conveys action. Standalone commands may need -their own restrained button surface or functional stroke, visible before hover in every -supported theme. A button's own shape identifies its target; an enclosing card, muted group -fill and row rules must still pass the distinct-boundary test above. - -Do not rely on hover, pointer cursors, verb labels or keyboard focus alone for initial -discovery. Inspect the resting full pane with the pointer away, including touch use. Keep -hover, press, disabled and keyboard focus distinguishable after the control is discovered. -Long translated labels may wrap inside a single command column rather than creating a -ragged horizontal button cloud. Do not add navigation chevrons to immediate commands. - -Declare which visible columns are shared with adjacent checkboxes or controls: measure -icon/checkbox edges and text Ranges, not only button rectangles or alignment within the -command group. A hit target may extend into an owned gutter to preserve those columns; -name the extension, keep it inside its pane and verify non-overlap and focus visibility. - -Separate destructive commands from recovery and transfer actions. A danger label and icon -can communicate intent without a large red outline on every resting command; reserve the -strongest danger fill for explicit confirmation. Keep confirmation, cancellation, focus -return and busy guards. Acceptance covers resting discoverability, declared shared columns, -full-pane boundary hierarchy, target bounds, label clipping, contrast and interaction states. -An automated surface-difference check catches invisible control fills but does not prove -that users recognize the action: inspect the actual render as well. Reuse foundation roles -and coarse-input target requirements; this guidance changes no component defaults. +- **D-CMD-1 · SHOULD** **Remove redundant group enclosures without erasing control affordance.** + +Headings, alignment and proximity establish groups inside a settings pane; by themselves they leave an +icon and label without a clickable look. + +- **D-CMD-2 · SHOULD** Use flat rows when a recognizable menu/list pattern already conveys action; do not + require them in every context. +- **D-CMD-3 · SHOULD** Give a standalone command that needs it its own restrained button surface or + functional stroke, visible before hover in every supported theme. +- **D-CMD-4 · SHOULD** Let a button's own shape identify its target; an enclosing card, muted group fill + and row rules still pass the distinct-boundary test (D-BOX-5). +- **D-CMD-5 · MUST** Initial discovery does not rely on hover, pointer cursors, verb labels or keyboard + focus alone. _Proof: browser_ +- **D-CMD-6 · SHOULD** Inspect the resting full pane with the pointer away, including touch use. +- **D-CMD-7 · MUST** Hover, press, disabled and keyboard focus stay distinguishable after the control is + discovered. _Proof: browser_ +- **D-CMD-8 · SHOULD** Wrap long translated labels inside one command column instead of a ragged horizontal + button cloud. +- **D-CMD-9 · MUST** Do not add navigation chevrons to immediate commands. _Proof: review_ +- **D-CMD-10 · SHOULD** Declare which visible columns are shared with adjacent checkboxes or controls: + measure icon/checkbox edges and text Ranges, not only button rectangles or alignment within the command + group. +- **D-CMD-11 · MAY** A hit target may extend into an owned gutter to preserve those columns; name the + extension. +- **D-CMD-12 · MUST** An extended hit target stays inside its pane, does not overlap other targets, and + keeps focus visible. _Proof: browser_ +- **D-CMD-13 · SHOULD** Separate destructive commands from recovery and transfer actions. +- **D-CMD-14 · SHOULD** Let a danger label and icon communicate intent without a large red outline on + every resting command; reserve the strongest danger fill for explicit confirmation. +- **D-CMD-15 · MUST** Destructive commands keep confirmation, cancellation, focus return and busy guards. + _Proof: browser_ +- **D-CMD-16 · SHOULD** Accept a command pane against its resting state, not only its hover state. + +Acceptance covers resting discoverability, declared shared columns, full-pane boundary hierarchy, target +bounds, label clipping, contrast and interaction states. + +- **D-CMD-17 · SHOULD** Inspect the actual render as well: an automated surface-difference check catches + invisible control fills but does not prove that users recognize the action. +- **D-CMD-18 · SHOULD** Reuse foundation roles and coarse-input target requirements; this guidance changes + no component defaults. ### One product, one token set -Use the product’s authoritative semantic tokens. Aliases may improve readability, but resolve -from that source rather than inventing a competing palette. Coordinate third-party components -through supported theme APIs and verify the composed result. +- **D-TOKEN-1 · SHOULD** Use the product’s authoritative semantic tokens. Aliases may improve readability, + but resolve from that source rather than inventing a competing palette. +- **D-TOKEN-2 · SHOULD** Coordinate third-party components through supported theme APIs and verify the + composed result. ### Density follows control roles Spacing, intrinsic dimensions and hit targets solve different problems. A small glyph can have -a larger native button target. Measure actual hit bounds, focus and adjacent gaps; derive repeated -host aliases from foundation tokens, not one application's dimensions copied across the library. +a larger native button target. -Choose target bounds for the actual input method without enlarging every glyph or reserving -space for hidden controls. Use supported public theme APIs. Name icon-only controls accessibly -and document legitimate viewport, hairline, artwork and optical geometry exceptions. +- **D-TOKEN-3 · SHOULD** Measure actual hit bounds, focus and adjacent gaps. +- **D-TOKEN-4 · SHOULD** Derive repeated host aliases from foundation tokens, not one application's + dimensions copied across the library. +- **D-TOKEN-5 · SHOULD** Choose target bounds for the actual input method without enlarging every glyph or + reserving space for hidden controls. Use supported public theme APIs. +- **D-TOKEN-6 · MUST** Name icon-only controls accessibly. _Proof: browser_ +- **D-TOKEN-7 · SHOULD** Document legitimate viewport, hairline, artwork and optical geometry exceptions. ### Evaluate craft through evidence -Polish follows task clarity, not a preset of cards, blur or entrances. Choose a deliberate -visual direction using realistic objects, copy and states; a generic empty shell cannot establish -product quality. Working interfaces need sustained reading and frequent actions; marketing pages -may need a different composition. Evaluate the smallest intervention against real content, language, -device and repeated use. Primary references answer -specific questions: [Linear hierarchy](https://linear.app/now/behind-the-latest-design-refresh), +Polish follows task clarity, not a preset of cards, blur or entrances. + +- **D-CRAFT-1 · SHOULD** Choose a deliberate visual direction using realistic objects, copy and states; a + generic empty shell cannot establish product quality. + +Working interfaces need sustained reading and frequent actions; marketing pages may need a different +composition. + +- **D-CRAFT-2 · SHOULD** Evaluate the smallest intervention against real content, language, device and + repeated use. + +Primary references answer specific questions: +[Linear hierarchy](https://linear.app/now/behind-the-latest-design-refresh), [Geist typography](https://vercel.com/geist/typography), [optical alignment](https://craft.gustavofior.com/optical-alignment) and -[interruptibility](https://craft.gustavofior.com/interruptibility). Adapt their principles with -the host’s tokens and actual capabilities; do not copy a product's pixel sizes or import recipes as rules. -[Don't Make Me Think](https://sensible.com/dont-make-me-think/) informs scanning, hierarchy and -clear choices; [taste-skill](https://github.com/leonxlnx/taste-skill) informs deliberate direction -and critique; [ryugi](https://github.com/Innei/ryugi) informs executable work and fresh evidence. -These references inform decisions, not additional instruction sets. A stylistic preference needs -context; a requirement must identify the task or observable contract it protects. - -Choose references by the job they support: logo/app identity, marketing composition and frequent -working interactions solve different problems. Record the observed pattern and its adaptation; -verify the result with real content. The optional [research index](./REFERENCE_RESEARCH.md) maps -sources to questions and demonstrates this translation without adding another rule set. - -When direction is unresolved, compare two small real-host variants with the same content, width -and theme, changing one dimension at a time. Explain what each difference does for the task. +[interruptibility](https://craft.gustavofior.com/interruptibility). + +- **D-CRAFT-3 · SHOULD** Adapt their principles with the host’s tokens and actual capabilities; do not + copy a product's pixel sizes or import recipes as rules. +- **D-CRAFT-4 · SHOULD** Treat references as inputs to decisions, not additional instruction sets; for + example, [Don't Make Me Think](https://sensible.com/dont-make-me-think/) informs scanning, hierarchy and + clear choices. + +[taste-skill](https://github.com/leonxlnx/taste-skill) informs deliberate direction and critique; +[ryugi](https://github.com/Innei/ryugi) informs executable work and fresh evidence. + +- **D-CRAFT-5 · SHOULD** A stylistic preference needs context; a requirement identifies the task or + observable contract it protects. +- **D-CRAFT-6 · SHOULD** Choose references by the job they support: logo/app identity, marketing + composition and frequent working interactions solve different problems. +- **D-CRAFT-7 · SHOULD** Record the observed pattern and its adaptation; verify the result with real + content. + +The optional [research index](./REFERENCE_RESEARCH.md) maps sources to questions and demonstrates this +translation without adding another rule set. + +- **D-CRAFT-8 · SHOULD** When direction is unresolved, compare two small real-host variants with the same + content, width and theme, changing one dimension at a time. Explain what each difference does for the + task. + A clear direction or small correction proceeds directly; there is no mandatory approval ritual. -- Balance short headings progressively with `text-wrap: balance`, preserving normal wrapping. - Do not insert locale-dependent manual breaks or clip text to preserve a screenshot. Check cost - and support before using `pretty` on long live content. -- Tabular figures stabilize digit advances if the font supports them; reserve a meaningful - column for digit-count changes such as 99 → 100 without truncating values. -- Correct asymmetric glyphs in a reusable asset/component, preserving exact layout spines. - Derive nested corner geometry from actual inset; remove an unnecessary nested surface first. -- Motion needs a job: immediate frequent feedback, interruptible transitions, reduced motion and - instant themes. Optional delight must not delay typing or obscure status. Pause offscreen - decoration and profile realistic content before adopting costly effects. See §5. +- **D-CRAFT-9 · SHOULD** Balance short headings progressively with `text-wrap: balance`, preserving normal + wrapping. +- **D-CRAFT-10 · MUST** Headings contain no locale-dependent manual line breaks and are not clipped to + preserve a screenshot. _Proof: browser_ +- **D-CRAFT-11 · SHOULD** Check cost and support before using `pretty` on long live content. +- **D-CRAFT-12 · SHOULD** Use tabular figures to stabilize digit advances if the font supports them. +- **D-CRAFT-13 · SHOULD** Reserve a meaningful column for digit-count changes such as 99 → 100. +- **D-CRAFT-14 · MUST** A digit-count change never truncates the value. _Proof: browser_ +- **D-CRAFT-15 · SHOULD** Correct asymmetric glyphs in a reusable asset/component, preserving exact layout + spines. +- **D-CRAFT-16 · SHOULD** Derive nested corner geometry from actual inset; remove an unnecessary nested + surface first. +- **D-CRAFT-17 · SHOULD** Give motion a job: immediate frequent feedback, interruptible transitions, + reduced motion and instant themes. See §5. +- **D-CRAFT-18 · MUST** Optional delight never delays typing or obscures status. _Proof: recording_ +- **D-CRAFT-19 · SHOULD** Pause offscreen decoration and profile realistic content before adopting costly + effects. ### Words are part of the interface -Avoid unnecessary secondary labels and descriptions. Let the layout do the explaining. -A heading, grouping, proximity or obvious control should not need another sentence repeating -its purpose. Remove copy with no new decision, constraint or consequence; do not replace it -with a decorative badge or tooltip. Preserve accessible names, field labels, units, errors, -risk disclosures and useful first-use guidance. Test whether the task remains understandable. - -Let context carry context: `Users` rather than `User Management`; do not repeat a dialog's -question in its body. Name results (`Save`, `Move`, `Delete`), not gestures (`Click to save`). -Include the object only when context does not establish it. `Cancel` leaves without committing. -Use sentence case, no period on short labels, and a period on explanatory sentences. -Use one `…` when an action needs more input before completing; reserve ALL CAPS for short -statuses, eyebrows and acronyms. +- **D-COPY-6 · SHOULD** Avoid unnecessary secondary labels and descriptions. Let the layout do the + explaining. +- **D-COPY-7 · SHOULD** A heading, grouping, proximity or obvious control needs no other sentence + repeating its purpose. +- **D-COPY-8 · SHOULD** Remove copy with no new decision, constraint or consequence; do not replace it + with a decorative badge or tooltip. +- **D-COPY-9 · MUST** Preserve accessible names, field labels, units, errors and risk disclosures. + _Proof: browser_ +- **D-COPY-10 · SHOULD** Preserve useful first-use guidance, and test whether the task remains + understandable. +- **D-COPY-11 · SHOULD** Let context carry context: `Users` rather than `User Management`; do not repeat a + dialog's question in its body. +- **D-COPY-12 · SHOULD** Name results (`Save`, `Move`, `Delete`), not gestures (`Click to save`). Include + the object only when context does not establish it. +- **D-COPY-13 · MUST** `Cancel` leaves without committing. _Proof: browser_ +- **D-COPY-14 · SHOULD** Use sentence case, no period on short labels, and a period on explanatory + sentences. +- **D-COPY-15 · SHOULD** Use one `…` when an action needs more input before completing; reserve ALL CAPS + for short statuses, eyebrows and acronyms. ### Review checklist for a composed surface -Apply these questions to the affected surface and reachable changed states. Skip irrelevant variants; -a small patch does not require redesigning unrelated pages. A "no" within scope is a finding: +- **D-REVIEW-1 · SHOULD** Apply these questions to the affected surface and reachable changed states. + Skip irrelevant variants; a small patch does not require redesigning unrelated pages. A "no" within + scope is a finding. 1. Can a new reader recognise the purpose and the primary action without guessing? 2. Does every control's label, state and result describe one consistent outcome? -3. Is emphasis scarce — does the core thing get the weight while colour, badges and - primary buttons stay rare? +3. Is emphasis scarce — does the core thing get the weight while colour, badges and primary buttons stay rare? 4. Could this do less: any entry point, option or state removable without weakening the task? -5. Are named spines exact at the visible text/content layer, including nested and empty states, - with intentional navigation columns and no doubled padding? Do adjacent controls share their declared - icon/text columns? Are title hit zones content-hugging? Do repeated vertical relationships use - consistent gaps with one owner, including wrapped copy and hidden helper/error content? -6. Is running text comfortable in its actual scripts, fonts and zoom levels, and is - everything that is **not** running text free of that measure cap? -7. Does every element that looks like part of one object agree — border, radius **and** - spacing — and does every reserved column actually have content? Does each persistent frame, - fill or divider communicate a distinct boundary instead of nesting the same group again? -8. Does it hold in relevant changed states: empty, loading, failure, long translations, narrow - width, supported themes, reduced motion and **RTL**, where affected? -9. For layout or interaction changes, has the affected surface been checked in a real browser - at the relevant widths and themes? Are standalone actions recognizable before hover, including - touch use, without redundant group enclosures? +5. Are named spines exact at the visible text/content layer, including nested and empty states, with intentional navigation columns and no doubled padding? +6. Do adjacent controls share their declared icon/text columns? +7. Are title hit zones content-hugging? +8. Do repeated vertical relationships use consistent gaps with one owner, including wrapped copy and hidden helper/error content? +9. Is running text comfortable in its actual scripts, fonts and zoom levels, and is everything that is **not** running text free of that measure cap? +10. Does every element that looks like part of one object agree — border, radius **and** spacing — and does every reserved column actually have content? Does each persistent frame, fill or divider communicate a distinct boundary instead of nesting the same group again? +11. Does it hold in relevant changed states: empty, loading, failure, long translations, narrow width, supported themes, reduced motion and **RTL**, where affected? +12. For layout or interaction changes, has the affected surface been checked in a real browser at the relevant widths and themes? Are standalone actions recognizable before hover, including touch use, without redundant group enclosures? A happy-path screenshot does not prove keyboard behavior, focus, dynamic content, -resizing or failure recovery. Follow [ACCEPTANCE](./ACCEPTANCE.md) for independent expectations, -actual runtime/capture identity, browser measurements and separate automated, agent and human -results. The source-only [browser example](https://github.com/chaxus/ran/blob/main/packages/ranui/test/e2e/layout-contract.md) +resizing or failure recovery. + +- **D-REVIEW-2 · SHOULD** Follow [ACCEPTANCE](./ACCEPTANCE.md) for independent expectations, actual + runtime/capture identity, browser measurements and separate automated, agent and human results. + +The source-only [browser example](https://github.com/chaxus/ran/blob/main/packages/ranui/test/e2e/layout-contract.md) contains setup commands and test utility details; they are not public package APIs. ## 10. Information architecture — the shape before the composition -Choose structure before composition for information-dense consoles, dashboards and workbenches. +- **D-IA-1 · SHOULD** Choose structure before composition for information-dense consoles, dashboards and + workbenches. + Complete API data is not a page design: the reader still needs a clear order for understanding -objects, their state and relationships, then acting. Single-conversion and landing pages need -their own task structure rather than a dense-console template. +objects, their state and relationships, then acting. + +- **D-IA-2 · SHOULD** Give single-conversion and landing pages their own task structure rather than a + dense-console template. ### Answer three questions before naming a component -1. **What must the reader see on arrival?** That is the primary information. -2. **What else has to be visible for it to make sense?** Related resources, related models, - context. +- **D-IA-3 · SHOULD** Settle all three answers before choosing components. + +1. **What does the reader need to see on arrival?** That is the primary information. +2. **What else has to be visible for it to make sense?** Related resources, related models, context. 3. **What do they do next?** Judge, act, or keep thinking. -**A page has exactly one primary model.** Supporting models may help the reader understand or -operate it; they may not compete for the first screen. Prefer a clear, unambiguous next choice -over an arbitrary click budget. Test whether readers recognize the next action, not merely -whether the destination is reachable. +- **D-IA-4 · SHOULD** **A page has exactly one primary model.** Supporting models may help the reader + understand or operate it; they may not compete for the first screen. +- **D-IA-5 · SHOULD** Prefer a clear, unambiguous next choice over an arbitrary click budget. Test whether + readers recognize the next action, not merely whether the destination is reachable. ### The shape follows the task, not the payload -Choose a skeleton from the reader's question, not an API array or route ID: +- **D-IA-6 · SHOULD** Choose a skeleton from the reader's question, not an API array or route ID, using + the table below. | Task | Useful shape | | --------------------------------------------- | -------------------------------------------------------- | @@ -454,13 +610,17 @@ Choose a skeleton from the reader's question, not an API array or route ID: | Find occupancy/conflicts or location | Calendar or map/canvas | | Work on the next item / change rules | Master-detail workbench / configuration form | -Do not confuse historical events with future steps, a filter with kanban, or ordered prose -with a field grid. A date alone does not justify a calendar. Ship multiple views only when -each serves a distinct task worth its filter/state/action maintenance. +- **D-IA-7 · SHOULD** Do not confuse historical events with future steps, a filter with kanban, or ordered + prose with a field grid. A date alone does not justify a calendar. +- **D-IA-8 · SHOULD** Ship multiple views only when each serves a distinct task worth its + filter/state/action maintenance. ### Each kind of information has a place -| Information | Belongs | Must not end up | +- **D-IA-9 · SHOULD** Lead with identity and status in the title or summary region, and keep actions, + feedback and evidence beside their subject, as the table below lays out. + +| Information | Belongs | Wrong place | | ------------- | ------------------------------------------------------------ | ---------------------------------------- | | Identity | Title, object summary | The last column, or behind a tab | | Status | Title or summary region | Findable only in a detail field | @@ -471,62 +631,78 @@ each serves a distinct task worth its filter/state/action maintenance. | Actions | Primary in the title region, the rest beside their object | Buried under "more" | | Feedback | Beside the action, keeping task context | A global toast detached from its subject | -**Every fact has exactly one authoritative location.** Elsewhere shows a summary or an entry -point that links back to it. - -Default reading order — page identity → current status or exception → primary task and primary -action → the information the judgement needs → relationships, changes, evidence → secondary -information and low-frequency actions. **One primary action per task region**, and it is the -most likely next step, not the most destructive one (§9, "Emphasis is a budget", governs how -much weight it may take). +- **D-IA-10 · SHOULD** **Every fact has exactly one authoritative location.** Elsewhere shows a summary or + an entry point that links back to it. +- **D-IA-11 · SHOULD** Follow the default reading order: page identity → current status or exception → + primary task and primary action → the information the judgement needs → relationships, changes, + evidence → secondary information and low-frequency actions. +- **D-IA-12 · SHOULD** **One primary action per task region**, and it is the most likely next step, not + the most destructive one (§9, "Emphasis is a budget", governs how much weight it may take). ### Density is effective information, not controls per inch Tightening spacing raises visual density and leaves effective density where it was. Removing irrelevant fields and putting the comparison in one place raises the real thing. -**Use at most two adjacent density levels on one page** — spacious for first use and risky -confirmation, standard for most lists and details, compact for expert workbenches. Container -padding and line height may tighten; readable body text, a visible focus ring and pointer -target size may not (§8). +- **D-IA-13 · SHOULD** **Use at most two adjacent density levels on one page**: spacious for first use and + risky confirmation, standard for most lists and details, compact for expert workbenches. +- **D-IA-14 · MAY** Container padding and line height may tighten. +- **D-IA-15 · MUST** Readable body text, a visible focus ring and pointer target size do not shrink with + density (§7, §8). _Proof: browser_ ### Containers do not fix structure -**One contextual return per view.** Do not show an outer “Back to chat” and an inner -“Back to artifacts” together. The detail view returns to its immediate parent; ancestors remain -reachable through the route hierarchy or clearly named breadcrumbs. Verify the full journey, -including focus return and long translated destination names. If text does not fit, reflow or -use a meaningful shorter destination; an ellipsis or tooltip must not hide the only destination cue. -Choose one composition for the parent context. Do not add a breadcrumb merely to relabel a -redundant return shortcut, or repeat that parent as an inert crumb above its return command. +- **D-IA-16 · SHOULD** **One contextual return per view.** Do not show an outer “Back to chat” and an + inner “Back to artifacts” together. +- **D-IA-17 · SHOULD** The detail view returns to its immediate parent. +- **D-IA-18 · MUST** Ancestors remain reachable through the route hierarchy or clearly named breadcrumbs. + _Proof: browser_ +- **D-IA-19 · SHOULD** Verify the full journey, including focus return and long translated destination + names. +- **D-IA-27 · MUST** Returning to the parent restores focus to the originating item. _Proof: browser_ +- **D-IA-20 · MUST** If text does not fit, reflow or use a meaningful shorter destination; an ellipsis or + tooltip never hides the only destination cue. _Proof: browser_ +- **D-IA-21 · SHOULD** Choose one composition for the parent context. Do not add a breadcrumb merely to + relabel a redundant return shortcut, or repeat that parent as an inert crumb above its return command. + When returning through the parent already reaches the ancestor, that journey preserves access -without adding another detail-header row. Use breadcrumbs when the hierarchy itself helps the task. +without adding another detail-header row. -**A modal is not a navigation layer.** Anything needing a copyable link, history, a -side-by-side comparison, or work that survives a refresh gets a route. Reserve a modal for a -short confirmation or a single field, a popover or menu for transient context, and a disclosure -row for progressive disclosure. +- **D-IA-22 · SHOULD** Use breadcrumbs when the hierarchy itself helps the task. -**Tabs carry peer views of one object** — its conversation, its checks, its diff — never -unrelated modules; that is navigation's job. +**A modal is not a navigation layer.** -If the chosen library lacks a suitable primitive, use existing semantic elements and host tokens; -verify the required interaction rather than inventing unsupported APIs. +- **D-IA-23 · SHOULD** Give a route to anything needing a copyable link, history, a side-by-side + comparison, or work that survives a refresh. +- **D-IA-24 · SHOULD** Reserve a modal for a short confirmation or a single field, a popover or menu for + transient context, and a disclosure row for progressive disclosure. +- **D-IA-25 · SHOULD** **Tabs carry peer views of one object** — its conversation, its checks, its diff — + never unrelated modules; that is navigation's job. +- **D-IA-26 · SHOULD** If the chosen library lacks a suitable primitive, use existing semantic elements + and host tokens; verify the required interaction rather than inventing unsupported APIs. ## Verification checklist (before shipping UI) -Select checks for the affected surface, supported variants and changed behavior; do not create -unrelated flows merely to satisfy the list. Repository CI still runs its complete gates. - -- [ ] Primary task and primary action are unmistakable. -- [ ] Affected layout/theme changes work at relevant **narrow and wide** widths and supported themes. -- [ ] Works with **mouse and touch** — any drag/gesture uses Pointer Events, not mouse-only; any - `trigger="hover"` has a tap fallback. -- [ ] All changed **states** exercised (hover, active, focus, disabled, loading, empty, error). -- [ ] **Keyboard / focus** behavior verified; visible focus everywhere. -- [ ] Relevant edge cases: long text, large numbers and the host's supported scripts/locales. -- [ ] Spacing comes from the scale; type uses a role; color uses semantic tokens. -- [ ] Run the host’s applicable checks and inspect actual browser evidence. -- [ ] Copy follows §6; nothing signals state by color alone (§7). -- [ ] For a dense surface: one primary model, the skeleton chosen from the question rather - than the payload, and every fact with one authoritative location (§10). +- **D-VERIFY-1 · SHOULD** Select checks for the affected surface, supported variants and changed behavior; + do not create unrelated flows merely to satisfy the list. + +Repository CI still runs its complete gates. + +- **D-VERIFY-2 · SHOULD** Primary task and primary action are unmistakable. + +Affected layout/theme changes work at relevant **narrow and wide** widths and supported themes (D-INPUT-1). +Mouse and touch both work: drags use Pointer Events and any `trigger="hover"` has a tap fallback (D-INPUT-3, +D-INPUT-4). Keyboard and visible focus are covered by D-ACCESS-3 and D-ACCESS-4. + +- **D-VERIFY-5 · SHOULD** Exercise all changed **states** (hover, active, focus, disabled, loading, empty, + error). +- **D-VERIFY-7 · SHOULD** Cover relevant edge cases: long text, large numbers and the host's supported + scripts/locales. +- **D-VERIFY-8 · SHOULD** Spacing comes from the scale; type uses a role; color uses semantic tokens. +- **D-VERIFY-9 · SHOULD** Run the host’s applicable checks and inspect actual browser evidence. +- **D-VERIFY-10 · SHOULD** Copy follows §6. + +Color-only state signals are covered by D-ACCESS-2. + +- **D-VERIFY-12 · SHOULD** For a dense surface: one primary model, the skeleton chosen from the question + rather than the payload, and every fact with one authoritative location (§10). diff --git a/plugins/ran/skills/design/references/INTERACTION.md b/plugins/ran/skills/design/references/INTERACTION.md index c236022cd5..ab42186447 100644 --- a/plugins/ran/skills/design/references/INTERACTION.md +++ b/plugins/ran/skills/design/references/INTERACTION.md @@ -8,79 +8,116 @@ This file owns **observable behavior**, independent of the screen's domain or vi Use [WORKFLOW_PATTERNS.md](./WORKFLOW_PATTERNS.md) only for a relevant multi-step task. No workflow infrastructure is implied by these general rules. +Each rule carries an ID and a level. **MUST** is a defect when broken and names its proof; +**SHOULD** is the default, and a deviation states its reason for that case; **MAY** is optional. +The [rule index](./RULES.md) lists every graded rule with its proof. + ## 1. Inputs, drafts and intent -Keep labels, prerequisites and validation near the affected field. Distinguish a missing value from -an unavailable service. Explain why submission is unavailable when the prerequisite is not obvious. -Use appropriate native input semantics and autocomplete; preserve platform editing conventions. +- **X-INPUT-1 · MUST** Keep labels, prerequisites and validation messages near the field they affect. + _Proof: browser_ +- **X-INPUT-2 · SHOULD** Distinguish a missing value from an unavailable service, and explain why + submission is unavailable when the prerequisite is not obvious. +- **X-INPUT-3 · SHOULD** Use the appropriate native input semantics and autocomplete, and preserve + platform editing conventions; expose keyboard shortcuts where they help. -| Transition | Required decision | +| Transition | Expected behavior | | ------------------------------- | -------------------------------------------------------------------------------- | | Empty → valid form | Enable the relevant submit action; leave context controls usable | | Submit → running | Dispatch once; expose busy state without unexpectedly moving focus | | Running → new edits | Specify whether edits affect this job, a later job or a retained draft | -| Failed → restore | Restore only repopulates fields; it must not submit them | +| Failed → restore | Restore only repopulates fields; it does not submit them | | Failed → retry | Retry dispatches again; preserve the original scope or ask about changed context | | Settings/source change → return | Keep unfinished input and the user's selection where possible | -Enter submits only where the field's documented convention calls for it, outside IME composition; -check composition state and `isComposing`. Preserve multiline entry and expose shortcuts when useful. -Dictation updates text and never submits by itself; retain the text that preceded capture. -Do not rebuild a focused input on each search response, stream update, resize or status change. - -The latest intent remains authoritative: an older query must not replace newer results, and an -import preview for one file must not appear under another. Use the host’s async identity and cancellation mechanisms; ranui contributors can consult -[CODING §3](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/CODING.md#3-state-and-ownership). Retaining a draft does not prove it was saved: -promise persistence only after it succeeds. - -Search highlights literal matches in titles or snippets using safe text nodes or equivalent -escaping, never query-as-HTML. Keep enough surrounding text to explain why an item matched. +- **X-INPUT-4 · MUST** Submit dispatches once per activation, and Restore only repopulates fields without + submitting them. _Proof: unit_ +- **X-INPUT-5 · SHOULD** Follow the remaining rows of the input transition table. +- **X-INPUT-6 · MUST** Enter submits only where the field's documented convention calls for it, and never + during IME composition (check `isComposing`). Multiline entry keeps working. _Proof: browser_ +- **X-INPUT-7 · MUST** Dictation updates text and never submits by itself; the text that preceded the + capture is retained. _Proof: browser_ +- **X-INPUT-8 · MUST** Do not rebuild a focused input on a search response, stream update, resize or + status change. _Proof: browser_ +- **X-INPUT-9 · MUST** The latest intent wins: an older query never replaces newer results, and a preview + for one file never appears under another. _Proof: unit_ +- **X-INPUT-10 · SHOULD** Implement X-INPUT-9 with the host’s async identity and cancellation mechanisms; + ranui contributors can consult + [CODING §3](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/CODING.md#3-state-and-ownership). +- **X-INPUT-11 · MUST** Promise persistence only after it succeeds; a retained draft is not a saved one. + _Proof: unit_ +- **X-INPUT-12 · MUST** Highlight search matches with safe text nodes or equivalent escaping, never by + inserting the query as HTML. _Proof: unit_ +- **X-INPUT-13 · SHOULD** Keep enough text around a highlighted match to explain why the item matched. ## 2. Reachable controls and predictable focus -Use native semantics and supported component contracts. Name icon-only actions accessibly. -Secondary actions may appear on hover **and keyboard focus**, with a persistent tap route on -coarse input. Keep essential recovery visible. Disabled controls remain quiet on hover and -cannot dispatch. Preserve a stable target box and focus ring as labels or status change. - -Use public attributes/events/slots/variables/parts. A keyboard-safe primitive does not prove a composed -dialog is safe: a focus trap may not discover a closed-shadow button in a footer. Verify initial focus, -Tab/Shift+Tab, Escape/Cancel, Enter and focus restoration. Fix the supported integration or use native -buttons in its supported slot; do not patch private shadow roots or add misleading host tab stops. - -A collapsed region must still identify its contents and state; disclosure must not hide an -unresolved actionable error. Collapsing a region containing focus returns focus to its toggle. -Use the collapsed summary to help readers decide whether to expand. When the expanded body -already displays that content, omit the repeated summary and keep the title and state visible. -Removing the focused object moves focus to the next meaningful action or a stable parent control. -Do not move focus merely because a background request completed. +- **X-FOCUS-1 · SHOULD** Build controls from native semantics and supported component contracts. +- **X-FOCUS-2 · MUST** Every icon-only action has an accessible name. _Proof: browser_ +- **X-FOCUS-3 · MUST** A secondary action revealed on hover is also revealed on keyboard focus and has a + persistent tap route on coarse input. _Proof: browser_ +- **X-FOCUS-4 · SHOULD** Keep essential recovery actions visible rather than revealed on hover. +- **X-FOCUS-5 · MUST** Disabled controls stay quiet on hover and cannot dispatch. _Proof: browser_ +- **X-FOCUS-6 · MUST** The target box and focus ring keep their size and position while a label or status + changes. _Proof: browser_ +- **X-FOCUS-7 · MUST** Integrate through public attributes, events, slots, variables and parts; do not + patch private shadow roots. _Proof: review_ +- **X-FOCUS-8 · MUST** Do not add host tab stops that lead nowhere or duplicate an inner control. + _Proof: browser_ +- **X-FOCUS-9 · MUST** A composed dialog places initial focus inside itself, keeps Tab and Shift+Tab + within it, closes on Escape and Cancel, activates its default action on Enter, and returns focus to + the opener when it closes. _Proof: browser_ +- **X-FOCUS-10 · SHOULD** When a focus trap cannot reach a control (for example a closed-shadow button in + a footer), fix the supported integration or place native buttons in its supported slot. +- **X-FOCUS-11 · MUST** A collapsed region still identifies its contents and state, and disclosure never + hides an unresolved actionable error. _Proof: browser_ +- **X-FOCUS-12 · MUST** Collapsing a region that contains focus returns focus to its toggle; removing the + focused object moves focus to the next meaningful action or a stable parent control. _Proof: browser_ +- **X-FOCUS-13 · MUST** Do not move focus merely because a background request completed. _Proof: browser_ +- **X-FOCUS-14 · SHOULD** Write the collapsed summary to help readers decide whether to expand; when the + expanded body already shows that content, omit the repeated summary and keep the title and state. + +A keyboard-safe primitive does not make a composed dialog safe; X-FOCUS-9 is about the composition. ## 3. Feedback and reading position -Associate feedback with the object acted on. A “Copied” acknowledgment beside Copy is easier -to associate than an unrelated global notification. Show it only after clipboard success; handle -rejection locally, preserving button geometry and focus. Use a toast for changes beyond the current -object or when local feedback would not be noticed. Do not announce every streamed character. -Bind async feedback to the operation's source snapshot. If code changes while Copy is pending, -the older completion must not mark the new content “Copied”; clear or identify stale feedback. -Verify delayed success and failure across an intervening stream update, not just an isolated delay. - -For live logs or streaming results, follow only while the reader is at the latest content. Scrolling -away suspends following; a “Jump to latest” action restores it without stealing reading position. -Use a supported follow/pin contract from the host or chosen component. Keep job and persistence -state in the application; an append-only reading log is not a job manager. +- **X-FEEDBACK-1 · SHOULD** Show feedback next to the object acted on. A “Copied” acknowledgment beside + Copy is easier to associate than an unrelated global notification; a toast suits changes beyond the + current object or feedback that would otherwise go unnoticed. +- **X-FEEDBACK-2 · MUST** Show a success acknowledgment only after the operation succeeded (for Copy, + after clipboard success), and handle rejection locally while keeping button geometry and focus. + _Proof: browser_ +- **X-FEEDBACK-3 · MUST** Async feedback belongs to the operation's source snapshot: when content changes + while Copy is pending, the older completion never marks the new content “Copied”, and stale feedback + is cleared or identified as stale. _Proof: unit, browser_ +- **X-FEEDBACK-4 · MUST** Do not announce every streamed character to assistive technology. + _Proof: browser_ +- **X-FEEDBACK-5 · MUST** Live logs and streaming results follow only while the reader is at the latest + content; scrolling away suspends following, and “Jump to latest” restores it without stealing the + reading position. _Proof: recording_ +- **X-FEEDBACK-6 · SHOULD** Use a supported follow/pin contract from the host or chosen component, and keep + job and persistence state in the application; an append-only reading log is not a job manager. + +For X-FEEDBACK-3, exercise delayed success and failure across an intervening stream update, not only an +isolated delay. ## 4. Progress, failure and recovery -Show a known phase: “Reading files”, “Checking permissions” or “Writing export”. A spinner signals -activity, not a percentage. Show percentage only with a measurable denominator; elapsed time is -secondary and must not masquerade as a completion estimate. Announce meaningful phase changes, -not every timer tick or streamed character. - -An error answers **what failed, whether anything changed, and what to do next**. For example: -“The import stopped before saving. Your selected files are still here. Retry the import.” -Only make that promise if the host can establish it. Preserve in-memory input if storage fails; -explain that persistence failed separately. Put credential-free diagnostics in optional details. +- **X-RECOVER-1 · SHOULD** Name a known phase (“Reading files”, “Checking permissions”, “Writing export”) + rather than showing only a spinner, and announce meaningful phase changes, not every timer tick. +- **X-RECOVER-2 · MUST** Show a percentage only with a measurable denominator; elapsed time is secondary + and never presented as a completion estimate. _Proof: review_ +- **X-RECOVER-3 · MUST** An error answers what failed, whether anything changed, and what to do next, and + promises only what the host can establish. _Proof: review_ +- **X-RECOVER-4 · MUST** Preserve in-memory input when storage fails, and report the persistence failure + separately. _Proof: unit_ +- **X-RECOVER-5 · SHOULD** Put credential-free diagnostics in optional details. +- **X-RECOVER-6 · SHOULD** Give one failure one coherent recovery, not duplicate rows and toasts. +- **X-RECOVER-7 · MUST** A timeout does not prove rollback: establish whether a mutating operation + committed before retrying, using the host's idempotency or deduplication contract where available. + _Proof: unit_ + +For example: “The import stopped before saving. Your selected files are still here. Retry the import.” | Condition | Useful response | Avoid | | ------------------------------------ | ----------------------------------------------------------- | ------------------------------------------------------------------ | @@ -90,24 +127,27 @@ explain that persistence failed separately. Put credential-free diagnostics in o | User cancelled | Mark cancelled; retain recoverable output | Red failure styling or restarting paused work | | Context changed | Identify stale output; refresh against current data | Applying an old result to a newer document silently | -One failure should have one coherent recovery, not duplicate rows and toasts. A timeout does not -prove rollback: establish whether a mutating operation committed before retrying, using the host's -idempotency/deduplication contract where available. Do not promise that Cancel undoes a completed write. - ## 5. Cancel, retry and completion are different outcomes -Keep Cancel and Submit independently reachable when both are supported, with clear accessible -names and stable positions. A cancellation request is not an acknowledged cancellation: show its -pending state until confirmed. If the service keeps running and the client can only stop waiting, -name that limitation instead of claiming the job stopped. Distinguish cancellation from failure. - -Retry dispatches work again; Restore only repopulates editable inputs. Do not silently broaden scope -or switch destinations on retry. Confirm a completed write before reporting saved/applied success; -retain recoverable partial output. Offer Undo only when the host can actually reverse the operation. +- **X-CANCEL-1 · MUST** Keep Cancel and Submit independently reachable when both are supported, with + clear accessible names and stable positions. _Proof: browser_ +- **X-CANCEL-2 · MUST** A cancellation request is not an acknowledged cancellation: show its pending state + until confirmed, and when the client can only stop waiting, say so instead of claiming the job stopped. + _Proof: unit_ +- **X-CANCEL-3 · MUST** Cancellation is not shown as failure. _Proof: browser_ +- **X-CANCEL-4 · MUST** Retry dispatches work again without silently broadening scope or switching + destinations; Restore only repopulates editable inputs. _Proof: unit_ +- **X-CANCEL-5 · MUST** Report saved or applied success only after the write is confirmed, and retain + recoverable partial output. _Proof: unit_ +- **X-CANCEL-6 · MUST** Offer Undo only when the host can actually reverse the operation, and never promise + that Cancel undoes a completed write. _Proof: review_ ## 6. Verify what a person can observe -Choose evidence by the defect, rather than requiring every possible test for a small change: +- **X-VERIFY-1 · SHOULD** Choose evidence by the defect, using the layers below, rather than running every + possible test for a small change. +- **X-VERIFY-2 · SHOULD** Include offline and failure paths when the task depends on external work. +- **X-VERIFY-3 · SHOULD** Report the tested commit and any remaining external prerequisites. | Evidence layer | Relevant regression | | -------------- | ------------------------------------------------------------------------------------------- | @@ -117,7 +157,6 @@ Choose evidence by the defect, rather than requiring every possible test for a s | Cross-surface | Affected forms, search, history, settings, import/export, previews and confirmations | | Variants | Relevant themes, widths, resize, keyboard/coarse input, translations/RTL and reduced motion | -Include offline/failure paths when the task depends on external work. Static audits cannot prove -focus restoration, the winning cascade or preserved reading position. Report the tested commit and -remaining external prerequisites. Library contributors use [CODING's checklist](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/CODING.md#checklist-before-opening-a-pull-request); +Static audits cannot prove focus restoration, the winning cascade or a preserved reading position. +Library contributors use [CODING's checklist](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/CODING.md#checklist-before-opening-a-pull-request); consumer applications use the host's relevant commands, not ranui's source-checkout commands. diff --git a/plugins/ran/skills/design/references/README.md b/plugins/ran/skills/design/references/README.md index 1163d8cc62..f312bdb1c6 100644 --- a/plugins/ran/skills/design/references/README.md +++ b/plugins/ran/skills/design/references/README.md @@ -10,6 +10,7 @@ capabilities; use the existing product’s visual language and implementation co | Find the relevant owner | [Task index](./INTERFACE_GUIDE.md) | | Structure, proportion, wording, visual coherence or motion | [Design](./DESIGN.md) | | Input, focus, progress and recovery | [Interaction](./INTERACTION.md) | +| Every graded rule with its level and proof | [Rule index](./RULES.md) | | A supported multi-step workflow | [Optional patterns](./WORKFLOW_PATTERNS.md) | | Browser proof, recordings and evidence freshness | [Acceptance](./ACCEPTANCE.md) | | Research an unresolved design question | [References](./REFERENCE_RESEARCH.md) | diff --git a/plugins/ran/skills/design/references/RULES.md b/plugins/ran/skills/design/references/RULES.md new file mode 100644 index 0000000000..43eab90dfb --- /dev/null +++ b/plugins/ran/skills/design/references/RULES.md @@ -0,0 +1,403 @@ + + + + +# Rule index + +Every graded rule, with its level and proof (152 MUST, 230 SHOULD, 6 MAY). Open a rule's ID for its section. + +Proof kinds: **browser** — real browser interaction ([browser proof](./ACCEPTANCE.md#browser-proof)); +**recording** — continuous recording ([motion over time](./ACCEPTANCE.md#review-motion-over-time)); +**unit** — a unit or state test in the host; **review** — inspection of the change itself; +`check:` — a `verify:design` rule; `test:` — a named test (paths from the repository root). + +| ID | Level | Rule | Proof | +| -------------------------------------------------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | +| [D-COLOR-1](./DESIGN.md#1-color--a-state-ladder-not-a-palette) | SHOULD | Choose semantic roles for content, surfaces, boundaries, actions and status. | | +| [D-COLOR-2](./DESIGN.md#1-color--a-state-ladder-not-a-palette) | SHOULD | Define default, hover, active, disabled and focus states together. | | +| [D-COLOR-3](./DESIGN.md#1-color--a-state-ladder-not-a-palette) | SHOULD | Resolve light/dark values through the host’s authoritative tokens, and verify contrast in the actual composition. | | +| [D-SPACE-1](./DESIGN.md#2-spacing--a-limited-rhythmic-scale) | SHOULD | Use the host’s existing spacing scale. | | +| [D-SPACE-2](./DESIGN.md#2-spacing--a-limited-rhythmic-scale) | SHOULD | Use tight gaps to group related parts and larger gaps to separate tasks or regions. | | +| [D-SPACE-3](./DESIGN.md#2-spacing--a-limited-rhythmic-scale) | SHOULD | Establish inside padding before outside gaps. | | +| [D-SPACE-4](./DESIGN.md#2-spacing--a-limited-rhythmic-scale) | SHOULD | Do not copy a reference product’s dimensions without evaluating content, density and input method. | | +| [D-TYPE-1](./DESIGN.md#3-typography--choose-a-role-not-a-size) | SHOULD | Choose heading, label, body, action and code roles with coherent size, weight and line height, and keep a single authority for each role. | | +| [D-TYPE-2](./DESIGN.md#3-typography--choose-a-role-not-a-size) | SHOULD | Verify actual scripts, fallback metrics and long content. | | +| [D-TYPE-3](./DESIGN.md#3-typography--choose-a-role-not-a-size) | SHOULD | Establish hierarchy with typography before decorative containers. | | +| [D-ELEV-1](./DESIGN.md#4-radius--elevation) | SHOULD | Coordinate nested corner radii with their real inset. | | +| [D-ELEV-2](./DESIGN.md#4-radius--elevation) | SHOULD | Use elevation to explain a floating layer, not importance, through the host’s stacking roles, and verify overlays against surrounding chrome. | | +| [D-MOTION-1](./DESIGN.md#5-motion--prefer-none) | SHOULD | Use the host system’s motion tokens and verify frequency, continuity and reduced motion in the actual task. | | +| [D-MOTION-2](./DESIGN.md#5-motion--prefer-none) | SHOULD | Default repeated interaction to immediate feedback; a larger change is not automatically a reason to animate. | | +| [D-MOTION-3](./DESIGN.md#5-motion--prefer-none) | SHOULD | Use existing tokens for purposeful motion and keep it quick and interruptible. Reduced motion is graded under D-MOTION-10 and D-ACCESS-5. | | +| [D-MOTION-4](./DESIGN.md#functional-motion-contract) | SHOULD | Specify the trigger, state change and destination before choosing an effect. | | +| [D-MOTION-5](./DESIGN.md#functional-motion-contract) | MUST | Motion preserves unrelated content, drafts and reading position. | recording | +| [D-MOTION-6](./DESIGN.md#functional-motion-contract) | SHOULD | Give sibling components with the same interaction meaning a shared motion language; consistency does not require every component to animate. | | +| [D-MOTION-7](./DESIGN.md#functional-motion-contract) | MUST | When motion is used, a new intent can interrupt or reverse it from the current presentation, without a jump, stale completion or duplicate activation. | recording | +| [D-MOTION-8](./DESIGN.md#functional-motion-contract) | MUST | Feedback stays immediate; animation is not an input lock. | recording | +| [D-MOTION-9](./DESIGN.md#functional-motion-contract) | SHOULD | During insertion/removal, avoid visibly squeezing text or passing exiting controls across live actions; coordinate opacity and layout so the result stays readable. | | +| [D-MOTION-10](./DESIGN.md#functional-motion-contract) | MUST | Reduced motion retains a visible, understandable final state. | browser | +| [D-MOTION-11](./DESIGN.md#functional-motion-contract) | MAY | Use a spring; it need not overshoot. | | +| [D-MOTION-12](./DESIGN.md#functional-motion-contract) | SHOULD | Let theme palettes settle coherently. Avoid implicit palette fades or `transition: all`; opt in explicitly only where the host intends and verifies a coordinated effect. | | +| [D-MOTION-13](./DESIGN.md#functional-motion-contract) | MUST | Factual values and readable content are shown immediately on return to a working surface, without animated counts. | browser | +| [D-MOTION-16](./DESIGN.md#functional-motion-contract) | SHOULD | Do not replay decorative title entrances on each return to a working surface. | | +| [D-MOTION-14](./DESIGN.md#functional-motion-contract) | MAY | A loading demonstration may move to demonstrate loading. | | +| [D-MOTION-15](./DESIGN.md#functional-motion-contract) | MUST | A status pulse appears only while the activity is live. | review | +| [D-COPY-1](./DESIGN.md#6-content--copy-is-part-of-the-system) | SHOULD | Buttons name the outcome. Include the object when context does not establish it: "Deploy project", "Delete member". In a clearly named object or dialog, "Save" or "Delete" is sufficient; avoid generic "OK". See [contextual wording](#words-are-part-of-the-interface). | | +| [D-COPY-2](./DESIGN.md#6-content--copy-is-part-of-the-system) | SHOULD | Errors say **what happened**, then **how to fix it**. ✅ "Build failed: the bundle exceeds the size limit. Reduce it or raise the limit." ❌ "Operation failed, please try again." | | +| [D-COPY-3](./DESIGN.md#6-content--copy-is-part-of-the-system) | SHOULD | Confirmations and toasts state the completed **change**. ✅ "Project deleted". ❌ "Operation successful". | | +| [D-COPY-4](./DESIGN.md#6-content--copy-is-part-of-the-system) | MUST | Show completion feedback only after the operation is confirmed. | unit, browser | +| [D-COPY-5](./DESIGN.md#6-content--copy-is-part-of-the-system) | SHOULD | Be specific; every sentence removes guesswork rather than adding it. | | +| [D-ACCESS-1](./DESIGN.md#7-accessibility--the-system-serves-everyone) | MUST | Text keeps sufficient contrast with its background (**WCAG AA**). | browser | +| [D-ACCESS-2](./DESIGN.md#7-accessibility--the-system-serves-everyone) | MUST | Never signal state with color alone; pair it with an icon or text (e.g. ✓ / ✕ labels, not just green/red). | review | +| [D-ACCESS-3](./DESIGN.md#7-accessibility--the-system-serves-everyone) | MUST | Every interactive element has a **visible focus ring** appropriate to its surface; never remove it for "cleanliness". | browser | +| [D-ACCESS-4](./DESIGN.md#7-accessibility--the-system-serves-everyone) | MUST | **Full keyboard navigation**: nothing is mouse-only. | browser | +| [D-ACCESS-5](./DESIGN.md#7-accessibility--the-system-serves-everyone) | MUST | Respect `prefers-reduced-motion`. | browser | +| [D-ACCESS-6](./DESIGN.md#7-accessibility--the-system-serves-everyone) | MUST | When the host supports both schemes, the initial render follows `prefers-color-scheme`. | browser | +| [D-INPUT-1](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | MUST | Support mouse, keyboard and touch at narrow and wide widths. | browser | +| [D-INPUT-2](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | MUST | Reflow controls instead of hiding the only route to an action. | browser | +| [D-INPUT-3](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | MUST | A hover affordance has a click/tap alternative. | browser | +| [D-INPUT-4](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | MUST | Drags use Pointer Events, including cancellation. | browser | +| [D-INPUT-5](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | MUST | Scope `touch-action` to the actual drag surface and pair it with a real handler; ordinary scrolling keeps working elsewhere. | browser | +| [D-INPUT-6](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | SHOULD | Prefer content/viewport-relative bounds (`min()`, `max()`, `clamp()`) to arbitrary breakpoints. | | +| [D-INPUT-7](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | SHOULD | Use existing host breakpoints, and introduce a justified shared role only when repeated needs emerge. | | +| [D-INPUT-8](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | SHOULD | Exercise resizing into a narrow layout, not only loading there. | | +| [D-INPUT-9](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | MUST | Overlay placement tracks its trigger while open. | browser | +| [D-INPUT-10](./DESIGN.md#8-input--viewport--mobile-and-desktop-both) | SHOULD | Test actual pointer/touch gestures, hover fallback and keyboard routes where affected; checking two color schemes does not establish input parity. | | +| [D-COMP-1](./DESIGN.md#9-composition--the-page-not-the-component) | SHOULD | Choose the page structure in §10 first; then compose its components with these rules. | | +| [D-COMP-2](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | Before implementation, record a short design brief in the existing task note or product design owner. Use the existing system; a small fix only needs the affected decisions. The brief settles each applicable decision in the table below (D-COMP-2). | | +| [D-COMP-4](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | Reuse the brief across sibling components and docs chrome; change emphasis according to the task. | | +| [D-COMP-5](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | Keep an expressive demo visibly distinct from its documentation chrome. | | +| [D-COMP-6](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | Update the brief before changing the visual direction; do not silently rewrite the standard to justify an implementation that failed review. | | +| [D-COMP-7](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | Compare a representative composed still with different real content before animating it, then inspect the state change and return path at normal speed. | | +| [D-COMP-8](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | When an example looks imported from another product, fix that relationship at its owning token/component/style rather than adding a page-specific patch. | | +| [D-COMP-9](./DESIGN.md#style-coherence-across-components-and-pages) | SHOULD | Keep reference observations and validation limits in the optional [research index](./REFERENCE_RESEARCH.md#style-coherence-and-transition-research). | | +| [D-BOX-1](./DESIGN.md#structure-before-containers) | SHOULD | **Reach for spacing, alignment, weight and a hairline before a box.** | | +| [D-BOX-2](./DESIGN.md#structure-before-containers) | SHOULD | Reserve a box for **a genuinely separate object**: a live demo stage, an overlay, a panel that floats. Not for a code block, a table, a list row, a previous/next link, or a cell in a gallery. | | +| [D-BOX-3](./DESIGN.md#structure-before-containers) | SHOULD | **Avoid redundant nested containers and doubled padding.** A card inside a padded panel does not need another decorative frame or inset. | | +| [D-BOX-4](./DESIGN.md#structure-before-containers) | SHOULD | Keep functionally required nested controls or surfaces, and coordinate their radii with the actual inset; containment alone is not a reason to add a box. | | +| [D-BOX-5](./DESIGN.md#structure-before-containers) | SHOULD | **Every persistent boundary needs a distinct job.** Start with headings, alignment and proximity to group content. Add a border, surface fill or elevation only when that structure leaves a real boundary unclear. | | +| [D-BOX-6](./DESIGN.md#structure-before-containers) | SHOULD | Do not repeat the same grouping with a panel frame, a nested card, row rules and another inset at the same time. | | +| [D-BOX-8](./DESIGN.md#structure-before-containers) | MUST | Keep keyboard focus and persistent selection cues; never remove them to make the resting layout quieter. | browser | +| [D-BOX-9](./DESIGN.md#structure-before-containers) | SHOULD | Review the full pane or page, not only the cropped group. For every remaining stable outline, fill or separator, name the relationship or interaction it communicates and check what becomes ambiguous if it is removed. | | +| [D-COMP-10](./DESIGN.md#emphasis-is-a-budget) | SHOULD | Establish priority with structure and proximity first, and spend emphasis on the one thing that deserves it. | | +| [D-ELEV-3](./DESIGN.md#emphasis-is-a-budget) | SHOULD | Reserve shadows for surfaces that genuinely float above content: popovers, menus, dialogs, notifications. Do not shadow every card. | | +| [D-SPACE-6](./DESIGN.md#spacing-carries-meaning) | SHOULD | Do not undo a gap's relationship with a decorative divider. | | +| [D-SPACE-7](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Repeat relationships, not fixed heights. Choose title/description, field/hint, group and region gaps from the host scale according to their relationship. | | +| [D-SPACE-8](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Let translations grow naturally, and leave no vacant row for a missing description. | | +| [D-SPACE-9](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Give each gap one owner: parent stack or explicit child margin. Reset authored local heading/paragraph margins rather than adding them to the parent's gap. | | +| [D-SPACE-10](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Account for owned control padding, collapsing margins and inline baseline space; do not compensate with transforms or smaller type. | | +| [D-SPACE-11](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Place hints with the field/action they explain. | | +| [D-SPACE-12](./DESIGN.md#vertical-rhythm-across-sibling-views) | MUST | Hidden helpers leave no accidental gap. | browser | +| [D-SPACE-16](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | A reserved async status line has a stated purpose and bounds. Measure both axes after content settles; [ACCEPTANCE](./ACCEPTANCE.md) grades this as V-PROOF-6. | | +| [D-SPACE-14](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Keep leading, padding and hairlines distinct: 16px padding plus a 1px rule is not a 17px spacing token. | | +| [D-SPACE-15](./DESIGN.md#vertical-rhythm-across-sibling-views) | SHOULD | Use independent expectations and exercise wrapping, resizing, helper/error visibility and relevant density. | | +| [D-TYPE-4](./DESIGN.md#the-measure-governs-text-not-the-column) | SHOULD | **Choose a readable measure for the actual script and font.** | | +| [D-TYPE-5](./DESIGN.md#the-measure-governs-text-not-the-column) | SHOULD | Review realistic text at supported widths, zoom levels and font fallbacks; CJK and mixed-script text need their own judgment. | | +| [D-TYPE-6](./DESIGN.md#the-measure-governs-text-not-the-column) | SHOULD | Let a table, code fence, demo stage or image retain the width its content needs. Cap the text elements, not the column that holds them. | | +| [D-TYPE-7](./DESIGN.md#the-measure-governs-text-not-the-column) | SHOULD | **Declare the measure in `rem`, never `em`.** | | +| [D-TYPE-8](./DESIGN.md#the-measure-governs-text-not-the-column) | SHOULD | **Build hierarchy out of type before reaching for a box.** | | +| [D-ALIGN-1](./DESIGN.md#if-the-layout-says-two-things-are-one-object-the-spacing-must-agree) | SHOULD | Decide whether the elements are one object or two, and make every property say the same thing. | | +| [D-ALIGN-2](./DESIGN.md#if-the-layout-says-two-things-are-one-object-the-spacing-must-agree) | SHOULD | **When a value must equal another value, name it.** A gap that a child has to subtract is not a literal in two places; it is one custom property referenced twice. | | +| [D-ALIGN-3](./DESIGN.md#reserve-a-column-only-when-its-content-exists) | SHOULD | Condition the track on the element actually being there (`:has()`), not on a page type you assume implies it. | | +| [D-ALIGN-4](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | **Name each alignment spine and the geometry it aligns.** State which siblings share each one; include headers, body copy, rows, actions and empty/loading/error content. | | +| [D-ALIGN-5](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | A back button plus gap may establish a separate title column: do not silently treat that title as aligned to body text. | | +| [D-ALIGN-6](./DESIGN.md#alignment-is-structure-not-polish) | MUST | A missing optional icon or badge never moves labels out of their declared column. | browser | +| [D-ALIGN-7](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | A nested level returns exactly to its parent's spine when it ends. Compare the right boxes. These are separate measurements, even when they happen to coincide; measuring the defect's layer is graded in [ACCEPTANCE](./ACCEPTANCE.md) as V-PROOF-10. | | +| [D-ALIGN-9](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | Inspect optical glyph corrections separately. For web components, measure the host, documented shadow parts and slotted text through supported test seams. V-PROOF-11 in [ACCEPTANCE](./ACCEPTANCE.md) owns this, including the ban on production shadow queries and on assuming document CSS crosses the boundary. | | +| [D-ALIGN-11](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | **Keep a header command's hit zone separate from layout filler.** A title/menu trigger hugs its content plus deliberate padding, bounded by the available width. Let a separate flex/grid track absorb remaining space. | | +| [D-ALIGN-12](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | Define truncation/wrapping and adjacent action behavior for long translations. | | +| [D-ALIGN-13](./DESIGN.md#alignment-is-structure-not-polish) | MUST | The title trigger's hit area does not span unused header space (for example through `flex: 1` on the title button) unless D-ALIGN-14 applies. | browser | +| [D-ALIGN-14](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | A row that intentionally selects its whole width is a different role; record that contract explicitly. | | +| [D-ALIGN-15](./DESIGN.md#alignment-is-structure-not-polish) | MAY | Coarse-pointer targets may grow larger. | | +| [D-ALIGN-22](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | A grown target's hover fill stays on the control's own content, not across unrelated content. | | +| [D-ALIGN-16](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | **Count every inset from the shared ancestor to the visible text.** Parent padding, nested panel padding, a control's internal padding, borders and navigation columns all contribute. | | +| [D-ALIGN-17](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | Remove a redundant inset at its owner instead of cancelling it with an unexplained negative margin. | | +| [D-ALIGN-18](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | Empty states retain their region's content spine and width; extra breathing room never introduces a second left edge or makes a compact action fill the region. | | +| [D-ALIGN-19](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | Keep product-specific insets and target sizes as application aliases, not new foundation values or universal component defaults. | | +| [D-ALIGN-20](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | **When two layout edges or gaps are meant to be equal, a one-pixel difference is a defect.** Optical glyph correction (D-ALIGN-21) is the only tolerance. | | +| [D-ALIGN-21](./DESIGN.md#alignment-is-structure-not-polish) | SHOULD | Treat optical correction for an asymmetric glyph as a separate, documented asset/component decision, reused at supported sizes while sibling gaps, baselines and spines stay exact. | | +| [D-SEM-1](./DESIGN.md#selection-links-and-other-semantic-lies) | MUST | **Selection is persistent state and never looks like hover.** | browser | +| [D-SEM-2](./DESIGN.md#selection-links-and-other-semantic-lies) | MUST | **Action and link roles remain distinguishable.** | browser | +| [D-SEM-3](./DESIGN.md#selection-links-and-other-semantic-lies) | SHOULD | Verify inline links against surrounding prose, including themes where action and body colors happen to match. | | +| [D-SEM-4](./DESIGN.md#selection-links-and-other-semantic-lies) | MUST | Anything that changes application state is a button, whatever it looks like. | review, browser | +| [D-SEM-5](./DESIGN.md#selection-links-and-other-semantic-lies) | SHOULD | A button-shaped link carries no underline. | | +| [D-CMD-1](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | **Remove redundant group enclosures without erasing control affordance.** | | +| [D-CMD-2](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Use flat rows when a recognizable menu/list pattern already conveys action; do not require them in every context. | | +| [D-CMD-3](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Give a standalone command that needs it its own restrained button surface or functional stroke, visible before hover in every supported theme. | | +| [D-CMD-4](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Let a button's own shape identify its target; an enclosing card, muted group fill and row rules still pass the distinct-boundary test (D-BOX-5). | | +| [D-CMD-5](./DESIGN.md#command-hierarchy-in-constrained-panes) | MUST | Initial discovery does not rely on hover, pointer cursors, verb labels or keyboard focus alone. | browser | +| [D-CMD-6](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Inspect the resting full pane with the pointer away, including touch use. | | +| [D-CMD-7](./DESIGN.md#command-hierarchy-in-constrained-panes) | MUST | Hover, press, disabled and keyboard focus stay distinguishable after the control is discovered. | browser | +| [D-CMD-8](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Wrap long translated labels inside one command column instead of a ragged horizontal button cloud. | | +| [D-CMD-9](./DESIGN.md#command-hierarchy-in-constrained-panes) | MUST | Do not add navigation chevrons to immediate commands. | review | +| [D-CMD-10](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Declare which visible columns are shared with adjacent checkboxes or controls: measure icon/checkbox edges and text Ranges, not only button rectangles or alignment within the command group. | | +| [D-CMD-11](./DESIGN.md#command-hierarchy-in-constrained-panes) | MAY | A hit target may extend into an owned gutter to preserve those columns; name the extension. | | +| [D-CMD-12](./DESIGN.md#command-hierarchy-in-constrained-panes) | MUST | An extended hit target stays inside its pane, does not overlap other targets, and keeps focus visible. | browser | +| [D-CMD-13](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Separate destructive commands from recovery and transfer actions. | | +| [D-CMD-14](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Let a danger label and icon communicate intent without a large red outline on every resting command; reserve the strongest danger fill for explicit confirmation. | | +| [D-CMD-15](./DESIGN.md#command-hierarchy-in-constrained-panes) | MUST | Destructive commands keep confirmation, cancellation, focus return and busy guards. | browser | +| [D-CMD-16](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Accept a command pane against its resting state, not only its hover state. | | +| [D-CMD-17](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Inspect the actual render as well: an automated surface-difference check catches invisible control fills but does not prove that users recognize the action. | | +| [D-CMD-18](./DESIGN.md#command-hierarchy-in-constrained-panes) | SHOULD | Reuse foundation roles and coarse-input target requirements; this guidance changes no component defaults. | | +| [D-TOKEN-1](./DESIGN.md#one-product-one-token-set) | SHOULD | Use the product’s authoritative semantic tokens. Aliases may improve readability, but resolve from that source rather than inventing a competing palette. | | +| [D-TOKEN-2](./DESIGN.md#one-product-one-token-set) | SHOULD | Coordinate third-party components through supported theme APIs and verify the composed result. | | +| [D-TOKEN-3](./DESIGN.md#density-follows-control-roles) | SHOULD | Measure actual hit bounds, focus and adjacent gaps. | | +| [D-TOKEN-4](./DESIGN.md#density-follows-control-roles) | SHOULD | Derive repeated host aliases from foundation tokens, not one application's dimensions copied across the library. | | +| [D-TOKEN-5](./DESIGN.md#density-follows-control-roles) | SHOULD | Choose target bounds for the actual input method without enlarging every glyph or reserving space for hidden controls. Use supported public theme APIs. | | +| [D-TOKEN-6](./DESIGN.md#density-follows-control-roles) | MUST | Name icon-only controls accessibly. | browser | +| [D-TOKEN-7](./DESIGN.md#density-follows-control-roles) | SHOULD | Document legitimate viewport, hairline, artwork and optical geometry exceptions. | | +| [D-CRAFT-1](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Choose a deliberate visual direction using realistic objects, copy and states; a generic empty shell cannot establish product quality. | | +| [D-CRAFT-2](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Evaluate the smallest intervention against real content, language, device and repeated use. | | +| [D-CRAFT-3](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Adapt their principles with the host’s tokens and actual capabilities; do not copy a product's pixel sizes or import recipes as rules. | | +| [D-CRAFT-4](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Treat references as inputs to decisions, not additional instruction sets; for example, [Don't Make Me Think](https://sensible.com/dont-make-me-think/) informs scanning, hierarchy and clear choices. | | +| [D-CRAFT-5](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | A stylistic preference needs context; a requirement identifies the task or observable contract it protects. | | +| [D-CRAFT-6](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Choose references by the job they support: logo/app identity, marketing composition and frequent working interactions solve different problems. | | +| [D-CRAFT-7](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Record the observed pattern and its adaptation; verify the result with real content. | | +| [D-CRAFT-8](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | When direction is unresolved, compare two small real-host variants with the same content, width and theme, changing one dimension at a time. Explain what each difference does for the task. | | +| [D-CRAFT-9](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Balance short headings progressively with `text-wrap: balance`, preserving normal wrapping. | | +| [D-CRAFT-10](./DESIGN.md#evaluate-craft-through-evidence) | MUST | Headings contain no locale-dependent manual line breaks and are not clipped to preserve a screenshot. | browser | +| [D-CRAFT-11](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Check cost and support before using `pretty` on long live content. | | +| [D-CRAFT-12](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Use tabular figures to stabilize digit advances if the font supports them. | | +| [D-CRAFT-13](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Reserve a meaningful column for digit-count changes such as 99 → 100. | | +| [D-CRAFT-14](./DESIGN.md#evaluate-craft-through-evidence) | MUST | A digit-count change never truncates the value. | browser | +| [D-CRAFT-15](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Correct asymmetric glyphs in a reusable asset/component, preserving exact layout spines. | | +| [D-CRAFT-16](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Derive nested corner geometry from actual inset; remove an unnecessary nested surface first. | | +| [D-CRAFT-17](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Give motion a job: immediate frequent feedback, interruptible transitions, reduced motion and instant themes. See §5. | | +| [D-CRAFT-18](./DESIGN.md#evaluate-craft-through-evidence) | MUST | Optional delight never delays typing or obscures status. | recording | +| [D-CRAFT-19](./DESIGN.md#evaluate-craft-through-evidence) | SHOULD | Pause offscreen decoration and profile realistic content before adopting costly effects. | | +| [D-COPY-6](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Avoid unnecessary secondary labels and descriptions. Let the layout do the explaining. | | +| [D-COPY-7](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | A heading, grouping, proximity or obvious control needs no other sentence repeating its purpose. | | +| [D-COPY-8](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Remove copy with no new decision, constraint or consequence; do not replace it with a decorative badge or tooltip. | | +| [D-COPY-9](./DESIGN.md#words-are-part-of-the-interface) | MUST | Preserve accessible names, field labels, units, errors and risk disclosures. | browser | +| [D-COPY-10](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Preserve useful first-use guidance, and test whether the task remains understandable. | | +| [D-COPY-11](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Let context carry context: `Users` rather than `User Management`; do not repeat a dialog's question in its body. | | +| [D-COPY-12](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Name results (`Save`, `Move`, `Delete`), not gestures (`Click to save`). Include the object only when context does not establish it. | | +| [D-COPY-13](./DESIGN.md#words-are-part-of-the-interface) | MUST | `Cancel` leaves without committing. | browser | +| [D-COPY-14](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Use sentence case, no period on short labels, and a period on explanatory sentences. | | +| [D-COPY-15](./DESIGN.md#words-are-part-of-the-interface) | SHOULD | Use one `…` when an action needs more input before completing; reserve ALL CAPS for short statuses, eyebrows and acronyms. | | +| [D-REVIEW-1](./DESIGN.md#review-checklist-for-a-composed-surface) | SHOULD | Apply these questions to the affected surface and reachable changed states. Skip irrelevant variants; a small patch does not require redesigning unrelated pages. A "no" within scope is a finding. | | +| [D-REVIEW-2](./DESIGN.md#review-checklist-for-a-composed-surface) | SHOULD | Follow [ACCEPTANCE](./ACCEPTANCE.md) for independent expectations, actual runtime/capture identity, browser measurements and separate automated, agent and human results. | | +| [D-IA-1](./DESIGN.md#10-information-architecture--the-shape-before-the-composition) | SHOULD | Choose structure before composition for information-dense consoles, dashboards and workbenches. | | +| [D-IA-2](./DESIGN.md#10-information-architecture--the-shape-before-the-composition) | SHOULD | Give single-conversion and landing pages their own task structure rather than a dense-console template. | | +| [D-IA-3](./DESIGN.md#answer-three-questions-before-naming-a-component) | SHOULD | Settle all three answers before choosing components. | | +| [D-IA-4](./DESIGN.md#answer-three-questions-before-naming-a-component) | SHOULD | **A page has exactly one primary model.** Supporting models may help the reader understand or operate it; they may not compete for the first screen. | | +| [D-IA-5](./DESIGN.md#answer-three-questions-before-naming-a-component) | SHOULD | Prefer a clear, unambiguous next choice over an arbitrary click budget. Test whether readers recognize the next action, not merely whether the destination is reachable. | | +| [D-IA-6](./DESIGN.md#the-shape-follows-the-task-not-the-payload) | SHOULD | Choose a skeleton from the reader's question, not an API array or route ID, using the table below. | | +| [D-IA-7](./DESIGN.md#the-shape-follows-the-task-not-the-payload) | SHOULD | Do not confuse historical events with future steps, a filter with kanban, or ordered prose with a field grid. A date alone does not justify a calendar. | | +| [D-IA-8](./DESIGN.md#the-shape-follows-the-task-not-the-payload) | SHOULD | Ship multiple views only when each serves a distinct task worth its filter/state/action maintenance. | | +| [D-IA-9](./DESIGN.md#each-kind-of-information-has-a-place) | SHOULD | Lead with identity and status in the title or summary region, and keep actions, feedback and evidence beside their subject, as the table below lays out. | | +| [D-IA-10](./DESIGN.md#each-kind-of-information-has-a-place) | SHOULD | **Every fact has exactly one authoritative location.** Elsewhere shows a summary or an entry point that links back to it. | | +| [D-IA-11](./DESIGN.md#each-kind-of-information-has-a-place) | SHOULD | Follow the default reading order: page identity → current status or exception → primary task and primary action → the information the judgement needs → relationships, changes, evidence → secondary information and low-frequency actions. | | +| [D-IA-12](./DESIGN.md#each-kind-of-information-has-a-place) | SHOULD | **One primary action per task region**, and it is the most likely next step, not the most destructive one (§9, "Emphasis is a budget", governs how much weight it may take). | | +| [D-IA-13](./DESIGN.md#density-is-effective-information-not-controls-per-inch) | SHOULD | **Use at most two adjacent density levels on one page**: spacious for first use and risky confirmation, standard for most lists and details, compact for expert workbenches. | | +| [D-IA-14](./DESIGN.md#density-is-effective-information-not-controls-per-inch) | MAY | Container padding and line height may tighten. | | +| [D-IA-15](./DESIGN.md#density-is-effective-information-not-controls-per-inch) | MUST | Readable body text, a visible focus ring and pointer target size do not shrink with density (§7, §8). | browser | +| [D-IA-16](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | **One contextual return per view.** Do not show an outer “Back to chat” and an inner “Back to artifacts” together. | | +| [D-IA-17](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | The detail view returns to its immediate parent. | | +| [D-IA-18](./DESIGN.md#containers-do-not-fix-structure) | MUST | Ancestors remain reachable through the route hierarchy or clearly named breadcrumbs. | browser | +| [D-IA-19](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | Verify the full journey, including focus return and long translated destination names. | | +| [D-IA-27](./DESIGN.md#containers-do-not-fix-structure) | MUST | Returning to the parent restores focus to the originating item. | browser | +| [D-IA-20](./DESIGN.md#containers-do-not-fix-structure) | MUST | If text does not fit, reflow or use a meaningful shorter destination; an ellipsis or tooltip never hides the only destination cue. | browser | +| [D-IA-21](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | Choose one composition for the parent context. Do not add a breadcrumb merely to relabel a redundant return shortcut, or repeat that parent as an inert crumb above its return command. | | +| [D-IA-22](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | Use breadcrumbs when the hierarchy itself helps the task. | | +| [D-IA-23](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | Give a route to anything needing a copyable link, history, a side-by-side comparison, or work that survives a refresh. | | +| [D-IA-24](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | Reserve a modal for a short confirmation or a single field, a popover or menu for transient context, and a disclosure row for progressive disclosure. | | +| [D-IA-25](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | **Tabs carry peer views of one object** — its conversation, its checks, its diff — never unrelated modules; that is navigation's job. | | +| [D-IA-26](./DESIGN.md#containers-do-not-fix-structure) | SHOULD | If the chosen library lacks a suitable primitive, use existing semantic elements and host tokens; verify the required interaction rather than inventing unsupported APIs. | | +| [D-VERIFY-1](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Select checks for the affected surface, supported variants and changed behavior; do not create unrelated flows merely to satisfy the list. | | +| [D-VERIFY-2](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Primary task and primary action are unmistakable. | | +| [D-VERIFY-5](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Exercise all changed **states** (hover, active, focus, disabled, loading, empty, error). | | +| [D-VERIFY-7](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Cover relevant edge cases: long text, large numbers and the host's supported scripts/locales. | | +| [D-VERIFY-8](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Spacing comes from the scale; type uses a role; color uses semantic tokens. | | +| [D-VERIFY-9](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Run the host’s applicable checks and inspect actual browser evidence. | | +| [D-VERIFY-10](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | Copy follows §6. | | +| [D-VERIFY-12](./DESIGN.md#verification-checklist-before-shipping-ui) | SHOULD | For a dense surface: one primary model, the skeleton chosen from the question rather than the payload, and every fact with one authoritative location (§10). | | +| [X-INPUT-1](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Keep labels, prerequisites and validation messages near the field they affect. | browser | +| [X-INPUT-2](./INTERACTION.md#1-inputs-drafts-and-intent) | SHOULD | Distinguish a missing value from an unavailable service, and explain why submission is unavailable when the prerequisite is not obvious. | | +| [X-INPUT-3](./INTERACTION.md#1-inputs-drafts-and-intent) | SHOULD | Use the appropriate native input semantics and autocomplete, and preserve platform editing conventions; expose keyboard shortcuts where they help. | | +| [X-INPUT-4](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Submit dispatches once per activation, and Restore only repopulates fields without submitting them. | unit | +| [X-INPUT-5](./INTERACTION.md#1-inputs-drafts-and-intent) | SHOULD | Follow the remaining rows of the input transition table. | | +| [X-INPUT-6](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Enter submits only where the field's documented convention calls for it, and never during IME composition (check `isComposing`). Multiline entry keeps working. | browser | +| [X-INPUT-7](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Dictation updates text and never submits by itself; the text that preceded the capture is retained. | browser | +| [X-INPUT-8](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Do not rebuild a focused input on a search response, stream update, resize or status change. | browser | +| [X-INPUT-9](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | The latest intent wins: an older query never replaces newer results, and a preview for one file never appears under another. | unit | +| [X-INPUT-10](./INTERACTION.md#1-inputs-drafts-and-intent) | SHOULD | Implement X-INPUT-9 with the host’s async identity and cancellation mechanisms; ranui contributors can consult [CODING §3](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/CODING.md#3-state-and-ownership). | | +| [X-INPUT-11](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Promise persistence only after it succeeds; a retained draft is not a saved one. | unit | +| [X-INPUT-12](./INTERACTION.md#1-inputs-drafts-and-intent) | MUST | Highlight search matches with safe text nodes or equivalent escaping, never by inserting the query as HTML. | unit | +| [X-INPUT-13](./INTERACTION.md#1-inputs-drafts-and-intent) | SHOULD | Keep enough text around a highlighted match to explain why the item matched. | | +| [X-FOCUS-1](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | SHOULD | Build controls from native semantics and supported component contracts. | | +| [X-FOCUS-2](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | Every icon-only action has an accessible name. | browser | +| [X-FOCUS-3](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | A secondary action revealed on hover is also revealed on keyboard focus and has a persistent tap route on coarse input. | browser | +| [X-FOCUS-4](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | SHOULD | Keep essential recovery actions visible rather than revealed on hover. | | +| [X-FOCUS-5](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | Disabled controls stay quiet on hover and cannot dispatch. | browser | +| [X-FOCUS-6](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | The target box and focus ring keep their size and position while a label or status changes. | browser | +| [X-FOCUS-7](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | Integrate through public attributes, events, slots, variables and parts; do not patch private shadow roots. | review | +| [X-FOCUS-8](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | Do not add host tab stops that lead nowhere or duplicate an inner control. | browser | +| [X-FOCUS-9](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | A composed dialog places initial focus inside itself, keeps Tab and Shift+Tab within it, closes on Escape and Cancel, activates its default action on Enter, and returns focus to the opener when it closes. | browser | +| [X-FOCUS-10](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | SHOULD | When a focus trap cannot reach a control (for example a closed-shadow button in a footer), fix the supported integration or place native buttons in its supported slot. | | +| [X-FOCUS-11](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | A collapsed region still identifies its contents and state, and disclosure never hides an unresolved actionable error. | browser | +| [X-FOCUS-12](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | Collapsing a region that contains focus returns focus to its toggle; removing the focused object moves focus to the next meaningful action or a stable parent control. | browser | +| [X-FOCUS-13](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | MUST | Do not move focus merely because a background request completed. | browser | +| [X-FOCUS-14](./INTERACTION.md#2-reachable-controls-and-predictable-focus) | SHOULD | Write the collapsed summary to help readers decide whether to expand; when the expanded body already shows that content, omit the repeated summary and keep the title and state. | | +| [X-FEEDBACK-1](./INTERACTION.md#3-feedback-and-reading-position) | SHOULD | Show feedback next to the object acted on. A “Copied” acknowledgment beside Copy is easier to associate than an unrelated global notification; a toast suits changes beyond the current object or feedback that would otherwise go unnoticed. | | +| [X-FEEDBACK-2](./INTERACTION.md#3-feedback-and-reading-position) | MUST | Show a success acknowledgment only after the operation succeeded (for Copy, after clipboard success), and handle rejection locally while keeping button geometry and focus. | browser | +| [X-FEEDBACK-3](./INTERACTION.md#3-feedback-and-reading-position) | MUST | Async feedback belongs to the operation's source snapshot: when content changes while Copy is pending, the older completion never marks the new content “Copied”, and stale feedback is cleared or identified as stale. | unit, browser | +| [X-FEEDBACK-4](./INTERACTION.md#3-feedback-and-reading-position) | MUST | Do not announce every streamed character to assistive technology. | browser | +| [X-FEEDBACK-5](./INTERACTION.md#3-feedback-and-reading-position) | MUST | Live logs and streaming results follow only while the reader is at the latest content; scrolling away suspends following, and “Jump to latest” restores it without stealing the reading position. | recording | +| [X-FEEDBACK-6](./INTERACTION.md#3-feedback-and-reading-position) | SHOULD | Use a supported follow/pin contract from the host or chosen component, and keep job and persistence state in the application; an append-only reading log is not a job manager. | | +| [X-RECOVER-1](./INTERACTION.md#4-progress-failure-and-recovery) | SHOULD | Name a known phase (“Reading files”, “Checking permissions”, “Writing export”) rather than showing only a spinner, and announce meaningful phase changes, not every timer tick. | | +| [X-RECOVER-2](./INTERACTION.md#4-progress-failure-and-recovery) | MUST | Show a percentage only with a measurable denominator; elapsed time is secondary and never presented as a completion estimate. | review | +| [X-RECOVER-3](./INTERACTION.md#4-progress-failure-and-recovery) | MUST | An error answers what failed, whether anything changed, and what to do next, and promises only what the host can establish. | review | +| [X-RECOVER-4](./INTERACTION.md#4-progress-failure-and-recovery) | MUST | Preserve in-memory input when storage fails, and report the persistence failure separately. | unit | +| [X-RECOVER-5](./INTERACTION.md#4-progress-failure-and-recovery) | SHOULD | Put credential-free diagnostics in optional details. | | +| [X-RECOVER-6](./INTERACTION.md#4-progress-failure-and-recovery) | SHOULD | Give one failure one coherent recovery, not duplicate rows and toasts. | | +| [X-RECOVER-7](./INTERACTION.md#4-progress-failure-and-recovery) | MUST | A timeout does not prove rollback: establish whether a mutating operation committed before retrying, using the host's idempotency or deduplication contract where available. | unit | +| [X-CANCEL-1](./INTERACTION.md#5-cancel-retry-and-completion-are-different-outcomes) | MUST | Keep Cancel and Submit independently reachable when both are supported, with clear accessible names and stable positions. | browser | +| [X-CANCEL-2](./INTERACTION.md#5-cancel-retry-and-completion-are-different-outcomes) | MUST | A cancellation request is not an acknowledged cancellation: show its pending state until confirmed, and when the client can only stop waiting, say so instead of claiming the job stopped. | unit | +| [X-CANCEL-3](./INTERACTION.md#5-cancel-retry-and-completion-are-different-outcomes) | MUST | Cancellation is not shown as failure. | browser | +| [X-CANCEL-4](./INTERACTION.md#5-cancel-retry-and-completion-are-different-outcomes) | MUST | Retry dispatches work again without silently broadening scope or switching destinations; Restore only repopulates editable inputs. | unit | +| [X-CANCEL-5](./INTERACTION.md#5-cancel-retry-and-completion-are-different-outcomes) | MUST | Report saved or applied success only after the write is confirmed, and retain recoverable partial output. | unit | +| [X-CANCEL-6](./INTERACTION.md#5-cancel-retry-and-completion-are-different-outcomes) | MUST | Offer Undo only when the host can actually reverse the operation, and never promise that Cancel undoes a completed write. | review | +| [X-VERIFY-1](./INTERACTION.md#6-verify-what-a-person-can-observe) | SHOULD | Choose evidence by the defect, using the layers below, rather than running every possible test for a small change. | | +| [X-VERIFY-2](./INTERACTION.md#6-verify-what-a-person-can-observe) | SHOULD | Include offline and failure paths when the task depends on external work. | | +| [X-VERIFY-3](./INTERACTION.md#6-verify-what-a-person-can-observe) | SHOULD | Report the tested commit and any remaining external prerequisites. | | +| [V-AGREE-1](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | Before editing, settle the [design brief](./DESIGN.md#style-coherence-across-components-and-pages), and state the affected task, views/states and independent expectations in that same note. | | +| [V-AGREE-2](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | Name the relevant owner, supported public API and host capability limits. | | +| [V-AGREE-3](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | Select widths, themes, languages and input paths from the actual change, without inventing unrelated work. | | +| [V-AGREE-4](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | For composition, state content/text/icon spines, intentional navigation columns, hit bounds and vertical relationships. | | +| [V-AGREE-5](./ACCEPTANCE.md#agree-on-the-observable-result) | MUST | Do not derive the expected value from the implementation under test. | review | +| [V-AGREE-6](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | Review in order: task/hierarchy, static composition, reachable combined states, then motion. | | +| [V-AGREE-7](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | Do not add animation to compensate for unresolved proportions or unclear navigation. | | +| [V-AGREE-8](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | When a review changes the intended design, update the existing brief first and identify the evidence that prompted it. | | +| [V-AGREE-9](./ACCEPTANCE.md#agree-on-the-observable-result) | SHOULD | Keep one rule owner. | | +| [V-AGREE-10](./ACCEPTANCE.md#agree-on-the-observable-result) | MUST | Do not let one kind of check substitute for another: an API/token check, an image comparison and an agent run each establish only their own result. | review | +| [V-AGREE-11](./ACCEPTANCE.md#agree-on-the-observable-result) | MUST | A fixture demonstrates its own result, not acceptance of a host application it did not render; do not claim it as host acceptance. | review | +| [V-PROOF-1](./ACCEPTANCE.md#browser-proof) | MUST | When reproducing a visible defect, capture the actual running surface before the fix. | browser | +| [V-PROOF-2](./ACCEPTANCE.md#browser-proof) | MUST | After the fix, capture the same relevant state and inspect the full pane with realistic copy and surrounding chrome. | browser | +| [V-PROOF-3](./ACCEPTANCE.md#browser-proof) | MUST | Do not offer a mockup, source diff, changed assertion or screenshot of an unrelated build as proof. | review | +| [V-PROOF-4](./ACCEPTANCE.md#browser-proof) | SHOULD | Inspect the composition as well as the link count: relabeling a redundant return as a breadcrumb can still repeat the same context and add an unnecessary header row. | | +| [V-PROOF-5](./ACCEPTANCE.md#browser-proof) | SHOULD | Trace ancestor reachability through the parent before deciding another visible shortcut is needed. | | +| [V-PROOF-6](./ACCEPTANCE.md#browser-proof) | MUST | Wait for component registration, pending content, fonts and rendered frames before measuring. | browser | +| [V-PROOF-7](./ACCEPTANCE.md#browser-proof) | MUST | Fail on missing or zero-sized targets. | browser | +| [V-PROOF-8](./ACCEPTANCE.md#browser-proof) | MUST | Record any subpixel tolerance, and keep it below a full CSS pixel for exact spines (the source example uses 0.5px). | review | +| [V-PROOF-9](./ACCEPTANCE.md#browser-proof) | MUST | For live overlays, verify that capture preserves the viewport and includes the full trigger/panel; a full-page screenshot can invalidate a crop by causing responsive reflow or portal repositioning. | browser | +| [V-PROOF-10](./ACCEPTANCE.md#browser-proof) | MUST | Measure the defect's layer: wrapper, inner content, rendered text Range, icon viewport and actual pointer target are different. A Range is not exact glyph ink. | browser | +| [V-PROOF-11](./ACCEPTANCE.md#browser-proof) | MUST | Account for shadow parts through supported test seams; do not introduce production shadow queries for acceptance. | review | +| [V-PROOF-12](./ACCEPTANCE.md#browser-proof) | SHOULD | Exercise affected reflow, wrapped translations, optional content and input paths. | | +| [V-PROOF-13](./ACCEPTANCE.md#browser-proof) | MUST | Do not use a static screenshot as proof of focus return, keyboard navigation, touch discovery, interruptibility or async settlement. | review | +| [V-PROOF-14](./ACCEPTANCE.md#browser-proof) | MUST | Record a continuous interaction when the defect unfolds over time. | recording | +| [V-PROOF-15](./ACCEPTANCE.md#browser-proof) | MUST | For a layout assertion, prove it rejects the relevant defect: move only label text while the frame stays fixed, introduce a doubled margin/phantom helper, or expand a title target into filler. | browser | +| [V-PROOF-16](./ACCEPTANCE.md#browser-proof) | MUST | Confirm the mutation won the cascade, observe failure, restore and observe success. | browser | +| [V-PROOF-17](./ACCEPTANCE.md#browser-proof) | SHOULD | Use mutations relevant to the task rather than repeating the entire example suite for a tiny fix. | | +| [V-PROOF-18](./ACCEPTANCE.md#browser-proof) | MUST | Verify raw data when changing presentation. | browser, unit | +| [V-PROOF-19](./ACCEPTANCE.md#browser-proof) | MUST | For code, copy the source payload, not highlighted DOM text: preserve tabs, blank lines, final newline and streamed additions. | unit | +| [V-PROOF-20](./ACCEPTANCE.md#browser-proof) | MUST | Verify clipboard success/failure, native selection and long-line scrolling independently. | browser | +| [V-PROOF-21](./ACCEPTANCE.md#browser-proof) | MUST | Document browser selection normalization without allowing it to alter the raw clipboard payload. | unit, review | +| [V-PROOF-22](./ACCEPTANCE.md#browser-proof) | MUST | Do not claim native browser zoom or text-size accessibility support from CSS zoom stress. | review | +| [V-PROOF-23](./ACCEPTANCE.md#browser-proof) | MUST | Say which paths remain unverified. | review | +| [V-MOTION-1](./ACCEPTANCE.md#review-motion-over-time) | MUST | For changed motion, inspect a continuous action at normal speed, then replay slowly where a transition breaks. | recording | +| [V-MOTION-2](./ACCEPTANCE.md#review-motion-over-time) | MUST | Include activation, intermediate state, completion and the reverse path; repeat or reverse input before completion. | recording | +| [V-MOTION-3](./ACCEPTANCE.md#review-motion-over-time) | MUST | Verify the final state, focus, event count and reading position independently of the animation. | browser | +| [V-MOTION-4](./ACCEPTANCE.md#review-motion-over-time) | MUST | Use a slow replay to diagnose discontinuity, not as a frame-rate benchmark. | review | +| [V-MOTION-5](./ACCEPTANCE.md#review-motion-over-time) | MUST | Give reduced motion its own visible-state check, not just a shorter recording. | browser | +| [V-MOTION-6](./ACCEPTANCE.md#review-motion-over-time) | SHOULD | Report a temporal defect as `element → input → observed frame/state → expected continuity`. | | +| [V-MOTION-7](./ACCEPTANCE.md#review-motion-over-time) | SHOULD | Use one representative real task across affected siblings to detect inconsistent motion language. | | +| [V-MOTION-8](./ACCEPTANCE.md#review-motion-over-time) | SHOULD | Check the full composition again after local fixes. | | +| [V-MOTION-9](./ACCEPTANCE.md#review-motion-over-time) | SHOULD | Preserve failed iterations as diagnostics, separate from accepted captures. | | +| [V-WALK-1](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | For a changed workflow or composition, start with a realistic goal expressed without naming the control to use. | | +| [V-WALK-2](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | Complete the goal from the actual entry point through result, recovery and return, as relevant. | | +| [V-WALK-3](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | Record the first choice, any ambiguous label, hesitation or backtracking, and whether the result makes the next step clear. | | +| [V-WALK-4](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | Use realistic content, including a representative long or failing case, rather than a polished empty state alone. | | +| [V-WALK-5](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | Fix task structure and content hierarchy before adding decorative effects. | | +| [V-WALK-6](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | Report the most consequential observed problems (up to three), with the state and evidence that show each one. | | +| [V-WALK-7](./ACCEPTANCE.md#walk-through-the-task) | MUST | Do not invent findings to fill a quota. | review | +| [V-WALK-8](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | Repeat the affected path after correction. | | +| [V-WALK-9](./ACCEPTANCE.md#walk-through-the-task) | MUST | Report an agent walkthrough as a defect search, not as a human usability study. | review | +| [V-WALK-10](./ACCEPTANCE.md#walk-through-the-task) | SHOULD | When observing a real participant, let them attempt the goal before explaining the interface, and preserve that distinction in the results below. | | +| [V-FRESH-1](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Runtime: record the tested source revision/build and relevant dirty inputs or their fingerprint, including shared styles/components and incoming data when they affect the result. A Git HEAD alone is insufficient if the running build or working tree differs. | review | +| [V-FRESH-2](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Scope/environment: record affected view/group membership, route/state, viewport, theme, locale, browser and input or reduced-motion settings where relevant. | review | +| [V-FRESH-3](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Media: record capture/recording locations and content identity (for example SHA-256), tied to that runtime and state. | review | +| [V-FRESH-4](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Keep before and after distinct; never overwrite the only before image. | review | +| [V-FRESH-5](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Review: record what the automated check and agent inspection actually established, separate from any human decision and any unverified state. | review | +| [V-FRESH-6](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MAY | Use a short Markdown record; an existing manifest may supply these fields. | | +| [V-FRESH-7](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | SHOULD | Do not add a new evidence service or configuration framework for an ordinary change. | | +| [V-FRESH-8](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Keep hashes beside the files or in a referenced manifest so the claimed binding can be checked independently. | review | +| [V-FRESH-9](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Treat the affected review as stale after replacing an image, editing a recording, changing group membership or changing relevant runtime inputs. | review | +| [V-FRESH-10](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | After a shared style/component change, refresh every affected state, rebuild if needed, then inspect the new evidence. | browser, review | +| [V-FRESH-11](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | Present no old capture as current proof. | review | +| [V-FRESH-12](./ACCEPTANCE.md#bind-evidence-to-what-actually-ran) | MUST | When keeping runtime evidence across a docs-only edit or unrelated source change, record why its dependency scope is unaffected. | review | +| [V-REPORT-1](./ACCEPTANCE.md#report-authority-truthfully) | MUST | Keep these three results separate, even in a one-paragraph report. | review | +| [V-REPORT-2](./ACCEPTANCE.md#report-authority-truthfully) | MUST | Claim each result only from the source the table above names, and never for what the table says it does not establish. | review | +| [V-REPORT-3](./ACCEPTANCE.md#report-authority-truthfully) | SHOULD | Use explicit outcomes such as `automated: passed`, `agent review: passed with limits`, `human: not requested`. | | +| [V-REPORT-4](./ACCEPTANCE.md#report-authority-truthfully) | MUST | If human review was requested and is outstanding, say `pending`. | review | +| [V-REPORT-5](./ACCEPTANCE.md#report-authority-truthfully) | MUST | If a user rejected it, preserve `rejected` and the finding until that user reviews the updated scope. | review | +| [V-REPORT-6](./ACCEPTANCE.md#report-authority-truthfully) | MUST | An agent may report its correction and fresh checks, but never invents a user decision or clears a human rejection. | review | +| [V-REPORT-7](./ACCEPTANCE.md#report-authority-truthfully) | MUST | Avoid an unattributed “accepted”, “approved” or “验收通过”. | review | +| [V-REPORT-8](./ACCEPTANCE.md#report-authority-truthfully) | SHOULD | Finish and deliver authorized work while reporting `not requested`/`pending` truthfully. | | +| [V-REPORT-9](./ACCEPTANCE.md#report-authority-truthfully) | SHOULD | Ask only when the actual authorization or a necessary product decision is missing. | | +| [V-REPORT-10](./ACCEPTANCE.md#report-authority-truthfully) | MUST | Never label the agent's own visual check “user accepted”. | review | +| [V-EVAL-1](./ACCEPTANCE.md#regression-of-the-guidance-itself) | SHOULD | For opt-in or task-authorized live evaluation of skill/routing/acceptance changes, use the source-only [six agent scenarios](https://github.com/chaxus/ran/tree/main/plugins/ran/evals). | | +| [V-EVAL-2](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Freeze tasks, fixtures, tools and independent rubrics before live runs. | review | +| [V-EVAL-3](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Use fresh sessions with matching model/settings. | review | +| [V-EVAL-4](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Pin baseline and candidate guidance in separate workspaces. | review | +| [V-EVAL-5](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Retain actual agent work, prompts, outcome checks and evidence. | review | +| [V-EVAL-6](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Record baseline passes as controls and candidate failures as failures. | review | +| [V-EVAL-7](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Do not show workers the assessor's thresholds or repair patches. | review | +| [V-EVAL-8](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Claim behavior on those tasks only from live agent runs; prose matching, hand-authored answers or corrected test expectations are no evidence of it. | review | +| [V-EVAL-9](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Inspect visual judgment independently of mechanical checks. | review | +| [V-EVAL-10](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Report sample size, tools and uncovered conditions. | review | +| [V-EVAL-11](./ACCEPTANCE.md#regression-of-the-guidance-itself) | MUST | Six successful tasks are no proof of reliability across models; do not claim it from them. | review | +| [W-RECIPE-1](./WORKFLOW_PATTERNS.md#optional-workflow-patterns) | SHOULD | For each selected recipe, establish what the application supports before choosing controls. | | +| [W-RECIPE-2](./WORKFLOW_PATTERNS.md#optional-workflow-patterns) | SHOULD | Keep the state for jobs, configuration, selected sources and revisions in their existing owners. Views read that state and dispatch commands instead of duplicating service clients and request flags in each panel. | | +| [W-QUEUE-1](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | SHOULD | Settle the contract for each state in the queue table above. | | +| [W-QUEUE-2](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | MUST | New work never silently replaces or cancels the active job. Without queue support, retain the new input and explain the constraint. | unit | +| [W-QUEUE-3](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | MUST | Editing a pending item in a paused queue neither dispatches it nor resumes the queue. | unit | +| [W-QUEUE-4](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | SHOULD | Summarize queued work in one line, such as “2 queued” or “Paused · 2 queued”. | | +| [W-QUEUE-5](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | SHOULD | Collapse queue details when unneeded; expand into rows rather than nested cards. | | +| [W-QUEUE-6](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | MUST | Each row has an identifiable item and offers only supported actions: Edit, Remove, or Run now. | browser | +| [W-QUEUE-7](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | SHOULD | Define whether Run now waits, preempts or runs concurrently. | | +| [W-QUEUE-8](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | SHOULD | Represent preparation within the row instead of adding a large panel full of disabled commands. | | +| [W-QUEUE-9](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | MUST | Queue editing preserves the current draft, and cancelling editing restores it. | unit | +| [W-QUEUE-10](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | MUST | Reconnection does not auto-dispatch queued work unless the user chose that policy. | unit | +| [W-QUEUE-11](./WORKFLOW_PATTERNS.md#1-batch-work-and-queues) | SHOULD | Apply [general focus and cancel rules](./INTERACTION.md) when removing a row, collapsing the queue or stopping work. | | +| [W-SETTINGS-1](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | Saving configuration and checking availability are separate actions with separate outcomes, and each shows what it accomplished. | unit, browser | +| [W-SETTINGS-2](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Derive controls from capability and state, not a single `connected` boolean. | | +| [W-SETTINGS-3](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Make each state in the settings table above distinguishable to the user. | | +| [W-SETTINGS-4](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | Reconnecting an account whose authorization is required or expired keeps saved work. | unit | +| [W-SETTINGS-5](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | Invalidate verification when relevant endpoint, account or credential settings change. | unit | +| [W-SETTINGS-6](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Treat browser online/offline events as hints, not as proof that a particular endpoint works. | | +| [W-SETTINGS-7](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Reveal settings for the chosen path. A local conversion path may need cache/download/device controls; a remote path may need endpoint/account controls. | | +| [W-SETTINGS-8](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | Hide irrelevant fields **and their stale status messages**. | browser | +| [W-SETTINGS-9](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Settings headers, field groups, account rows and unavailable/empty states retain the named content and navigation spines from [DESIGN's alignment contract](./DESIGN.md#alignment-is-structure-not-polish). | | +| [W-SETTINGS-10](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Keep a title/menu command content-hugging; a separate track absorbs header filler. | | +| [W-SETTINGS-11](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Count nested panel and control padding through to visible labels before declaring regions aligned. | | +| [W-SETTINGS-12](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | Switching modes preserves the other modes' configuration. | unit | +| [W-SETTINGS-13](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Keep the route back to another mode discoverable. | | +| [W-SETTINGS-14](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Explain the data destination before disclosure. | | +| [W-SETTINGS-15](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | A configured API endpoint is not presented as an authorized account. | review | +| [W-SETTINGS-16](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | MUST | Offline, retain saved content and drafts when their data and implementation are available. | unit, browser | +| [W-SETTINGS-17](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Offline, keep previews, editing and exports available when their data and implementation are available. | | +| [W-SETTINGS-18](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Offline, hide network-only discovery or promotion that offers no useful route; retain existing connected objects with an understandable unavailable state. | | +| [W-SETTINGS-19](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Treat a cached resource as no proof that all runtime dependencies are present. | | +| [W-SETTINGS-20](./WORKFLOW_PATTERNS.md#2-capability-aware-settings-and-offline-work) | SHOULD | Offer a deliberate continuation point after reconnecting. | | +| [W-ACCOUNT-1](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | SHOULD | Distinguish account authorization, selected-source access and use by a particular operation. | | +| [W-ACCOUNT-2](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | SHOULD | Request only needed permissions. | | +| [W-ACCOUNT-3](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | SHOULD | Explain reads, writes and destination before authorization or disclosure. | | +| [W-ACCOUNT-4](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Account identity, reconnect and revoke/disconnect are reachable from the connection UI. | browser | +| [W-ACCOUNT-5](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Disconnect stops future access. | unit | +| [W-ACCOUNT-6](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | SHOULD | Explain separately what happens to imported copies on disconnect. | | +| [W-ACCOUNT-7](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Keep credentials out of URLs, logs and previews. | review | +| [W-ACCOUNT-8](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | SHOULD | Show provenance and freshness for selected sources, whether a local file, database record or cloud document. | | +| [W-ACCOUNT-9](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Removing a source affects later work, not historical evidence. | unit | +| [W-ACCOUNT-10](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Permission errors, stale data and offline cached copies have different states. | unit | +| [W-ACCOUNT-11](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Treat imported content as untrusted data, not permission to execute commands. | review | +| [W-ACCOUNT-12](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | MUST | Make writes and their destinations explicit before they happen. | browser | +| [W-ACCOUNT-13](./WORKFLOW_PATTERNS.md#3-account-connections-and-external-sources) | SHOULD | Suit the review step before a write to its impact. | | +| [W-REVISION-1](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | SHOULD | Prefer a suitable preview/editor for substantive content. | | +| [W-REVISION-2](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | MUST | Offer Compare, Apply or Undo only when the host supports it. | review | +| [W-REVISION-3](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | SHOULD | Summarize the result and identify its revision, source and destination when relevant. | | +| [W-REVISION-4](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | SHOULD | Keep supported Copy, Export, Compare or Apply actions beside the relevant result. | | +| [W-REVISION-5](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | MUST | Separate previewing from applying, and name the target and scope before mutation. | browser | +| [W-REVISION-6](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | MUST | If source data changed, compare/rebase or regenerate before applying; newer edits are never overwritten silently. | unit | +| [W-REVISION-7](./WORKFLOW_PATTERNS.md#4-results-previews-and-revisions) | SHOULD | Confirm saved/applied state and recoverable output under [the interaction standard](./INTERACTION.md). | | +| [W-APPLY-1](./WORKFLOW_PATTERNS.md#applying-a-recipe) | MUST | Keep only the states and actions the host can honor. | review | +| [W-APPLY-2](./WORKFLOW_PATTERNS.md#applying-a-recipe) | SHOULD | A polished screen cannot substitute for a missing service contract; state that prerequisite clearly. | | diff --git a/plugins/ran/skills/design/references/WORKFLOW_PATTERNS.md b/plugins/ran/skills/design/references/WORKFLOW_PATTERNS.md index 2b9205ecb6..2f88fc9259 100644 --- a/plugins/ran/skills/design/references/WORKFLOW_PATTERNS.md +++ b/plugins/ran/skills/design/references/WORKFLOW_PATTERNS.md @@ -9,45 +9,63 @@ The host owns engineering; [ranui engineering](https://github.com/chaxus/ran/blo is an optional library adapter. These are host application decisions, not additional component APIs, storage guarantees or bundled services. -For each selected recipe, establish what the application supports before choosing controls. Keep -the state for jobs, configuration, selected sources and revisions in their existing owners. Views -read that state and dispatch commands instead of duplicating service clients and request flags -in each panel. +Each rule carries an ID and a level. **MUST** is a defect when broken and names its proof; +**SHOULD** is the default, and a deviation states its reason for that case; **MAY** is optional. +The [rule index](./RULES.md) lists every graded rule with its proof. + +- **W-RECIPE-1 · SHOULD** For each selected recipe, establish what the application supports before + choosing controls. +- **W-RECIPE-2 · SHOULD** Keep the state for jobs, configuration, selected sources and revisions in + their existing owners. Views read that state and dispatch commands instead of duplicating service + clients and request flags in each panel. ## 1. Batch work and queues **Use when:** people benefit from staging another import, upload or export while work runs. **Host contract:** dispatch, preparation, concurrency/preemption, cancellation acknowledgment, -partial writes, duplicate prevention and persistence. Without queue support, retain new input -and explain the constraint instead of silently replacing the active job. - -| State | Main action | Contract to settle | -| --------------------- | ---------------------------- | ------------------------------------------------------ | -| Ready | Upload files / Export report | Scope, destination and duplicate-submit prevention | -| Running | Cancel, if supported | What stops, what remains and whether a write committed | -| Running with new work | Add to queue, if supported | Never silently replace or cancel the active job | -| Queue paused | Resume queue | Editing a pending item does not dispatch or resume it | -| Failed | Retry / Restore inputs | Use the distinct outcomes in INTERACTION §5 | - -Summarize queued work in one line, such as “2 queued” or “Paused · 2 queued”. Collapse details when -unneeded; expand into rows rather than nested cards. Each row has an identifiable item and only -supported actions: Edit, Remove, or Run now. Define whether Run now waits, preempts or runs concurrently. -Represent preparation within the row instead of adding a large panel full of disabled commands. - -Queue editing preserves the current draft; cancelling editing restores it. Reconnection does not -auto-dispatch unless the user chose that policy. Apply [general focus and cancel rules](./INTERACTION.md) -when removing a row, collapsing the queue or stopping work. +partial writes, duplicate prevention and persistence. + +| State | Main action | Contract to settle | +| --------------------- | ---------------------------- | ----------------------------------------------------------- | +| Ready | Upload files / Export report | Scope, destination and duplicate-submit prevention | +| Running | Cancel, if supported | What stops, what remains and whether a write committed | +| Running with new work | Add to queue, if supported | The active job keeps running; new input is kept (W-QUEUE-2) | +| Queue paused | Resume queue | Editing a pending item does not dispatch or resume it | +| Failed | Retry / Restore inputs | Use the distinct outcomes in INTERACTION §5 | + +- **W-QUEUE-1 · SHOULD** Settle the contract for each state in the queue table above. +- **W-QUEUE-2 · MUST** New work never silently replaces or cancels the active job. Without queue + support, retain the new input and explain the constraint. _Proof: unit_ +- **W-QUEUE-3 · MUST** Editing a pending item in a paused queue neither dispatches it nor resumes the + queue. _Proof: unit_ +- **W-QUEUE-4 · SHOULD** Summarize queued work in one line, such as “2 queued” or “Paused · 2 queued”. +- **W-QUEUE-5 · SHOULD** Collapse queue details when unneeded; expand into rows rather than nested cards. +- **W-QUEUE-6 · MUST** Each row has an identifiable item and offers only supported actions: Edit, Remove, + or Run now. _Proof: browser_ +- **W-QUEUE-7 · SHOULD** Define whether Run now waits, preempts or runs concurrently. +- **W-QUEUE-8 · SHOULD** Represent preparation within the row instead of adding a large panel full of + disabled commands. +- **W-QUEUE-9 · MUST** Queue editing preserves the current draft, and cancelling editing restores it. + _Proof: unit_ +- **W-QUEUE-10 · MUST** Reconnection does not auto-dispatch queued work unless the user chose that + policy. _Proof: unit_ +- **W-QUEUE-11 · SHOULD** Apply [general focus and cancel rules](./INTERACTION.md) when removing a row, + collapsing the queue or stopping work. ## 2. Capability-aware settings and offline work **Use when:** a task offers distinct providers, modes, devices or network-dependent capabilities. **Host contract:** configuration storage, a real verification operation, reachability and local -runtime dependencies. Saving configuration and checking availability must be separate outcomes. +runtime dependencies. + +- **W-SETTINGS-1 · MUST** Saving configuration and checking availability are separate actions with + separate outcomes, and each shows what it accomplished. _Proof: unit, browser_ +- **W-SETTINGS-2 · SHOULD** Derive controls from capability and state, not a single `connected` boolean. -Derive controls from capability and state, not a single `connected` boolean. A remote storage account, -a database service or a local converter can be configured without currently being usable. +A remote storage account, a database service or a local converter can be configured without +currently being usable. -| State | What the user should understand | +| State | What the user understands | | ------------------------------ | --------------------------------------------------------------------- | | Unconfigured | A required choice or credential is missing | | Configured, unverified | Settings are saved; availability has not been established | @@ -56,22 +74,37 @@ a database service or a local converter can be configured without currently bein | Service unreachable | Saved configuration does not prove present reachability | | Device offline | Network operations are unavailable; available local work may continue | -Invalidate verification when relevant endpoint, account or credential settings change. Browser -online/offline events are hints, not proof that a particular endpoint works. Separate saving from -testing configuration and show what each action accomplished. - -Reveal settings for the chosen path. A local conversion path may need cache/download/device controls; -a remote path may need endpoint/account controls. Hide irrelevant fields **and their stale status -messages**. Settings headers, field groups, account rows and unavailable/empty states retain the -named content and navigation spines from [DESIGN's alignment contract](./DESIGN.md#alignment-is-structure-not-polish). -Keep a title/menu command content-hugging; a separate track absorbs header filler. Count nested -panel and control padding through to visible labels before declaring regions aligned. Preserve other modes' configuration and keep the route back discoverable. Explain the -data destination before disclosure; do not confuse an API endpoint with an authorized account. - -Offline, retain saved content, drafts, previews, editing and exports when their data and implementation -are available. Hide network-only discovery or promotion that offers no useful route; retain existing -connected objects with an understandable unavailable state. A cached resource is not proof that all -runtime dependencies are present. Offer a deliberate continuation point after reconnecting. +- **W-SETTINGS-3 · SHOULD** Make each state in the settings table above distinguishable to the user. +- **W-SETTINGS-4 · MUST** Reconnecting an account whose authorization is required or expired keeps saved + work. _Proof: unit_ +- **W-SETTINGS-5 · MUST** Invalidate verification when relevant endpoint, account or credential settings + change. _Proof: unit_ +- **W-SETTINGS-6 · SHOULD** Treat browser online/offline events as hints, not as proof that a particular + endpoint works. +- **W-SETTINGS-7 · SHOULD** Reveal settings for the chosen path. A local conversion path may need + cache/download/device controls; a remote path may need endpoint/account controls. +- **W-SETTINGS-8 · MUST** Hide irrelevant fields **and their stale status messages**. _Proof: browser_ +- **W-SETTINGS-9 · SHOULD** Settings headers, field groups, account rows and unavailable/empty states + retain the named content and navigation spines from + [DESIGN's alignment contract](./DESIGN.md#alignment-is-structure-not-polish). +- **W-SETTINGS-10 · SHOULD** Keep a title/menu command content-hugging; a separate track absorbs header + filler. +- **W-SETTINGS-11 · SHOULD** Count nested panel and control padding through to visible labels before + declaring regions aligned. +- **W-SETTINGS-12 · MUST** Switching modes preserves the other modes' configuration. _Proof: unit_ +- **W-SETTINGS-13 · SHOULD** Keep the route back to another mode discoverable. +- **W-SETTINGS-14 · SHOULD** Explain the data destination before disclosure. +- **W-SETTINGS-15 · MUST** A configured API endpoint is not presented as an authorized account. + _Proof: review_ +- **W-SETTINGS-16 · MUST** Offline, retain saved content and drafts when their data and implementation + are available. _Proof: unit, browser_ +- **W-SETTINGS-17 · SHOULD** Offline, keep previews, editing and exports available when their data and + implementation are available. +- **W-SETTINGS-18 · SHOULD** Offline, hide network-only discovery or promotion that offers no useful + route; retain existing connected objects with an understandable unavailable state. +- **W-SETTINGS-19 · SHOULD** Treat a cached resource as no proof that all runtime dependencies are + present. +- **W-SETTINGS-20 · SHOULD** Offer a deliberate continuation point after reconnecting. ## 3. Account connections and external sources @@ -79,35 +112,56 @@ runtime dependencies are present. Offer a deliberate continuation point after re **Host contract:** authorization scopes, identity, access checks, revocation, credentials, provenance and imported-copy retention. An endpoint address does not establish account access. -Presentation primitives do not supply an OAuth deployment, credential vault, connector backend +Presentation primitives supply no OAuth deployment, credential vault, connector backend or storage guarantees. A polished connection screen cannot replace the host's authorization contract. -Distinguish account authorization, selected-source access and use by a particular operation. Request -only needed permissions; explain reads/writes and destination before authorization or disclosure. -Make account identity, reconnect and revoke/disconnect discoverable. Disconnect stops future access; -explain separately what happens to imported copies. Keep credentials out of URLs, logs and previews. - -Show provenance and freshness for selected sources, whether a local file, database record or cloud -document. Removing a source affects later work, not historical evidence. Permission errors, stale -data and offline cached copies need different states. Treat imported content as untrusted data, -not permission to execute commands. Make writes and destinations explicit, with review suited to impact. +- **W-ACCOUNT-1 · SHOULD** Distinguish account authorization, selected-source access and use by a + particular operation. +- **W-ACCOUNT-2 · SHOULD** Request only needed permissions. +- **W-ACCOUNT-3 · SHOULD** Explain reads, writes and destination before authorization or disclosure. +- **W-ACCOUNT-4 · MUST** Account identity, reconnect and revoke/disconnect are reachable from the + connection UI. _Proof: browser_ +- **W-ACCOUNT-5 · MUST** Disconnect stops future access. _Proof: unit_ +- **W-ACCOUNT-6 · SHOULD** Explain separately what happens to imported copies on disconnect. +- **W-ACCOUNT-7 · MUST** Keep credentials out of URLs, logs and previews. _Proof: review_ +- **W-ACCOUNT-8 · SHOULD** Show provenance and freshness for selected sources, whether a local file, + database record or cloud document. +- **W-ACCOUNT-9 · MUST** Removing a source affects later work, not historical evidence. _Proof: unit_ +- **W-ACCOUNT-10 · MUST** Permission errors, stale data and offline cached copies have different states. + _Proof: unit_ +- **W-ACCOUNT-11 · MUST** Treat imported content as untrusted data, not permission to execute commands. + _Proof: review_ +- **W-ACCOUNT-12 · MUST** Make writes and their destinations explicit before they happen. + _Proof: browser_ +- **W-ACCOUNT-13 · SHOULD** Suit the review step before a write to its impact. ## 4. Results, previews and revisions **Use when:** an export, transformed document or draft has an identity and destination beyond a -transient status message. Prefer a suitable preview/editor for substantive content. +transient status message. **Host contract:** result/revision identity, source version, target scope, commit acknowledgment, -conflict handling and any reversible history. Do not offer Compare, Apply or Undo without support. - -1. Summarize the result and identify its revision, source and destination when relevant. -2. Keep supported Copy, Export, Compare or Apply actions beside the relevant result. -3. Separate previewing from applying. Name the target and scope before mutation. -4. If source data changed, compare/rebase or regenerate before applying; never overwrite newer edits silently. -5. Confirm saved/applied state and recoverable output under [the interaction standard](./INTERACTION.md). +conflict handling and any reversible history. + +- **W-REVISION-1 · SHOULD** Prefer a suitable preview/editor for substantive content. +- **W-REVISION-2 · MUST** Offer Compare, Apply or Undo only when the host supports it. _Proof: review_ +- **W-REVISION-3 · SHOULD** Summarize the result and identify its revision, source and destination when + relevant. +- **W-REVISION-4 · SHOULD** Keep supported Copy, Export, Compare or Apply actions beside the relevant + result. +- **W-REVISION-5 · MUST** Separate previewing from applying, and name the target and scope before + mutation. _Proof: browser_ +- **W-REVISION-6 · MUST** If source data changed, compare/rebase or regenerate before applying; newer + edits are never overwritten silently. _Proof: unit_ +- **W-REVISION-7 · SHOULD** Confirm saved/applied state and recoverable output under + [the interaction standard](./INTERACTION.md). ## Applying a recipe -Keep only the states and actions the host can honor. Verify its relevant transitions: paused work -stays paused, changed configuration invalidates verification, source removal affects future work, -and stale revisions cannot overwrite new edits. Include interrupted/offline work when relevant. -A polished screen cannot substitute for a missing service contract; state that prerequisite clearly. +- **W-APPLY-1 · MUST** Keep only the states and actions the host can honor. _Proof: review_ +- **W-APPLY-2 · SHOULD** A polished screen cannot substitute for a missing service contract; state that + prerequisite clearly. + +The key transitions already carry MUST rules with unit proof: paused work stays paused (W-QUEUE-3), +changed configuration invalidates verification (W-SETTINGS-5), source removal affects only future +work (W-ACCOUNT-9), and stale revisions cannot overwrite new edits (W-REVISION-6). Their tests cover +interrupted and offline work where relevant. diff --git a/plugins/ran/skills/design/references/manifest.json b/plugins/ran/skills/design/references/manifest.json index bb84f3fadf..f1ccb2f468 100644 --- a/plugins/ran/skills/design/references/manifest.json +++ b/plugins/ran/skills/design/references/manifest.json @@ -4,21 +4,21 @@ "sources": [ { "source": "design/ACCEPTANCE.md", - "sha256": "27981d3f8ecc8edab6b17a5a47f1ec0b7ef1d0868dffbd1526fc6e9472396cec", + "sha256": "d75ecc4e72b39c5aa4d93d63fca5a263510b7ef57ff900a4ec68c20040b4d839", "output": "ACCEPTANCE.md", - "outputSha256": "ea4da86a81b3cc81f42f15a852d253d5cbc97db3c0f1176472a9e0b41421cf3c" + "outputSha256": "aa0ecd99d01831a926c9e191074e7dd99180073310743e3a87a3120a5a208197" }, { "source": "design/DESIGN.md", - "sha256": "87b6fa30179fea6dd20d6b9d2eedc6e3421f1bec624c500357d5e0da923ea6b3", + "sha256": "d86047263a277d826b78f47be9047347c185644519848182f201bd88290ec023", "output": "DESIGN.md", - "outputSha256": "cfc4e6451444b4da4db4df4b96b3cca7a2358f5c3beee850eb56159a0d0919c1" + "outputSha256": "9ab31940344312fc9ec6e8a3a0d72e91b5b041147c96f9db3655c020d3856d8b" }, { "source": "design/INTERACTION.md", - "sha256": "539cdaa4527eca61cfe6098f2759d63396ec17215a7bfc444518cb8981c41b95", + "sha256": "90a0634789c55b6b63d3e54f2cabbd71ac817ee53c9dd576142373bcb6548be2", "output": "INTERACTION.md", - "outputSha256": "677d911bbd22abc1214d25360d36809e3e1ad3c57f079d9c0b080ac5fa91eac7" + "outputSha256": "8b8e14163a0e576ca034ec55ccc7103f422ef48a7d5167927760e67e82fb30f2" }, { "source": "design/INTERFACE_GUIDE.md", @@ -28,9 +28,9 @@ }, { "source": "design/README.md", - "sha256": "18071bb2be15f839ccfb1a1ffe46f5d4725faeadb0f5d2040cb1fe4a65381926", + "sha256": "7556995d30a8b1630ab0056a13c32e74eb48b6ceafa2e9eca440cc11328a80a5", "output": "README.md", - "outputSha256": "08cbaf0b5bee3bd92dfc4804ff90c2df1f74f562d813a4494cf26dd03c19677a" + "outputSha256": "7049f0e282f25484f5830882b56a88beee0a8fe9f5c3d015ad7869e2a995e455" }, { "source": "design/REFERENCE_RESEARCH.md", @@ -38,11 +38,17 @@ "output": "REFERENCE_RESEARCH.md", "outputSha256": "815f6889cb4838b2f1eaf09031e8b75ef1b8109672657068700fc7f780fe58b0" }, + { + "source": "design/RULES.md", + "sha256": "90c1776aa1e745e8c635f6395105c1e617ddb87dafb512b416947c6dd9d5ea70", + "output": "RULES.md", + "outputSha256": "ac8c03b4435291145a95f722da1a98b99ebe307d8be099d6734a5b09472595a2" + }, { "source": "design/WORKFLOW_PATTERNS.md", - "sha256": "ae519b57ea95ca0c5ea27481df7e62bdda90cd6b55872bcb3c02a9ff7b72e0e3", + "sha256": "232d8bec54146854cf08dafad9aaad32604fd10764c4a598d2d494aa34fb0c78", "output": "WORKFLOW_PATTERNS.md", - "outputSha256": "b163aa489e0cce0ee8ea113bbae9ddbe997c16b9426bdb23cc1727d34c14f076" + "outputSha256": "11a2428dba51d5a76771dc722f702a1b430ef8a0fc8802fca50309b5e64a341e" } ] }