openuispec 0.2.15 → 0.2.17

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.
Files changed (50) hide show
  1. package/README.md +4 -2
  2. package/check/audit.ts +291 -0
  3. package/check/index.ts +19 -3
  4. package/docs/cli.md +29 -3
  5. package/docs/file-formats.md +83 -0
  6. package/docs/implementation-notes.md +8 -0
  7. package/examples/social-app/openuispec/contracts/action_trigger.yaml +8 -0
  8. package/examples/social-app/openuispec/contracts/collection.yaml +8 -0
  9. package/examples/social-app/openuispec/contracts/data_display.yaml +8 -0
  10. package/examples/social-app/openuispec/contracts/feedback.yaml +8 -0
  11. package/examples/social-app/openuispec/contracts/input_field.yaml +8 -0
  12. package/examples/social-app/openuispec/contracts/nav_container.yaml +9 -0
  13. package/examples/social-app/openuispec/contracts/surface.yaml +8 -0
  14. package/examples/social-app/openuispec/openuispec.yaml +40 -0
  15. package/examples/social-app/openuispec/tokens/color.yaml +4 -0
  16. package/examples/social-app/openuispec/tokens/motion.yaml +4 -0
  17. package/examples/social-app/openuispec/tokens/typography.yaml +11 -0
  18. package/examples/taskflow/openuispec/contracts/action_trigger.yaml +9 -1
  19. package/examples/taskflow/openuispec/contracts/collection.yaml +9 -1
  20. package/examples/taskflow/openuispec/contracts/data_display.yaml +9 -1
  21. package/examples/taskflow/openuispec/contracts/feedback.yaml +9 -1
  22. package/examples/taskflow/openuispec/contracts/input_field.yaml +8 -0
  23. package/examples/taskflow/openuispec/contracts/nav_container.yaml +10 -1
  24. package/examples/taskflow/openuispec/contracts/surface.yaml +9 -1
  25. package/examples/taskflow/openuispec/openuispec.yaml +40 -0
  26. package/examples/taskflow/openuispec/tokens/color.yaml +4 -0
  27. package/examples/taskflow/openuispec/tokens/motion.yaml +4 -0
  28. package/examples/taskflow/openuispec/tokens/typography.yaml +11 -0
  29. package/examples/todo-orbit/openuispec/contracts/action_trigger.yaml +7 -0
  30. package/examples/todo-orbit/openuispec/contracts/collection.yaml +7 -0
  31. package/examples/todo-orbit/openuispec/contracts/data_display.yaml +7 -0
  32. package/examples/todo-orbit/openuispec/contracts/feedback.yaml +7 -0
  33. package/examples/todo-orbit/openuispec/contracts/input_field.yaml +7 -0
  34. package/examples/todo-orbit/openuispec/contracts/nav_container.yaml +8 -0
  35. package/examples/todo-orbit/openuispec/contracts/surface.yaml +7 -0
  36. package/examples/todo-orbit/openuispec/openuispec.yaml +40 -0
  37. package/examples/todo-orbit/openuispec/tokens/color.yaml +4 -0
  38. package/examples/todo-orbit/openuispec/tokens/motion.yaml +4 -0
  39. package/examples/todo-orbit/openuispec/tokens/typography.yaml +11 -0
  40. package/mcp-server/index.ts +15 -3
  41. package/package.json +1 -1
  42. package/prepare/index.ts +102 -0
  43. package/schema/component.schema.json +5 -0
  44. package/schema/contract.schema.json +11 -1
  45. package/schema/custom-contract.schema.json +5 -0
  46. package/schema/openuispec.schema.json +47 -0
  47. package/schema/tokens/color.schema.json +5 -0
  48. package/schema/tokens/motion.schema.json +5 -0
  49. package/schema/tokens/typography.schema.json +10 -0
  50. package/schema/validate.ts +21 -22
@@ -32,6 +32,46 @@ generation:
32
32
  android: { language: kotlin, framework: compose, min_sdk: 26 }
33
33
  web: { language: typescript, framework: react, bundler: vite }
34
34
 
