openuispec 0.2.17 → 0.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,19 +19,11 @@ The result: each platform feels native, but every app stays semantically consist
19
19
 
20
20
  ## How it works
21
21
 
22
- <picture>
23
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/rsktash/openuispec/main/docs/images/how-it-works-dark.png">
24
- <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/rsktash/openuispec/main/docs/images/how-it-works-light.png">
25
- <img alt="How OpenUISpec works" src="https://raw.githubusercontent.com/rsktash/openuispec/main/docs/images/how-it-works-light.png">
26
- </picture>
22
+ <img alt="How OpenUISpec works" src="./docs/images/how-it-works.svg">
27
23
 
28
24
  ## Workflows
29
25
 
30
- <picture>
31
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/rsktash/openuispec/main/docs/images/workflows-dark.png">
32
- <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/rsktash/openuispec/main/docs/images/workflows-light.png">
33
- <img alt="OpenUISpec workflows" src="https://raw.githubusercontent.com/rsktash/openuispec/main/docs/images/workflows-light.png">
34
- </picture>
26
+ <img alt="OpenUISpec workflows" src="./docs/images/workflows.svg">
35
27
 
36
28
  ## Quick start
37
29
 
@@ -54,8 +46,9 @@ This scaffolds a spec directory, starter tokens, and **configures the MCP server
54
46
  - **Data binding** — reactive state, format expressions, caching, and loading/error/empty states
55
47
  - **Adaptive layout** — size classes (compact/regular/expanded) with per-section overrides
56
48
  - **Platform adaptation** — per-target overrides for iOS, Android, and Web behaviors
57
- - **Design intent** — `design` section in the manifest captures brand personality, complexity level, and audience — generators match visual elaborateness accordingly
58
- - **Anti-patterns** — `must_avoid` in contracts and `generation_guidance.universal_anti_patterns` in the manifest steer AI away from generic, statistically common design mistakes
49
+ - **Design intent** — `design` section captures brand personality, complexity level (`restrained`/`balanced`/`elaborate`), quality tier (`mvp`/`production`/`flagship`), and audience — generators match visual elaborateness and polish level accordingly
50
+ - **Anti-patterns** — `must_avoid` in contracts and `universal_anti_patterns` in the manifest (9 domains: typography, color, spacing, motion, elevation, layout, visual, interaction, accessibility) steer AI away from generic design mistakes. Platform-scoped with `[web]`/`[ios]`/`[android]` tags
51
+ - **Design quality audit** — `check --audit` scores the spec against 18 heuristic checks across all token and contract domains, producing a numeric score with CI-gatable thresholds
59
52
 
60
53
  ## The 7 contract families
61
54
 
@@ -101,7 +94,7 @@ Screenshots of the generated apps are in the [artifacts](./artifacts/) directory
101
94
  |-----|-------------|
102
95
  | [CLI & MCP Tools](./docs/cli.md) | All CLI commands, MCP tools, screenshot params, target workflow |
103
96
  | [File Formats & Schemas](./docs/file-formats.md) | File types, JSON schemas, output directories, spec sections |
104
- | [Full Specification](./spec/openuispec-v0.2.md) | Complete v0.2 spec (15 sections) |
97
+ | [Full Specification](./spec/openuispec-v0.2.md) | Complete v0.2 spec (16 sections) |
105
98
  | [llms-full.txt](https://openuispec.rsteam.uz/llms-full.txt) | Spec + all schemas in one file (for AI consumption) |
106
99
 
107
100
  ## Status
package/check/audit.ts CHANGED
@@ -10,7 +10,7 @@
10
10
  * openuispec check --target web --audit --format json
11
11
  */
12
12
 
13
- import { existsSync, readFileSync } from "node:fs";
13
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
14
14
  import { join, resolve } from "node:path";
15
15
  import YAML from "yaml";
16
16
 
@@ -103,6 +103,24 @@ function checkTypography(tokensDir: string, findings: AuditFinding[]): void {
103
103
  message: `Only ${scaleKeys.length} type scale level(s) defined. Use ≥4 distinct levels for clear hierarchy.`,
104
104
  });
105
105
  }
106
+
107
+ // Weight hierarchy: at least 2 distinct weights
108
+ if (doc.typography.scale) {
109
+ const weights = new Set<number>();
110
+ for (const level of Object.values(doc.typography.scale)) {
111
+ if (typeof (level as any)?.weight === "number") {
112
+ weights.add((level as any).weight);
113
+ }
114
+ }
115
+ if (weights.size > 0 && weights.size < 2) {
116
+ findings.push({
117
+ domain: "typography",
118
+ rule: "weight_hierarchy",
119
+ severity: "warning",
120
+ message: "Only 1 distinct font weight used across the type scale. Use ≥2 weights (e.g. 400 + 700) for clear hierarchy.",
121
+ });
122
+ }
123
+ }
106
124
  }
107
125
 
108
126
  function checkColor(tokensDir: string, findings: AuditFinding[]): void {
@@ -139,6 +157,22 @@ function checkColor(tokensDir: string, findings: AuditFinding[]): void {
139
157
  }
140
158
  scanForPure(doc.color, "color");
141
159
 
160
+ // Semantic color completeness: success, warning, danger, info
161
+ if (doc.color.semantic) {
162
+ const required = ["success", "warning", "danger", "info"];
163
+ const defined = Object.keys(doc.color.semantic);
164
+ for (const name of required) {
165
+ if (!defined.includes(name)) {
166
+ findings.push({
167
+ domain: "color",
168
+ rule: "semantic_completeness",
169
+ severity: "warning",
170
+ message: `Semantic color "${name}" is missing. Define all four (success, warning, danger, info) for complete state coverage.`,
171
+ });
172
+ }
173
+ }
174
+ }
175
+
142
176
  // Theme coverage: check themes.yaml for both light + dark
143
177
  {
144
178
  const themes = readYamlForAudit(join(tokensDir, "themes.yaml"), "color", findings);
@@ -219,6 +253,104 @@ function checkMotion(tokensDir: string, findings: AuditFinding[]): void {
219
253
  message: "motion.reduced_motion is not defined. Must specify policy for prefers-reduced-motion.",
220
254
  });
221
255
  }