35
+ generation_guidance:
36
+ universal_anti_patterns:
37
+ typography:
38
+ - "Do not fall back to Inter, Roboto, Arial, or system defaults when the spec defines a custom font_family"
39
+ - "Do not use a single font weight throughout — the typography scale defines different weights per level"
40
+ - "Do not use monospace fonts as shorthand for 'technical' aesthetic unless the spec explicitly calls for it"
41
+ color:
42
+ - "Do not use pure black (#000000) or pure white (#FFFFFF) — always resolve through the token layer"
43
+ - "Do not use the AI-default palette: cyan-on-dark, purple-to-blue gradients, neon accents"
44
+ - "Do not use gray text on colored backgrounds — derive text color from the on_color token or a darkened shade"
45
+ - "Do not apply the same opacity to all border tokens — border.default and border.emphasis have distinct opacity values"
46
+ spacing:
47
+ - "Do not use the same spacing value everywhere — the spec defines xxs through xxxl for a reason"
48
+ - "Do not ignore the page_margin and card_padding aliases — they exist to enforce consistent spatial rhythm"
49
+ - "Do not add spacing that the spec doesn't define — if a gap isn't in the token scale, it shouldn't exist in output"
50
+ motion:
51
+ - "Do not use bounce or elastic easing — these feel dated and the spec defines specific easing curves"
52
+ - "Do not apply the same duration to all animations — instant, quick, normal, and slow serve different purposes"
53
+ - "Do not ignore reduced_motion — when the user prefers reduced motion, animations must be removed entirely"
54
+ elevation:
55
+ - "Do not add shadows to elements that don't specify an elevation token"
56
+ - "Do not use the same shadow depth for cards and modals — cards use elevation.md, modals use elevation.lg"
57
+ layout:
58
+ - "Do not assume large-screen layouts work on compact — every multi-pane pattern needs an explicit compact fallback"
59
+ - "Do not use pixel breakpoints — reference size classes by name (compact, regular, expanded)"
60
+ accessibility:
61
+ - "Do not use color as the only differentiator between states — combine with icon, border, or text changes"
62
+ - "Do not skip focus ring styles — keyboard users must see where focus is at all times"
63
+ - "Do not set tab order manually unless the contract explicitly requires it — use document order"
64
+ audit_threshold: 70
65
+
66
+ design:
67
+ personality: "Vibrant, social, content-rich — engagement and discovery are primary, utility is secondary"
68
+ complexity: "elaborate"
69
+ audience: "Mobile-first social media users who expect rich media, smooth animations, and expressive interactions"
70
+ avoid:
71
+ - "Do not use muted or desaturated brand colors — the palette should feel energetic"
72
+ - "[ios] Do not skip spring animations on content reveal — they are expected in this context"
73
+ - "[android] Do not use non-Material motion patterns — stick to the platform's shared element transitions"
74
+
35
75
  data_model:
36
76
  user:
37
77
  id: { type: string, required: true }
@@ -10,6 +10,10 @@ color:
10
10
  on_color:
11
11
  reference: "#FFFFFF"
12
12
  contrast_min: 7.0
13
+ generation_notes:
14
+ - "This is the most important brand color — use consistently for primary actions, active states, and key interactive elements"
15
+ - "Do not dilute by applying it to backgrounds, borders, and text simultaneously — pick one dominant usage per surface"
16
+ - "The on_color token exists specifically for text/icons placed on this background — do not calculate contrast manually"
13
17
  accent:
14
18
  semantic: "Accent highlights — muted indigo"
15
19
  reference: "#5B52A3"
@@ -18,6 +18,10 @@ motion:
18
18
  duration: "instant"
19
19
  easing: "default"
20
20
  property: "transform"
21
+ generation_notes:
22
+ - "The scale value is subtle by design — do not exaggerate to 0.9 or lower"
23
+ - "[ios] Complement with haptic feedback (.impact(style: .light))"
24
+ - "[android] Use ripple effect instead of or in addition to scale"
21
25
  slide_up:
22
26
  duration: "normal"
23
27
  easing: "enter"
@@ -6,6 +6,11 @@ typography:
6
6
  platform:
7
7
  ios: { system_alternative: "SF Pro" }
8
8
  android: { system_alternative: "Google Sans" }
9
+ generation_notes:
10
+ - "If the primary font is unavailable on a platform, choose a geometric sans with similar x-height — never fall back to Inter or Roboto"
11
+ - "[web] Use font-display: swap and a size-adjust fallback to prevent FOUT (Flash of Unstyled Text)"
12
+ - "[ios] Register the font in Info.plist and verify it loads before first render"
13
+ - "[android] Place the font in res/font/ and reference via XML font family — do not load programmatically"
9
14
 
10
15
  scale:
11
16
  display:
@@ -33,6 +38,9 @@ typography:
33
38
  size: { base: 16, range: [14, 16] }
34
39
  weight: 400
35
40
  line_height: 1.5
41
+ generation_notes:
42
+ - "This is the most-used text style — ensure line_height is comfortable for reading (1.5 default)"
43
+ - "On compact screens: the lower end of the size range is acceptable — do not go below 14pt/px"
36
44
  body_sm:
37
45
  semantic: "Secondary body text"
38
46
  size: { base: 14, range: [13, 15] }
@@ -43,6 +51,9 @@ typography:
43
51
  size: { base: 12, range: [11, 13] }
44
52
  weight: 400
45
53
  line_height: 1.4