256
+
257
+ // Easing quality: enter + exit curves, at least one cubic-bezier
258
+ if (doc.motion.easing) {
259
+ const easings = doc.motion.easing;
260
+ const keys = Object.keys(easings);
261
+ if (!keys.includes("enter") || !keys.includes("exit")) {
262
+ findings.push({
263
+ domain: "motion",
264
+ rule: "easing_quality",
265
+ severity: "warning",
266
+ message: "Motion easing should define at least 'enter' and 'exit' curves for asymmetric transitions.",
267
+ });
268
+ }
269
+ const hasCubicBezier = Object.values(easings).some(
270
+ (v) => typeof v === "string" && v.includes("cubic-bezier"),
271
+ );
272
+ if (!hasCubicBezier) {
273
+ findings.push({
274
+ domain: "motion",
275
+ rule: "easing_quality",
276
+ severity: "warning",
277
+ message: "All easing curves are generic keywords. Use at least one cubic-bezier() for nuanced motion.",
278
+ });
279
+ }
280
+ }
281
+ }
282
+
283
+ function checkElevationProgression(tokensDir: string, findings: AuditFinding[]): void {
284
+ const doc = readYamlForAudit(join(tokensDir, "elevation.yaml"), "elevation", findings);
285
+ if (!doc?.elevation) return;
286
+ const levels = Object.keys(doc.elevation).filter((k) => k !== "none");
287
+ if (levels.length < 2) {
288
+ findings.push({
289
+ domain: "elevation",
290
+ rule: "level_count",
291
+ severity: "warning",
292
+ message: `Only ${levels.length} non-none elevation level(s) defined. Define ≥2 (e.g. sm, md, lg) for meaningful depth hierarchy.`,
293
+ });
294
+ return;
295
+ }
296
+ const androidValues: number[] = [];
297
+ for (const level of levels) {
298
+ const val = doc.elevation[level]?.platform?.android?.elevation;
299
+ if (typeof val === "number") androidValues.push(val);
300
+ }
301
+ if (androidValues.length >= 2) {
302
+ for (let i = 1; i < androidValues.length; i++) {
303
+ if (androidValues[i] <= androidValues[i - 1]) {
304
+ findings.push({
305
+ domain: "elevation",
306
+ rule: "progression",
307
+ severity: "warning",
308
+ message: "Elevation levels do not increase monotonically. Each level should cast a deeper shadow than the previous.",
309
+ });
310
+ break;
311
+ }
312
+ }
313
+ }
314
+ }
315
+
316
+ function checkLayoutSizeClasses(tokensDir: string, findings: AuditFinding[]): void {
317
+ const doc = readYamlForAudit(join(tokensDir, "layout.yaml"), "layout", findings);
318
+ if (!doc?.layout?.size_classes) return;
319
+ const classes = Object.keys(doc.layout.size_classes);
320
+ if (classes.length < 2) {
321
+ findings.push({
322
+ domain: "layout",
323
+ rule: "size_class_coverage",
324
+ severity: "warning",
325
+ message: `Only ${classes.length} size class(es) defined. Define at least compact + regular for responsive layouts.`,
326
+ });
327
+ } else if (!classes.includes("compact")) {
328
+ findings.push({
329
+ domain: "layout",
330
+ rule: "size_class_coverage",
331
+ severity: "warning",
332
+ message: "No 'compact' size class defined. Mobile-first layouts require a compact breakpoint.",
333
+ });
334
+ }
335
+ }
336
+
337
+ function checkContractStateCoverage(contractsDir: string, findings: AuditFinding[]): void {
338
+ if (!existsSync(contractsDir)) return;
339
+ for (const file of readdirSync(contractsDir).filter((f) => f.endsWith(".yaml") && !f.startsWith("x_"))) {
340
+ const doc = readYamlForAudit(join(contractsDir, file), "contracts", findings);
341
+ if (!doc) continue;
342
+ const contractName = Object.keys(doc)[0];
343
+ const contract = doc[contractName];
344
+ const mustHandle: string[] = contract?.generation?.must_handle ?? [];
345
+ if (mustHandle.length === 0) {
346
+ findings.push({
347
+ domain: "contracts",
348
+ rule: "state_coverage",
349
+ severity: "warning",
350
+ message: `Contract "${contractName}" has no generation.must_handle entries. Define required states for AI compliance.`,
351
+ });
352
+ }
353
+ }
222
354
  }
223
355
 