54
+ generation_notes:
55
+ - "Caption text must still meet the contrast_min requirement from the text token it pairs with"
56
+ - "Do not use caption size for primary content — it is for metadata, timestamps, and labels only"
46
57
  button:
47
58
  semantic: "Button labels"
48
59
  size: { base: 16, range: [14, 16] }
@@ -1,4 +1,12 @@
1
1
  # action_trigger contract extension
2
2
  # Base definition: spec Section 4.1
3
3
 
4
- action_trigger: {}
4
+ action_trigger:
5
+ generation:
6
+ must_avoid:
7
+ - "Do not apply gradient backgrounds to buttons — use flat token-defined colors"
8
+ - "Do not make primary and secondary variants visually identical — they must be immediately distinguishable by background, border, or text color"
9
+ - "Do not use bounce or elastic easing on press feedback — use the motion.easing tokens"
10
+ - "Do not add drop shadows to every button variant — only elevated contexts use elevation tokens"
11
+ - "Do not use pure black (#000) or pure white (#fff) — always resolve through color.text and color.surface tokens"
12
+ - "Do not set identical min_height across all size variants — sm, md, lg must feel proportionally different"
@@ -1,4 +1,12 @@
1
1
  # collection contract extension
2
2
  # Base definition: spec Section 4.7
3
3
 
4
- collection: {}
4
+ collection:
5
+ generation:
6
+ must_avoid:
7
+ - "Do not omit empty states — every collection must render the empty_state component when data is empty"
8
+ - "Do not use infinite scroll without a visible loading indicator at the bottom"
9
+ - "Do not render loading skeletons that mismatch the actual item layout — skeleton shapes must reflect the item_contract variant"
10
+ - "Do not use identical item spacing for list and grid variants — grid uses gap, list uses separator"
11
+ - "Do not render table headers with the same visual weight as table rows — headers use header_background and header_weight tokens"
12
+ - "[web] Do not skip keyboard navigation support — lists need ArrowUp/ArrowDown, grids need 2D arrow navigation"
@@ -1,4 +1,12 @@
1
1
  # data_display contract extension
2
2
  # Base definition: spec Section 4.2
3
3
 
4
- data_display: {}
4
+ data_display:
5
+ generation:
6
+ must_avoid:
7
+ - "Do not nest cards inside cards — use flat hierarchy with spacing and subtle background shifts"
8
+ - "Do not give every card variant the same border-radius and shadow — vary by emphasis level"
9
+ - "Do not use gray text on colored backgrounds — use a darker shade of the background color or the on_color token"
10
+ - "Do not make all stat cards identical size with centered text — vary layout to match the data shape"
11
+ - "Do not use the same skeleton shape for all variants — skeleton must match the specific variant layout (card vs compact vs hero)"
12
+ - "Do not omit the highlighted state visual differentiation — unread/new items must be visually distinct from default items"
@@ -1,4 +1,12 @@
1
1
  # feedback contract extension
2
2
  # Base definition: spec Section 4.5
3
3
 
4
- feedback: {}
4
+ feedback:
5
+ generation:
6
+ must_avoid:
7
+ - "Do not use the same visual treatment for all severity levels — info, success, warning, and error must be immediately distinguishable by color, icon, and border"
8
+ - "Do not stack multiple toasts without spacing or queue management — overlapping toasts are a usability failure"
9
+ - "Do not use alert dialogs for non-blocking informational feedback — use toasts or banners per the variant definition"
10
+ - "Do not skip enter/exit animations — feedback appearing or disappearing instantly feels broken"
11
+ - "Do not use the same position for toasts and banners — toasts float, banners are inline"
12
+ - "Do not auto-dismiss error-severity feedback with a short timer — errors need manual dismissal or a longer duration"
@@ -3,6 +3,14 @@
3
3
  # Add project-specific variants, token overrides, and generation hints.
4
4
 
5
5
  input_field:
6
+ generation:
7
+ must_avoid:
8
+ - "Do not use placeholder text as the only label — always render a persistent visible label"
9
+ - "Do not use a single generic gray border for all states — focused, error, and disabled must each have distinct visual treatment"
10
+ - "Do not hardcode red for error states — use color.semantic.danger which may differ from pure red per the token definition"
11
+ - "Do not skip the label animation between resting and active positions — this is a must_handle requirement"
12
+ - "Do not use the same visual weight for required and optional fields — required fields need a visible indicator"
13
+ - "Do not render select fields identically across platforms — respect the render_hint and platform_mapping"
6
14
  variants:
7
15
  cut_corner:
8
16
  semantic: "Angled corner input for branded forms"
@@ -1,4 +1,13 @@
1
1
  # nav_container contract extension
2
2
  # Base definition: spec Section 4.4
3
3
 