224
356
  function checkContracts(contractsDir: string, findings: AuditFinding[]): void {
@@ -255,7 +387,10 @@ export function buildAuditResult(projectDir: string, threshold: number = 0): Aud
255
387
  checkColor(tokensDir, findings);
256
388
  checkSpacing(tokensDir, findings);
257
389
  checkMotion(tokensDir, findings);
390
+ checkElevationProgression(tokensDir, findings);
391
+ checkLayoutSizeClasses(tokensDir, findings);
258
392
  checkContracts(contractsDir, findings);
393
+ checkContractStateCoverage(contractsDir, findings);
259
394
 
260
395
  const errors = findings.filter((f) => f.severity === "error").length;
261
396
  const warnings = findings.filter((f) => f.severity === "warning").length;
package/docs/cli.md CHANGED
@@ -289,10 +289,15 @@ openuispec check --target web --audit --json # machine-readable output
289
289
 
290
290
  | Domain | What's checked |
291
291
  |--------|---------------|
292
- | Typography | Primary font not an AI default (Inter/Roboto/Arial/Open Sans) · ≥4 scale levels defined |
293
- | Color | No pure #000000/#FFFFFF · Both light + dark themes present |
292
+ | Tokens | All 8 required token files exist |
293
+ | Typography | Primary font not an AI default · ≥4 scale levels · ≥2 distinct weights |
294
+ | Color | No pure #000000/#FFFFFF · success/warning/danger/info semantic colors · light + dark themes |
294
295
  | Spacing | ≥4 scale values · `page_margin` and `card_padding` aliases present |
295
- | Motion | ≥2 distinct durations · `reduced_motion` policy defined |
296
- | Contracts | `collection` has `empty_state` in `must_handle` |
296
+ | Motion | ≥2 distinct durations · `reduced_motion` policy · enter/exit easing · ≥1 cubic-bezier curve |
297
+ | Elevation | ≥2 non-none levels · monotonically increasing progression |
298
+ | Layout | ≥2 size classes · compact class defined |
299
+ | Contracts | `collection` has `empty_state` in `must_handle` · all contracts have non-empty `must_handle` |
297
300
 
298
301
  The `audit_threshold` in `generation_guidance` sets the project-wide minimum score. `--min-score` overrides it per-run.
302
+
303
+ See [Section 16 of the spec](../spec/openuispec-v0.2.md#16-design-intent-and-generation-guidance) for complete documentation of design intent, anti-patterns, and quality tiers.
@@ -0,0 +1,56 @@
1
+ <svg width="100%" viewBox="0 0 680 470" xmlns="http://www.w3.org/2000/svg">
2
+ <defs>
3
+ <marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></marker>
4
+
5
+ <mask id="imagine-text-gaps-o7de63" maskUnits="userSpaceOnUse"><rect x="0" y="0" width="680" height="470" fill="white"/><rect x="256.6324462890625" y="12.305379867553711" width="166.73512268066406" height="20.52798843383789" fill="black" rx="2"/><rect x="69.6505126953125" y="62.305381774902344" width="90.69898223876953" height="20.527990341186523" fill="black" rx="2"/><rect x="46.96881103515625" y="84.19429016113281" width="136.0624237060547" height="18.16684913635254" fill="black" rx="2"/><rect x="91.05573272705078" y="120.19429779052734" width="47.88853454589844" height="18.16684913635254" fill="black" rx="2"/><rect x="83.20494079589844" y="156.1942901611328" width="63.590126037597656" height="18.16684913635254" fill="black" rx="2"/><rect x="75.04055786132812" y="192.19430541992188" width="79.91889953613281" height="18.16684913635254" fill="black" rx="2"/><rect x="88.12275695800781" y="228.19430541992188" width="53.75449752807617" height="18.16684913635254" fill="black" rx="2"/><rect x="94.92210388183594" y="264.1943054199219" width="40.15579605102539" height="18.16684913635254" fill="black" rx="2"/><rect x="69.73905181884766" y="300.1943054199219" width="90.52189636230469" height="18.16684913635254" fill="black" rx="2"/><rect x="67.63247680664062" y="336.1943054199219" width="94.73506164550781" height="18.16684913635254" fill="black" rx="2"/><rect x="73.51688385009766" y="372.1943054199219" width="82.96624755859375" height="18.16684913635254" fill="black" rx="2"/><rect x="217" y="214.1942901611328" width="35.17526435852051" height="18.16684913635254" fill="black" rx="2"/><rect x="292.1412048339844" y="152.30538940429688" width="89.71763610839844" height="20.527990341186523" fill="black" rx="2"/><rect x="300.2318115234375" y="174.1942901611328" width="73.53643798828125" height="18.16684913635254" fill="black" rx="2"/><rect x="280.6084899902344" y="208.1942901611328" width="112.78303527832031" height="18.16684913635254" fill="black" rx="2"/><rect x="272.58062744140625" y="244.1942901611328" width="128.83879852294922" height="18.16684913635254" fill="black" rx="2"/><rect x="288.5773620605469" y="280.1943054199219" width="96.84532928466797" height="18.16684913635254" fill="black" rx="2"/><rect x="442.0000305175781" y="138.1942901611328" width="36.606706619262695" height="18.16684913635254" fill="black" rx="2"/><rect x="442.0000305175781" y="214.1942901611328" width="39.89016914367676" height="18.16684913635254" fill="black" rx="2"/><rect x="442.0000305175781" y="304.1943054199219" width="69.11670303344727" height="18.16684913635254" fill="black" rx="2"/><rect x="519.6734619140625" y="106.30538177490234" width="98.65308380126953" height="20.527990341186523" fill="black" rx="2"/><rect x="507.6279602050781" y="128.1942901611328" width="122.74410247802734" height="18.16684913635254" fill="black" rx="2"/><rect x="497.83660888671875" y="208.30538940429688" width="142.3268280029297" height="20.527990341186523" fill="black" rx="2"/><rect x="505.4365234375" y="230.1942901611328" width="127.12696838378906" height="18.16684913635254" fill="black" rx="2"/><rect x="521.5771484375" y="314.3053894042969" width="94.84574127197266" height="20.527990341186523" fill="black" rx="2"/><rect x="499.4488220214844" y="336.1943054199219" width="139.10238647460938" height="18.16684913635254" fill="black" rx="2"/><rect x="138.05886840820312" y="434.1943054199219" width="403.8822937011719" height="18.16684913635254" fill="black" rx="2"/></mask></defs>
6
+
7
+ <rect width="680" height="460" rx="12" fill="#F5F1E8" style="fill:rgb(245, 241, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
8
+
9
+ <text x="340" y="28" text-anchor="middle" style="fill:rgb(44, 44, 42);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">How OpenUISpec works</text>
10
+
11
+ <rect x="40" y="52" width="150" height="370" rx="12" fill="#FAEEDA" stroke="#EF9F27" stroke-width="0.5" style="fill:rgb(250, 238, 218);stroke:rgb(239, 159, 39);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
12
+ <text x="115" y="78" text-anchor="middle" fill="#633806" style="fill:rgb(44, 44, 42);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">OpenUISpec</text>
13
+ <text x="115" y="98" text-anchor="middle" fill="#854F0B" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Semantic spec (YAML)</text>
14
+
15
+ <rect x="56" y="116" width="118" height="28" rx="6" fill="#FAEEDA" stroke="#EF9F27" stroke-width="0.5" style="fill:rgb(250, 238, 218);stroke:rgb(239, 159, 39);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><text x="115" y="134" text-anchor="middle" fill="#633806" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Tokens</text>
16
+ <rect x="56" y="152" width="118" height="28" rx="6" fill="#FAEEDA" stroke="#EF9F27" stroke-width="0.5" style="fill:rgb(250, 238, 218);stroke:rgb(239, 159, 39);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><text x="115" y="170" text-anchor="middle" fill="#633806" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Contracts</text>
17
+ <rect x="56" y="188" width="118" height="28" rx="6" fill="#FAEEDA" stroke="#EF9F27" stroke-width="0.5" style="fill:rgb(250, 238, 218);stroke:rgb(239, 159, 39);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><text x="115" y="206" text-anchor="middle" fill="#633806" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Components</text>
18
+ <rect x="56" y="224" width="118" height="28" rx="6" fill="#FAEEDA" stroke="#EF9F27" stroke-width="0.5" style="fill:rgb(250, 238, 218);stroke:rgb(239, 159, 39);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><text x="115" y="242" text-anchor="middle" fill="#633806" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Screens</text>
19
+ <rect x="56" y="260" width="118" height="28" rx="6" fill="#FAEEDA" stroke="#EF9F27" stroke-width="0.5" style="fill:rgb(250, 238, 218);stroke:rgb(239, 159, 39);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><text x="115" y="278" text-anchor="middle" fill="#633806" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Flows</text>
20
+ <rect x="56" y="296" width="118" height="28" rx="6" fill="#FAEEDA" stroke="#EF9F27" stroke-width="0.5" style="fill:rgb(250, 238, 218);stroke:rgb(239, 159, 39);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><text x="115" y="314" text-anchor="middle" fill="#633806" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Actions &amp; data</text>
21
+ <rect x="56" y="332" width="118" height="28" rx="6" fill="#FAEEDA" stroke="#EF9F27" stroke-width="0.5" style="fill:rgb(250, 238, 218);stroke:rgb(239, 159, 39);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><text x="115" y="350" text-anchor="middle" fill="#633806" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Platform config</text>
22
+ <rect x="56" y="368" width="118" height="28" rx="6" fill="#FAEEDA" stroke="#EF9F27" stroke-width="0.5" style="fill:rgb(250, 238, 218);stroke:rgb(239, 159, 39);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><text x="115" y="386" text-anchor="middle" fill="#633806" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Design intent</text>
23
+
24
+ <line x1="194" y1="237" x2="248" y2="237" stroke="#BA7517" stroke-width="1.5" stroke-dasharray="4 3" marker-end="url(#arrow)" style="fill:rgb(0, 0, 0);stroke:rgb(186, 117, 23);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-dasharray:4px, 3px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
25
+ <text x="221" y="228" fill="#854F0B" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:start;dominant-baseline:auto">spec</text>
26
+
27
+ <rect x="252" y="140" width="170" height="194" rx="12" fill="#EEEDFE" stroke="#7F77DD" stroke-width="0.5" style="fill:rgb(238, 237, 254);stroke:rgb(127, 119, 221);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
28
+ <text x="337" y="168" text-anchor="middle" fill="#26215C" style="fill:rgb(44, 44, 42);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">AI generator</text>
29
+ <text x="337" y="188" text-anchor="middle" fill="#534AB7" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">MCP server</text>
30
+
31
+ <rect x="268" y="204" width="138" height="28" rx="6" fill="#EEEDFE" stroke="#7F77DD" stroke-width="0.5" style="fill:rgb(238, 237, 254);stroke:rgb(127, 119, 221);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><text x="337" y="222" text-anchor="middle" fill="#3C3489" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Read spec context</text>
32
+ <rect x="268" y="240" width="138" height="28" rx="6" fill="#EEEDFE" stroke="#7F77DD" stroke-width="0.5" style="fill:rgb(238, 237, 254);stroke:rgb(127, 119, 221);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><text x="337" y="258" text-anchor="middle" fill="#3C3489" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Generate native code</text>
33
+ <rect x="268" y="276" width="138" height="28" rx="6" fill="#EEEDFE" stroke="#7F77DD" stroke-width="0.5" style="fill:rgb(238, 237, 254);stroke:rgb(127, 119, 221);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><text x="337" y="294" text-anchor="middle" fill="#3C3489" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Validate &amp; audit</text>
34
+
35
+ <line x1="426" y1="198" x2="490" y2="134" stroke="#534AB7" stroke-width="1.5" marker-end="url(#arrow)" mask="url(#imagine-text-gaps-o7de63)" style="fill:rgb(0, 0, 0);stroke:rgb(83, 74, 183);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
36
+ <line x1="426" y1="237" x2="490" y2="237" stroke="#534AB7" stroke-width="1.5" marker-end="url(#arrow)" style="fill:rgb(0, 0, 0);stroke:rgb(83, 74, 183);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
37
+ <line x1="426" y1="276" x2="490" y2="340" stroke="#534AB7" stroke-width="1.5" marker-end="url(#arrow)" mask="url(#imagine-text-gaps-o7de63)" style="fill:rgb(0, 0, 0);stroke:rgb(83, 74, 183);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
38
+
39
+ <text x="446" y="152" fill="#5F5E5A" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:start;dominant-baseline:auto">Swift</text>
40
+ <text x="446" y="228" fill="#5F5E5A" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:start;dominant-baseline:auto">Kotlin</text>
41
+ <text x="446" y="318" fill="#5F5E5A" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:start;dominant-baseline:auto">TypeScript</text>
42
+
43
+ <rect x="494" y="92" width="150" height="80" rx="10" fill="#E6F1FB" stroke="#85B7EB" stroke-width="0.5" style="fill:rgb(230, 241, 251);stroke:rgb(133, 183, 235);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
44
+ <text x="569" y="122" text-anchor="middle" fill="#042C53" style="fill:rgb(44, 44, 42);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">iOS — SwiftUI</text>
45
+ <text x="569" y="142" text-anchor="middle" fill="#185FA5" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Native, platform feel</text>
46
+
47
+ <rect x="494" y="198" width="150" height="80" rx="10" fill="#EAF3DE" stroke="#97C459" stroke-width="0.5" style="fill:rgb(234, 243, 222);stroke:rgb(151, 196, 89);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
48
+ <text x="569" y="224" text-anchor="middle" fill="#173404" style="fill:rgb(44, 44, 42);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">Android — Compose</text>
49
+ <text x="569" y="244" text-anchor="middle" fill="#3B6D11" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Material You theming</text>
50
+
51
+ <rect x="494" y="304" width="150" height="80" rx="10" fill="#FAECE7" stroke="#F0997B" stroke-width="0.5" style="fill:rgb(250, 236, 231);stroke:rgb(240, 153, 123);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
52
+ <text x="569" y="330" text-anchor="middle" fill="#4A1B0C" style="fill:rgb(44, 44, 42);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">Web — React</text>
53
+ <text x="569" y="350" text-anchor="middle" fill="#993C1D" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Responsive, accessible</text>
54
+
55
+ <text x="340" y="448" text-anchor="middle" fill="#888780" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Share the intent · generate native code · each platform feels like home</text>
56
+ </svg>
@@ -0,0 +1,76 @@
1
+ <svg width="100%" viewBox="0 0 680 530" xmlns="http://www.w3.org/2000/svg">
2
+ <defs>
3
+ <marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></marker>
4
+
5
+ </defs>
6
+
7
+ <rect width="680" height="520" rx="12" fill="#F5F1E8" style="fill:rgb(245, 241, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
8
+
9
+ <text x="340" y="28" text-anchor="middle" style="fill:rgb(44, 44, 42);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">Workflows</text>
10
+
11
+ <rect x="40" y="48" width="106" height="26" rx="13" fill="#FAEEDA" stroke="#EF9F27" stroke-width="0.5" style="fill:rgb(250, 238, 218);stroke:rgb(239, 159, 39);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><text x="93" y="65" text-anchor="middle" fill="#633806" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Design mode</text>
12
+ <text x="158" y="65" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:start;dominant-baseline:auto">Spec-first — new features, design system changes</text>
13
+
14
+ <rect x="40" y="86" width="152" height="106" rx="10" fill="#FAEEDA" stroke="#EF9F27" stroke-width="0.5" style="fill:rgb(250, 238, 218);stroke:rgb(239, 159, 39);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
15
+ <text x="116" y="108" text-anchor="middle" fill="#633806" style="fill:rgb(44, 44, 42);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">1 · Define</text>
16
+ <text x="116" y="128" text-anchor="middle" fill="#854F0B" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Author spec YAML</text>
17
+ <text x="116" y="144" text-anchor="middle" fill="#854F0B" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Tokens, contracts,</text>
18
+ <text x="116" y="160" text-anchor="middle" fill="#854F0B" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">screens, flows</text>
19
+ <text x="116" y="208" text-anchor="middle" fill="#888780" font-style="italic" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;font-style:italic;text-anchor:middle;dominant-baseline:auto">openuispec validate</text>
20
+
21
+ <line x1="196" y1="139" x2="228" y2="139" stroke="#BA7517" stroke-width="1.5" marker-end="url(#arrow)" style="fill:rgb(0, 0, 0);stroke:rgb(186, 117, 23);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
22
+
23
+ <rect x="232" y="86" width="162" height="106" rx="10" fill="#EEEDFE" stroke="#7F77DD" stroke-width="0.5" style="fill:rgb(238, 237, 254);stroke:rgb(127, 119, 221);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
24
+ <text x="313" y="108" text-anchor="middle" fill="#26215C" style="fill:rgb(44, 44, 42);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">2 · Generate</text>
25
+ <text x="313" y="128" text-anchor="middle" fill="#534AB7" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">AI reads spec via MCP</text>
26
+ <text x="313" y="144" text-anchor="middle" fill="#534AB7" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Produces native code</text>
27
+ <text x="313" y="160" text-anchor="middle" fill="#534AB7" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">per platform target</text>
28
+ <text x="313" y="208" text-anchor="middle" fill="#888780" font-style="italic" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;font-style:italic;text-anchor:middle;dominant-baseline:auto">openuispec prepare</text>
29
+
30
+ <line x1="398" y1="139" x2="430" y2="139" stroke="#BA7517" stroke-width="1.5" marker-end="url(#arrow)" style="fill:rgb(0, 0, 0);stroke:rgb(186, 117, 23);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
31
+
32
+ <rect x="434" y="86" width="152" height="106" rx="10" fill="#EAF3DE" stroke="#97C459" stroke-width="0.5" style="fill:rgb(234, 243, 222);stroke:rgb(151, 196, 89);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
33
+ <text x="510" y="108" text-anchor="middle" fill="#173404" style="fill:rgb(44, 44, 42);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">3 · Refine</text>
34
+ <text x="510" y="128" text-anchor="middle" fill="#3B6D11" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Platform teams adjust</text>
35
+ <text x="510" y="144" text-anchor="middle" fill="#3B6D11" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">for native polish</text>
36
+ <text x="510" y="160" text-anchor="middle" fill="#3B6D11" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Snapshot baseline</text>
37
+ <text x="510" y="208" text-anchor="middle" fill="#888780" font-style="italic" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;font-style:italic;text-anchor:middle;dominant-baseline:auto">openuispec drift --snapshot</text>
38
+
39
+ <line x1="590" y1="139" x2="610" y2="139" stroke="#B4B2A9" stroke-width="0.5" stroke-dasharray="3 2" style="fill:rgb(0, 0, 0);stroke:rgb(180, 178, 169);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-dasharray:3px, 2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
40
+ <rect x="614" y="115" width="52" height="48" rx="8" fill="#E1F5EE" stroke="#5DCAA5" stroke-width="0.5" style="fill:rgb(225, 245, 238);stroke:rgb(93, 202, 165);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><text x="640" y="135" text-anchor="middle" fill="#085041" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Native</text><text x="640" y="149" text-anchor="middle" fill="#085041" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">apps</text>
41
+
42
+ <line x1="40" y1="242" x2="640" y2="242" stroke="#D3D1C7" stroke-width="0.5" stroke-dasharray="6 4" style="fill:rgb(0, 0, 0);stroke:rgb(211, 209, 199);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-dasharray:6px, 4px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
43
+ <text x="340" y="258" text-anchor="middle" fill="#888780" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">or</text>
44
+
45
+ <rect x="40" y="274" width="132" height="26" rx="13" fill="#E6F1FB" stroke="#85B7EB" stroke-width="0.5" style="fill:rgb(230, 241, 251);stroke:rgb(133, 183, 235);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><text x="106" y="291" text-anchor="middle" fill="#0C447C" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Development mode</text>
46
+ <text x="184" y="291" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:start;dominant-baseline:auto">Platform-first — day-to-day iteration, fixes, polish</text>
47
+
48
+ <rect x="40" y="312" width="152" height="106" rx="10" fill="#EAF3DE" stroke="#97C459" stroke-width="0.5" style="fill:rgb(234, 243, 222);stroke:rgb(151, 196, 89);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
49
+ <text x="116" y="334" text-anchor="middle" fill="#173404" style="fill:rgb(44, 44, 42);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">1 · Code</text>
50
+ <text x="116" y="354" text-anchor="middle" fill="#3B6D11" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Edit native code</text>
51
+ <text x="116" y="370" text-anchor="middle" fill="#3B6D11" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Live preview, hot reload</text>
52
+ <text x="116" y="386" text-anchor="middle" fill="#3B6D11" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Fix, tweak, polish</text>
53
+
54
+ <line x1="196" y1="365" x2="228" y2="365" stroke="#185FA5" stroke-width="1.5" marker-end="url(#arrow)" style="fill:rgb(0, 0, 0);stroke:rgb(24, 95, 165);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
55
+
56
+ <rect x="232" y="312" width="162" height="106" rx="10" fill="#FAEEDA" stroke="#EF9F27" stroke-width="0.5" style="fill:rgb(250, 238, 218);stroke:rgb(239, 159, 39);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
57
+ <text x="313" y="334" text-anchor="middle" fill="#633806" style="fill:rgb(44, 44, 42);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">2 · Sync</text>
58
+ <text x="313" y="354" text-anchor="middle" fill="#854F0B" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">AI reads code changes</text>
59
+ <text x="313" y="370" text-anchor="middle" fill="#854F0B" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Updates spec YAML</text>
60
+ <text x="313" y="386" text-anchor="middle" fill="#854F0B" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">to match platform code</text>
61
+ <text x="313" y="434" text-anchor="middle" fill="#888780" font-style="italic" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;font-style:italic;text-anchor:middle;dominant-baseline:auto">openuispec drift --explain</text>
62
+
63
+ <line x1="398" y1="365" x2="430" y2="365" stroke="#185FA5" stroke-width="1.5" marker-end="url(#arrow)" style="fill:rgb(0, 0, 0);stroke:rgb(24, 95, 165);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
64
+
65
+ <rect x="434" y="312" width="152" height="106" rx="10" fill="#EEEDFE" stroke="#7F77DD" stroke-width="0.5" style="fill:rgb(238, 237, 254);stroke:rgb(127, 119, 221);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
66
+ <text x="510" y="334" text-anchor="middle" fill="#26215C" style="fill:rgb(44, 44, 42);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">3 · Propagate</text>
67
+ <text x="510" y="354" text-anchor="middle" fill="#534AB7" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Other platforms see</text>
68
+ <text x="510" y="370" text-anchor="middle" fill="#534AB7" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">spec diff and update</text>
69
+ <text x="510" y="386" text-anchor="middle" fill="#534AB7" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">their code accordingly</text>
70
+ <text x="510" y="434" text-anchor="middle" fill="#888780" font-style="italic" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;font-style:italic;text-anchor:middle;dominant-baseline:auto">openuispec status</text>
71
+
72
+ <line x1="590" y1="365" x2="610" y2="365" stroke="#B4B2A9" stroke-width="0.5" stroke-dasharray="3 2" style="fill:rgb(0, 0, 0);stroke:rgb(180, 178, 169);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-dasharray:3px, 2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
73
+ <rect x="614" y="341" width="52" height="48" rx="8" fill="#E1F5EE" stroke="#5DCAA5" stroke-width="0.5" style="fill:rgb(225, 245, 238);stroke:rgb(93, 202, 165);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><text x="640" y="361" text-anchor="middle" fill="#085041" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">All in</text><text x="640" y="375" text-anchor="middle" fill="#085041" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">sync</text>
74
+
75
+ <text x="340" y="500" text-anchor="middle" fill="#888780" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:system-ui, -apple-system, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Spec lives in version control · diffs are human-readable · drift is detected, not hidden</text>
76
+ </svg>
@@ -166,6 +166,6 @@
166
166
 
167
167
  `openuispec check --audit` runs design quality heuristics against token and contract files, returning a numeric score (`max(0, 100 - errors × 10 - warnings × 3)`) and categorized findings. The `audit_threshold` in `generation_guidance` sets a project-wide minimum; `--min-score N` overrides per-run.
168
168
 
169
- `openuispec prepare` includes `anti_patterns` (universal + contract-specific + project-specific, filtered by target platform) and `design_context` (personality, complexity, audience, complexity_rule) in its output when the manifest defines `generation_guidance` and `design` sections.
169
+ `openuispec prepare` includes `anti_patterns` (universal + contract-specific + project-specific, filtered by target platform) and `design_context` (personality, complexity, quality_tier, audience, complexity_rule, quality_tier_rule, quality_test) in its output when the manifest defines `generation_guidance` and `design` sections. The `quality_test` field is an auto-generated AI-slop checklist tuned to the project's complexity and quality tier.
170
170
 
171
171
  `generation.extra_rules` in the manifest is included in prepare output and filtered by platform tag.
@@ -57,6 +57,15 @@ generation_guidance:
57
57
  layout:
58
58
  - "Do not assume large-screen layouts work on compact — every multi-pane pattern needs an explicit compact fallback"
59
59
  - "Do not use pixel breakpoints — reference size classes by name (compact, regular, expanded)"
60
+ visual:
61
+ - "Do not flatten depth — use elevation tokens to separate layers and give content visual weight"
62
+ - "Do not use generic drop shadows — map every shadow to a specific elevation level from tokens"
63
+ - "Do not apply glassmorphism to interactive controls — reserve frosted effects for non-interactive overlays only"
64
+ interaction:
65
+ - "Do not make swipe gestures the only way to access actions — provide a visible alternative"
66
+ - "Do not delay visual feedback on tap — social content requires instant response"
67
+ - "[web] Do not use :focus for focus rings — use :focus-visible to show rings only for keyboard navigation"
68
+ - "[ios] Do not replace native swipe-back with custom gestures — users expect system navigation"
60
69
  accessibility:
61
70
  - "Do not use color as the only differentiator between states — combine with icon, border, or text changes"
62
71
  - "Do not skip focus ring styles — keyboard users must see where focus is at all times"
@@ -66,6 +75,7 @@ generation_guidance:
66
75
  design:
67
76
  personality: "Vibrant, social, content-rich — engagement and discovery are primary, utility is secondary"
68
77
  complexity: "elaborate"
78
+ quality_tier: "flagship"
69
79
  audience: "Mobile-first social media users who expect rich media, smooth animations, and expressive interactions"
70
80
  avoid:
71
81
  - "Do not use muted or desaturated brand colors — the palette should feel energetic"
@@ -64,6 +64,14 @@ generation_guidance:
64
64
  layout:
65
65
  - "Do not assume large-screen layouts work on compact — every multi-pane pattern needs an explicit compact fallback"
66
66
  - "Do not use pixel breakpoints — reference size classes by name (compact, regular, expanded)"
67
+ visual:
68
+ - "Do not use glassmorphism or frosted-glass effects — this is a professional productivity tool"
69
+ - "Do not apply gradient text — it fails contrast checks and looks dated"
70
+ - "[web] Do not use box-shadow on every container — reserve elevation for interactive or raised elements"
71
+ interaction:
72
+ - "Do not treat hover and focus as the same state — keyboard users never see hover"
73
+ - "Do not use confirmation dialogs for reversible actions — use undo with a toast instead"
74
+ - "[web] Do not use :focus for focus rings — use :focus-visible to show rings only for keyboard navigation"
67
75
  accessibility:
68
76
  - "Do not use color as the only differentiator between states — combine with icon, border, or text changes"
69
77
  - "Do not skip focus ring styles — keyboard users must see where focus is at all times"
@@ -73,6 +81,7 @@ generation_guidance:
73
81
  design:
74
82
  personality: "Clean, focused, productivity-first — no decorative flourishes. Color is used for priority and status clarity, not aesthetics."
75
83
  complexity: "balanced"
84
+ quality_tier: "production"
76
85
  audience: "Individual contributors and small teams managing personal and shared task lists"
77
86
  avoid:
78
87
  - "Do not use playful or rounded UI patterns — this is a professional productivity tool"
@@ -63,6 +63,16 @@ generation_guidance:
63
63
  layout:
64
64
  - "Do not assume large-screen layouts work on compact — every multi-pane pattern needs an explicit compact fallback"
65
65
  - "Do not use pixel breakpoints — reference size classes by name (compact, regular, expanded)"
66
+ visual:
67
+ - "Do not mix rounded and sharp corners within the same card or surface — the spec uses cut-corner shapes exclusively"
68
+ - "Do not add decorative gradients, glows, or background textures — this is a restrained design"
69
+ - "Do not apply gradient text — it fails contrast checks and undermines the minimal aesthetic"
70
+ - "[web] Do not use box-shadow on every container — reserve elevation for interactive or raised elements"
71
+ interaction:
72
+ - "Do not treat hover and focus as the same state — keyboard users never see hover"
73
+ - "Do not use long-press as the primary action trigger — use explicit buttons or swipe actions"
74
+ - "Do not delay visual feedback on tap — the press_feedback pattern requires instant response"
75
+ - "[web] Do not use :focus for focus rings — use :focus-visible to show rings only for keyboard navigation"
66
76
  accessibility:
67
77
  - "Do not use color as the only differentiator between states — combine with icon, border, or text changes"
68
78
  - "Do not skip focus ring styles — keyboard users must see where focus is at all times"
@@ -72,6 +82,7 @@ generation_guidance:
72
82
  design:
73
83
  personality: "Minimal and calm — orbital metaphor suggests clarity and organization without visual noise"
74
84
  complexity: "restrained"
85
+ quality_tier: "production"
75
86
  audience: "Bilingual individuals who prefer a clean, low-distraction task environment"
76
87
  avoid:
77
88
  - "Do not add decorative illustrations or background patterns"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openuispec",
3
- "version": "0.2.17",
3
+ "version": "0.2.19",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "A semantic UI specification format for AI-native, platform-native app development",
package/prepare/index.ts CHANGED
@@ -180,8 +180,11 @@ export interface PrepareResult {
180
180
  design_context?: {
181
181
  personality?: string;
182
182
  complexity: 'restrained' | 'balanced' | 'elaborate';
183
+ quality_tier: 'mvp' | 'production' | 'flagship';
183
184
  audience?: string;
184
185
  complexity_rule: string;
186
+ quality_tier_rule: string;
187
+ quality_test: string;
185
188
  };
186
189
  next_steps: string[];
187
190
  }
@@ -681,15 +684,63 @@ function complexityRule(complexity: string): string {
681
684
  }
682
685
  }
683
686
 
687
+ function qualityTierRule(tier: string): string {
688
+ switch (tier) {
689
+ case 'mvp':
690
+ return 'Functional-only. Use semantic tokens but tolerate simple layouts. Skip elevation, motion patterns, and adaptive breakpoints.';
691
+ case 'flagship':
692
+ return 'Pixel-perfect. Every token, motion pattern, elevation level, and adaptive breakpoint must be implemented. All contract states required. No shortcuts.';
693
+ default:
694
+ return 'Production-quality. Apply all tokens, handle accessibility, support adaptive breakpoints. Motion and elevation expected but minor shortcuts acceptable.';
695
+ }
696
+ }
697
+
698
+ function buildQualityTest(complexity: string, qualityTier: string, personality?: string): string {
699
+ const items: string[] = [
700
+ 'Inter/Roboto/Arial as the primary font when the spec defines a custom font_family.',
701
+ 'Pure black (#000000) or pure white (#FFFFFF) — all colors must resolve through tokens.',
702
+ 'Cyan-on-dark, purple-to-blue gradient, or neon accent color schemes not in color tokens.',
703
+ 'Card-wrapping every content group — cards are for distinct, comparable items only.',
704
+ 'Identical spacing values throughout — the spec defines a scale with distinct levels.',
705
+ 'Bounce or elastic easing — use only the easing curves from motion tokens.',
706
+ 'Shadows on elements with no elevation token assigned.',
707
+ 'A single font weight everywhere — the type scale defines multiple weights for hierarchy.',
708
+ ];
709
+
710
+ if (complexity === 'restrained') {
711
+ items.push('Any decorative animation, gradient, glassmorphism, or background effect — this is a restrained design.');
712
+ } else if (complexity === 'elaborate') {
713
+ items.push('Missing entrance animations or transition effects — this is an elaborate design that expects rich motion.');
714
+ }
715
+
716
+ if (qualityTier === 'flagship') {
717
+ items.push('Any adaptive breakpoint missing — flagship quality requires all size classes implemented.');
718
+ items.push('Any must_handle state not implemented — flagship requires full contract compliance.');
719
+ }
720
+
721
+ const numbered = items.map((item, i) => `${i + 1}. ${item}`);
722
+ numbered.unshift('After generation, verify the output does NOT exhibit these AI-slop indicators:');
723
+
724
+ if (personality) {
725
+ numbered.push(`Design personality check: "${personality}" — verify the output tone matches.`);
726
+ }
727
+
728
+ return numbered.join('\n');
729
+ }
730
+
684
731
  function buildDesignContext(manifest: Record<string, any>): PrepareResult['design_context'] {
685
732
  const design = manifest.design;
686
733
  if (!design) return undefined;
687
734
  const complexity = (design.complexity as 'restrained' | 'balanced' | 'elaborate') ?? 'balanced';
735
+ const tier = (design.quality_tier as 'mvp' | 'production' | 'flagship') ?? 'production';
688
736
  return {
689
737
  ...(design.personality ? { personality: design.personality } : {}),
690
738
  complexity,
739
+ quality_tier: tier,
691
740
  ...(design.audience ? { audience: design.audience } : {}),
692
741
  complexity_rule: complexityRule(complexity),
742
+ quality_tier_rule: qualityTierRule(tier),
743
+ quality_test: buildQualityTest(complexity, tier, design.personality),
693
744
  };
694
745
  }
695
746
 
@@ -271,6 +271,12 @@
271
271
  "description": "How elaborate animations, effects, and visual details should be"
272
272
  },