4
- nav_container: {}
4
+ nav_container:
5
+ generation:
6
+ must_avoid:
7
+ - "Do not use the same icon treatment for active and inactive items — differentiate with fill/outline variants, color, or both"
8
+ - "Do not center-align sidebar items — use leading alignment with consistent padding per the item_padding_h token"
9
+ - "Do not ignore the collapsed state for sidebar and rail variants — collapsed mode must show only icons with proper spacing"
10
+ - "Do not use a generic hamburger menu icon when the spec defines a specific drawer trigger"
11
+ - "Do not render tab_bar and sidebar with the same visual weight — tab_bar is compact and subordinate, sidebar is prominent"
12
+ - "[ios] Do not forget safe area insets on tab_bar — iOS bottom inset must be respected"
13
+ - "[android] Do not ignore gesture navigation bar insets on tab_bar and bottom navigation"
@@ -1,4 +1,12 @@
1
1
  # surface contract extension
2
2
  # Base definition: spec Section 4.6
3
3
 
4
- surface: {}
4
+ surface:
5
+ generation:
6
+ must_avoid:
7
+ - "Do not use the same overlay opacity for modal, sheet, and popover — each has a distinct tokens definition"
8
+ - "Do not omit the drag indicator on sheet variants — it signals that the surface is interactive and dismissible"
9
+ - "Do not use glassmorphism or frosted-glass effects unless the project's design personality explicitly calls for it"
10
+ - "Do not skip focus trapping on modal and sheet variants — keyboard users must not be able to tab behind the surface"
11
+ - "Do not render popover without an arrow/caret pointing to its trigger element"
12
+ - "Do not present fullscreen surfaces without a clear close affordance"
@@ -39,6 +39,46 @@ generation:
39
39
  android: { language: kotlin, framework: compose, min_sdk: 26 }
40
40
  web: { language: typescript, framework: react, bundler: vite }
41
41
 
42
+ generation_guidance:
43
+ universal_anti_patterns:
44
+ typography:
45
+ - "Do not fall back to Inter, Roboto, Arial, or system defaults when the spec defines a custom font_family"
46
+ - "Do not use a single font weight throughout — the typography scale defines different weights per level"
47
+ - "Do not use monospace fonts as shorthand for 'technical' aesthetic unless the spec explicitly calls for it"
48
+ color:
49
+ - "Do not use pure black (#000000) or pure white (#FFFFFF) — always resolve through the token layer"
50
+ - "Do not use the AI-default palette: cyan-on-dark, purple-to-blue gradients, neon accents"
51
+ - "Do not use gray text on colored backgrounds — derive text color from the on_color token or a darkened shade"
52
+ - "Do not apply the same opacity to all border tokens — border.default and border.emphasis have distinct opacity values"
53
+ spacing:
54
+ - "Do not use the same spacing value everywhere — the spec defines xxs through xxxl for a reason"
55
+ - "Do not ignore the page_margin and card_padding aliases — they exist to enforce consistent spatial rhythm"
56
+ - "Do not add spacing that the spec doesn't define — if a gap isn't in the token scale, it shouldn't exist in output"
57
+ motion:
58
+ - "Do not use bounce or elastic easing — these feel dated and the spec defines specific easing curves"
59
+ - "Do not apply the same duration to all animations — instant, quick, normal, and slow serve different purposes"
60
+ - "Do not ignore reduced_motion — when the user prefers reduced motion, animations must be removed entirely"
61
+ elevation:
62
+ - "Do not add shadows to elements that don't specify an elevation token"
63
+ - "Do not use the same shadow depth for cards and modals — cards use elevation.md, modals use elevation.lg"
64
+ layout:
65
+ - "Do not assume large-screen layouts work on compact — every multi-pane pattern needs an explicit compact fallback"
66
+ - "Do not use pixel breakpoints — reference size classes by name (compact, regular, expanded)"
67
+ accessibility:
68
+ - "Do not use color as the only differentiator between states — combine with icon, border, or text changes"
69
+ - "Do not skip focus ring styles — keyboard users must see where focus is at all times"
70
+ - "Do not set tab order manually unless the contract explicitly requires it — use document order"
71
+ audit_threshold: 70
72
+
73
+ design:
74
+ personality: "Clean, focused, productivity-first — no decorative flourishes. Color is used for priority and status clarity, not aesthetics."
75
+ complexity: "balanced"
76
+ audience: "Individual contributors and small teams managing personal and shared task lists"
77
+ avoid:
78
+ - "Do not use playful or rounded UI patterns — this is a professional productivity tool"
79
+ - "Do not use color gradients — flat, token-driven color only"
80
+ - "[web] Do not use CSS animations for non-interactive decorative purposes"
81
+
42
82
  # ============================================================
43
83
  # Custom formatters & mappers (see spec Section 10.5)
44
84
  # ============================================================
@@ -13,6 +13,10 @@ color:
13
13
  on_color:
14
14
  reference: "#FFFFFF"
15
15
  contrast_min: 4.5
16
+ generation_notes:
17
+ - "This is the most important brand color — use consistently for primary actions, active states, and key interactive elements"
18
+ - "Do not dilute by applying it to backgrounds, borders, and text simultaneously — pick one dominant usage per surface"
19
+ - "The on_color token exists specifically for text/icons placed on this background — do not calculate contrast manually"
16
20
 
17
21
  secondary:
18
22
  semantic: "Accent for highlights, badges, urgency"
@@ -20,6 +20,10 @@ motion:
20
20
  duration: "instant"
21
21
  property: "scale"
22
22
  value: 0.97
23
+ generation_notes:
24
+ - "The scale value is subtle by design — do not exaggerate to 0.9 or lower"
25
+ - "[ios] Complement with haptic feedback (.impact(style: .light))"
26
+ - "[android] Use ripple effect instead of or in addition to scale"
23
27
  state_change:
24
28
  duration: "quick"
25
29
  property: "opacity, background"
@@ -10,6 +10,11 @@ typography:
10
10
  ios: { system_alternative: "SF Pro" }
11
11
  android: { system_alternative: "Google Sans" }
12
12
  web: { load_strategy: "swap", source: "google_fonts" }
13
+ generation_notes:
14
+ - "If the primary font is unavailable on a platform, choose a geometric sans with similar x-height — never fall back to Inter or Roboto"
15
+ - "[web] Use font-display: swap and a size-adjust fallback to prevent FOUT (Flash of Unstyled Text)"
16
+ - "[ios] Register the font in Info.plist and verify it loads before first render"
17
+ - "[android] Place the font in res/font/ and reference via XML font family — do not load programmatically"
13
18
 
14
19
  scale:
15
20
  display:
@@ -40,6 +45,9 @@ typography:
40
45
  size: { base: 16, range: [14, 16] }
41
46
  weight: 400
42
47
  line_height: 1.5
48
+ generation_notes:
49
+ - "This is the most-used text style — ensure line_height is comfortable for reading (1.5 default)"
50
+ - "On compact screens: the lower end of the size range is acceptable — do not go below 14pt/px"
43
51
  body_sm:
44
52
  semantic: "Secondary text, descriptions"
45
53
  size: { base: 14, range: [13, 15] }
@@ -52,6 +60,9 @@ typography:
52
60
  weight: 400
53
61
  tracking: 0.02
54
62
  line_height: 1.35
63
+ generation_notes:
64
+ - "Caption text must still meet the contrast_min requirement from the text token it pairs with"
65
+ - "Do not use caption size for primary content — it is for metadata, timestamps, and labels only"
55
66
  overline:
56
67
  semantic: "Section tags, category labels"
57
68
  size: { base: 11, range: [10, 12] }
@@ -31,6 +31,13 @@ action_trigger:
31
31
  primary: { style: "clip-path" }
32
32
  ghost: { style: "clip-path" }
33
33
  generation:
34
+ must_avoid:
35
+ - "Do not apply gradient backgrounds to buttons — use flat token-defined colors"
36
+ - "Do not make primary and secondary variants visually identical — they must be immediately distinguishable by background, border, or text color"
37
+ - "Do not use bounce or elastic easing on press feedback — use the motion.easing tokens"
38
+ - "Do not add drop shadows to every button variant — only elevated contexts use elevation tokens"
39
+ - "Do not use pure black (#000) or pure white (#fff) — always resolve through color.text and color.surface tokens"
40
+ - "Do not set identical min_height across all size variants — sm, md, lg must feel proportionally different"
34
41
  must_handle:
35
42
  - "Cut top-left and bottom-right corners by cut_size for primary and ghost"
36
43
  - "Preserve button hit area and loading state inside the cut shape"
@@ -23,6 +23,13 @@ collection:
23
23
  list: { element: "ul", class: "todo-list" }
24
24
  chips: { element: "div", class: "chip-row" }
25
25
  generation:
26
+ must_avoid:
27
+ - "Do not omit empty states — every collection must render the empty_state component when data is empty"
28
+ - "Do not use infinite scroll without a visible loading indicator at the bottom"
29
+ - "Do not render loading skeletons that mismatch the actual item layout — skeleton shapes must reflect the item_contract variant"
30
+ - "Do not use identical item spacing for list and grid variants — grid uses gap, list uses separator"
31
+ - "Do not render table headers with the same visual weight as table rows — headers use header_background and header_weight tokens"
32
+ - "[web] Do not skip keyboard navigation support — lists need ArrowUp/ArrowDown, grids need 2D arrow navigation"
26
33
  must_handle:
27
34
  - "Preserve empty_state and pull_to_refresh behavior for task lists"
28
35
  - "Render chips as single-select filters bound to state.active_filter"