273
273
  "audience": { "type": "string", "description": "Who uses this app — informs tone and complexity" },
274
+ "quality_tier": {
275
+ "type": "string",
276
+ "enum": ["mvp", "production", "flagship"],
277
+ "default": "production",
278
+ "description": "Quality bar — mvp: functional-only, production: polished with full tokens, flagship: pixel-perfect with every state and motion pattern"
279
+ },
274
280
  "avoid": {
275
281
  "type": "array",
276
282
  "items": { "type": "string" },
@@ -293,6 +299,8 @@
293
299
  "motion": { "type": "array", "items": { "type": "string" } },
294
300
  "elevation": { "type": "array", "items": { "type": "string" } },
295
301
  "layout": { "type": "array", "items": { "type": "string" } },
302
+ "visual": { "type": "array", "items": { "type": "string" } },
303
+ "interaction": { "type": "array", "items": { "type": "string" } },
296
304
  "accessibility": { "type": "array", "items": { "type": "string" } }
297
305
  },
298
306
  "additionalProperties": false
@@ -92,19 +92,23 @@ generation:
92
92
  ios: { language: swift, framework: swiftui }
93
93
  android: { language: kotlin, framework: compose }
94
94
  web: { language: typescript, framework: react }
95
- # shared: # optional: cross-platform shared code layers
96
- # mobile_common:
97
- # platforms: [ios, android]
98
- # language: kotlin
99
- # root: "../shared"
100
- # scope: "Business logic, data models, repositories, view models. No UI."
101
- # paths:
102
- # domain: "commonMain/domain/"
103
- # structure: # optional: per-target directory structure (overrides heuristics)
104
- # ios:
105
- # root: "../shared"
106
- # scope: "Pure SwiftUI views and navigation."
107
- # paths: { ui: "iosApp/ui/" }
95
+
96
+ generation_guidance: # Section 16.2
97
+ universal_anti_patterns:
98
+ typography:
99
+ - "Do not fall back to Inter, Roboto, Arial, or system defaults"
100
+ color:
101
+ - "Do not use pure black (#000000) or pure white (#FFFFFF)"
102
+ # ... additional domains: spacing, motion, elevation, layout, visual, interaction, accessibility
103
+ audit_threshold: 70
104
+
105
+ design: # Section 16.1
106
+ personality: "Clean, focused..."
107
+ complexity: "balanced" # restrained | balanced | elaborate
108
+ quality_tier: "production" # mvp | production | flagship
109
+ audience: "..."
110
+ avoid:
111
+ - "Do not use decorative gradients"
108
112
  ```
109
113
 
110
114
  ---
@@ -4098,6 +4102,148 @@ Each `.yaml` file in the components directory defines one component. The file mu
4098
4102
 
4099
4103
  ---
4100
4104
 
4105
+ ## 16. Design intent and generation guidance
4106
+
4107
+ This section defines how the manifest communicates design intent, anti-patterns, and quality expectations to AI generators.
4108
+
4109
+ ### 16.1 Design section
4110
+
4111
+ The `design` section in `openuispec.yaml` captures the project's visual identity and quality bar:
4112
+
4113
+ ```yaml
4114
+ design:
4115
+ personality: "Minimal and calm — clarity over decoration"
4116
+ complexity: "restrained" # restrained | balanced | elaborate
4117
+ quality_tier: "production" # mvp | production | flagship
4118
+ audience: "Professionals who prefer low-distraction tools"
4119
+ avoid:
4120
+ - "Do not add decorative illustrations or background patterns"
4121
+ - "[web] Do not use CSS animations for non-interactive purposes"
4122
+ ```
4123
+
4124
+ | Field | Type | Default | Description |
4125
+ |-------|------|---------|-------------|
4126
+ | `personality` | string | — | Brief description of the brand's visual personality |
4127
+ | `complexity` | enum | `balanced` | How elaborate animations, effects, and visual details should be |
4128
+ | `quality_tier` | enum | `production` | Quality bar for this project |
4129
+ | `audience` | string | — | Who uses this app — informs tone and complexity |
4130
+ | `avoid` | string[] | — | Project-specific anti-patterns. May use `[web]`/`[ios]`/`[android]` scope tags |
4131
+
4132
+ **Complexity levels:**
4133
+
4134
+ | Level | Meaning |
4135
+ |-------|---------|
4136
+ | `restrained` | Minimal motion (required state transitions only). No decorative shadows. Clean whitespace. No background effects. |
4137
+ | `balanced` | Apply all motion patterns. Use elevation tokens fully. Standard state animations. |
4138
+ | `elaborate` | Rich animations with staggered reveals. Creative elevation. Platform-specific flourishes. |
4139
+
4140
+ **Quality tiers:**
4141
+
4142
+ | Tier | Meaning |
4143
+ |------|---------|
4144
+ | `mvp` | Functional-only. Use semantic tokens but tolerate simple layouts. Skip elevation, motion patterns, and adaptive breakpoints. |
4145
+ | `production` | Production-quality. Apply all tokens, handle accessibility, support adaptive breakpoints. Motion and elevation expected. |
4146
+ | `flagship` | Pixel-perfect. Every token, motion pattern, elevation level, and adaptive breakpoint must be implemented. All contract states required. No shortcuts. |
4147
+
4148
+ ### 16.2 Generation guidance
4149
+
4150
+ The `generation_guidance` section in `openuispec.yaml` provides cross-contract anti-patterns and quality thresholds:
4151
+
4152
+ ```yaml
4153
+ generation_guidance:
4154
+ universal_anti_patterns:
4155
+ typography:
4156
+ - "Do not fall back to Inter, Roboto, Arial, or system defaults when the spec defines a custom font_family"
4157
+ color:
4158
+ - "Do not use pure black (#000000) or pure white (#FFFFFF)"
4159
+ visual:
4160
+ - "Do not apply gradient text — it fails contrast checks"
4161
+ interaction:
4162
+ - "Do not treat hover and focus as the same state"
4163
+ # ... additional domains
4164
+ audit_threshold: 70
4165
+ ```
4166
+
4167
+ **Anti-pattern domains:**
4168
+
4169
+ | Domain | Scope |
4170
+ |--------|-------|
4171
+ | `typography` | Font choices, weights, scale usage |
4172
+ | `color` | Palettes, contrast, pure values |
4173
+ | `spacing` | Scale adherence, alias usage |
4174
+ | `motion` | Easing, duration, reduced-motion |
4175
+ | `elevation` | Shadow usage, depth hierarchy |
4176
+ | `layout` | Size classes, breakpoints, card usage |
4177
+ | `visual` | Decoration, gradients, glassmorphism |
4178
+ | `interaction` | State handling, focus vs hover, gestures |
4179
+ | `accessibility` | Color-only differentiation, focus rings, tab order |
4180
+
4181
+ Anti-patterns are scoped with platform tags (`[web]`, `[ios]`, `[android]`). The `prepare` command filters them to the target platform before delivery to the generator.
4182
+
4183
+ Anti-patterns exist at three levels:
4184
+ 1. **Universal** — `generation_guidance.universal_anti_patterns` in the manifest (cross-contract)
4185
+ 2. **Contract-specific** — `generation.must_avoid` in each contract file
4186
+ 3. **Project-specific** — `design.avoid` in the manifest
4187
+
4188
+ ### 16.3 Design quality audit
4189
+
4190
+ The `check --audit` command scores the spec against design quality heuristics.
4191
+
4192
+ **Score formula:** `max(0, 100 - errors × 10 - warnings × 3)`
4193
+
4194
+ **Checks performed:**
4195
+
4196
+ | Domain | Rule | Severity | What it catches |
4197
+ |--------|------|----------|----------------|
4198
+ | tokens | `missing_file` | error | Required token file not found |
4199
+ | typography | `font_diversity` | error | Primary font is an AI default (Inter, Roboto, Arial, Open Sans) |
4200
+ | typography | `scale_usage` | warning | Fewer than 4 type scale levels |
4201
+ | typography | `weight_hierarchy` | warning | Single font weight across the entire type scale |
4202
+ | color | `pure_black` | error | Literal #000000 in token values |
4203
+ | color | `pure_white` | error | Literal #FFFFFF in token values |
4204
+ | color | `semantic_completeness` | warning | Missing success, warning, danger, or info semantic color |
4205
+ | color | `theme_coverage` | warning | Missing light or dark theme |
4206
+ | spacing | `scale_usage` | warning | Fewer than 4 spacing scale values |
4207
+ | spacing | `alias_page_margin` | warning | No page_margin alias defined |
4208
+ | spacing | `alias_card_padding` | warning | No card_padding alias defined |
4209
+ | motion | `duration_variety` | warning | Single duration value for all animations |
4210
+ | motion | `reduced_motion` | error | No reduced_motion policy |
4211
+ | motion | `easing_quality` | warning | Missing enter/exit curves or no cubic-bezier easing |
4212
+ | elevation | `level_count` | warning | Fewer than 2 non-none elevation levels |
4213
+ | elevation | `progression` | warning | Elevation levels not monotonically increasing |
4214
+ | layout | `size_class_coverage` | warning | Fewer than 2 size classes or no compact class |
4215
+ | contracts | `collection_empty_state` | warning | Collection missing empty_state in must_handle |
4216
+ | contracts | `state_coverage` | warning | Contract with empty must_handle |
4217
+
4218
+ The `audit_threshold` in `generation_guidance` sets the project-wide minimum score. The `--min-score` CLI flag overrides it per-run.
4219
+
4220
+ ### 16.4 Prepare output
4221
+
4222
+ The `prepare` command includes `design_context` and `anti_patterns` in its output for AI generators:
4223
+
4224
+ ```json
4225
+ {
4226
+ "design_context": {
4227
+ "personality": "Minimal and calm...",
4228
+ "complexity": "restrained",
4229
+ "quality_tier": "production",
4230
+ "audience": "...",
4231
+ "complexity_rule": "Minimal motion (required state transitions only)...",
4232
+ "quality_tier_rule": "Production-quality. Apply all tokens...",
4233
+ "quality_test": "After generation, verify the output does NOT exhibit these AI-slop indicators:\n1. Inter/Roboto/Arial as the primary font..."
4234
+ },
4235
+ "anti_patterns": {
4236
+ "universal": { "typography": ["..."], "color": ["..."] },
4237
+ "contract_specific": { "action_trigger": ["..."] },
4238
+ "project_specific": ["..."]
4239
+ }
4240
+ }
4241
+ ```
4242
+
4243
+ The `quality_test` field is an auto-generated checklist tuned to the project's complexity and quality tier. AI generators should use it as a post-generation self-review step.
4244
+
4245
+ ---
4246
+
4101
4247
  ## Appendix A: Type reference
4102
4248
 
4103
4249
  | Type | Description | Example |
Binary file
Binary file
Binary file
Binary file