@@ -29,6 +29,13 @@ data_display:
29
29
  compact: { element: "li", class: "todo-row" }
30
30
  inline: { element: "div", class: "section-heading" }
31
31
  generation:
32
+ must_avoid:
33
+ - "Do not nest cards inside cards — use flat hierarchy with spacing and subtle background shifts"
34
+ - "Do not give every card variant the same border-radius and shadow — vary by emphasis level"
35
+ - "Do not use gray text on colored backgrounds — use a darker shade of the background color or the on_color token"
36
+ - "Do not make all stat cards identical size with centered text — vary layout to match the data shape"
37
+ - "Do not use the same skeleton shape for all variants — skeleton must match the specific variant layout (card vs compact vs hero)"
38
+ - "Do not omit the highlighted state visual differentiation — unread/new items must be visually distinct from default items"
32
39
  must_handle:
33
40
  - "Render compact task rows with clear title/subtitle hierarchy"
34
41
  - "Support inline title/subtitle blocks for section headers"
@@ -23,6 +23,13 @@ feedback:
23
23
  toast: { pattern: "toast container", position: "top-right" }
24
24
  banner: { element: "div", role: "alert", position: "inline-top" }
25
25
  generation:
26
+ must_avoid:
27
+ - "Do not use the same visual treatment for all severity levels — info, success, warning, and error must be immediately distinguishable by color, icon, and border"
28
+ - "Do not stack multiple toasts without spacing or queue management — overlapping toasts are a usability failure"
29
+ - "Do not use alert dialogs for non-blocking informational feedback — use toasts or banners per the variant definition"
30
+ - "Do not skip enter/exit animations — feedback appearing or disappearing instantly feels broken"
31
+ - "Do not use the same position for toasts and banners — toasts float, banners are inline"
32
+ - "Do not auto-dismiss error-severity feedback with a short timer — errors need manual dismissal or a longer duration"
26
33
  must_handle:
27
34
  - "Map success and error feedback to distinct visuals and live-region behavior"
28
35
  - "Auto-dismiss toast feedback after the declared duration"
@@ -45,6 +45,13 @@ input_field:
45
45
  date: { style: "clip-path" }
46
46
  number: { style: "clip-path" }
47
47
  generation:
48
+ must_avoid:
49
+ - "Do not use placeholder text as the only label — always render a persistent visible label"
50
+ - "Do not use a single generic gray border for all states — focused, error, and disabled must each have distinct visual treatment"
51
+ - "Do not hardcode red for error states — use color.semantic.danger which may differ from pure red per the token definition"
52
+ - "Do not skip the label animation between resting and active positions — this is a must_handle requirement"
53
+ - "Do not use the same visual weight for required and optional fields — required fields need a visible indicator"
54
+ - "Do not render select fields identically across platforms — respect the render_hint and platform_mapping"
48
55
  must_handle:
49
56
  - "Cut top-left and bottom-right corners by cut_size for text-like inputs"
50
57
  - "Keep focus, error, and disabled borders aligned with the cut shape"
@@ -58,6 +58,14 @@ nav_container:
58
58
  rail: { element: "aside", pattern: "icon rail" }
59
59
  sidebar: { element: "aside", pattern: "collapsible sidebar" }
60
60
  generation:
61
+ must_avoid:
62
+ - "Do not use the same icon treatment for active and inactive items — differentiate with fill/outline variants, color, or both"
63
+ - "Do not center-align sidebar items — use leading alignment with consistent padding per the item_padding_h token"
64
+ - "Do not ignore the collapsed state for sidebar and rail variants — collapsed mode must show only icons with proper spacing"
65
+ - "Do not use a generic hamburger menu icon when the spec defines a specific drawer trigger"
66
+ - "Do not render tab_bar and sidebar with the same visual weight — tab_bar is compact and subordinate, sidebar is prominent"
67
+ - "[ios] Do not forget safe area insets on tab_bar — iOS bottom inset must be respected"
68
+ - "[android] Do not ignore gesture navigation bar insets on tab_bar and bottom navigation"
61
69
  must_handle:
62
70
  - "Switch between tab_bar, rail, and sidebar according to adaptive screen config"
63
71
  - "Keep the selected item visually distinct with active icon/text treatment"
@@ -21,6 +21,13 @@ surface:
21
21
  sheet: { style: "clip-path" }
22
22
  modal: { style: "clip-path" }
23
23
  generation:
24
+ must_avoid:
25
+ - "Do not use the same overlay opacity for modal, sheet, and popover — each has a distinct tokens definition"
26
+ - "Do not omit the drag indicator on sheet variants — it signals that the surface is interactive and dismissible"
27
+ - "Do not use glassmorphism or frosted-glass effects unless the project's design personality explicitly calls for it"
28
+ - "Do not skip focus trapping on modal and sheet variants — keyboard users must not be able to tab behind the surface"
29
+ - "Do not render popover without an arrow/caret pointing to its trigger element"
30
+ - "Do not present fullscreen surfaces without a clear close affordance"
24
31
  must_handle:
25
32
  - "Apply the cut-corner silhouette to sheet and modal surfaces"
26
33
  - "Maintain shadows and overlays without square corners bleeding through"
@@ -38,6 +38,46 @@ generation:
38
38
  android: { language: kotlin, framework: compose, min_sdk: 26 }
39
39
  web: { language: typescript, framework: react, bundler: vite }
40
40
 
41
+ generation_guidance:
42
+ universal_anti_patterns:
43
+ typography:
44
+ - "Do not fall back to Inter, Roboto, Arial, or system defaults when the spec defines a custom font_family"
45
+ - "Do not use a single font weight throughout — the typography scale defines different weights per level"
46
+ - "Do not use monospace fonts as shorthand for 'technical' aesthetic unless the spec explicitly calls for it"
47
+ color:
48
+ - "Do not use pure black (#000000) or pure white (#FFFFFF) — always resolve through the token layer"
49
+ - "Do not use the AI-default palette: cyan-on-dark, purple-to-blue gradients, neon accents"
50
+ - "Do not use gray text on colored backgrounds — derive text color from the on_color token or a darkened shade"
51
+ - "Do not apply the same opacity to all border tokens — border.default and border.emphasis have distinct opacity values"
52
+ spacing:
53
+ - "Do not use the same spacing value everywhere — the spec defines xxs through xxxl for a reason"
54
+ - "Do not ignore the page_margin and card_padding aliases — they exist to enforce consistent spatial rhythm"
55
+ - "Do not add spacing that the spec doesn't define — if a gap isn't in the token scale, it shouldn't exist in output"
56
+ motion:
57
+ - "Do not use bounce or elastic easing — these feel dated and the spec defines specific easing curves"
58
+ - "Do not apply the same duration to all animations — instant, quick, normal, and slow serve different purposes"
59
+ - "Do not ignore reduced_motion — when the user prefers reduced motion, animations must be removed entirely"
60
+ elevation:
61
+ - "Do not add shadows to elements that don't specify an elevation token"
62
+ - "Do not use the same shadow depth for cards and modals — cards use elevation.md, modals use elevation.lg"
63
+ layout:
64
+ - "Do not assume large-screen layouts work on compact — every multi-pane pattern needs an explicit compact fallback"
65
+ - "Do not use pixel breakpoints — reference size classes by name (compact, regular, expanded)"
66
+ accessibility:
67
+ - "Do not use color as the only differentiator between states — combine with icon, border, or text changes"
68
+ - "Do not skip focus ring styles — keyboard users must see where focus is at all times"
69
+ - "Do not set tab order manually unless the contract explicitly requires it — use document order"
70
+ audit_threshold: 70
71
+
72
+ design:
73
+ personality: "Minimal and calm — orbital metaphor suggests clarity and organization without visual noise"
74
+ complexity: "restrained"
75
+ audience: "Bilingual individuals who prefer a clean, low-distraction task environment"
76
+ avoid:
77
+ - "Do not add decorative illustrations or background patterns"
78
+ - "Do not use more than 2 accent colors on any screen"
79
+ - "Do not animate list items on page load — only animate user-triggered state changes"
80
+
41
81
  formatters:
42
82
  priority_label:
43
83
  input: enum
@@ -10,6 +10,10 @@ color:
10
10
  on_color:
11
11
  reference: "#FFFFFF"
12
12
  contrast_min: 4.5
13
+ generation_notes:
14
+ - "This is the most important brand color — use consistently for primary actions, active states, and key interactive elements"
15
+ - "Do not dilute by applying it to backgrounds, borders, and text simultaneously — pick one dominant usage per surface"
16
+ - "The on_color token exists specifically for text/icons placed on this background — do not calculate contrast manually"
13
17
  secondary:
14
18
  semantic: "Warm accent for highlights and due-date emphasis"
15
19
  reference: "#F59E0B"
@@ -18,6 +18,10 @@ motion:
18
18
  duration: "instant"
19
19
  property: "scale"
20
20
  value: 0.98
21
+ generation_notes:
22
+ - "The scale value is subtle by design — do not exaggerate to 0.9 or lower"
23
+ - "[ios] Complement with haptic feedback (.impact(style: .light))"
24
+ - "[android] Use ripple effect instead of or in addition to scale"
21
25
  state_change:
22
26
  duration: "quick"
23
27
  property: "opacity, border-color, background"
@@ -8,6 +8,11 @@ typography:
8
8
  ios: { system_alternative: "SF Pro" }
9
9
  android: { system_alternative: "Google Sans" }
10
10
  web: { load_strategy: "swap", source: "google_fonts" }
11
+ generation_notes:
12
+ - "If the primary font is unavailable on a platform, choose a geometric sans with similar x-height — never fall back to Inter or Roboto"
13
+ - "[web] Use font-display: swap and a size-adjust fallback to prevent FOUT (Flash of Unstyled Text)"
14
+ - "[ios] Register the font in Info.plist and verify it loads before first render"
15
+ - "[android] Place the font in res/font/ and reference via XML font family — do not load programmatically"
11
16
 
12
17
  scale:
13
18
  display:
@@ -38,6 +43,9 @@ typography:
38
43
  size: { base: 16, range: [15, 17] }
39
44
  weight: 400
40
45
  line_height: 1.5
46
+ generation_notes:
47
+ - "This is the most-used text style — ensure line_height is comfortable for reading (1.5 default)"
48
+ - "On compact screens: the lower end of the size range is acceptable — do not go below 14pt/px"
41
49
  body_sm:
42
50
  semantic: "Supporting text and subtitles"
43
51
  size: { base: 14, range: [13, 15] }
@@ -50,3 +58,6 @@ typography:
50
58
  weight: 500
51
59
  tracking: 0.015
52
60
  line_height: 1.35
61
+ generation_notes:
62
+ - "Caption text must still meet the contrast_min requirement from the text token it pairs with"
63
+ - "Do not use caption size for primary content — it is for metadata, timestamps, and labels only"
@@ -127,7 +127,12 @@ SPEC AUTHORING: spec_types → spec_schema(type, summary?) → write YAML
127
127
  PREVIEW: openuispec_preview(screen) → render spec as HTML with mock data, returns screenshot (no app needed)
128
128
  SCREENSHOTS: screenshot (web), screenshot_android, screenshot_ios — single + batch variants
129
129
 
130
- Skip only for purely non-UI requests.`,
130
+ Skip only for purely non-UI requests.
131
+
132
+ When generating UI code:
133
+ 1. Check anti_patterns in the prepare result — hard constraints on what NOT to produce
134
+ 2. Check design_context — match the complexity level and personality description
135
+ 3. Apply the AI Fingerprint Test: would a viewer immediately say "AI made this"? If yes, revise.`,
131
136
  }
132
137
  );
133
138
 
@@ -319,7 +324,7 @@ function buildAuditChecklist(projectDir: string, target: string, screenFilter?:
319
324
  server.registerTool(
320
325
  "openuispec_check",
321
326
  {
322
- description: "Validate spec files (schema + semantic lint) and check prepare readiness. Does NOT inspect generated code. With audit=true, returns a spec-derived checklist of must_handle items, screen sections, and locale files — use it as a guide when YOU manually review the generated code.",
327
+ description: "Validate spec files (schema + semantic lint) and check prepare readiness. Does NOT inspect generated code. With audit=true, returns a spec-derived checklist of must_handle items, must_avoid anti-patterns, screen sections, and locale files — use it as a guide when YOU manually review the generated code. design_quality_score and audit_findings are included when audit=true.",
323
328
  inputSchema: {
324
329
  target: targetSchema,
325
330
  audit: z.boolean().optional().default(false).describe("Include the full audit checklist. Omit for a compact pass/fail summary."),
@@ -329,7 +334,7 @@ server.registerTool(
329
334
  },
330
335
  async ({ target, audit: includeAudit, screens, contracts }) => {
331
336
  try {
332
- const result = buildCheckResult(target, projectCwd);
337
+ const result = buildCheckResult(target, projectCwd, includeAudit);
333
338
  const totalErrors = result.validation.total_errors + result.semantic.total_errors;
334
339
  const passing = totalErrors === 0 && result.prepare.ready;
335
340
 
@@ -358,6 +363,13 @@ server.registerTool(
358
363
  hints.push(buildAuditChecklist(projectDir, target, screenFilter, contractFilter));
359
364
  }
360
365
 
366
+ if (includeAudit && result.audit) {
367
+ hints.push(`DESIGN QUALITY SCORE: ${result.audit.score}/100 (${result.audit.errors} errors, ${result.audit.warnings} warnings)`);
368
+ if (!result.audit.passed && result.audit.threshold > 0) {
369
+ hints.push(`SCORE BELOW THRESHOLD: ${result.audit.score} < ${result.audit.threshold}`);
370
+ }
371
+ }
372
+
361
373
  if (baselineHint) hints.push(baselineHint);
362
374
  hints.push(passing ? "next_tool: openuispec_drift --snapshot (to create/update baseline)" : "Fix validation errors, then re-run openuispec_check.");
363
375
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openuispec",
3
- "version": "0.2.15",
3
+ "version": "0.2.17",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "A semantic UI specification format for AI-native, platform-native app development",