9 Commits

Author SHA1 Message Date
Viet Tran
6678e96b7b fix(cli): read version dynamically from package.json
- Remove hardcoded version string (was stuck at 1.9.0)
- Read version from package.json at runtime using ESM imports
- Bump version to 2.1.0

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 22:10:08 +07:00
Shehab Tarek
fef14a0f33 fix: Restructure plugin for Claude Code skill discovery (#94)
- Move .claude/skills/ui-ux-pro-max to skills/ui-ux-pro-max
- Update marketplace.json source from specific path to root ("./")
- Update script paths in SKILL.md to match new location
- Bump version to 2.0.1

Claude Code auto-discovers skills from the skills/ subdirectory relative
to the source path. The previous structure placed SKILL.md at the source
path directly, which prevented skill discovery.

Tested locally and confirmed the skill now loads correctly.
2026-01-18 22:04:31 +07:00
mmcc9988
ee852c3f56 Feat/intelligent page overrides (#93)
* feat: intelligent page override generation

- Add _generate_intelligent_overrides() function
- Detect page types from page name AND query keywords
- Generate filled-in rules for: Dashboard, Checkout, Settings, Landing, Auth
- Fix combined_context to check both page name and query

* feat: project-based folder structure

- Each project now gets its own directory: design-system/<project>/
- Multiple projects can coexist without conflicts
- Structure: design-system/<project>/MASTER.md + pages/

* docs: update examples to Marketing Website and SaaS App

* docs: add merge request note to maintainer

* refactor: data-driven intelligent overrides using layered search

- Remove hardcoded page type rules
- Use existing search infrastructure (style, ux, landing CSVs)
- Extract layout, spacing, colors from search results
- Keep page type detection with keyword patterns
- No new CSV files needed - leverages existing data

* docs: update PR description for data-driven approach

* docs: add note about design quality needing review

* Delete PR_DESCRIPTION.md

---------

Co-authored-by: Viet Tran <viettranx@gmail.com>
2026-01-17 19:40:12 +07:00
mmcc9988
f5978e096b feat: Add design system persistence with Master + Overrides pattern (#91)
- Add --persist flag to save design system to design-system/MASTER.md
- Add --page flag to create page-specific override files in design-system/pages/
- MASTER.md includes hierarchical override logic header, color palette, typography, spacing, shadows, component specs, and anti-patterns
- Page override files are templates for documenting deviations from Master
- Updated documentation for all AI assistants (Claude, Cursor, Windsurf, Kiro, Qoder, Roo)
- Updated README.md with usage examples

This enables persistent design context across chat sessions using a 'Master + Overrides' pattern where page-specific rules override the global design system.
2026-01-17 14:26:07 +07:00
September999999999
319a9393cd fix(cli): support OpenCode ai type (#84) 2026-01-17 09:48:00 +07:00
Shehab Tarek
e7c27e60af feat: add Claude skill marketplace installation support (#87)
- Add .claude-plugin/marketplace.json for /plugin marketplace add command
- Update README.md with marketplace installation instructions

Enables quick installation via:
/plugin marketplace add nextlevelbuilder/ui-ux-pro-max-skill
/plugin install ui-ux-pro-max@ui-ux-pro-max-skill
2026-01-17 07:31:48 +07:00
Đặng Tú
caaa535c0a Merge pull request #45 from tusdang33/feature/jetpack_compose_support
Support guideline for Jetpack Compose
2026-01-16 11:46:06 +07:00
Viet Tran
0967edf62c docs: enhance design system example in README with full details
- Add Conversion and CTA placement details
- Add numbered Sections list
- Add Style keywords, Best For, Performance/Accessibility
- Add color names and Notes
- Add Typography Mood, Best For, Google Fonts
- Add Pre-Delivery Checklist (7 items)
- Fix ASCII box alignment for consistent right border

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 10:58:51 +07:00
Viet Tran
0fdfd33497 docs: update README badges to dynamic + add donate section
- Replace static version badge with dynamic GitHub release badge
- Replace static license badge with dynamic GitHub license badge
- Add prominent PayPal donate section below demo image

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 10:56:35 +07:00
77 changed files with 8945 additions and 1786 deletions

View File

@@ -1,8 +1,3 @@
---
description: Plan and implement UI
auto_execution_mode: 3
---
# ui-ux-pro-max # ui-ux-pro-max
Comprehensive design guide for web and mobile applications. Contains 50+ styles, 97 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 9 technology stacks. Searchable database with priority-based recommendations. Comprehensive design guide for web and mobile applications. Contains 50+ styles, 97 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 9 technology stacks. Searchable database with priority-based recommendations.
@@ -65,6 +60,31 @@ This command:
python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa" python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa"
``` ```
### Step 2b: Persist Design System (Master + Overrides Pattern)
To save the design system for hierarchical retrieval across sessions, add `--persist`:
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name"
```
This creates:
- `design-system/MASTER.md` — Global Source of Truth with all design rules
- `design-system/pages/` — Folder for page-specific overrides
**With page-specific override:**
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name" --page "dashboard"
```
This also creates:
- `design-system/pages/dashboard.md` — Page-specific deviations from Master
**How hierarchical retrieval works:**
1. When building a specific page (e.g., "Checkout"), first check `design-system/pages/checkout.md`
2. If the page file exists, its rules **override** the Master file
3. If not, use `design-system/MASTER.md` exclusively
### Step 3: Supplement with Detailed Searches (as needed) ### Step 3: Supplement with Detailed Searches (as needed)
After getting the design system, use domain searches to get additional details: After getting the design system, use domain searches to get additional details:
@@ -91,8 +111,8 @@ Get implementation-specific best practices. If user doesn't specify a stack, **d
python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind
``` ```
Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn` Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn`, `jetpack-compose`
, `jetpack-compose`
--- ---
## Search Reference ## Search Reference
@@ -125,6 +145,7 @@ Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`
| `react-native` | Components, Navigation, Lists | | `react-native` | Components, Navigation, Lists |
| `flutter` | Widgets, State, Layout, Theming | | `flutter` | Widgets, State, Layout, Theming |
| `shadcn` | shadcn/ui components, theming, forms, patterns | | `shadcn` | shadcn/ui components, theming, forms, patterns |
| `jetpack-compose` | Composables, Modifiers, State Hoisting, Recomposition |
--- ---

View File

@@ -0,0 +1,35 @@
{
"name": "ui-ux-pro-max-skill",
"id": "ui-ux-pro-max-skill",
"owner": {
"name": "nextlevelbuilder"
},
"metadata": {
"description": "UI/UX design intelligence skill with 50 styles, 21 palettes, 50 font pairings, 20 charts, and 9 stack guidelines",
"version": "2.0.1"
},
"plugins": [
{
"name": "ui-ux-pro-max",
"source": "./",
"description": "Professional UI/UX design intelligence for AI coding assistants. Includes searchable databases of styles, colors, typography, charts, and UX guidelines for React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind, and shadcn/ui.",
"version": "2.0.1",
"author": {
"name": "nextlevelbuilder"
},
"keywords": [
"ui",
"ux",
"design",
"styles",
"typography",
"color-palette",
"accessibility",
"charts",
"components"
],
"category": "design",
"strict": false
}
]
}

View File

@@ -1,487 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Design System Generator - Aggregates search results and applies reasoning
to generate comprehensive design system recommendations.
Usage:
from design_system import generate_design_system
result = generate_design_system("SaaS dashboard", "My Project")
"""
import csv
import json
from pathlib import Path
from core import search, DATA_DIR
# ============ CONFIGURATION ============
REASONING_FILE = "ui-reasoning.csv"
SEARCH_CONFIG = {
"product": {"max_results": 1},
"style": {"max_results": 3},
"color": {"max_results": 2},
"landing": {"max_results": 2},
"typography": {"max_results": 2}
}
# ============ DESIGN SYSTEM GENERATOR ============
class DesignSystemGenerator:
"""Generates design system recommendations from aggregated searches."""
def __init__(self):
self.reasoning_data = self._load_reasoning()
def _load_reasoning(self) -> list:
"""Load reasoning rules from CSV."""
filepath = DATA_DIR / REASONING_FILE
if not filepath.exists():
return []
with open(filepath, 'r', encoding='utf-8') as f:
return list(csv.DictReader(f))
def _multi_domain_search(self, query: str, style_priority: list = None) -> dict:
"""Execute searches across multiple domains."""
results = {}
for domain, config in SEARCH_CONFIG.items():
if domain == "style" and style_priority:
# For style, also search with priority keywords
priority_query = " ".join(style_priority[:2]) if style_priority else query
combined_query = f"{query} {priority_query}"
results[domain] = search(combined_query, domain, config["max_results"])
else:
results[domain] = search(query, domain, config["max_results"])
return results
def _find_reasoning_rule(self, category: str) -> dict:
"""Find matching reasoning rule for a category."""
category_lower = category.lower()
# Try exact match first
for rule in self.reasoning_data:
if rule.get("UI_Category", "").lower() == category_lower:
return rule
# Try partial match
for rule in self.reasoning_data:
ui_cat = rule.get("UI_Category", "").lower()
if ui_cat in category_lower or category_lower in ui_cat:
return rule
# Try keyword match
for rule in self.reasoning_data:
ui_cat = rule.get("UI_Category", "").lower()
keywords = ui_cat.replace("/", " ").replace("-", " ").split()
if any(kw in category_lower for kw in keywords):
return rule
return {}
def _apply_reasoning(self, category: str, search_results: dict) -> dict:
"""Apply reasoning rules to search results."""
rule = self._find_reasoning_rule(category)
if not rule:
return {
"pattern": "Hero + Features + CTA",
"style_priority": ["Minimalism", "Flat Design"],
"color_mood": "Professional",
"typography_mood": "Clean",
"key_effects": "Subtle hover transitions",
"anti_patterns": "",
"decision_rules": {},
"severity": "MEDIUM"
}
# Parse decision rules JSON
decision_rules = {}
try:
decision_rules = json.loads(rule.get("Decision_Rules", "{}"))
except json.JSONDecodeError:
pass
return {
"pattern": rule.get("Recommended_Pattern", ""),
"style_priority": [s.strip() for s in rule.get("Style_Priority", "").split("+")],
"color_mood": rule.get("Color_Mood", ""),
"typography_mood": rule.get("Typography_Mood", ""),
"key_effects": rule.get("Key_Effects", ""),
"anti_patterns": rule.get("Anti_Patterns", ""),
"decision_rules": decision_rules,
"severity": rule.get("Severity", "MEDIUM")
}
def _select_best_match(self, results: list, priority_keywords: list) -> dict:
"""Select best matching result based on priority keywords."""
if not results:
return {}
if not priority_keywords:
return results[0]
# First: try exact style name match
for priority in priority_keywords:
priority_lower = priority.lower().strip()
for result in results:
style_name = result.get("Style Category", "").lower()
if priority_lower in style_name or style_name in priority_lower:
return result
# Second: score by keyword match in all fields
scored = []
for result in results:
result_str = str(result).lower()
score = 0
for kw in priority_keywords:
kw_lower = kw.lower().strip()
# Higher score for style name match
if kw_lower in result.get("Style Category", "").lower():
score += 10
# Lower score for keyword field match
elif kw_lower in result.get("Keywords", "").lower():
score += 3
# Even lower for other field matches
elif kw_lower in result_str:
score += 1
scored.append((score, result))
scored.sort(key=lambda x: x[0], reverse=True)
return scored[0][1] if scored and scored[0][0] > 0 else results[0]
def _extract_results(self, search_result: dict) -> list:
"""Extract results list from search result dict."""
return search_result.get("results", [])
def generate(self, query: str, project_name: str = None) -> dict:
"""Generate complete design system recommendation."""
# Step 1: First search product to get category
product_result = search(query, "product", 1)
product_results = product_result.get("results", [])
category = "General"
if product_results:
category = product_results[0].get("Product Type", "General")
# Step 2: Get reasoning rules for this category
reasoning = self._apply_reasoning(category, {})
style_priority = reasoning.get("style_priority", [])
# Step 3: Multi-domain search with style priority hints
search_results = self._multi_domain_search(query, style_priority)
search_results["product"] = product_result # Reuse product search
# Step 4: Select best matches from each domain using priority
style_results = self._extract_results(search_results.get("style", {}))
color_results = self._extract_results(search_results.get("color", {}))
typography_results = self._extract_results(search_results.get("typography", {}))
landing_results = self._extract_results(search_results.get("landing", {}))
best_style = self._select_best_match(style_results, reasoning.get("style_priority", []))
best_color = color_results[0] if color_results else {}
best_typography = typography_results[0] if typography_results else {}
best_landing = landing_results[0] if landing_results else {}
# Step 5: Build final recommendation
# Combine effects from both reasoning and style search
style_effects = best_style.get("Effects & Animation", "")
reasoning_effects = reasoning.get("key_effects", "")
combined_effects = style_effects if style_effects else reasoning_effects
return {
"project_name": project_name or query.upper(),
"category": category,
"pattern": {
"name": best_landing.get("Pattern Name", reasoning.get("pattern", "Hero + Features + CTA")),
"sections": best_landing.get("Section Order", "Hero > Features > CTA"),
"cta_placement": best_landing.get("Primary CTA Placement", "Above fold"),
"color_strategy": best_landing.get("Color Strategy", ""),
"conversion": best_landing.get("Conversion Optimization", "")
},
"style": {
"name": best_style.get("Style Category", "Minimalism"),
"type": best_style.get("Type", "General"),
"effects": style_effects,
"keywords": best_style.get("Keywords", ""),
"best_for": best_style.get("Best For", ""),
"performance": best_style.get("Performance", ""),
"accessibility": best_style.get("Accessibility", "")
},
"colors": {
"primary": best_color.get("Primary (Hex)", "#2563EB"),
"secondary": best_color.get("Secondary (Hex)", "#3B82F6"),
"cta": best_color.get("CTA (Hex)", "#F97316"),
"background": best_color.get("Background (Hex)", "#F8FAFC"),
"text": best_color.get("Text (Hex)", "#1E293B"),
"notes": best_color.get("Notes", "")
},
"typography": {
"heading": best_typography.get("Heading Font", "Inter"),
"body": best_typography.get("Body Font", "Inter"),
"mood": best_typography.get("Mood/Style Keywords", reasoning.get("typography_mood", "")),
"best_for": best_typography.get("Best For", ""),
"google_fonts_url": best_typography.get("Google Fonts URL", ""),
"css_import": best_typography.get("CSS Import", "")
},
"key_effects": combined_effects,
"anti_patterns": reasoning.get("anti_patterns", ""),
"decision_rules": reasoning.get("decision_rules", {}),
"severity": reasoning.get("severity", "MEDIUM")
}
# ============ OUTPUT FORMATTERS ============
BOX_WIDTH = 90 # Wider box for more content
def format_ascii_box(design_system: dict) -> str:
"""Format design system as ASCII box with emojis (MCP-style)."""
project = design_system.get("project_name", "PROJECT")
pattern = design_system.get("pattern", {})
style = design_system.get("style", {})
colors = design_system.get("colors", {})
typography = design_system.get("typography", {})
effects = design_system.get("key_effects", "")
anti_patterns = design_system.get("anti_patterns", "")
def wrap_text(text: str, prefix: str, width: int) -> list:
"""Wrap long text into multiple lines."""
if not text:
return []
words = text.split()
lines = []
current_line = prefix
for word in words:
if len(current_line) + len(word) + 1 <= width - 2:
current_line += (" " if current_line != prefix else "") + word
else:
if current_line != prefix:
lines.append(current_line)
current_line = prefix + word
if current_line != prefix:
lines.append(current_line)
return lines
# Build sections from pattern
sections = pattern.get("sections", "").split(">")
sections = [s.strip() for s in sections if s.strip()]
# Build output lines
lines = []
w = BOX_WIDTH - 1
lines.append("+" + "-" * w + "+")
lines.append(f"| TARGET: {project} - RECOMMENDED DESIGN SYSTEM".ljust(BOX_WIDTH) + "|")
lines.append("+" + "-" * w + "+")
lines.append("|" + " " * BOX_WIDTH + "|")
# Pattern section
lines.append(f"| PATTERN: {pattern.get('name', '')}".ljust(BOX_WIDTH) + "|")
if pattern.get('conversion'):
lines.append(f"| Conversion: {pattern.get('conversion', '')}".ljust(BOX_WIDTH) + "|")
if pattern.get('cta_placement'):
lines.append(f"| CTA: {pattern.get('cta_placement', '')}".ljust(BOX_WIDTH) + "|")
lines.append("| Sections:".ljust(BOX_WIDTH) + "|")
for i, section in enumerate(sections, 1):
lines.append(f"| {i}. {section}".ljust(BOX_WIDTH) + "|")
lines.append("|" + " " * BOX_WIDTH + "|")
# Style section
lines.append(f"| STYLE: {style.get('name', '')}".ljust(BOX_WIDTH) + "|")
if style.get("keywords"):
for line in wrap_text(f"Keywords: {style.get('keywords', '')}", "| ", BOX_WIDTH):
lines.append(line.ljust(BOX_WIDTH) + "|")
if style.get("best_for"):
for line in wrap_text(f"Best For: {style.get('best_for', '')}", "| ", BOX_WIDTH):
lines.append(line.ljust(BOX_WIDTH) + "|")
if style.get("performance") or style.get("accessibility"):
perf_a11y = f"Performance: {style.get('performance', '')} | Accessibility: {style.get('accessibility', '')}"
lines.append(f"| {perf_a11y}".ljust(BOX_WIDTH) + "|")
lines.append("|" + " " * BOX_WIDTH + "|")
# Colors section
lines.append("| COLORS:".ljust(BOX_WIDTH) + "|")
lines.append(f"| Primary: {colors.get('primary', '')}".ljust(BOX_WIDTH) + "|")
lines.append(f"| Secondary: {colors.get('secondary', '')}".ljust(BOX_WIDTH) + "|")
lines.append(f"| CTA: {colors.get('cta', '')}".ljust(BOX_WIDTH) + "|")
lines.append(f"| Background: {colors.get('background', '')}".ljust(BOX_WIDTH) + "|")
lines.append(f"| Text: {colors.get('text', '')}".ljust(BOX_WIDTH) + "|")
if colors.get("notes"):
for line in wrap_text(f"Notes: {colors.get('notes', '')}", "| ", BOX_WIDTH):
lines.append(line.ljust(BOX_WIDTH) + "|")
lines.append("|" + " " * BOX_WIDTH + "|")
# Typography section
lines.append(f"| TYPOGRAPHY: {typography.get('heading', '')} / {typography.get('body', '')}".ljust(BOX_WIDTH) + "|")
if typography.get("mood"):
for line in wrap_text(f"Mood: {typography.get('mood', '')}", "| ", BOX_WIDTH):
lines.append(line.ljust(BOX_WIDTH) + "|")
if typography.get("best_for"):
for line in wrap_text(f"Best For: {typography.get('best_for', '')}", "| ", BOX_WIDTH):
lines.append(line.ljust(BOX_WIDTH) + "|")
if typography.get("google_fonts_url"):
lines.append(f"| Google Fonts: {typography.get('google_fonts_url', '')}".ljust(BOX_WIDTH) + "|")
if typography.get("css_import"):
lines.append(f"| CSS Import: {typography.get('css_import', '')[:70]}...".ljust(BOX_WIDTH) + "|")
lines.append("|" + " " * BOX_WIDTH + "|")
# Key Effects section
if effects:
lines.append("| KEY EFFECTS:".ljust(BOX_WIDTH) + "|")
for line in wrap_text(effects, "| ", BOX_WIDTH):
lines.append(line.ljust(BOX_WIDTH) + "|")
lines.append("|" + " " * BOX_WIDTH + "|")
# Anti-patterns section
if anti_patterns:
lines.append("| AVOID (Anti-patterns):".ljust(BOX_WIDTH) + "|")
for line in wrap_text(anti_patterns, "| ", BOX_WIDTH):
lines.append(line.ljust(BOX_WIDTH) + "|")
lines.append("|" + " " * BOX_WIDTH + "|")
# Pre-Delivery Checklist section
lines.append("| PRE-DELIVERY CHECKLIST:".ljust(BOX_WIDTH) + "|")
checklist_items = [
"[ ] No emojis as icons (use SVG: Heroicons/Lucide)",
"[ ] cursor-pointer on all clickable elements",
"[ ] Hover states with smooth transitions (150-300ms)",
"[ ] Light mode: text contrast 4.5:1 minimum",
"[ ] Focus states visible for keyboard nav",
"[ ] prefers-reduced-motion respected",
"[ ] Responsive: 375px, 768px, 1024px, 1440px"
]
for item in checklist_items:
lines.append(f"| {item}".ljust(BOX_WIDTH) + "|")
lines.append("|" + " " * BOX_WIDTH + "|")
lines.append("+" + "-" * w + "+")
return "\n".join(lines)
def format_markdown(design_system: dict) -> str:
"""Format design system as markdown."""
project = design_system.get("project_name", "PROJECT")
pattern = design_system.get("pattern", {})
style = design_system.get("style", {})
colors = design_system.get("colors", {})
typography = design_system.get("typography", {})
effects = design_system.get("key_effects", "")
anti_patterns = design_system.get("anti_patterns", "")
lines = []
lines.append(f"## Design System: {project}")
lines.append("")
# Pattern section
lines.append("### Pattern")
lines.append(f"- **Name:** {pattern.get('name', '')}")
if pattern.get('conversion'):
lines.append(f"- **Conversion Focus:** {pattern.get('conversion', '')}")
if pattern.get('cta_placement'):
lines.append(f"- **CTA Placement:** {pattern.get('cta_placement', '')}")
if pattern.get('color_strategy'):
lines.append(f"- **Color Strategy:** {pattern.get('color_strategy', '')}")
lines.append(f"- **Sections:** {pattern.get('sections', '')}")
lines.append("")
# Style section
lines.append("### Style")
lines.append(f"- **Name:** {style.get('name', '')}")
if style.get('keywords'):
lines.append(f"- **Keywords:** {style.get('keywords', '')}")
if style.get('best_for'):
lines.append(f"- **Best For:** {style.get('best_for', '')}")
if style.get('performance') or style.get('accessibility'):
lines.append(f"- **Performance:** {style.get('performance', '')} | **Accessibility:** {style.get('accessibility', '')}")
lines.append("")
# Colors section
lines.append("### Colors")
lines.append(f"| Role | Hex |")
lines.append(f"|------|-----|")
lines.append(f"| Primary | {colors.get('primary', '')} |")
lines.append(f"| Secondary | {colors.get('secondary', '')} |")
lines.append(f"| CTA | {colors.get('cta', '')} |")
lines.append(f"| Background | {colors.get('background', '')} |")
lines.append(f"| Text | {colors.get('text', '')} |")
if colors.get("notes"):
lines.append(f"\n*Notes: {colors.get('notes', '')}*")
lines.append("")
# Typography section
lines.append("### Typography")
lines.append(f"- **Heading:** {typography.get('heading', '')}")
lines.append(f"- **Body:** {typography.get('body', '')}")
if typography.get("mood"):
lines.append(f"- **Mood:** {typography.get('mood', '')}")
if typography.get("best_for"):
lines.append(f"- **Best For:** {typography.get('best_for', '')}")
if typography.get("google_fonts_url"):
lines.append(f"- **Google Fonts:** {typography.get('google_fonts_url', '')}")
if typography.get("css_import"):
lines.append(f"- **CSS Import:**")
lines.append(f"```css")
lines.append(f"{typography.get('css_import', '')}")
lines.append(f"```")
lines.append("")
# Key Effects section
if effects:
lines.append("### Key Effects")
lines.append(f"{effects}")
lines.append("")
# Anti-patterns section
if anti_patterns:
lines.append("### Avoid (Anti-patterns)")
lines.append(f"- {anti_patterns.replace(' + ', '\n- ')}")
lines.append("")
# Pre-Delivery Checklist section
lines.append("### Pre-Delivery Checklist")
lines.append("- [ ] No emojis as icons (use SVG: Heroicons/Lucide)")
lines.append("- [ ] cursor-pointer on all clickable elements")
lines.append("- [ ] Hover states with smooth transitions (150-300ms)")
lines.append("- [ ] Light mode: text contrast 4.5:1 minimum")
lines.append("- [ ] Focus states visible for keyboard nav")
lines.append("- [ ] prefers-reduced-motion respected")
lines.append("- [ ] Responsive: 375px, 768px, 1024px, 1440px")
lines.append("")
return "\n".join(lines)
# ============ MAIN ENTRY POINT ============
def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii") -> str:
"""
Main entry point for design system generation.
Args:
query: Search query (e.g., "SaaS dashboard", "e-commerce luxury")
project_name: Optional project name for output header
output_format: "ascii" (default) or "markdown"
Returns:
Formatted design system string
"""
generator = DesignSystemGenerator()
design_system = generator.generate(query, project_name)
if output_format == "markdown":
return format_markdown(design_system)
return format_ascii_box(design_system)
# ============ CLI SUPPORT ============
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Generate Design System")
parser.add_argument("query", help="Search query (e.g., 'SaaS dashboard')")
parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name")
parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format")
args = parser.parse_args()
result = generate_design_system(args.query, args.project_name, args.format)
print(result)

View File

@@ -7,10 +7,16 @@ to generate comprehensive design system recommendations.
Usage: Usage:
from design_system import generate_design_system from design_system import generate_design_system
result = generate_design_system("SaaS dashboard", "My Project") result = generate_design_system("SaaS dashboard", "My Project")
# With persistence (Master + Overrides pattern)
result = generate_design_system("SaaS dashboard", "My Project", persist=True)
result = generate_design_system("SaaS dashboard", "My Project", persist=True, page="dashboard")
""" """
import csv import csv
import json import json
import os
from datetime import datetime
from pathlib import Path from pathlib import Path
from core import search, DATA_DIR from core import search, DATA_DIR
@@ -338,6 +344,21 @@ def format_ascii_box(design_system: dict) -> str:
lines.append(line.ljust(BOX_WIDTH) + "|") lines.append(line.ljust(BOX_WIDTH) + "|")
lines.append("|" + " " * BOX_WIDTH + "|") lines.append("|" + " " * BOX_WIDTH + "|")
# Pre-Delivery Checklist section
lines.append("| PRE-DELIVERY CHECKLIST:".ljust(BOX_WIDTH) + "|")
checklist_items = [
"[ ] No emojis as icons (use SVG: Heroicons/Lucide)",
"[ ] cursor-pointer on all clickable elements",
"[ ] Hover states with smooth transitions (150-300ms)",
"[ ] Light mode: text contrast 4.5:1 minimum",
"[ ] Focus states visible for keyboard nav",
"[ ] prefers-reduced-motion respected",
"[ ] Responsive: 375px, 768px, 1024px, 1440px"
]
for item in checklist_items:
lines.append(f"| {item}".ljust(BOX_WIDTH) + "|")
lines.append("|" + " " * BOX_WIDTH + "|")
lines.append("+" + "-" * w + "+") lines.append("+" + "-" * w + "+")
return "\n".join(lines) return "\n".join(lines)
@@ -422,11 +443,23 @@ def format_markdown(design_system: dict) -> str:
lines.append(f"- {anti_patterns.replace(' + ', '\n- ')}") lines.append(f"- {anti_patterns.replace(' + ', '\n- ')}")
lines.append("") lines.append("")
# Pre-Delivery Checklist section
lines.append("### Pre-Delivery Checklist")
lines.append("- [ ] No emojis as icons (use SVG: Heroicons/Lucide)")
lines.append("- [ ] cursor-pointer on all clickable elements")
lines.append("- [ ] Hover states with smooth transitions (150-300ms)")
lines.append("- [ ] Light mode: text contrast 4.5:1 minimum")
lines.append("- [ ] Focus states visible for keyboard nav")
lines.append("- [ ] prefers-reduced-motion respected")
lines.append("- [ ] Responsive: 375px, 768px, 1024px, 1440px")
lines.append("")
return "\n".join(lines) return "\n".join(lines)
# ============ MAIN ENTRY POINT ============ # ============ MAIN ENTRY POINT ============
def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii") -> str: def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii",
persist: bool = False, page: str = None, output_dir: str = None) -> str:
""" """
Main entry point for design system generation. Main entry point for design system generation.
@@ -434,6 +467,9 @@ def generate_design_system(query: str, project_name: str = None, output_format:
query: Search query (e.g., "SaaS dashboard", "e-commerce luxury") query: Search query (e.g., "SaaS dashboard", "e-commerce luxury")
project_name: Optional project name for output header project_name: Optional project name for output header
output_format: "ascii" (default) or "markdown" output_format: "ascii" (default) or "markdown"
persist: If True, save design system to design-system/ folder
page: Optional page name for page-specific override file
output_dir: Optional output directory (defaults to current working directory)
Returns: Returns:
Formatted design system string Formatted design system string
@@ -441,11 +477,580 @@ def generate_design_system(query: str, project_name: str = None, output_format:
generator = DesignSystemGenerator() generator = DesignSystemGenerator()
design_system = generator.generate(query, project_name) design_system = generator.generate(query, project_name)
# Persist to files if requested
if persist:
persist_design_system(design_system, page, output_dir, query)
if output_format == "markdown": if output_format == "markdown":
return format_markdown(design_system) return format_markdown(design_system)
return format_ascii_box(design_system) return format_ascii_box(design_system)
# ============ PERSISTENCE FUNCTIONS ============
def persist_design_system(design_system: dict, page: str = None, output_dir: str = None, page_query: str = None) -> dict:
"""
Persist design system to design-system/<project>/ folder using Master + Overrides pattern.
Args:
design_system: The generated design system dictionary
page: Optional page name for page-specific override file
output_dir: Optional output directory (defaults to current working directory)
page_query: Optional query string for intelligent page override generation
Returns:
dict with created file paths and status
"""
base_dir = Path(output_dir) if output_dir else Path.cwd()
# Use project name for project-specific folder
project_name = design_system.get("project_name", "default")
project_slug = project_name.lower().replace(' ', '-')
design_system_dir = base_dir / "design-system" / project_slug
pages_dir = design_system_dir / "pages"
created_files = []
# Create directories
design_system_dir.mkdir(parents=True, exist_ok=True)
pages_dir.mkdir(parents=True, exist_ok=True)
master_file = design_system_dir / "MASTER.md"
# Generate and write MASTER.md
master_content = format_master_md(design_system)
with open(master_file, 'w', encoding='utf-8') as f:
f.write(master_content)
created_files.append(str(master_file))
# If page is specified, create page override file with intelligent content
if page:
page_file = pages_dir / f"{page.lower().replace(' ', '-')}.md"
page_content = format_page_override_md(design_system, page, page_query)
with open(page_file, 'w', encoding='utf-8') as f:
f.write(page_content)
created_files.append(str(page_file))
return {
"status": "success",
"design_system_dir": str(design_system_dir),
"created_files": created_files
}
def format_master_md(design_system: dict) -> str:
"""Format design system as MASTER.md with hierarchical override logic."""
project = design_system.get("project_name", "PROJECT")
pattern = design_system.get("pattern", {})
style = design_system.get("style", {})
colors = design_system.get("colors", {})
typography = design_system.get("typography", {})
effects = design_system.get("key_effects", "")
anti_patterns = design_system.get("anti_patterns", "")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
lines = []
# Logic header
lines.append("# Design System Master File")
lines.append("")
lines.append("> **LOGIC:** When building a specific page, first check `design-system/pages/[page-name].md`.")
lines.append("> If that file exists, its rules **override** this Master file.")
lines.append("> If not, strictly follow the rules below.")
lines.append("")
lines.append("---")
lines.append("")
lines.append(f"**Project:** {project}")
lines.append(f"**Generated:** {timestamp}")
lines.append(f"**Category:** {design_system.get('category', 'General')}")
lines.append("")
lines.append("---")
lines.append("")
# Global Rules section
lines.append("## Global Rules")
lines.append("")
# Color Palette
lines.append("### Color Palette")
lines.append("")
lines.append("| Role | Hex | CSS Variable |")
lines.append("|------|-----|--------------|")
lines.append(f"| Primary | `{colors.get('primary', '#2563EB')}` | `--color-primary` |")
lines.append(f"| Secondary | `{colors.get('secondary', '#3B82F6')}` | `--color-secondary` |")
lines.append(f"| CTA/Accent | `{colors.get('cta', '#F97316')}` | `--color-cta` |")
lines.append(f"| Background | `{colors.get('background', '#F8FAFC')}` | `--color-background` |")
lines.append(f"| Text | `{colors.get('text', '#1E293B')}` | `--color-text` |")
lines.append("")
if colors.get("notes"):
lines.append(f"**Color Notes:** {colors.get('notes', '')}")
lines.append("")
# Typography
lines.append("### Typography")
lines.append("")
lines.append(f"- **Heading Font:** {typography.get('heading', 'Inter')}")
lines.append(f"- **Body Font:** {typography.get('body', 'Inter')}")
if typography.get("mood"):
lines.append(f"- **Mood:** {typography.get('mood', '')}")
if typography.get("google_fonts_url"):
lines.append(f"- **Google Fonts:** [{typography.get('heading', '')} + {typography.get('body', '')}]({typography.get('google_fonts_url', '')})")
lines.append("")
if typography.get("css_import"):
lines.append("**CSS Import:**")
lines.append("```css")
lines.append(typography.get("css_import", ""))
lines.append("```")
lines.append("")
# Spacing Variables
lines.append("### Spacing Variables")
lines.append("")
lines.append("| Token | Value | Usage |")
lines.append("|-------|-------|-------|")
lines.append("| `--space-xs` | `4px` / `0.25rem` | Tight gaps |")
lines.append("| `--space-sm` | `8px` / `0.5rem` | Icon gaps, inline spacing |")
lines.append("| `--space-md` | `16px` / `1rem` | Standard padding |")
lines.append("| `--space-lg` | `24px` / `1.5rem` | Section padding |")
lines.append("| `--space-xl` | `32px` / `2rem` | Large gaps |")
lines.append("| `--space-2xl` | `48px` / `3rem` | Section margins |")
lines.append("| `--space-3xl` | `64px` / `4rem` | Hero padding |")
lines.append("")
# Shadow Depths
lines.append("### Shadow Depths")
lines.append("")
lines.append("| Level | Value | Usage |")
lines.append("|-------|-------|-------|")
lines.append("| `--shadow-sm` | `0 1px 2px rgba(0,0,0,0.05)` | Subtle lift |")
lines.append("| `--shadow-md` | `0 4px 6px rgba(0,0,0,0.1)` | Cards, buttons |")
lines.append("| `--shadow-lg` | `0 10px 15px rgba(0,0,0,0.1)` | Modals, dropdowns |")
lines.append("| `--shadow-xl` | `0 20px 25px rgba(0,0,0,0.15)` | Hero images, featured cards |")
lines.append("")
# Component Specs section
lines.append("---")
lines.append("")
lines.append("## Component Specs")
lines.append("")
# Buttons
lines.append("### Buttons")
lines.append("")
lines.append("```css")
lines.append("/* Primary Button */")
lines.append(".btn-primary {")
lines.append(f" background: {colors.get('cta', '#F97316')};")
lines.append(" color: white;")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("")
lines.append(".btn-primary:hover {")
lines.append(" opacity: 0.9;")
lines.append(" transform: translateY(-1px);")
lines.append("}")
lines.append("")
lines.append("/* Secondary Button */")
lines.append(".btn-secondary {")
lines.append(f" background: transparent;")
lines.append(f" color: {colors.get('primary', '#2563EB')};")
lines.append(f" border: 2px solid {colors.get('primary', '#2563EB')};")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("```")
lines.append("")
# Cards
lines.append("### Cards")
lines.append("")
lines.append("```css")
lines.append(".card {")
lines.append(f" background: {colors.get('background', '#FFFFFF')};")
lines.append(" border-radius: 12px;")
lines.append(" padding: 24px;")
lines.append(" box-shadow: var(--shadow-md);")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("")
lines.append(".card:hover {")
lines.append(" box-shadow: var(--shadow-lg);")
lines.append(" transform: translateY(-2px);")
lines.append("}")
lines.append("```")
lines.append("")
# Inputs
lines.append("### Inputs")
lines.append("")
lines.append("```css")
lines.append(".input {")
lines.append(" padding: 12px 16px;")
lines.append(" border: 1px solid #E2E8F0;")
lines.append(" border-radius: 8px;")
lines.append(" font-size: 16px;")
lines.append(" transition: border-color 200ms ease;")
lines.append("}")
lines.append("")
lines.append(".input:focus {")
lines.append(f" border-color: {colors.get('primary', '#2563EB')};")
lines.append(" outline: none;")
lines.append(f" box-shadow: 0 0 0 3px {colors.get('primary', '#2563EB')}20;")
lines.append("}")
lines.append("```")
lines.append("")
# Modals
lines.append("### Modals")
lines.append("")
lines.append("```css")
lines.append(".modal-overlay {")
lines.append(" background: rgba(0, 0, 0, 0.5);")
lines.append(" backdrop-filter: blur(4px);")
lines.append("}")
lines.append("")
lines.append(".modal {")
lines.append(" background: white;")
lines.append(" border-radius: 16px;")
lines.append(" padding: 32px;")
lines.append(" box-shadow: var(--shadow-xl);")
lines.append(" max-width: 500px;")
lines.append(" width: 90%;")
lines.append("}")
lines.append("```")
lines.append("")
# Style section
lines.append("---")
lines.append("")
lines.append("## Style Guidelines")
lines.append("")
lines.append(f"**Style:** {style.get('name', 'Minimalism')}")
lines.append("")
if style.get("keywords"):
lines.append(f"**Keywords:** {style.get('keywords', '')}")
lines.append("")
if style.get("best_for"):
lines.append(f"**Best For:** {style.get('best_for', '')}")
lines.append("")
if effects:
lines.append(f"**Key Effects:** {effects}")
lines.append("")
# Layout Pattern
lines.append("### Page Pattern")
lines.append("")
lines.append(f"**Pattern Name:** {pattern.get('name', '')}")
lines.append("")
if pattern.get('conversion'):
lines.append(f"- **Conversion Strategy:** {pattern.get('conversion', '')}")
if pattern.get('cta_placement'):
lines.append(f"- **CTA Placement:** {pattern.get('cta_placement', '')}")
lines.append(f"- **Section Order:** {pattern.get('sections', '')}")
lines.append("")
# Anti-Patterns section
lines.append("---")
lines.append("")
lines.append("## Anti-Patterns (Do NOT Use)")
lines.append("")
if anti_patterns:
anti_list = [a.strip() for a in anti_patterns.split("+")]
for anti in anti_list:
if anti:
lines.append(f"- ❌ {anti}")
lines.append("")
lines.append("### Additional Forbidden Patterns")
lines.append("")
lines.append("- ❌ **Emojis as icons** — Use SVG icons (Heroicons, Lucide, Simple Icons)")
lines.append("- ❌ **Missing cursor:pointer** — All clickable elements must have cursor:pointer")
lines.append("- ❌ **Layout-shifting hovers** — Avoid scale transforms that shift layout")
lines.append("- ❌ **Low contrast text** — Maintain 4.5:1 minimum contrast ratio")
lines.append("- ❌ **Instant state changes** — Always use transitions (150-300ms)")
lines.append("- ❌ **Invisible focus states** — Focus states must be visible for a11y")
lines.append("")
# Pre-Delivery Checklist
lines.append("---")
lines.append("")
lines.append("## Pre-Delivery Checklist")
lines.append("")
lines.append("Before delivering any UI code, verify:")
lines.append("")
lines.append("- [ ] No emojis used as icons (use SVG instead)")
lines.append("- [ ] All icons from consistent icon set (Heroicons/Lucide)")
lines.append("- [ ] `cursor-pointer` on all clickable elements")
lines.append("- [ ] Hover states with smooth transitions (150-300ms)")
lines.append("- [ ] Light mode: text contrast 4.5:1 minimum")
lines.append("- [ ] Focus states visible for keyboard navigation")
lines.append("- [ ] `prefers-reduced-motion` respected")
lines.append("- [ ] Responsive: 375px, 768px, 1024px, 1440px")
lines.append("- [ ] No content hidden behind fixed navbars")
lines.append("- [ ] No horizontal scroll on mobile")
lines.append("")
return "\n".join(lines)
def format_page_override_md(design_system: dict, page_name: str, page_query: str = None) -> str:
"""Format a page-specific override file with intelligent AI-generated content."""
project = design_system.get("project_name", "PROJECT")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
page_title = page_name.replace("-", " ").replace("_", " ").title()
# Detect page type and generate intelligent overrides
page_overrides = _generate_intelligent_overrides(page_name, page_query, design_system)
lines = []
lines.append(f"# {page_title} Page Overrides")
lines.append("")
lines.append(f"> **PROJECT:** {project}")
lines.append(f"> **Generated:** {timestamp}")
lines.append(f"> **Page Type:** {page_overrides.get('page_type', 'General')}")
lines.append("")
lines.append("> ⚠️ **IMPORTANT:** Rules in this file **override** the Master file (`design-system/MASTER.md`).")
lines.append("> Only deviations from the Master are documented here. For all other rules, refer to the Master.")
lines.append("")
lines.append("---")
lines.append("")
# Page-specific rules with actual content
lines.append("## Page-Specific Rules")
lines.append("")
# Layout Overrides
lines.append("### Layout Overrides")
lines.append("")
layout = page_overrides.get("layout", {})
if layout:
for key, value in layout.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master layout")
lines.append("")
# Spacing Overrides
lines.append("### Spacing Overrides")
lines.append("")
spacing = page_overrides.get("spacing", {})
if spacing:
for key, value in spacing.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master spacing")
lines.append("")
# Typography Overrides
lines.append("### Typography Overrides")
lines.append("")
typography = page_overrides.get("typography", {})
if typography:
for key, value in typography.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master typography")
lines.append("")
# Color Overrides
lines.append("### Color Overrides")
lines.append("")
colors = page_overrides.get("colors", {})
if colors:
for key, value in colors.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master colors")
lines.append("")
# Component Overrides
lines.append("### Component Overrides")
lines.append("")
components = page_overrides.get("components", [])
if components:
for comp in components:
lines.append(f"- {comp}")
else:
lines.append("- No overrides — use Master component specs")
lines.append("")
# Page-Specific Components
lines.append("---")
lines.append("")
lines.append("## Page-Specific Components")
lines.append("")
unique_components = page_overrides.get("unique_components", [])
if unique_components:
for comp in unique_components:
lines.append(f"- {comp}")
else:
lines.append("- No unique components for this page")
lines.append("")
# Recommendations
lines.append("---")
lines.append("")
lines.append("## Recommendations")
lines.append("")
recommendations = page_overrides.get("recommendations", [])
if recommendations:
for rec in recommendations:
lines.append(f"- {rec}")
lines.append("")
return "\n".join(lines)
def _generate_intelligent_overrides(page_name: str, page_query: str, design_system: dict) -> dict:
"""
Generate intelligent overrides based on page type using layered search.
Uses the existing search infrastructure to find relevant style, UX, and layout
data instead of hardcoded page types.
"""
from core import search
page_lower = page_name.lower()
query_lower = (page_query or "").lower()
combined_context = f"{page_lower} {query_lower}"
# Search across multiple domains for page-specific guidance
style_search = search(combined_context, "style", max_results=1)
ux_search = search(combined_context, "ux", max_results=3)
landing_search = search(combined_context, "landing", max_results=1)
# Extract results from search response
style_results = style_search.get("results", [])
ux_results = ux_search.get("results", [])
landing_results = landing_search.get("results", [])
# Detect page type from search results or context
page_type = _detect_page_type(combined_context, style_results)
# Build overrides from search results
layout = {}
spacing = {}
typography = {}
colors = {}
components = []
unique_components = []
recommendations = []
# Extract style-based overrides
if style_results:
style = style_results[0]
style_name = style.get("Style Category", "")
keywords = style.get("Keywords", "")
best_for = style.get("Best For", "")
effects = style.get("Effects & Animation", "")
# Infer layout from style keywords
if any(kw in keywords.lower() for kw in ["data", "dense", "dashboard", "grid"]):
layout["Max Width"] = "1400px or full-width"
layout["Grid"] = "12-column grid for data flexibility"
spacing["Content Density"] = "High — optimize for information display"
elif any(kw in keywords.lower() for kw in ["minimal", "simple", "clean", "single"]):
layout["Max Width"] = "800px (narrow, focused)"
layout["Layout"] = "Single column, centered"
spacing["Content Density"] = "Low — focus on clarity"
else:
layout["Max Width"] = "1200px (standard)"
layout["Layout"] = "Full-width sections, centered content"
if effects:
recommendations.append(f"Effects: {effects}")
# Extract UX guidelines as recommendations
for ux in ux_results:
category = ux.get("Category", "")
do_text = ux.get("Do", "")
dont_text = ux.get("Don't", "")
if do_text:
recommendations.append(f"{category}: {do_text}")
if dont_text:
components.append(f"Avoid: {dont_text}")
# Extract landing pattern info for section structure
if landing_results:
landing = landing_results[0]
sections = landing.get("Section Order", "")
cta_placement = landing.get("Primary CTA Placement", "")
color_strategy = landing.get("Color Strategy", "")
if sections:
layout["Sections"] = sections
if cta_placement:
recommendations.append(f"CTA Placement: {cta_placement}")
if color_strategy:
colors["Strategy"] = color_strategy
# Add page-type specific defaults if no search results
if not layout:
layout["Max Width"] = "1200px"
layout["Layout"] = "Responsive grid"
if not recommendations:
recommendations = [
"Refer to MASTER.md for all design rules",
"Add specific overrides as needed for this page"
]
return {
"page_type": page_type,
"layout": layout,
"spacing": spacing,
"typography": typography,
"colors": colors,
"components": components,
"unique_components": unique_components,
"recommendations": recommendations
}
def _detect_page_type(context: str, style_results: list) -> str:
"""Detect page type from context and search results."""
context_lower = context.lower()
# Check for common page type patterns
page_patterns = [
(["dashboard", "admin", "analytics", "data", "metrics", "stats", "monitor", "overview"], "Dashboard / Data View"),
(["checkout", "payment", "cart", "purchase", "order", "billing"], "Checkout / Payment"),
(["settings", "profile", "account", "preferences", "config"], "Settings / Profile"),
(["landing", "marketing", "homepage", "hero", "home", "promo"], "Landing / Marketing"),
(["login", "signin", "signup", "register", "auth", "password"], "Authentication"),
(["pricing", "plans", "subscription", "tiers", "packages"], "Pricing / Plans"),
(["blog", "article", "post", "news", "content", "story"], "Blog / Article"),
(["product", "item", "detail", "pdp", "shop", "store"], "Product Detail"),
(["search", "results", "browse", "filter", "catalog", "list"], "Search Results"),
(["empty", "404", "error", "not found", "zero"], "Empty State"),
]
for keywords, page_type in page_patterns:
if any(kw in context_lower for kw in keywords):
return page_type
# Fallback: try to infer from style results
if style_results:
style_name = style_results[0].get("Style Category", "").lower()
best_for = style_results[0].get("Best For", "").lower()
if "dashboard" in best_for or "data" in best_for:
return "Dashboard / Data View"
elif "landing" in best_for or "marketing" in best_for:
return "Landing / Marketing"
return "General"
# ============ CLI SUPPORT ============ # ============ CLI SUPPORT ============
if __name__ == "__main__": if __name__ == "__main__":
import argparse import argparse

View File

@@ -4,14 +4,19 @@
UI/UX Pro Max Search - BM25 search engine for UI/UX style guides UI/UX Pro Max Search - BM25 search engine for UI/UX style guides
Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3] Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3]
python search.py "<query>" --design-system [-p "Project Name"] python search.py "<query>" --design-system [-p "Project Name"]
python search.py "<query>" --design-system --persist [-p "Project Name"] [--page "dashboard"]
Domains: style, prompt, color, chart, landing, product, ux, typography Domains: style, prompt, color, chart, landing, product, ux, typography
Stacks: html-tailwind, react, nextjs Stacks: html-tailwind, react, nextjs
Persistence (Master + Overrides pattern):
--persist Save design system to design-system/MASTER.md
--page Also create a page-specific override file in design-system/pages/
""" """
import argparse import argparse
from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack
from design_system import generate_design_system from design_system import generate_design_system, persist_design_system
def format_output(result): def format_output(result):
@@ -51,13 +56,38 @@ if __name__ == "__main__":
parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation") parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation")
parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output") parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output")
parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system") parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system")
# Persistence (Master + Overrides pattern)
parser.add_argument("--persist", action="store_true", help="Save design system to design-system/MASTER.md (creates hierarchical structure)")
parser.add_argument("--page", type=str, default=None, help="Create page-specific override file in design-system/pages/")
parser.add_argument("--output-dir", "-o", type=str, default=None, help="Output directory for persisted files (default: current directory)")
args = parser.parse_args() args = parser.parse_args()
# Design system takes priority # Design system takes priority
if args.design_system: if args.design_system:
result = generate_design_system(args.query, args.project_name, args.format) result = generate_design_system(
args.query,
args.project_name,
args.format,
persist=args.persist,
page=args.page,
output_dir=args.output_dir
)
print(result) print(result)
# Print persistence confirmation
if args.persist:
project_slug = args.project_name.lower().replace(' ', '-') if args.project_name else "default"
print("\n" + "=" * 60)
print(f"✅ Design system persisted to design-system/{project_slug}/")
print(f" 📄 design-system/{project_slug}/MASTER.md (Global Source of Truth)")
if args.page:
page_filename = args.page.lower().replace(' ', '-')
print(f" 📄 design-system/{project_slug}/pages/{page_filename}.md (Page Overrides)")
print("")
print(f"📖 Usage: When building a page, check design-system/{project_slug}/pages/[page].md first.")
print(f" If exists, its rules override MASTER.md. Otherwise, use MASTER.md.")
print("=" * 60)
# Stack search # Stack search
elif args.stack: elif args.stack:
result = search_stack(args.query, args.stack, args.max_results) result = search_stack(args.query, args.stack, args.max_results)

View File

@@ -60,6 +60,31 @@ This command:
python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa" python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa"
``` ```
### Step 2b: Persist Design System (Master + Overrides Pattern)
To save the design system for hierarchical retrieval across sessions, add `--persist`:
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name"
```
This creates:
- `design-system/MASTER.md` — Global Source of Truth with all design rules
- `design-system/pages/` — Folder for page-specific overrides
**With page-specific override:**
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name" --page "dashboard"
```
This also creates:
- `design-system/pages/dashboard.md` — Page-specific deviations from Master
**How hierarchical retrieval works:**
1. When building a specific page (e.g., "Checkout"), first check `design-system/pages/checkout.md`
2. If the page file exists, its rules **override** the Master file
3. If not, use `design-system/MASTER.md` exclusively
### Step 3: Supplement with Detailed Searches (as needed) ### Step 3: Supplement with Detailed Searches (as needed)
After getting the design system, use domain searches to get additional details: After getting the design system, use domain searches to get additional details:
@@ -86,8 +111,8 @@ Get implementation-specific best practices. If user doesn't specify a stack, **d
python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind
``` ```
Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn` Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn`, `jetpack-compose`
, `jetpack-compose`
--- ---
## Search Reference ## Search Reference
@@ -120,6 +145,7 @@ Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`
| `react-native` | Components, Navigation, Lists | | `react-native` | Components, Navigation, Lists |
| `flutter` | Widgets, State, Layout, Theming | | `flutter` | Widgets, State, Layout, Theming |
| `shadcn` | shadcn/ui components, theming, forms, patterns | | `shadcn` | shadcn/ui components, theming, forms, patterns |
| `jetpack-compose` | Composables, Modifiers, State Hoisting, Recomposition |
--- ---

View File

@@ -7,10 +7,16 @@ to generate comprehensive design system recommendations.
Usage: Usage:
from design_system import generate_design_system from design_system import generate_design_system
result = generate_design_system("SaaS dashboard", "My Project") result = generate_design_system("SaaS dashboard", "My Project")
# With persistence (Master + Overrides pattern)
result = generate_design_system("SaaS dashboard", "My Project", persist=True)
result = generate_design_system("SaaS dashboard", "My Project", persist=True, page="dashboard")
""" """
import csv import csv
import json import json
import os
from datetime import datetime
from pathlib import Path from pathlib import Path
from core import search, DATA_DIR from core import search, DATA_DIR
@@ -452,7 +458,8 @@ def format_markdown(design_system: dict) -> str:
# ============ MAIN ENTRY POINT ============ # ============ MAIN ENTRY POINT ============
def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii") -> str: def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii",
persist: bool = False, page: str = None, output_dir: str = None) -> str:
""" """
Main entry point for design system generation. Main entry point for design system generation.
@@ -460,6 +467,9 @@ def generate_design_system(query: str, project_name: str = None, output_format:
query: Search query (e.g., "SaaS dashboard", "e-commerce luxury") query: Search query (e.g., "SaaS dashboard", "e-commerce luxury")
project_name: Optional project name for output header project_name: Optional project name for output header
output_format: "ascii" (default) or "markdown" output_format: "ascii" (default) or "markdown"
persist: If True, save design system to design-system/ folder
page: Optional page name for page-specific override file
output_dir: Optional output directory (defaults to current working directory)
Returns: Returns:
Formatted design system string Formatted design system string
@@ -467,11 +477,580 @@ def generate_design_system(query: str, project_name: str = None, output_format:
generator = DesignSystemGenerator() generator = DesignSystemGenerator()
design_system = generator.generate(query, project_name) design_system = generator.generate(query, project_name)
# Persist to files if requested
if persist:
persist_design_system(design_system, page, output_dir, query)
if output_format == "markdown": if output_format == "markdown":
return format_markdown(design_system) return format_markdown(design_system)
return format_ascii_box(design_system) return format_ascii_box(design_system)
# ============ PERSISTENCE FUNCTIONS ============
def persist_design_system(design_system: dict, page: str = None, output_dir: str = None, page_query: str = None) -> dict:
"""
Persist design system to design-system/<project>/ folder using Master + Overrides pattern.
Args:
design_system: The generated design system dictionary
page: Optional page name for page-specific override file
output_dir: Optional output directory (defaults to current working directory)
page_query: Optional query string for intelligent page override generation
Returns:
dict with created file paths and status
"""
base_dir = Path(output_dir) if output_dir else Path.cwd()
# Use project name for project-specific folder
project_name = design_system.get("project_name", "default")
project_slug = project_name.lower().replace(' ', '-')
design_system_dir = base_dir / "design-system" / project_slug
pages_dir = design_system_dir / "pages"
created_files = []
# Create directories
design_system_dir.mkdir(parents=True, exist_ok=True)
pages_dir.mkdir(parents=True, exist_ok=True)
master_file = design_system_dir / "MASTER.md"
# Generate and write MASTER.md
master_content = format_master_md(design_system)
with open(master_file, 'w', encoding='utf-8') as f:
f.write(master_content)
created_files.append(str(master_file))
# If page is specified, create page override file with intelligent content
if page:
page_file = pages_dir / f"{page.lower().replace(' ', '-')}.md"
page_content = format_page_override_md(design_system, page, page_query)
with open(page_file, 'w', encoding='utf-8') as f:
f.write(page_content)
created_files.append(str(page_file))
return {
"status": "success",
"design_system_dir": str(design_system_dir),
"created_files": created_files
}
def format_master_md(design_system: dict) -> str:
"""Format design system as MASTER.md with hierarchical override logic."""
project = design_system.get("project_name", "PROJECT")
pattern = design_system.get("pattern", {})
style = design_system.get("style", {})
colors = design_system.get("colors", {})
typography = design_system.get("typography", {})
effects = design_system.get("key_effects", "")
anti_patterns = design_system.get("anti_patterns", "")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
lines = []
# Logic header
lines.append("# Design System Master File")
lines.append("")
lines.append("> **LOGIC:** When building a specific page, first check `design-system/pages/[page-name].md`.")
lines.append("> If that file exists, its rules **override** this Master file.")
lines.append("> If not, strictly follow the rules below.")
lines.append("")
lines.append("---")
lines.append("")
lines.append(f"**Project:** {project}")
lines.append(f"**Generated:** {timestamp}")
lines.append(f"**Category:** {design_system.get('category', 'General')}")
lines.append("")
lines.append("---")
lines.append("")
# Global Rules section
lines.append("## Global Rules")
lines.append("")
# Color Palette
lines.append("### Color Palette")
lines.append("")
lines.append("| Role | Hex | CSS Variable |")
lines.append("|------|-----|--------------|")
lines.append(f"| Primary | `{colors.get('primary', '#2563EB')}` | `--color-primary` |")
lines.append(f"| Secondary | `{colors.get('secondary', '#3B82F6')}` | `--color-secondary` |")
lines.append(f"| CTA/Accent | `{colors.get('cta', '#F97316')}` | `--color-cta` |")
lines.append(f"| Background | `{colors.get('background', '#F8FAFC')}` | `--color-background` |")
lines.append(f"| Text | `{colors.get('text', '#1E293B')}` | `--color-text` |")
lines.append("")
if colors.get("notes"):
lines.append(f"**Color Notes:** {colors.get('notes', '')}")
lines.append("")
# Typography
lines.append("### Typography")
lines.append("")
lines.append(f"- **Heading Font:** {typography.get('heading', 'Inter')}")
lines.append(f"- **Body Font:** {typography.get('body', 'Inter')}")
if typography.get("mood"):
lines.append(f"- **Mood:** {typography.get('mood', '')}")
if typography.get("google_fonts_url"):
lines.append(f"- **Google Fonts:** [{typography.get('heading', '')} + {typography.get('body', '')}]({typography.get('google_fonts_url', '')})")
lines.append("")
if typography.get("css_import"):
lines.append("**CSS Import:**")
lines.append("```css")
lines.append(typography.get("css_import", ""))
lines.append("```")
lines.append("")
# Spacing Variables
lines.append("### Spacing Variables")
lines.append("")
lines.append("| Token | Value | Usage |")
lines.append("|-------|-------|-------|")
lines.append("| `--space-xs` | `4px` / `0.25rem` | Tight gaps |")
lines.append("| `--space-sm` | `8px` / `0.5rem` | Icon gaps, inline spacing |")
lines.append("| `--space-md` | `16px` / `1rem` | Standard padding |")
lines.append("| `--space-lg` | `24px` / `1.5rem` | Section padding |")
lines.append("| `--space-xl` | `32px` / `2rem` | Large gaps |")
lines.append("| `--space-2xl` | `48px` / `3rem` | Section margins |")
lines.append("| `--space-3xl` | `64px` / `4rem` | Hero padding |")
lines.append("")
# Shadow Depths
lines.append("### Shadow Depths")
lines.append("")
lines.append("| Level | Value | Usage |")
lines.append("|-------|-------|-------|")
lines.append("| `--shadow-sm` | `0 1px 2px rgba(0,0,0,0.05)` | Subtle lift |")
lines.append("| `--shadow-md` | `0 4px 6px rgba(0,0,0,0.1)` | Cards, buttons |")
lines.append("| `--shadow-lg` | `0 10px 15px rgba(0,0,0,0.1)` | Modals, dropdowns |")
lines.append("| `--shadow-xl` | `0 20px 25px rgba(0,0,0,0.15)` | Hero images, featured cards |")
lines.append("")
# Component Specs section
lines.append("---")
lines.append("")
lines.append("## Component Specs")
lines.append("")
# Buttons
lines.append("### Buttons")
lines.append("")
lines.append("```css")
lines.append("/* Primary Button */")
lines.append(".btn-primary {")
lines.append(f" background: {colors.get('cta', '#F97316')};")
lines.append(" color: white;")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("")
lines.append(".btn-primary:hover {")
lines.append(" opacity: 0.9;")
lines.append(" transform: translateY(-1px);")
lines.append("}")
lines.append("")
lines.append("/* Secondary Button */")
lines.append(".btn-secondary {")
lines.append(f" background: transparent;")
lines.append(f" color: {colors.get('primary', '#2563EB')};")
lines.append(f" border: 2px solid {colors.get('primary', '#2563EB')};")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("```")
lines.append("")
# Cards
lines.append("### Cards")
lines.append("")
lines.append("```css")
lines.append(".card {")
lines.append(f" background: {colors.get('background', '#FFFFFF')};")
lines.append(" border-radius: 12px;")
lines.append(" padding: 24px;")
lines.append(" box-shadow: var(--shadow-md);")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("")
lines.append(".card:hover {")
lines.append(" box-shadow: var(--shadow-lg);")
lines.append(" transform: translateY(-2px);")
lines.append("}")
lines.append("```")
lines.append("")
# Inputs
lines.append("### Inputs")
lines.append("")
lines.append("```css")
lines.append(".input {")
lines.append(" padding: 12px 16px;")
lines.append(" border: 1px solid #E2E8F0;")
lines.append(" border-radius: 8px;")
lines.append(" font-size: 16px;")
lines.append(" transition: border-color 200ms ease;")
lines.append("}")
lines.append("")
lines.append(".input:focus {")
lines.append(f" border-color: {colors.get('primary', '#2563EB')};")
lines.append(" outline: none;")
lines.append(f" box-shadow: 0 0 0 3px {colors.get('primary', '#2563EB')}20;")
lines.append("}")
lines.append("```")
lines.append("")
# Modals
lines.append("### Modals")
lines.append("")
lines.append("```css")
lines.append(".modal-overlay {")
lines.append(" background: rgba(0, 0, 0, 0.5);")
lines.append(" backdrop-filter: blur(4px);")
lines.append("}")
lines.append("")
lines.append(".modal {")
lines.append(" background: white;")
lines.append(" border-radius: 16px;")
lines.append(" padding: 32px;")
lines.append(" box-shadow: var(--shadow-xl);")
lines.append(" max-width: 500px;")
lines.append(" width: 90%;")
lines.append("}")
lines.append("```")
lines.append("")
# Style section
lines.append("---")
lines.append("")
lines.append("## Style Guidelines")
lines.append("")
lines.append(f"**Style:** {style.get('name', 'Minimalism')}")
lines.append("")
if style.get("keywords"):
lines.append(f"**Keywords:** {style.get('keywords', '')}")
lines.append("")
if style.get("best_for"):
lines.append(f"**Best For:** {style.get('best_for', '')}")
lines.append("")
if effects:
lines.append(f"**Key Effects:** {effects}")
lines.append("")
# Layout Pattern
lines.append("### Page Pattern")
lines.append("")
lines.append(f"**Pattern Name:** {pattern.get('name', '')}")
lines.append("")
if pattern.get('conversion'):
lines.append(f"- **Conversion Strategy:** {pattern.get('conversion', '')}")
if pattern.get('cta_placement'):
lines.append(f"- **CTA Placement:** {pattern.get('cta_placement', '')}")
lines.append(f"- **Section Order:** {pattern.get('sections', '')}")
lines.append("")
# Anti-Patterns section
lines.append("---")
lines.append("")
lines.append("## Anti-Patterns (Do NOT Use)")
lines.append("")
if anti_patterns:
anti_list = [a.strip() for a in anti_patterns.split("+")]
for anti in anti_list:
if anti:
lines.append(f"- ❌ {anti}")
lines.append("")
lines.append("### Additional Forbidden Patterns")
lines.append("")
lines.append("- ❌ **Emojis as icons** — Use SVG icons (Heroicons, Lucide, Simple Icons)")
lines.append("- ❌ **Missing cursor:pointer** — All clickable elements must have cursor:pointer")
lines.append("- ❌ **Layout-shifting hovers** — Avoid scale transforms that shift layout")
lines.append("- ❌ **Low contrast text** — Maintain 4.5:1 minimum contrast ratio")
lines.append("- ❌ **Instant state changes** — Always use transitions (150-300ms)")
lines.append("- ❌ **Invisible focus states** — Focus states must be visible for a11y")
lines.append("")
# Pre-Delivery Checklist
lines.append("---")
lines.append("")
lines.append("## Pre-Delivery Checklist")
lines.append("")
lines.append("Before delivering any UI code, verify:")
lines.append("")
lines.append("- [ ] No emojis used as icons (use SVG instead)")
lines.append("- [ ] All icons from consistent icon set (Heroicons/Lucide)")
lines.append("- [ ] `cursor-pointer` on all clickable elements")
lines.append("- [ ] Hover states with smooth transitions (150-300ms)")
lines.append("- [ ] Light mode: text contrast 4.5:1 minimum")
lines.append("- [ ] Focus states visible for keyboard navigation")
lines.append("- [ ] `prefers-reduced-motion` respected")
lines.append("- [ ] Responsive: 375px, 768px, 1024px, 1440px")
lines.append("- [ ] No content hidden behind fixed navbars")
lines.append("- [ ] No horizontal scroll on mobile")
lines.append("")
return "\n".join(lines)
def format_page_override_md(design_system: dict, page_name: str, page_query: str = None) -> str:
"""Format a page-specific override file with intelligent AI-generated content."""
project = design_system.get("project_name", "PROJECT")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
page_title = page_name.replace("-", " ").replace("_", " ").title()
# Detect page type and generate intelligent overrides
page_overrides = _generate_intelligent_overrides(page_name, page_query, design_system)
lines = []
lines.append(f"# {page_title} Page Overrides")
lines.append("")
lines.append(f"> **PROJECT:** {project}")
lines.append(f"> **Generated:** {timestamp}")
lines.append(f"> **Page Type:** {page_overrides.get('page_type', 'General')}")
lines.append("")
lines.append("> ⚠️ **IMPORTANT:** Rules in this file **override** the Master file (`design-system/MASTER.md`).")
lines.append("> Only deviations from the Master are documented here. For all other rules, refer to the Master.")
lines.append("")
lines.append("---")
lines.append("")
# Page-specific rules with actual content
lines.append("## Page-Specific Rules")
lines.append("")
# Layout Overrides
lines.append("### Layout Overrides")
lines.append("")
layout = page_overrides.get("layout", {})
if layout:
for key, value in layout.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master layout")
lines.append("")
# Spacing Overrides
lines.append("### Spacing Overrides")
lines.append("")
spacing = page_overrides.get("spacing", {})
if spacing:
for key, value in spacing.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master spacing")
lines.append("")
# Typography Overrides
lines.append("### Typography Overrides")
lines.append("")
typography = page_overrides.get("typography", {})
if typography:
for key, value in typography.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master typography")
lines.append("")
# Color Overrides
lines.append("### Color Overrides")
lines.append("")
colors = page_overrides.get("colors", {})
if colors:
for key, value in colors.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master colors")
lines.append("")
# Component Overrides
lines.append("### Component Overrides")
lines.append("")
components = page_overrides.get("components", [])
if components:
for comp in components:
lines.append(f"- {comp}")
else:
lines.append("- No overrides — use Master component specs")
lines.append("")
# Page-Specific Components
lines.append("---")
lines.append("")
lines.append("## Page-Specific Components")
lines.append("")
unique_components = page_overrides.get("unique_components", [])
if unique_components:
for comp in unique_components:
lines.append(f"- {comp}")
else:
lines.append("- No unique components for this page")
lines.append("")
# Recommendations
lines.append("---")
lines.append("")
lines.append("## Recommendations")
lines.append("")
recommendations = page_overrides.get("recommendations", [])
if recommendations:
for rec in recommendations:
lines.append(f"- {rec}")
lines.append("")
return "\n".join(lines)
def _generate_intelligent_overrides(page_name: str, page_query: str, design_system: dict) -> dict:
"""
Generate intelligent overrides based on page type using layered search.
Uses the existing search infrastructure to find relevant style, UX, and layout
data instead of hardcoded page types.
"""
from core import search
page_lower = page_name.lower()
query_lower = (page_query or "").lower()
combined_context = f"{page_lower} {query_lower}"
# Search across multiple domains for page-specific guidance
style_search = search(combined_context, "style", max_results=1)
ux_search = search(combined_context, "ux", max_results=3)
landing_search = search(combined_context, "landing", max_results=1)
# Extract results from search response
style_results = style_search.get("results", [])
ux_results = ux_search.get("results", [])
landing_results = landing_search.get("results", [])
# Detect page type from search results or context
page_type = _detect_page_type(combined_context, style_results)
# Build overrides from search results
layout = {}
spacing = {}
typography = {}
colors = {}
components = []
unique_components = []
recommendations = []
# Extract style-based overrides
if style_results:
style = style_results[0]
style_name = style.get("Style Category", "")
keywords = style.get("Keywords", "")
best_for = style.get("Best For", "")
effects = style.get("Effects & Animation", "")
# Infer layout from style keywords
if any(kw in keywords.lower() for kw in ["data", "dense", "dashboard", "grid"]):
layout["Max Width"] = "1400px or full-width"
layout["Grid"] = "12-column grid for data flexibility"
spacing["Content Density"] = "High — optimize for information display"
elif any(kw in keywords.lower() for kw in ["minimal", "simple", "clean", "single"]):
layout["Max Width"] = "800px (narrow, focused)"
layout["Layout"] = "Single column, centered"
spacing["Content Density"] = "Low — focus on clarity"
else:
layout["Max Width"] = "1200px (standard)"
layout["Layout"] = "Full-width sections, centered content"
if effects:
recommendations.append(f"Effects: {effects}")
# Extract UX guidelines as recommendations
for ux in ux_results:
category = ux.get("Category", "")
do_text = ux.get("Do", "")
dont_text = ux.get("Don't", "")
if do_text:
recommendations.append(f"{category}: {do_text}")
if dont_text:
components.append(f"Avoid: {dont_text}")
# Extract landing pattern info for section structure
if landing_results:
landing = landing_results[0]
sections = landing.get("Section Order", "")
cta_placement = landing.get("Primary CTA Placement", "")
color_strategy = landing.get("Color Strategy", "")
if sections:
layout["Sections"] = sections
if cta_placement:
recommendations.append(f"CTA Placement: {cta_placement}")
if color_strategy:
colors["Strategy"] = color_strategy
# Add page-type specific defaults if no search results
if not layout:
layout["Max Width"] = "1200px"
layout["Layout"] = "Responsive grid"
if not recommendations:
recommendations = [
"Refer to MASTER.md for all design rules",
"Add specific overrides as needed for this page"
]
return {
"page_type": page_type,
"layout": layout,
"spacing": spacing,
"typography": typography,
"colors": colors,
"components": components,
"unique_components": unique_components,
"recommendations": recommendations
}
def _detect_page_type(context: str, style_results: list) -> str:
"""Detect page type from context and search results."""
context_lower = context.lower()
# Check for common page type patterns
page_patterns = [
(["dashboard", "admin", "analytics", "data", "metrics", "stats", "monitor", "overview"], "Dashboard / Data View"),
(["checkout", "payment", "cart", "purchase", "order", "billing"], "Checkout / Payment"),
(["settings", "profile", "account", "preferences", "config"], "Settings / Profile"),
(["landing", "marketing", "homepage", "hero", "home", "promo"], "Landing / Marketing"),
(["login", "signin", "signup", "register", "auth", "password"], "Authentication"),
(["pricing", "plans", "subscription", "tiers", "packages"], "Pricing / Plans"),
(["blog", "article", "post", "news", "content", "story"], "Blog / Article"),
(["product", "item", "detail", "pdp", "shop", "store"], "Product Detail"),
(["search", "results", "browse", "filter", "catalog", "list"], "Search Results"),
(["empty", "404", "error", "not found", "zero"], "Empty State"),
]
for keywords, page_type in page_patterns:
if any(kw in context_lower for kw in keywords):
return page_type
# Fallback: try to infer from style results
if style_results:
style_name = style_results[0].get("Style Category", "").lower()
best_for = style_results[0].get("Best For", "").lower()
if "dashboard" in best_for or "data" in best_for:
return "Dashboard / Data View"
elif "landing" in best_for or "marketing" in best_for:
return "Landing / Marketing"
return "General"
# ============ CLI SUPPORT ============ # ============ CLI SUPPORT ============
if __name__ == "__main__": if __name__ == "__main__":
import argparse import argparse

View File

@@ -4,14 +4,19 @@
UI/UX Pro Max Search - BM25 search engine for UI/UX style guides UI/UX Pro Max Search - BM25 search engine for UI/UX style guides
Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3] Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3]
python search.py "<query>" --design-system [-p "Project Name"] python search.py "<query>" --design-system [-p "Project Name"]
python search.py "<query>" --design-system --persist [-p "Project Name"] [--page "dashboard"]
Domains: style, prompt, color, chart, landing, product, ux, typography Domains: style, prompt, color, chart, landing, product, ux, typography
Stacks: html-tailwind, react, nextjs Stacks: html-tailwind, react, nextjs
Persistence (Master + Overrides pattern):
--persist Save design system to design-system/MASTER.md
--page Also create a page-specific override file in design-system/pages/
""" """
import argparse import argparse
from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack
from design_system import generate_design_system from design_system import generate_design_system, persist_design_system
def format_output(result): def format_output(result):
@@ -51,13 +56,38 @@ if __name__ == "__main__":
parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation") parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation")
parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output") parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output")
parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system") parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system")
# Persistence (Master + Overrides pattern)
parser.add_argument("--persist", action="store_true", help="Save design system to design-system/MASTER.md (creates hierarchical structure)")
parser.add_argument("--page", type=str, default=None, help="Create page-specific override file in design-system/pages/")
parser.add_argument("--output-dir", "-o", type=str, default=None, help="Output directory for persisted files (default: current directory)")
args = parser.parse_args() args = parser.parse_args()
# Design system takes priority # Design system takes priority
if args.design_system: if args.design_system:
result = generate_design_system(args.query, args.project_name, args.format) result = generate_design_system(
args.query,
args.project_name,
args.format,
persist=args.persist,
page=args.page,
output_dir=args.output_dir
)
print(result) print(result)
# Print persistence confirmation
if args.persist:
project_slug = args.project_name.lower().replace(' ', '-') if args.project_name else "default"
print("\n" + "=" * 60)
print(f"✅ Design system persisted to design-system/{project_slug}/")
print(f" 📄 design-system/{project_slug}/MASTER.md (Global Source of Truth)")
if args.page:
page_filename = args.page.lower().replace(' ', '-')
print(f" 📄 design-system/{project_slug}/pages/{page_filename}.md (Page Overrides)")
print("")
print(f"📖 Usage: When building a page, check design-system/{project_slug}/pages/[page].md first.")
print(f" If exists, its rules override MASTER.md. Otherwise, use MASTER.md.")
print("=" * 60)
# Stack search # Stack search
elif args.stack: elif args.stack:
result = search_stack(args.query, args.stack, args.max_results) result = search_stack(args.query, args.stack, args.max_results)

View File

@@ -86,7 +86,7 @@ Get implementation-specific best practices. If user doesn't specify a stack, **d
python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind
``` ```
Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn` Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`,`jetpack-compose`, `shadcn`
--- ---
@@ -120,6 +120,7 @@ Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`
| `react-native` | Components, Navigation, Lists | | `react-native` | Components, Navigation, Lists |
| `flutter` | Widgets, State, Layout, Theming | | `flutter` | Widgets, State, Layout, Theming |
| `shadcn` | shadcn/ui components, theming, forms, patterns | | `shadcn` | shadcn/ui components, theming, forms, patterns |
| `jetpack-compose` | Composables, Modifiers, State Hoisting, Recomposition |
--- ---

View File

@@ -60,6 +60,31 @@ This command:
python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa" python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa"
``` ```
### Step 2b: Persist Design System (Master + Overrides Pattern)
To save the design system for hierarchical retrieval across sessions, add `--persist`:
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name"
```
This creates:
- `design-system/MASTER.md` — Global Source of Truth with all design rules
- `design-system/pages/` — Folder for page-specific overrides
**With page-specific override:**
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name" --page "dashboard"
```
This also creates:
- `design-system/pages/dashboard.md` — Page-specific deviations from Master
**How hierarchical retrieval works:**
1. When building a specific page (e.g., "Checkout"), first check `design-system/pages/checkout.md`
2. If the page file exists, its rules **override** the Master file
3. If not, use `design-system/MASTER.md` exclusively
### Step 3: Supplement with Detailed Searches (as needed) ### Step 3: Supplement with Detailed Searches (as needed)
After getting the design system, use domain searches to get additional details: After getting the design system, use domain searches to get additional details:
@@ -86,8 +111,8 @@ Get implementation-specific best practices. If user doesn't specify a stack, **d
python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind
``` ```
Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn` Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn`, `jetpack-compose`
, `jetpack-compose`
--- ---
## Search Reference ## Search Reference
@@ -120,6 +145,7 @@ Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`
| `react-native` | Components, Navigation, Lists | | `react-native` | Components, Navigation, Lists |
| `flutter` | Widgets, State, Layout, Theming | | `flutter` | Widgets, State, Layout, Theming |
| `shadcn` | shadcn/ui components, theming, forms, patterns | | `shadcn` | shadcn/ui components, theming, forms, patterns |
| `jetpack-compose` | Composables, Modifiers, State Hoisting, Recomposition |
--- ---

View File

@@ -60,6 +60,31 @@ This command:
python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa" python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa"
``` ```
### Step 2b: Persist Design System (Master + Overrides Pattern)
To save the design system for hierarchical retrieval across sessions, add `--persist`:
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name"
```
This creates:
- `design-system/MASTER.md` — Global Source of Truth with all design rules
- `design-system/pages/` — Folder for page-specific overrides
**With page-specific override:**
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name" --page "dashboard"
```
This also creates:
- `design-system/pages/dashboard.md` — Page-specific deviations from Master
**How hierarchical retrieval works:**
1. When building a specific page (e.g., "Checkout"), first check `design-system/pages/checkout.md`
2. If the page file exists, its rules **override** the Master file
3. If not, use `design-system/MASTER.md` exclusively
### Step 3: Supplement with Detailed Searches (as needed) ### Step 3: Supplement with Detailed Searches (as needed)
After getting the design system, use domain searches to get additional details: After getting the design system, use domain searches to get additional details:
@@ -86,8 +111,8 @@ Get implementation-specific best practices. If user doesn't specify a stack, **d
python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind
``` ```
Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn` Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn`, `jetpack-compose`
, `jetpack-compose`
--- ---
## Search Reference ## Search Reference
@@ -120,6 +145,7 @@ Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`
| `react-native` | Components, Navigation, Lists | | `react-native` | Components, Navigation, Lists |
| `flutter` | Widgets, State, Layout, Theming | | `flutter` | Widgets, State, Layout, Theming |
| `shadcn` | shadcn/ui components, theming, forms, patterns | | `shadcn` | shadcn/ui components, theming, forms, patterns |
| `jetpack-compose` | Composables, Modifiers, State Hoisting, Recomposition |
--- ---

View File

@@ -60,6 +60,31 @@ This command:
python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa" python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa"
``` ```
### Step 2b: Persist Design System (Master + Overrides Pattern)
To save the design system for hierarchical retrieval across sessions, add `--persist`:
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name"
```
This creates:
- `design-system/MASTER.md` — Global Source of Truth with all design rules
- `design-system/pages/` — Folder for page-specific overrides
**With page-specific override:**
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name" --page "dashboard"
```
This also creates:
- `design-system/pages/dashboard.md` — Page-specific deviations from Master
**How hierarchical retrieval works:**
1. When building a specific page (e.g., "Checkout"), first check `design-system/pages/checkout.md`
2. If the page file exists, its rules **override** the Master file
3. If not, use `design-system/MASTER.md` exclusively
### Step 3: Supplement with Detailed Searches (as needed) ### Step 3: Supplement with Detailed Searches (as needed)
After getting the design system, use domain searches to get additional details: After getting the design system, use domain searches to get additional details:
@@ -86,8 +111,8 @@ Get implementation-specific best practices. If user doesn't specify a stack, **d
python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind
``` ```
Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn` Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn`, `jetpack-compose`
, `jetpack-compose`
--- ---
## Search Reference ## Search Reference
@@ -120,6 +145,7 @@ Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`
| `react-native` | Components, Navigation, Lists | | `react-native` | Components, Navigation, Lists |
| `flutter` | Widgets, State, Layout, Theming | | `flutter` | Widgets, State, Layout, Theming |
| `shadcn` | shadcn/ui components, theming, forms, patterns | | `shadcn` | shadcn/ui components, theming, forms, patterns |
| `jetpack-compose` | Composables, Modifiers, State Hoisting, Recomposition |
--- ---

View File

@@ -0,0 +1,53 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Composable,Pure UI composables,Composable functions should only render UI,Accept state and callbacks,Calling usecase/repo,Pure UI composable,Business logic in UI,High,https://developer.android.com/jetpack/compose/mental-model
2,Composable,Small composables,Each composable has single responsibility,Split into components,Huge composable,Reusable UI,Monolithic UI,Medium,
3,Composable,Stateless by default,Prefer stateless composables,Hoist state,Local mutable state,Stateless UI,Hidden state,High,https://developer.android.com/jetpack/compose/state#state-hoisting
4,State,Single source of truth,UI state comes from one source,StateFlow from VM,Multiple states,Unified UiState,Scattered state,High,https://developer.android.com/topic/architecture/ui-layer
5,State,Model UI State,Use sealed interface/data class,UiState.Loading,Boolean flags,Explicit state,Flag hell,High,
6,State,remember only UI state,remember for UI-only state,"Scroll, animation",Business state,Correct remember,Misuse remember,High,https://developer.android.com/jetpack/compose/state
7,State,rememberSaveable,Persist state across config,rememberSaveable,remember,State survives,State lost,High,https://developer.android.com/jetpack/compose/state#restore-ui-state
8,State,derivedStateOf,Optimize recomposition,derivedStateOf,Recompute always,Optimized,Jank,Medium,https://developer.android.com/jetpack/compose/performance
9,SideEffect,LaunchedEffect keys,Use correct keys,LaunchedEffect(id),LaunchedEffect(Unit),Scoped effect,Infinite loop,High,https://developer.android.com/jetpack/compose/side-effects
10,SideEffect,rememberUpdatedState,Avoid stale lambdas,rememberUpdatedState,Capture directly,Safe callback,Stale state,Medium,https://developer.android.com/jetpack/compose/side-effects
11,SideEffect,DisposableEffect,Clean up resources,onDispose,No cleanup,No leak,Memory leak,High,
12,Architecture,Unidirectional data flow,UI → VM → State,onEvent,Two-way binding,Predictable flow,Hard debug,High,https://developer.android.com/topic/architecture
13,Architecture,No business logic in UI,Logic belongs to VM,Collect state,Call repo,Clean UI,Fat UI,High,
14,Architecture,Expose immutable state,Expose StateFlow,asStateFlow,Mutable exposed,Safe API,State mutation,High,
15,Lifecycle,Lifecycle-aware collect,Use collectAsStateWithLifecycle,Lifecycle aware,collectAsState,No leak,Leak,High,https://developer.android.com/jetpack/compose/lifecycle
16,Navigation,Event-based navigation,VM emits navigation event,"VM: Channel + receiveAsFlow(), V: Collect with Dispatchers.Main.immediate",Nav in UI,Decoupled nav,Using State / SharedFlow for navigation -> event is replayed and navigation fires again (StateFlow),High,https://developer.android.com/jetpack/compose/navigation
17,Navigation,Typed routes,Use sealed routes,sealed class Route,String routes,Type-safe,Runtime crash,Medium,
18,Performance,Stable parameters,Prefer immutable/stable params,@Immutable,Mutable params,Stable recomposition,Extra recomposition,High,https://developer.android.com/jetpack/compose/performance
19,Performance,Use key in Lazy,Provide stable keys,key=id,No key,Stable list,Item jump,High,
20,Performance,Avoid heavy work,No heavy computation in UI,Precompute in VM,Compute in UI,Smooth UI,Jank,High,
21,Performance,Remember expensive objects,remember heavy objects,remember,Recreate each recomposition,Efficient,Wasteful,Medium,
22,Theming,Design system,Centralized theme,Material3 tokens,Hardcoded values,Consistent UI,Inconsistent,High,https://developer.android.com/jetpack/compose/themes
23,Theming,Dark mode support,Theme-based colors,colorScheme,Fixed color,Adaptive UI,Broken dark,Medium,
24,Layout,Prefer Modifier over extra layouts,Use Modifier to adjust layout instead of adding wrapper composables,Use Modifier.padding(),Wrap content with extra Box,Padding via modifier,Box just for padding,High,https://developer.android.com/jetpack/compose/modifiers
25,Layout,Avoid deep layout nesting,Deep layout trees increase measure & layout cost,Keep layout flat,Box ? Column ? Box ? Row,Flat hierarchy,Deep nested tree,High,
26,Layout,Use Row/Column for linear layout,Linear layouts are simpler and more performant,Use Row / Column,Custom layout for simple cases,Row/Column usage,Over-engineered layout,High,
27,Layout,Use Box only for overlapping content,Box should be used only when children overlap,Stack elements,Use Box as Column,Proper overlay,Misused Box,Medium,
28,Layout,Prefer LazyColumn over Column scroll,Lazy layouts are virtualized and efficient,LazyColumn,Column.verticalScroll(),Lazy list,Scrollable Column,High,https://developer.android.com/jetpack/compose/lists
29,Layout,Avoid nested scroll containers,Nested scrolling causes UX & performance issues,Single scroll container,Scroll inside scroll,One scroll per screen,Nested scroll,High,
30,Layout,Avoid fillMaxSize by default,fillMaxSize may break parent constraints,Use exact size,Fill max everywhere,Constraint-aware size,Overfilled layout,Medium,
31,Layout,Avoid intrinsic size unless necessary,Intrinsic measurement is expensive,Explicit sizing,IntrinsicSize.Min,Predictable layout,Expensive measure,High,https://developer.android.com/jetpack/compose/layout/intrinsics
32,Layout,Use Arrangement and Alignment APIs,Declare layout intent explicitly,Use Arrangement / Alignment,Manual spacing hacks,Declarative spacing,Magic spacing,High,
33,Layout,Extract reusable layout patterns,Repeated layouts should be shared,Create layout composable,Copy-paste layouts,Reusable scaffold,Duplicated layout,High,
34,Theming,No hardcoded text style,Use typography,MaterialTheme.typography,Hardcode sp,Scalable,Inconsistent,Medium,
35,Testing,Stateless UI testing,Composable easy to test,Pass state,Hidden state,Testable,Hard test,High,https://developer.android.com/jetpack/compose/testing
36,Testing,Use testTag,Stable UI selectors,Modifier.testTag,Find by text,Stable tests,Flaky tests,Medium,
37,Preview,Multiple previews,Preview multiple states,@Preview,Single preview,Better dev UX,Misleading,Low,https://developer.android.com/jetpack/compose/tooling/preview
38,DI,Inject VM via Hilt,Use hiltViewModel,@HiltViewModel,Manual VM,Clean DI,Coupling,High,https://developer.android.com/training/dependency-injection/hilt-jetpack
39,DI,No DI in UI,Inject in VM,Constructor inject,Inject composable,Proper scope,Wrong scope,High,
40,Accessibility,Content description,Accessible UI,contentDescription,Ignore a11y,Inclusive,A11y fail,Medium,https://developer.android.com/jetpack/compose/accessibility
41,Accessibility,Semantics,Use semantics API,Modifier.semantics,None,Testable a11y,Invisible,Medium,
42,Animation,Compose animation APIs,Use animate*AsState,AnimatedVisibility,Manual anim,Smooth,Jank,Medium,https://developer.android.com/jetpack/compose/animation
43,Animation,Avoid animation logic in VM,Animation is UI concern,Animate in UI,Animate in VM,Correct layering,Mixed concern,Low,
44,Modularization,Feature-based UI modules,UI per feature,:feature:ui,God module,Scalable,Tight coupling,High,https://developer.android.com/topic/modularization
45,Modularization,Public UI contracts,Expose minimal UI API,Interface/Route,Expose impl,Encapsulated,Leaky module,Medium,
46,State,Snapshot state only,Use Compose state,mutableStateOf,Custom observable,Compose aware,Buggy UI,Medium,
47,State,Avoid mutable collections,Immutable list/map,PersistentList,MutableList,Stable UI,Silent bug,High,
48,Lifecycle,RememberCoroutineScope usage,Only for UI jobs,UI coroutine,Long jobs,Scoped job,Leak,Medium,https://developer.android.com/jetpack/compose/side-effects#remembercoroutinescope
49,Interop,Interop View carefully,Use AndroidView,Isolated usage,Mix everywhere,Safe interop,Messy UI,Low,https://developer.android.com/jetpack/compose/interop
50,Interop,Avoid legacy patterns,No LiveData in UI,StateFlow,LiveData,Modern stack,Legacy debt,Medium,
51,Debug,Use layout inspector,Inspect recomposition,Tools,Blind debug,Fast debug,Guessing,Low,https://developer.android.com/studio/debug/layout-inspector
52,Debug,Enable recomposition counts,Track recomposition,Debug flags,Ignore,Performance aware,Hidden jank,Low,
1 No Category Guideline Description Do Don't Code Good Code Bad Severity Docs URL
2 1 Composable Pure UI composables Composable functions should only render UI Accept state and callbacks Calling usecase/repo Pure UI composable Business logic in UI High https://developer.android.com/jetpack/compose/mental-model
3 2 Composable Small composables Each composable has single responsibility Split into components Huge composable Reusable UI Monolithic UI Medium
4 3 Composable Stateless by default Prefer stateless composables Hoist state Local mutable state Stateless UI Hidden state High https://developer.android.com/jetpack/compose/state#state-hoisting
5 4 State Single source of truth UI state comes from one source StateFlow from VM Multiple states Unified UiState Scattered state High https://developer.android.com/topic/architecture/ui-layer
6 5 State Model UI State Use sealed interface/data class UiState.Loading Boolean flags Explicit state Flag hell High
7 6 State remember only UI state remember for UI-only state Scroll, animation Business state Correct remember Misuse remember High https://developer.android.com/jetpack/compose/state
8 7 State rememberSaveable Persist state across config rememberSaveable remember State survives State lost High https://developer.android.com/jetpack/compose/state#restore-ui-state
9 8 State derivedStateOf Optimize recomposition derivedStateOf Recompute always Optimized Jank Medium https://developer.android.com/jetpack/compose/performance
10 9 SideEffect LaunchedEffect keys Use correct keys LaunchedEffect(id) LaunchedEffect(Unit) Scoped effect Infinite loop High https://developer.android.com/jetpack/compose/side-effects
11 10 SideEffect rememberUpdatedState Avoid stale lambdas rememberUpdatedState Capture directly Safe callback Stale state Medium https://developer.android.com/jetpack/compose/side-effects
12 11 SideEffect DisposableEffect Clean up resources onDispose No cleanup No leak Memory leak High
13 12 Architecture Unidirectional data flow UI → VM → State onEvent Two-way binding Predictable flow Hard debug High https://developer.android.com/topic/architecture
14 13 Architecture No business logic in UI Logic belongs to VM Collect state Call repo Clean UI Fat UI High
15 14 Architecture Expose immutable state Expose StateFlow asStateFlow Mutable exposed Safe API State mutation High
16 15 Lifecycle Lifecycle-aware collect Use collectAsStateWithLifecycle Lifecycle aware collectAsState No leak Leak High https://developer.android.com/jetpack/compose/lifecycle
17 16 Navigation Event-based navigation VM emits navigation event VM: Channel + receiveAsFlow(), V: Collect with Dispatchers.Main.immediate Nav in UI Decoupled nav Using State / SharedFlow for navigation -> event is replayed and navigation fires again (StateFlow) High https://developer.android.com/jetpack/compose/navigation
18 17 Navigation Typed routes Use sealed routes sealed class Route String routes Type-safe Runtime crash Medium
19 18 Performance Stable parameters Prefer immutable/stable params @Immutable Mutable params Stable recomposition Extra recomposition High https://developer.android.com/jetpack/compose/performance
20 19 Performance Use key in Lazy Provide stable keys key=id No key Stable list Item jump High
21 20 Performance Avoid heavy work No heavy computation in UI Precompute in VM Compute in UI Smooth UI Jank High
22 21 Performance Remember expensive objects remember heavy objects remember Recreate each recomposition Efficient Wasteful Medium
23 22 Theming Design system Centralized theme Material3 tokens Hardcoded values Consistent UI Inconsistent High https://developer.android.com/jetpack/compose/themes
24 23 Theming Dark mode support Theme-based colors colorScheme Fixed color Adaptive UI Broken dark Medium
25 24 Layout Prefer Modifier over extra layouts Use Modifier to adjust layout instead of adding wrapper composables Use Modifier.padding() Wrap content with extra Box Padding via modifier Box just for padding High https://developer.android.com/jetpack/compose/modifiers
26 25 Layout Avoid deep layout nesting Deep layout trees increase measure & layout cost Keep layout flat Box ? Column ? Box ? Row Flat hierarchy Deep nested tree High
27 26 Layout Use Row/Column for linear layout Linear layouts are simpler and more performant Use Row / Column Custom layout for simple cases Row/Column usage Over-engineered layout High
28 27 Layout Use Box only for overlapping content Box should be used only when children overlap Stack elements Use Box as Column Proper overlay Misused Box Medium
29 28 Layout Prefer LazyColumn over Column scroll Lazy layouts are virtualized and efficient LazyColumn Column.verticalScroll() Lazy list Scrollable Column High https://developer.android.com/jetpack/compose/lists
30 29 Layout Avoid nested scroll containers Nested scrolling causes UX & performance issues Single scroll container Scroll inside scroll One scroll per screen Nested scroll High
31 30 Layout Avoid fillMaxSize by default fillMaxSize may break parent constraints Use exact size Fill max everywhere Constraint-aware size Overfilled layout Medium
32 31 Layout Avoid intrinsic size unless necessary Intrinsic measurement is expensive Explicit sizing IntrinsicSize.Min Predictable layout Expensive measure High https://developer.android.com/jetpack/compose/layout/intrinsics
33 32 Layout Use Arrangement and Alignment APIs Declare layout intent explicitly Use Arrangement / Alignment Manual spacing hacks Declarative spacing Magic spacing High
34 33 Layout Extract reusable layout patterns Repeated layouts should be shared Create layout composable Copy-paste layouts Reusable scaffold Duplicated layout High
35 34 Theming No hardcoded text style Use typography MaterialTheme.typography Hardcode sp Scalable Inconsistent Medium
36 35 Testing Stateless UI testing Composable easy to test Pass state Hidden state Testable Hard test High https://developer.android.com/jetpack/compose/testing
37 36 Testing Use testTag Stable UI selectors Modifier.testTag Find by text Stable tests Flaky tests Medium
38 37 Preview Multiple previews Preview multiple states @Preview Single preview Better dev UX Misleading Low https://developer.android.com/jetpack/compose/tooling/preview
39 38 DI Inject VM via Hilt Use hiltViewModel @HiltViewModel Manual VM Clean DI Coupling High https://developer.android.com/training/dependency-injection/hilt-jetpack
40 39 DI No DI in UI Inject in VM Constructor inject Inject composable Proper scope Wrong scope High
41 40 Accessibility Content description Accessible UI contentDescription Ignore a11y Inclusive A11y fail Medium https://developer.android.com/jetpack/compose/accessibility
42 41 Accessibility Semantics Use semantics API Modifier.semantics None Testable a11y Invisible Medium
43 42 Animation Compose animation APIs Use animate*AsState AnimatedVisibility Manual anim Smooth Jank Medium https://developer.android.com/jetpack/compose/animation
44 43 Animation Avoid animation logic in VM Animation is UI concern Animate in UI Animate in VM Correct layering Mixed concern Low
45 44 Modularization Feature-based UI modules UI per feature :feature:ui God module Scalable Tight coupling High https://developer.android.com/topic/modularization
46 45 Modularization Public UI contracts Expose minimal UI API Interface/Route Expose impl Encapsulated Leaky module Medium
47 46 State Snapshot state only Use Compose state mutableStateOf Custom observable Compose aware Buggy UI Medium
48 47 State Avoid mutable collections Immutable list/map PersistentList MutableList Stable UI Silent bug High
49 48 Lifecycle RememberCoroutineScope usage Only for UI jobs UI coroutine Long jobs Scoped job Leak Medium https://developer.android.com/jetpack/compose/side-effects#remembercoroutinescope
50 49 Interop Interop View carefully Use AndroidView Isolated usage Mix everywhere Safe interop Messy UI Low https://developer.android.com/jetpack/compose/interop
51 50 Interop Avoid legacy patterns No LiveData in UI StateFlow LiveData Modern stack Legacy debt Medium
52 51 Debug Use layout inspector Inspect recomposition Tools Blind debug Fast debug Guessing Low https://developer.android.com/studio/debug/layout-inspector
53 52 Debug Enable recomposition counts Track recomposition Debug flags Ignore Performance aware Hidden jank Low

View File

@@ -83,7 +83,8 @@ STACK_CONFIG = {
"swiftui": {"file": "stacks/swiftui.csv"}, "swiftui": {"file": "stacks/swiftui.csv"},
"react-native": {"file": "stacks/react-native.csv"}, "react-native": {"file": "stacks/react-native.csv"},
"flutter": {"file": "stacks/flutter.csv"}, "flutter": {"file": "stacks/flutter.csv"},
"shadcn": {"file": "stacks/shadcn.csv"} "shadcn": {"file": "stacks/shadcn.csv"},
"jetpack-compose": {"file": "stacks/jetpack-compose.csv"}
} }
# Common columns for all stacks # Common columns for all stacks

View File

@@ -7,10 +7,16 @@ to generate comprehensive design system recommendations.
Usage: Usage:
from design_system import generate_design_system from design_system import generate_design_system
result = generate_design_system("SaaS dashboard", "My Project") result = generate_design_system("SaaS dashboard", "My Project")
# With persistence (Master + Overrides pattern)
result = generate_design_system("SaaS dashboard", "My Project", persist=True)
result = generate_design_system("SaaS dashboard", "My Project", persist=True, page="dashboard")
""" """
import csv import csv
import json import json
import os
from datetime import datetime
from pathlib import Path from pathlib import Path
from core import search, DATA_DIR from core import search, DATA_DIR
@@ -452,7 +458,8 @@ def format_markdown(design_system: dict) -> str:
# ============ MAIN ENTRY POINT ============ # ============ MAIN ENTRY POINT ============
def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii") -> str: def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii",
persist: bool = False, page: str = None, output_dir: str = None) -> str:
""" """
Main entry point for design system generation. Main entry point for design system generation.
@@ -460,6 +467,9 @@ def generate_design_system(query: str, project_name: str = None, output_format:
query: Search query (e.g., "SaaS dashboard", "e-commerce luxury") query: Search query (e.g., "SaaS dashboard", "e-commerce luxury")
project_name: Optional project name for output header project_name: Optional project name for output header
output_format: "ascii" (default) or "markdown" output_format: "ascii" (default) or "markdown"
persist: If True, save design system to design-system/ folder
page: Optional page name for page-specific override file
output_dir: Optional output directory (defaults to current working directory)
Returns: Returns:
Formatted design system string Formatted design system string
@@ -467,11 +477,580 @@ def generate_design_system(query: str, project_name: str = None, output_format:
generator = DesignSystemGenerator() generator = DesignSystemGenerator()
design_system = generator.generate(query, project_name) design_system = generator.generate(query, project_name)
# Persist to files if requested
if persist:
persist_design_system(design_system, page, output_dir, query)
if output_format == "markdown": if output_format == "markdown":
return format_markdown(design_system) return format_markdown(design_system)
return format_ascii_box(design_system) return format_ascii_box(design_system)
# ============ PERSISTENCE FUNCTIONS ============
def persist_design_system(design_system: dict, page: str = None, output_dir: str = None, page_query: str = None) -> dict:
"""
Persist design system to design-system/<project>/ folder using Master + Overrides pattern.
Args:
design_system: The generated design system dictionary
page: Optional page name for page-specific override file
output_dir: Optional output directory (defaults to current working directory)
page_query: Optional query string for intelligent page override generation
Returns:
dict with created file paths and status
"""
base_dir = Path(output_dir) if output_dir else Path.cwd()
# Use project name for project-specific folder
project_name = design_system.get("project_name", "default")
project_slug = project_name.lower().replace(' ', '-')
design_system_dir = base_dir / "design-system" / project_slug
pages_dir = design_system_dir / "pages"
created_files = []
# Create directories
design_system_dir.mkdir(parents=True, exist_ok=True)
pages_dir.mkdir(parents=True, exist_ok=True)
master_file = design_system_dir / "MASTER.md"
# Generate and write MASTER.md
master_content = format_master_md(design_system)
with open(master_file, 'w', encoding='utf-8') as f:
f.write(master_content)
created_files.append(str(master_file))
# If page is specified, create page override file with intelligent content
if page:
page_file = pages_dir / f"{page.lower().replace(' ', '-')}.md"
page_content = format_page_override_md(design_system, page, page_query)
with open(page_file, 'w', encoding='utf-8') as f:
f.write(page_content)
created_files.append(str(page_file))
return {
"status": "success",
"design_system_dir": str(design_system_dir),
"created_files": created_files
}
def format_master_md(design_system: dict) -> str:
"""Format design system as MASTER.md with hierarchical override logic."""
project = design_system.get("project_name", "PROJECT")
pattern = design_system.get("pattern", {})
style = design_system.get("style", {})
colors = design_system.get("colors", {})
typography = design_system.get("typography", {})
effects = design_system.get("key_effects", "")
anti_patterns = design_system.get("anti_patterns", "")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
lines = []
# Logic header
lines.append("# Design System Master File")
lines.append("")
lines.append("> **LOGIC:** When building a specific page, first check `design-system/pages/[page-name].md`.")
lines.append("> If that file exists, its rules **override** this Master file.")
lines.append("> If not, strictly follow the rules below.")
lines.append("")
lines.append("---")
lines.append("")
lines.append(f"**Project:** {project}")
lines.append(f"**Generated:** {timestamp}")
lines.append(f"**Category:** {design_system.get('category', 'General')}")
lines.append("")
lines.append("---")
lines.append("")
# Global Rules section
lines.append("## Global Rules")
lines.append("")
# Color Palette
lines.append("### Color Palette")
lines.append("")
lines.append("| Role | Hex | CSS Variable |")
lines.append("|------|-----|--------------|")
lines.append(f"| Primary | `{colors.get('primary', '#2563EB')}` | `--color-primary` |")
lines.append(f"| Secondary | `{colors.get('secondary', '#3B82F6')}` | `--color-secondary` |")
lines.append(f"| CTA/Accent | `{colors.get('cta', '#F97316')}` | `--color-cta` |")
lines.append(f"| Background | `{colors.get('background', '#F8FAFC')}` | `--color-background` |")
lines.append(f"| Text | `{colors.get('text', '#1E293B')}` | `--color-text` |")
lines.append("")
if colors.get("notes"):
lines.append(f"**Color Notes:** {colors.get('notes', '')}")
lines.append("")
# Typography
lines.append("### Typography")
lines.append("")
lines.append(f"- **Heading Font:** {typography.get('heading', 'Inter')}")
lines.append(f"- **Body Font:** {typography.get('body', 'Inter')}")
if typography.get("mood"):
lines.append(f"- **Mood:** {typography.get('mood', '')}")
if typography.get("google_fonts_url"):
lines.append(f"- **Google Fonts:** [{typography.get('heading', '')} + {typography.get('body', '')}]({typography.get('google_fonts_url', '')})")
lines.append("")
if typography.get("css_import"):
lines.append("**CSS Import:**")
lines.append("```css")
lines.append(typography.get("css_import", ""))
lines.append("```")
lines.append("")
# Spacing Variables
lines.append("### Spacing Variables")
lines.append("")
lines.append("| Token | Value | Usage |")
lines.append("|-------|-------|-------|")
lines.append("| `--space-xs` | `4px` / `0.25rem` | Tight gaps |")
lines.append("| `--space-sm` | `8px` / `0.5rem` | Icon gaps, inline spacing |")
lines.append("| `--space-md` | `16px` / `1rem` | Standard padding |")
lines.append("| `--space-lg` | `24px` / `1.5rem` | Section padding |")
lines.append("| `--space-xl` | `32px` / `2rem` | Large gaps |")
lines.append("| `--space-2xl` | `48px` / `3rem` | Section margins |")
lines.append("| `--space-3xl` | `64px` / `4rem` | Hero padding |")
lines.append("")
# Shadow Depths
lines.append("### Shadow Depths")
lines.append("")
lines.append("| Level | Value | Usage |")
lines.append("|-------|-------|-------|")
lines.append("| `--shadow-sm` | `0 1px 2px rgba(0,0,0,0.05)` | Subtle lift |")
lines.append("| `--shadow-md` | `0 4px 6px rgba(0,0,0,0.1)` | Cards, buttons |")
lines.append("| `--shadow-lg` | `0 10px 15px rgba(0,0,0,0.1)` | Modals, dropdowns |")
lines.append("| `--shadow-xl` | `0 20px 25px rgba(0,0,0,0.15)` | Hero images, featured cards |")
lines.append("")
# Component Specs section
lines.append("---")
lines.append("")
lines.append("## Component Specs")
lines.append("")
# Buttons
lines.append("### Buttons")
lines.append("")
lines.append("```css")
lines.append("/* Primary Button */")
lines.append(".btn-primary {")
lines.append(f" background: {colors.get('cta', '#F97316')};")
lines.append(" color: white;")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("")
lines.append(".btn-primary:hover {")
lines.append(" opacity: 0.9;")
lines.append(" transform: translateY(-1px);")
lines.append("}")
lines.append("")
lines.append("/* Secondary Button */")
lines.append(".btn-secondary {")
lines.append(f" background: transparent;")
lines.append(f" color: {colors.get('primary', '#2563EB')};")
lines.append(f" border: 2px solid {colors.get('primary', '#2563EB')};")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("```")
lines.append("")
# Cards
lines.append("### Cards")
lines.append("")
lines.append("```css")
lines.append(".card {")
lines.append(f" background: {colors.get('background', '#FFFFFF')};")
lines.append(" border-radius: 12px;")
lines.append(" padding: 24px;")
lines.append(" box-shadow: var(--shadow-md);")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("")
lines.append(".card:hover {")
lines.append(" box-shadow: var(--shadow-lg);")
lines.append(" transform: translateY(-2px);")
lines.append("}")
lines.append("```")
lines.append("")
# Inputs
lines.append("### Inputs")
lines.append("")
lines.append("```css")
lines.append(".input {")
lines.append(" padding: 12px 16px;")
lines.append(" border: 1px solid #E2E8F0;")
lines.append(" border-radius: 8px;")
lines.append(" font-size: 16px;")
lines.append(" transition: border-color 200ms ease;")
lines.append("}")
lines.append("")
lines.append(".input:focus {")
lines.append(f" border-color: {colors.get('primary', '#2563EB')};")
lines.append(" outline: none;")
lines.append(f" box-shadow: 0 0 0 3px {colors.get('primary', '#2563EB')}20;")
lines.append("}")
lines.append("```")
lines.append("")
# Modals
lines.append("### Modals")
lines.append("")
lines.append("```css")
lines.append(".modal-overlay {")
lines.append(" background: rgba(0, 0, 0, 0.5);")
lines.append(" backdrop-filter: blur(4px);")
lines.append("}")
lines.append("")
lines.append(".modal {")
lines.append(" background: white;")
lines.append(" border-radius: 16px;")
lines.append(" padding: 32px;")
lines.append(" box-shadow: var(--shadow-xl);")
lines.append(" max-width: 500px;")
lines.append(" width: 90%;")
lines.append("}")
lines.append("```")
lines.append("")
# Style section
lines.append("---")
lines.append("")
lines.append("## Style Guidelines")
lines.append("")
lines.append(f"**Style:** {style.get('name', 'Minimalism')}")
lines.append("")
if style.get("keywords"):
lines.append(f"**Keywords:** {style.get('keywords', '')}")
lines.append("")
if style.get("best_for"):
lines.append(f"**Best For:** {style.get('best_for', '')}")
lines.append("")
if effects:
lines.append(f"**Key Effects:** {effects}")
lines.append("")
# Layout Pattern
lines.append("### Page Pattern")
lines.append("")
lines.append(f"**Pattern Name:** {pattern.get('name', '')}")
lines.append("")
if pattern.get('conversion'):
lines.append(f"- **Conversion Strategy:** {pattern.get('conversion', '')}")
if pattern.get('cta_placement'):
lines.append(f"- **CTA Placement:** {pattern.get('cta_placement', '')}")
lines.append(f"- **Section Order:** {pattern.get('sections', '')}")
lines.append("")
# Anti-Patterns section
lines.append("---")
lines.append("")
lines.append("## Anti-Patterns (Do NOT Use)")
lines.append("")
if anti_patterns:
anti_list = [a.strip() for a in anti_patterns.split("+")]
for anti in anti_list:
if anti:
lines.append(f"- ❌ {anti}")
lines.append("")
lines.append("### Additional Forbidden Patterns")
lines.append("")
lines.append("- ❌ **Emojis as icons** — Use SVG icons (Heroicons, Lucide, Simple Icons)")
lines.append("- ❌ **Missing cursor:pointer** — All clickable elements must have cursor:pointer")
lines.append("- ❌ **Layout-shifting hovers** — Avoid scale transforms that shift layout")
lines.append("- ❌ **Low contrast text** — Maintain 4.5:1 minimum contrast ratio")
lines.append("- ❌ **Instant state changes** — Always use transitions (150-300ms)")
lines.append("- ❌ **Invisible focus states** — Focus states must be visible for a11y")
lines.append("")
# Pre-Delivery Checklist
lines.append("---")
lines.append("")
lines.append("## Pre-Delivery Checklist")
lines.append("")
lines.append("Before delivering any UI code, verify:")
lines.append("")
lines.append("- [ ] No emojis used as icons (use SVG instead)")
lines.append("- [ ] All icons from consistent icon set (Heroicons/Lucide)")
lines.append("- [ ] `cursor-pointer` on all clickable elements")
lines.append("- [ ] Hover states with smooth transitions (150-300ms)")
lines.append("- [ ] Light mode: text contrast 4.5:1 minimum")
lines.append("- [ ] Focus states visible for keyboard navigation")
lines.append("- [ ] `prefers-reduced-motion` respected")
lines.append("- [ ] Responsive: 375px, 768px, 1024px, 1440px")
lines.append("- [ ] No content hidden behind fixed navbars")
lines.append("- [ ] No horizontal scroll on mobile")
lines.append("")
return "\n".join(lines)
def format_page_override_md(design_system: dict, page_name: str, page_query: str = None) -> str:
"""Format a page-specific override file with intelligent AI-generated content."""
project = design_system.get("project_name", "PROJECT")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
page_title = page_name.replace("-", " ").replace("_", " ").title()
# Detect page type and generate intelligent overrides
page_overrides = _generate_intelligent_overrides(page_name, page_query, design_system)
lines = []
lines.append(f"# {page_title} Page Overrides")
lines.append("")
lines.append(f"> **PROJECT:** {project}")
lines.append(f"> **Generated:** {timestamp}")
lines.append(f"> **Page Type:** {page_overrides.get('page_type', 'General')}")
lines.append("")
lines.append("> ⚠️ **IMPORTANT:** Rules in this file **override** the Master file (`design-system/MASTER.md`).")
lines.append("> Only deviations from the Master are documented here. For all other rules, refer to the Master.")
lines.append("")
lines.append("---")
lines.append("")
# Page-specific rules with actual content
lines.append("## Page-Specific Rules")
lines.append("")
# Layout Overrides
lines.append("### Layout Overrides")
lines.append("")
layout = page_overrides.get("layout", {})
if layout:
for key, value in layout.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master layout")
lines.append("")
# Spacing Overrides
lines.append("### Spacing Overrides")
lines.append("")
spacing = page_overrides.get("spacing", {})
if spacing:
for key, value in spacing.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master spacing")
lines.append("")
# Typography Overrides
lines.append("### Typography Overrides")
lines.append("")
typography = page_overrides.get("typography", {})
if typography:
for key, value in typography.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master typography")
lines.append("")
# Color Overrides
lines.append("### Color Overrides")
lines.append("")
colors = page_overrides.get("colors", {})
if colors:
for key, value in colors.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master colors")
lines.append("")
# Component Overrides
lines.append("### Component Overrides")
lines.append("")
components = page_overrides.get("components", [])
if components:
for comp in components:
lines.append(f"- {comp}")
else:
lines.append("- No overrides — use Master component specs")
lines.append("")
# Page-Specific Components
lines.append("---")
lines.append("")
lines.append("## Page-Specific Components")
lines.append("")
unique_components = page_overrides.get("unique_components", [])
if unique_components:
for comp in unique_components:
lines.append(f"- {comp}")
else:
lines.append("- No unique components for this page")
lines.append("")
# Recommendations
lines.append("---")
lines.append("")
lines.append("## Recommendations")
lines.append("")
recommendations = page_overrides.get("recommendations", [])
if recommendations:
for rec in recommendations:
lines.append(f"- {rec}")
lines.append("")
return "\n".join(lines)
def _generate_intelligent_overrides(page_name: str, page_query: str, design_system: dict) -> dict:
"""
Generate intelligent overrides based on page type using layered search.
Uses the existing search infrastructure to find relevant style, UX, and layout
data instead of hardcoded page types.
"""
from core import search
page_lower = page_name.lower()
query_lower = (page_query or "").lower()
combined_context = f"{page_lower} {query_lower}"
# Search across multiple domains for page-specific guidance
style_search = search(combined_context, "style", max_results=1)
ux_search = search(combined_context, "ux", max_results=3)
landing_search = search(combined_context, "landing", max_results=1)
# Extract results from search response
style_results = style_search.get("results", [])
ux_results = ux_search.get("results", [])
landing_results = landing_search.get("results", [])
# Detect page type from search results or context
page_type = _detect_page_type(combined_context, style_results)
# Build overrides from search results
layout = {}
spacing = {}
typography = {}
colors = {}
components = []
unique_components = []
recommendations = []
# Extract style-based overrides
if style_results:
style = style_results[0]
style_name = style.get("Style Category", "")
keywords = style.get("Keywords", "")
best_for = style.get("Best For", "")
effects = style.get("Effects & Animation", "")
# Infer layout from style keywords
if any(kw in keywords.lower() for kw in ["data", "dense", "dashboard", "grid"]):
layout["Max Width"] = "1400px or full-width"
layout["Grid"] = "12-column grid for data flexibility"
spacing["Content Density"] = "High — optimize for information display"
elif any(kw in keywords.lower() for kw in ["minimal", "simple", "clean", "single"]):
layout["Max Width"] = "800px (narrow, focused)"
layout["Layout"] = "Single column, centered"
spacing["Content Density"] = "Low — focus on clarity"
else:
layout["Max Width"] = "1200px (standard)"
layout["Layout"] = "Full-width sections, centered content"
if effects:
recommendations.append(f"Effects: {effects}")
# Extract UX guidelines as recommendations
for ux in ux_results:
category = ux.get("Category", "")
do_text = ux.get("Do", "")
dont_text = ux.get("Don't", "")
if do_text:
recommendations.append(f"{category}: {do_text}")
if dont_text:
components.append(f"Avoid: {dont_text}")
# Extract landing pattern info for section structure
if landing_results:
landing = landing_results[0]
sections = landing.get("Section Order", "")
cta_placement = landing.get("Primary CTA Placement", "")
color_strategy = landing.get("Color Strategy", "")
if sections:
layout["Sections"] = sections
if cta_placement:
recommendations.append(f"CTA Placement: {cta_placement}")
if color_strategy:
colors["Strategy"] = color_strategy
# Add page-type specific defaults if no search results
if not layout:
layout["Max Width"] = "1200px"
layout["Layout"] = "Responsive grid"
if not recommendations:
recommendations = [
"Refer to MASTER.md for all design rules",
"Add specific overrides as needed for this page"
]
return {
"page_type": page_type,
"layout": layout,
"spacing": spacing,
"typography": typography,
"colors": colors,
"components": components,
"unique_components": unique_components,
"recommendations": recommendations
}
def _detect_page_type(context: str, style_results: list) -> str:
"""Detect page type from context and search results."""
context_lower = context.lower()
# Check for common page type patterns
page_patterns = [
(["dashboard", "admin", "analytics", "data", "metrics", "stats", "monitor", "overview"], "Dashboard / Data View"),
(["checkout", "payment", "cart", "purchase", "order", "billing"], "Checkout / Payment"),
(["settings", "profile", "account", "preferences", "config"], "Settings / Profile"),
(["landing", "marketing", "homepage", "hero", "home", "promo"], "Landing / Marketing"),
(["login", "signin", "signup", "register", "auth", "password"], "Authentication"),
(["pricing", "plans", "subscription", "tiers", "packages"], "Pricing / Plans"),
(["blog", "article", "post", "news", "content", "story"], "Blog / Article"),
(["product", "item", "detail", "pdp", "shop", "store"], "Product Detail"),
(["search", "results", "browse", "filter", "catalog", "list"], "Search Results"),
(["empty", "404", "error", "not found", "zero"], "Empty State"),
]
for keywords, page_type in page_patterns:
if any(kw in context_lower for kw in keywords):
return page_type
# Fallback: try to infer from style results
if style_results:
style_name = style_results[0].get("Style Category", "").lower()
best_for = style_results[0].get("Best For", "").lower()
if "dashboard" in best_for or "data" in best_for:
return "Dashboard / Data View"
elif "landing" in best_for or "marketing" in best_for:
return "Landing / Marketing"
return "General"
# ============ CLI SUPPORT ============ # ============ CLI SUPPORT ============
if __name__ == "__main__": if __name__ == "__main__":
import argparse import argparse

View File

@@ -4,14 +4,19 @@
UI/UX Pro Max Search - BM25 search engine for UI/UX style guides UI/UX Pro Max Search - BM25 search engine for UI/UX style guides
Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3] Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3]
python search.py "<query>" --design-system [-p "Project Name"] python search.py "<query>" --design-system [-p "Project Name"]
python search.py "<query>" --design-system --persist [-p "Project Name"] [--page "dashboard"]
Domains: style, prompt, color, chart, landing, product, ux, typography Domains: style, prompt, color, chart, landing, product, ux, typography
Stacks: html-tailwind, react, nextjs Stacks: html-tailwind, react, nextjs
Persistence (Master + Overrides pattern):
--persist Save design system to design-system/MASTER.md
--page Also create a page-specific override file in design-system/pages/
""" """
import argparse import argparse
from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack
from design_system import generate_design_system from design_system import generate_design_system, persist_design_system
def format_output(result): def format_output(result):
@@ -51,13 +56,38 @@ if __name__ == "__main__":
parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation") parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation")
parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output") parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output")
parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system") parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system")
# Persistence (Master + Overrides pattern)
parser.add_argument("--persist", action="store_true", help="Save design system to design-system/MASTER.md (creates hierarchical structure)")
parser.add_argument("--page", type=str, default=None, help="Create page-specific override file in design-system/pages/")
parser.add_argument("--output-dir", "-o", type=str, default=None, help="Output directory for persisted files (default: current directory)")
args = parser.parse_args() args = parser.parse_args()
# Design system takes priority # Design system takes priority
if args.design_system: if args.design_system:
result = generate_design_system(args.query, args.project_name, args.format) result = generate_design_system(
args.query,
args.project_name,
args.format,
persist=args.persist,
page=args.page,
output_dir=args.output_dir
)
print(result) print(result)
# Print persistence confirmation
if args.persist:
project_slug = args.project_name.lower().replace(' ', '-') if args.project_name else "default"
print("\n" + "=" * 60)
print(f"✅ Design system persisted to design-system/{project_slug}/")
print(f" 📄 design-system/{project_slug}/MASTER.md (Global Source of Truth)")
if args.page:
page_filename = args.page.lower().replace(' ', '-')
print(f" 📄 design-system/{project_slug}/pages/{page_filename}.md (Page Overrides)")
print("")
print(f"📖 Usage: When building a page, check design-system/{project_slug}/pages/[page].md first.")
print(f" If exists, its rules override MASTER.md. Otherwise, use MASTER.md.")
print("=" * 60)
# Stack search # Stack search
elif args.stack: elif args.stack:
result = search_stack(args.query, args.stack, args.max_results) result = search_stack(args.query, args.stack, args.max_results)

View File

@@ -7,10 +7,16 @@ to generate comprehensive design system recommendations.
Usage: Usage:
from design_system import generate_design_system from design_system import generate_design_system
result = generate_design_system("SaaS dashboard", "My Project") result = generate_design_system("SaaS dashboard", "My Project")
# With persistence (Master + Overrides pattern)
result = generate_design_system("SaaS dashboard", "My Project", persist=True)
result = generate_design_system("SaaS dashboard", "My Project", persist=True, page="dashboard")
""" """
import csv import csv
import json import json
import os
from datetime import datetime
from pathlib import Path from pathlib import Path
from core import search, DATA_DIR from core import search, DATA_DIR
@@ -338,6 +344,21 @@ def format_ascii_box(design_system: dict) -> str:
lines.append(line.ljust(BOX_WIDTH) + "|") lines.append(line.ljust(BOX_WIDTH) + "|")
lines.append("|" + " " * BOX_WIDTH + "|") lines.append("|" + " " * BOX_WIDTH + "|")
# Pre-Delivery Checklist section
lines.append("| PRE-DELIVERY CHECKLIST:".ljust(BOX_WIDTH) + "|")
checklist_items = [
"[ ] No emojis as icons (use SVG: Heroicons/Lucide)",
"[ ] cursor-pointer on all clickable elements",
"[ ] Hover states with smooth transitions (150-300ms)",
"[ ] Light mode: text contrast 4.5:1 minimum",
"[ ] Focus states visible for keyboard nav",
"[ ] prefers-reduced-motion respected",
"[ ] Responsive: 375px, 768px, 1024px, 1440px"
]
for item in checklist_items:
lines.append(f"| {item}".ljust(BOX_WIDTH) + "|")
lines.append("|" + " " * BOX_WIDTH + "|")
lines.append("+" + "-" * w + "+") lines.append("+" + "-" * w + "+")
return "\n".join(lines) return "\n".join(lines)
@@ -422,11 +443,23 @@ def format_markdown(design_system: dict) -> str:
lines.append(f"- {anti_patterns.replace(' + ', '\n- ')}") lines.append(f"- {anti_patterns.replace(' + ', '\n- ')}")
lines.append("") lines.append("")
# Pre-Delivery Checklist section
lines.append("### Pre-Delivery Checklist")
lines.append("- [ ] No emojis as icons (use SVG: Heroicons/Lucide)")
lines.append("- [ ] cursor-pointer on all clickable elements")
lines.append("- [ ] Hover states with smooth transitions (150-300ms)")
lines.append("- [ ] Light mode: text contrast 4.5:1 minimum")
lines.append("- [ ] Focus states visible for keyboard nav")
lines.append("- [ ] prefers-reduced-motion respected")
lines.append("- [ ] Responsive: 375px, 768px, 1024px, 1440px")
lines.append("")
return "\n".join(lines) return "\n".join(lines)
# ============ MAIN ENTRY POINT ============ # ============ MAIN ENTRY POINT ============
def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii") -> str: def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii",
persist: bool = False, page: str = None, output_dir: str = None) -> str:
""" """
Main entry point for design system generation. Main entry point for design system generation.
@@ -434,6 +467,9 @@ def generate_design_system(query: str, project_name: str = None, output_format:
query: Search query (e.g., "SaaS dashboard", "e-commerce luxury") query: Search query (e.g., "SaaS dashboard", "e-commerce luxury")
project_name: Optional project name for output header project_name: Optional project name for output header
output_format: "ascii" (default) or "markdown" output_format: "ascii" (default) or "markdown"
persist: If True, save design system to design-system/ folder
page: Optional page name for page-specific override file
output_dir: Optional output directory (defaults to current working directory)
Returns: Returns:
Formatted design system string Formatted design system string
@@ -441,11 +477,580 @@ def generate_design_system(query: str, project_name: str = None, output_format:
generator = DesignSystemGenerator() generator = DesignSystemGenerator()
design_system = generator.generate(query, project_name) design_system = generator.generate(query, project_name)
# Persist to files if requested
if persist:
persist_design_system(design_system, page, output_dir, query)
if output_format == "markdown": if output_format == "markdown":
return format_markdown(design_system) return format_markdown(design_system)
return format_ascii_box(design_system) return format_ascii_box(design_system)
# ============ PERSISTENCE FUNCTIONS ============
def persist_design_system(design_system: dict, page: str = None, output_dir: str = None, page_query: str = None) -> dict:
"""
Persist design system to design-system/<project>/ folder using Master + Overrides pattern.
Args:
design_system: The generated design system dictionary
page: Optional page name for page-specific override file
output_dir: Optional output directory (defaults to current working directory)
page_query: Optional query string for intelligent page override generation
Returns:
dict with created file paths and status
"""
base_dir = Path(output_dir) if output_dir else Path.cwd()
# Use project name for project-specific folder
project_name = design_system.get("project_name", "default")
project_slug = project_name.lower().replace(' ', '-')
design_system_dir = base_dir / "design-system" / project_slug
pages_dir = design_system_dir / "pages"
created_files = []
# Create directories
design_system_dir.mkdir(parents=True, exist_ok=True)
pages_dir.mkdir(parents=True, exist_ok=True)
master_file = design_system_dir / "MASTER.md"
# Generate and write MASTER.md
master_content = format_master_md(design_system)
with open(master_file, 'w', encoding='utf-8') as f:
f.write(master_content)
created_files.append(str(master_file))
# If page is specified, create page override file with intelligent content
if page:
page_file = pages_dir / f"{page.lower().replace(' ', '-')}.md"
page_content = format_page_override_md(design_system, page, page_query)
with open(page_file, 'w', encoding='utf-8') as f:
f.write(page_content)
created_files.append(str(page_file))
return {
"status": "success",
"design_system_dir": str(design_system_dir),
"created_files": created_files
}
def format_master_md(design_system: dict) -> str:
"""Format design system as MASTER.md with hierarchical override logic."""
project = design_system.get("project_name", "PROJECT")
pattern = design_system.get("pattern", {})
style = design_system.get("style", {})
colors = design_system.get("colors", {})
typography = design_system.get("typography", {})
effects = design_system.get("key_effects", "")
anti_patterns = design_system.get("anti_patterns", "")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
lines = []
# Logic header
lines.append("# Design System Master File")
lines.append("")
lines.append("> **LOGIC:** When building a specific page, first check `design-system/pages/[page-name].md`.")
lines.append("> If that file exists, its rules **override** this Master file.")
lines.append("> If not, strictly follow the rules below.")
lines.append("")
lines.append("---")
lines.append("")
lines.append(f"**Project:** {project}")
lines.append(f"**Generated:** {timestamp}")
lines.append(f"**Category:** {design_system.get('category', 'General')}")
lines.append("")
lines.append("---")
lines.append("")
# Global Rules section
lines.append("## Global Rules")
lines.append("")
# Color Palette
lines.append("### Color Palette")
lines.append("")
lines.append("| Role | Hex | CSS Variable |")
lines.append("|------|-----|--------------|")
lines.append(f"| Primary | `{colors.get('primary', '#2563EB')}` | `--color-primary` |")
lines.append(f"| Secondary | `{colors.get('secondary', '#3B82F6')}` | `--color-secondary` |")
lines.append(f"| CTA/Accent | `{colors.get('cta', '#F97316')}` | `--color-cta` |")
lines.append(f"| Background | `{colors.get('background', '#F8FAFC')}` | `--color-background` |")
lines.append(f"| Text | `{colors.get('text', '#1E293B')}` | `--color-text` |")
lines.append("")
if colors.get("notes"):
lines.append(f"**Color Notes:** {colors.get('notes', '')}")
lines.append("")
# Typography
lines.append("### Typography")
lines.append("")
lines.append(f"- **Heading Font:** {typography.get('heading', 'Inter')}")
lines.append(f"- **Body Font:** {typography.get('body', 'Inter')}")
if typography.get("mood"):
lines.append(f"- **Mood:** {typography.get('mood', '')}")
if typography.get("google_fonts_url"):
lines.append(f"- **Google Fonts:** [{typography.get('heading', '')} + {typography.get('body', '')}]({typography.get('google_fonts_url', '')})")
lines.append("")
if typography.get("css_import"):
lines.append("**CSS Import:**")
lines.append("```css")
lines.append(typography.get("css_import", ""))
lines.append("```")
lines.append("")
# Spacing Variables
lines.append("### Spacing Variables")
lines.append("")
lines.append("| Token | Value | Usage |")
lines.append("|-------|-------|-------|")
lines.append("| `--space-xs` | `4px` / `0.25rem` | Tight gaps |")
lines.append("| `--space-sm` | `8px` / `0.5rem` | Icon gaps, inline spacing |")
lines.append("| `--space-md` | `16px` / `1rem` | Standard padding |")
lines.append("| `--space-lg` | `24px` / `1.5rem` | Section padding |")
lines.append("| `--space-xl` | `32px` / `2rem` | Large gaps |")
lines.append("| `--space-2xl` | `48px` / `3rem` | Section margins |")
lines.append("| `--space-3xl` | `64px` / `4rem` | Hero padding |")
lines.append("")
# Shadow Depths
lines.append("### Shadow Depths")
lines.append("")
lines.append("| Level | Value | Usage |")
lines.append("|-------|-------|-------|")
lines.append("| `--shadow-sm` | `0 1px 2px rgba(0,0,0,0.05)` | Subtle lift |")
lines.append("| `--shadow-md` | `0 4px 6px rgba(0,0,0,0.1)` | Cards, buttons |")
lines.append("| `--shadow-lg` | `0 10px 15px rgba(0,0,0,0.1)` | Modals, dropdowns |")
lines.append("| `--shadow-xl` | `0 20px 25px rgba(0,0,0,0.15)` | Hero images, featured cards |")
lines.append("")
# Component Specs section
lines.append("---")
lines.append("")
lines.append("## Component Specs")
lines.append("")
# Buttons
lines.append("### Buttons")
lines.append("")
lines.append("```css")
lines.append("/* Primary Button */")
lines.append(".btn-primary {")
lines.append(f" background: {colors.get('cta', '#F97316')};")
lines.append(" color: white;")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("")
lines.append(".btn-primary:hover {")
lines.append(" opacity: 0.9;")
lines.append(" transform: translateY(-1px);")
lines.append("}")
lines.append("")
lines.append("/* Secondary Button */")
lines.append(".btn-secondary {")
lines.append(f" background: transparent;")
lines.append(f" color: {colors.get('primary', '#2563EB')};")
lines.append(f" border: 2px solid {colors.get('primary', '#2563EB')};")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("```")
lines.append("")
# Cards
lines.append("### Cards")
lines.append("")
lines.append("```css")
lines.append(".card {")
lines.append(f" background: {colors.get('background', '#FFFFFF')};")
lines.append(" border-radius: 12px;")
lines.append(" padding: 24px;")
lines.append(" box-shadow: var(--shadow-md);")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("")
lines.append(".card:hover {")
lines.append(" box-shadow: var(--shadow-lg);")
lines.append(" transform: translateY(-2px);")
lines.append("}")
lines.append("```")
lines.append("")
# Inputs
lines.append("### Inputs")
lines.append("")
lines.append("```css")
lines.append(".input {")
lines.append(" padding: 12px 16px;")
lines.append(" border: 1px solid #E2E8F0;")
lines.append(" border-radius: 8px;")
lines.append(" font-size: 16px;")
lines.append(" transition: border-color 200ms ease;")
lines.append("}")
lines.append("")
lines.append(".input:focus {")
lines.append(f" border-color: {colors.get('primary', '#2563EB')};")
lines.append(" outline: none;")
lines.append(f" box-shadow: 0 0 0 3px {colors.get('primary', '#2563EB')}20;")
lines.append("}")
lines.append("```")
lines.append("")
# Modals
lines.append("### Modals")
lines.append("")
lines.append("```css")
lines.append(".modal-overlay {")
lines.append(" background: rgba(0, 0, 0, 0.5);")
lines.append(" backdrop-filter: blur(4px);")
lines.append("}")
lines.append("")
lines.append(".modal {")
lines.append(" background: white;")
lines.append(" border-radius: 16px;")
lines.append(" padding: 32px;")
lines.append(" box-shadow: var(--shadow-xl);")
lines.append(" max-width: 500px;")
lines.append(" width: 90%;")
lines.append("}")
lines.append("```")
lines.append("")
# Style section
lines.append("---")
lines.append("")
lines.append("## Style Guidelines")
lines.append("")
lines.append(f"**Style:** {style.get('name', 'Minimalism')}")
lines.append("")
if style.get("keywords"):
lines.append(f"**Keywords:** {style.get('keywords', '')}")
lines.append("")
if style.get("best_for"):
lines.append(f"**Best For:** {style.get('best_for', '')}")
lines.append("")
if effects:
lines.append(f"**Key Effects:** {effects}")
lines.append("")
# Layout Pattern
lines.append("### Page Pattern")
lines.append("")
lines.append(f"**Pattern Name:** {pattern.get('name', '')}")
lines.append("")
if pattern.get('conversion'):
lines.append(f"- **Conversion Strategy:** {pattern.get('conversion', '')}")
if pattern.get('cta_placement'):
lines.append(f"- **CTA Placement:** {pattern.get('cta_placement', '')}")
lines.append(f"- **Section Order:** {pattern.get('sections', '')}")
lines.append("")
# Anti-Patterns section
lines.append("---")
lines.append("")
lines.append("## Anti-Patterns (Do NOT Use)")
lines.append("")
if anti_patterns:
anti_list = [a.strip() for a in anti_patterns.split("+")]
for anti in anti_list:
if anti:
lines.append(f"- ❌ {anti}")
lines.append("")
lines.append("### Additional Forbidden Patterns")
lines.append("")
lines.append("- ❌ **Emojis as icons** — Use SVG icons (Heroicons, Lucide, Simple Icons)")
lines.append("- ❌ **Missing cursor:pointer** — All clickable elements must have cursor:pointer")
lines.append("- ❌ **Layout-shifting hovers** — Avoid scale transforms that shift layout")
lines.append("- ❌ **Low contrast text** — Maintain 4.5:1 minimum contrast ratio")
lines.append("- ❌ **Instant state changes** — Always use transitions (150-300ms)")
lines.append("- ❌ **Invisible focus states** — Focus states must be visible for a11y")
lines.append("")
# Pre-Delivery Checklist
lines.append("---")
lines.append("")
lines.append("## Pre-Delivery Checklist")
lines.append("")
lines.append("Before delivering any UI code, verify:")
lines.append("")
lines.append("- [ ] No emojis used as icons (use SVG instead)")
lines.append("- [ ] All icons from consistent icon set (Heroicons/Lucide)")
lines.append("- [ ] `cursor-pointer` on all clickable elements")
lines.append("- [ ] Hover states with smooth transitions (150-300ms)")
lines.append("- [ ] Light mode: text contrast 4.5:1 minimum")
lines.append("- [ ] Focus states visible for keyboard navigation")
lines.append("- [ ] `prefers-reduced-motion` respected")
lines.append("- [ ] Responsive: 375px, 768px, 1024px, 1440px")
lines.append("- [ ] No content hidden behind fixed navbars")
lines.append("- [ ] No horizontal scroll on mobile")
lines.append("")
return "\n".join(lines)
def format_page_override_md(design_system: dict, page_name: str, page_query: str = None) -> str:
"""Format a page-specific override file with intelligent AI-generated content."""
project = design_system.get("project_name", "PROJECT")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
page_title = page_name.replace("-", " ").replace("_", " ").title()
# Detect page type and generate intelligent overrides
page_overrides = _generate_intelligent_overrides(page_name, page_query, design_system)
lines = []
lines.append(f"# {page_title} Page Overrides")
lines.append("")
lines.append(f"> **PROJECT:** {project}")
lines.append(f"> **Generated:** {timestamp}")
lines.append(f"> **Page Type:** {page_overrides.get('page_type', 'General')}")
lines.append("")
lines.append("> ⚠️ **IMPORTANT:** Rules in this file **override** the Master file (`design-system/MASTER.md`).")
lines.append("> Only deviations from the Master are documented here. For all other rules, refer to the Master.")
lines.append("")
lines.append("---")
lines.append("")
# Page-specific rules with actual content
lines.append("## Page-Specific Rules")
lines.append("")
# Layout Overrides
lines.append("### Layout Overrides")
lines.append("")
layout = page_overrides.get("layout", {})
if layout:
for key, value in layout.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master layout")
lines.append("")
# Spacing Overrides
lines.append("### Spacing Overrides")
lines.append("")
spacing = page_overrides.get("spacing", {})
if spacing:
for key, value in spacing.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master spacing")
lines.append("")
# Typography Overrides
lines.append("### Typography Overrides")
lines.append("")
typography = page_overrides.get("typography", {})
if typography:
for key, value in typography.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master typography")
lines.append("")
# Color Overrides
lines.append("### Color Overrides")
lines.append("")
colors = page_overrides.get("colors", {})
if colors:
for key, value in colors.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master colors")
lines.append("")
# Component Overrides
lines.append("### Component Overrides")
lines.append("")
components = page_overrides.get("components", [])
if components:
for comp in components:
lines.append(f"- {comp}")
else:
lines.append("- No overrides — use Master component specs")
lines.append("")
# Page-Specific Components
lines.append("---")
lines.append("")
lines.append("## Page-Specific Components")
lines.append("")
unique_components = page_overrides.get("unique_components", [])
if unique_components:
for comp in unique_components:
lines.append(f"- {comp}")
else:
lines.append("- No unique components for this page")
lines.append("")
# Recommendations
lines.append("---")
lines.append("")
lines.append("## Recommendations")
lines.append("")
recommendations = page_overrides.get("recommendations", [])
if recommendations:
for rec in recommendations:
lines.append(f"- {rec}")
lines.append("")
return "\n".join(lines)
def _generate_intelligent_overrides(page_name: str, page_query: str, design_system: dict) -> dict:
"""
Generate intelligent overrides based on page type using layered search.
Uses the existing search infrastructure to find relevant style, UX, and layout
data instead of hardcoded page types.
"""
from core import search
page_lower = page_name.lower()
query_lower = (page_query or "").lower()
combined_context = f"{page_lower} {query_lower}"
# Search across multiple domains for page-specific guidance
style_search = search(combined_context, "style", max_results=1)
ux_search = search(combined_context, "ux", max_results=3)
landing_search = search(combined_context, "landing", max_results=1)
# Extract results from search response
style_results = style_search.get("results", [])
ux_results = ux_search.get("results", [])
landing_results = landing_search.get("results", [])
# Detect page type from search results or context
page_type = _detect_page_type(combined_context, style_results)
# Build overrides from search results
layout = {}
spacing = {}
typography = {}
colors = {}
components = []
unique_components = []
recommendations = []
# Extract style-based overrides
if style_results:
style = style_results[0]
style_name = style.get("Style Category", "")
keywords = style.get("Keywords", "")
best_for = style.get("Best For", "")
effects = style.get("Effects & Animation", "")
# Infer layout from style keywords
if any(kw in keywords.lower() for kw in ["data", "dense", "dashboard", "grid"]):
layout["Max Width"] = "1400px or full-width"
layout["Grid"] = "12-column grid for data flexibility"
spacing["Content Density"] = "High — optimize for information display"
elif any(kw in keywords.lower() for kw in ["minimal", "simple", "clean", "single"]):
layout["Max Width"] = "800px (narrow, focused)"
layout["Layout"] = "Single column, centered"
spacing["Content Density"] = "Low — focus on clarity"
else:
layout["Max Width"] = "1200px (standard)"
layout["Layout"] = "Full-width sections, centered content"
if effects:
recommendations.append(f"Effects: {effects}")
# Extract UX guidelines as recommendations
for ux in ux_results:
category = ux.get("Category", "")
do_text = ux.get("Do", "")
dont_text = ux.get("Don't", "")
if do_text:
recommendations.append(f"{category}: {do_text}")
if dont_text:
components.append(f"Avoid: {dont_text}")
# Extract landing pattern info for section structure
if landing_results:
landing = landing_results[0]
sections = landing.get("Section Order", "")
cta_placement = landing.get("Primary CTA Placement", "")
color_strategy = landing.get("Color Strategy", "")
if sections:
layout["Sections"] = sections
if cta_placement:
recommendations.append(f"CTA Placement: {cta_placement}")
if color_strategy:
colors["Strategy"] = color_strategy
# Add page-type specific defaults if no search results
if not layout:
layout["Max Width"] = "1200px"
layout["Layout"] = "Responsive grid"
if not recommendations:
recommendations = [
"Refer to MASTER.md for all design rules",
"Add specific overrides as needed for this page"
]
return {
"page_type": page_type,
"layout": layout,
"spacing": spacing,
"typography": typography,
"colors": colors,
"components": components,
"unique_components": unique_components,
"recommendations": recommendations
}
def _detect_page_type(context: str, style_results: list) -> str:
"""Detect page type from context and search results."""
context_lower = context.lower()
# Check for common page type patterns
page_patterns = [
(["dashboard", "admin", "analytics", "data", "metrics", "stats", "monitor", "overview"], "Dashboard / Data View"),
(["checkout", "payment", "cart", "purchase", "order", "billing"], "Checkout / Payment"),
(["settings", "profile", "account", "preferences", "config"], "Settings / Profile"),
(["landing", "marketing", "homepage", "hero", "home", "promo"], "Landing / Marketing"),
(["login", "signin", "signup", "register", "auth", "password"], "Authentication"),
(["pricing", "plans", "subscription", "tiers", "packages"], "Pricing / Plans"),
(["blog", "article", "post", "news", "content", "story"], "Blog / Article"),
(["product", "item", "detail", "pdp", "shop", "store"], "Product Detail"),
(["search", "results", "browse", "filter", "catalog", "list"], "Search Results"),
(["empty", "404", "error", "not found", "zero"], "Empty State"),
]
for keywords, page_type in page_patterns:
if any(kw in context_lower for kw in keywords):
return page_type
# Fallback: try to infer from style results
if style_results:
style_name = style_results[0].get("Style Category", "").lower()
best_for = style_results[0].get("Best For", "").lower()
if "dashboard" in best_for or "data" in best_for:
return "Dashboard / Data View"
elif "landing" in best_for or "marketing" in best_for:
return "Landing / Marketing"
return "General"
# ============ CLI SUPPORT ============ # ============ CLI SUPPORT ============
if __name__ == "__main__": if __name__ == "__main__":
import argparse import argparse

View File

@@ -4,14 +4,19 @@
UI/UX Pro Max Search - BM25 search engine for UI/UX style guides UI/UX Pro Max Search - BM25 search engine for UI/UX style guides
Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3] Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3]
python search.py "<query>" --design-system [-p "Project Name"] python search.py "<query>" --design-system [-p "Project Name"]
python search.py "<query>" --design-system --persist [-p "Project Name"] [--page "dashboard"]
Domains: style, prompt, color, chart, landing, product, ux, typography Domains: style, prompt, color, chart, landing, product, ux, typography
Stacks: html-tailwind, react, nextjs Stacks: html-tailwind, react, nextjs
Persistence (Master + Overrides pattern):
--persist Save design system to design-system/MASTER.md
--page Also create a page-specific override file in design-system/pages/
""" """
import argparse import argparse
from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack
from design_system import generate_design_system from design_system import generate_design_system, persist_design_system
def format_output(result): def format_output(result):
@@ -51,13 +56,38 @@ if __name__ == "__main__":
parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation") parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation")
parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output") parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output")
parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system") parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system")
# Persistence (Master + Overrides pattern)
parser.add_argument("--persist", action="store_true", help="Save design system to design-system/MASTER.md (creates hierarchical structure)")
parser.add_argument("--page", type=str, default=None, help="Create page-specific override file in design-system/pages/")
parser.add_argument("--output-dir", "-o", type=str, default=None, help="Output directory for persisted files (default: current directory)")
args = parser.parse_args() args = parser.parse_args()
# Design system takes priority # Design system takes priority
if args.design_system: if args.design_system:
result = generate_design_system(args.query, args.project_name, args.format) result = generate_design_system(
args.query,
args.project_name,
args.format,
persist=args.persist,
page=args.page,
output_dir=args.output_dir
)
print(result) print(result)
# Print persistence confirmation
if args.persist:
project_slug = args.project_name.lower().replace(' ', '-') if args.project_name else "default"
print("\n" + "=" * 60)
print(f"✅ Design system persisted to design-system/{project_slug}/")
print(f" 📄 design-system/{project_slug}/MASTER.md (Global Source of Truth)")
if args.page:
page_filename = args.page.lower().replace(' ', '-')
print(f" 📄 design-system/{project_slug}/pages/{page_filename}.md (Page Overrides)")
print("")
print(f"📖 Usage: When building a page, check design-system/{project_slug}/pages/[page].md first.")
print(f" If exists, its rules override MASTER.md. Otherwise, use MASTER.md.")
print("=" * 60)
# Stack search # Stack search
elif args.stack: elif args.stack:
result = search_stack(args.query, args.stack, args.max_results) result = search_stack(args.query, args.stack, args.max_results)

View File

@@ -60,6 +60,31 @@ This command:
python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa" python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa"
``` ```
### Step 2b: Persist Design System (Master + Overrides Pattern)
To save the design system for hierarchical retrieval across sessions, add `--persist`:
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name"
```
This creates:
- `design-system/MASTER.md` — Global Source of Truth with all design rules
- `design-system/pages/` — Folder for page-specific overrides
**With page-specific override:**
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name" --page "dashboard"
```
This also creates:
- `design-system/pages/dashboard.md` — Page-specific deviations from Master
**How hierarchical retrieval works:**
1. When building a specific page (e.g., "Checkout"), first check `design-system/pages/checkout.md`
2. If the page file exists, its rules **override** the Master file
3. If not, use `design-system/MASTER.md` exclusively
### Step 3: Supplement with Detailed Searches (as needed) ### Step 3: Supplement with Detailed Searches (as needed)
After getting the design system, use domain searches to get additional details: After getting the design system, use domain searches to get additional details:
@@ -86,8 +111,8 @@ Get implementation-specific best practices. If user doesn't specify a stack, **d
python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind
``` ```
Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn` Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn`, `jetpack-compose`
, `jetpack-compose`
--- ---
## Search Reference ## Search Reference
@@ -120,6 +145,7 @@ Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`
| `react-native` | Components, Navigation, Lists | | `react-native` | Components, Navigation, Lists |
| `flutter` | Widgets, State, Layout, Theming | | `flutter` | Widgets, State, Layout, Theming |
| `shadcn` | shadcn/ui components, theming, forms, patterns | | `shadcn` | shadcn/ui components, theming, forms, patterns |
| `jetpack-compose` | Composables, Modifiers, State Hoisting, Recomposition |
--- ---

View File

@@ -1,11 +1,11 @@
# UI UX Pro Max # UI UX Pro Max
<p align="center"> <p align="center">
<img src="https://img.shields.io/badge/version-2.0.0-blue?style=for-the-badge" alt="Version 2.0.0"> <a href="https://github.com/nextlevelbuilder/ui-ux-pro-max-skill/releases"><img src="https://img.shields.io/github/v/release/nextlevelbuilder/ui-ux-pro-max-skill?style=for-the-badge&color=blue" alt="GitHub Release"></a>
<img src="https://img.shields.io/badge/reasoning_rules-100-green?style=for-the-badge" alt="100 Reasoning Rules"> <img src="https://img.shields.io/badge/reasoning_rules-100-green?style=for-the-badge" alt="100 Reasoning Rules">
<img src="https://img.shields.io/badge/UI_styles-57-purple?style=for-the-badge" alt="57 UI Styles"> <img src="https://img.shields.io/badge/UI_styles-57-purple?style=for-the-badge" alt="57 UI Styles">
<img src="https://img.shields.io/badge/python-3.x-yellow?style=for-the-badge&logo=python&logoColor=white" alt="Python 3.x"> <img src="https://img.shields.io/badge/python-3.x-yellow?style=for-the-badge&logo=python&logoColor=white" alt="Python 3.x">
<img src="https://img.shields.io/badge/license-MIT-green?style=for-the-badge" alt="MIT License"> <a href="https://github.com/nextlevelbuilder/ui-ux-pro-max-skill/blob/main/LICENSE"><img src="https://img.shields.io/github/license/nextlevelbuilder/ui-ux-pro-max-skill?style=for-the-badge&color=green" alt="License"></a>
</p> </p>
<p align="center"> <p align="center">
@@ -21,6 +21,11 @@ An AI skill that provides design intelligence for building professional UI/UX ac
<img src="screenshots/website.png" alt="UI UX Pro Max" width="800"> <img src="screenshots/website.png" alt="UI UX Pro Max" width="800">
</p> </p>
<p align="center">
<b>If you find this useful, consider supporting the project:</b><br><br>
<a href="https://paypal.me/uiuxpromax"><img src="https://img.shields.io/badge/PayPal-Donate-00457C?style=for-the-badge&logo=paypal&logoColor=white" alt="PayPal Donate"></a>
</p>
## What's New in v2.0 ## What's New in v2.0
### Intelligent Design System Generation ### Intelligent Design System Generation
@@ -28,29 +33,54 @@ An AI skill that provides design intelligence for building professional UI/UX ac
The flagship feature of v2.0 is the **Design System Generator** - an AI-powered reasoning engine that analyzes your project requirements and generates a complete, tailored design system in seconds. The flagship feature of v2.0 is the **Design System Generator** - an AI-powered reasoning engine that analyzes your project requirements and generates a complete, tailored design system in seconds.
``` ```
+-----------------------------------------------------------------------------------------+ +----------------------------------------------------------------------------------------+
| TARGET: Serenity Spa - RECOMMENDED DESIGN SYSTEM | | TARGET: Serenity Spa - RECOMMENDED DESIGN SYSTEM |
+-----------------------------------------------------------------------------------------+ +----------------------------------------------------------------------------------------+
| | | |
| PATTERN: Hero-Centric + Social Proof | | PATTERN: Hero-Centric + Social Proof |
| Sections: Hero > Services > Testimonials > Booking > Contact | | Conversion: Emotion-driven with trust elements |
| CTA: Above fold, repeated after testimonials |
| Sections: |
| 1. Hero |
| 2. Services |
| 3. Testimonials |
| 4. Booking |
| 5. Contact |
| | | |
| STYLE: Soft UI Evolution + Neumorphism | | STYLE: Soft UI Evolution |
| Keywords: Soft shadows, subtle depth, calming, premium feel | | Keywords: Soft shadows, subtle depth, calming, premium feel, organic shapes |
| Best For: Wellness, beauty, lifestyle brands, premium services |
| Performance: Excellent | Accessibility: WCAG AA | | Performance: Excellent | Accessibility: WCAG AA |
| | | |
| COLORS: | | COLORS: |
| Primary: #E8B4B8 Secondary: #A8D5BA CTA: #D4AF37 | | Primary: #E8B4B8 (Soft Pink) |
| Background: #FFF5F5 Text: #2D3436 | | Secondary: #A8D5BA (Sage Green) |
| CTA: #D4AF37 (Gold) |
| Background: #FFF5F5 (Warm White) |
| Text: #2D3436 (Charcoal) |
| Notes: Calming palette with gold accents for luxury feel |
| | | |
| TYPOGRAPHY: Cormorant Garamond / Montserrat | | TYPOGRAPHY: Cormorant Garamond / Montserrat |
| Mood: Elegant, calming, sophisticated |
| Best For: Luxury brands, wellness, beauty, editorial |
| Google Fonts: https://fonts.google.com/share?selection.family=... | | Google Fonts: https://fonts.google.com/share?selection.family=... |
| | | |
| KEY EFFECTS: Soft shadows + Smooth transitions (200-300ms) + Gentle hover | | KEY EFFECTS: |
| Soft shadows + Smooth transitions (200-300ms) + Gentle hover states |
| | | |
| AVOID: Bright neon colors + Harsh animations + Dark mode + AI purple/pink gradients | | AVOID (Anti-patterns): |
| Bright neon colors + Harsh animations + Dark mode + AI purple/pink gradients |
| | | |
+-----------------------------------------------------------------------------------------+ | PRE-DELIVERY CHECKLIST: |
| [ ] No emojis as icons (use SVG: Heroicons/Lucide) |
| [ ] cursor-pointer on all clickable elements |
| [ ] Hover states with smooth transitions (150-300ms) |
| [ ] Light mode: text contrast 4.5:1 minimum |
| [ ] Focus states visible for keyboard nav |
| [ ] prefers-reduced-motion respected |
| [ ] Responsive: 375px, 768px, 1024px, 1440px |
| |
+----------------------------------------------------------------------------------------+
``` ```
### How Design System Generation Works ### How Design System Generation Works
@@ -122,6 +152,15 @@ Each rule includes:
## Installation ## Installation
### Using Claude Marketplace (Claude Code)
Install directly in Claude Code with two commands:
```
/plugin marketplace add nextlevelbuilder/ui-ux-pro-max-skill
/plugin install ui-ux-pro-max@ui-ux-pro-max-skill
```
### Using CLI (Recommended) ### Using CLI (Recommended)
```bash ```bash
@@ -320,6 +359,41 @@ python3 .claude/skills/ui-ux-pro-max/scripts/search.py "form validation" --stack
python3 .claude/skills/ui-ux-pro-max/scripts/search.py "responsive layout" --stack html-tailwind python3 .claude/skills/ui-ux-pro-max/scripts/search.py "responsive layout" --stack html-tailwind
``` ```
### Persist Design System (Master + Overrides Pattern)
Save your design system to files for **hierarchical retrieval across sessions**:
```bash
# Generate and persist to design-system/MASTER.md
python3 .claude/skills/ui-ux-pro-max/scripts/search.py "SaaS dashboard" --design-system --persist -p "MyApp"
# Also create a page-specific override file
python3 .claude/skills/ui-ux-pro-max/scripts/search.py "SaaS dashboard" --design-system --persist -p "MyApp" --page "dashboard"
```
This creates a `design-system/` folder structure:
```
design-system/
├── MASTER.md # Global Source of Truth (colors, typography, spacing, components)
└── pages/
└── dashboard.md # Page-specific overrides (only deviations from Master)
```
**How hierarchical retrieval works:**
1. When building a specific page (e.g., "Checkout"), first check `design-system/pages/checkout.md`
2. If the page file exists, its rules **override** the Master file
3. If not, use `design-system/MASTER.md` exclusively
**Context-aware retrieval prompt:**
```
I am building the [Page Name] page. Please read design-system/MASTER.md.
Also check if design-system/pages/[page-name].md exists.
If the page file exists, prioritize its rules.
If not, use the Master rules exclusively.
Now, generate the code...
```
## Star History ## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=nextlevelbuilder/ui-ux-pro-max-skill&type=Date)](https://star-history.com/#nextlevelbuilder/ui-ux-pro-max-skill&Date) [![Star History Chart](https://api.star-history.com/svg?repos=nextlevelbuilder/ui-ux-pro-max-skill&type=Date)](https://star-history.com/#nextlevelbuilder/ui-ux-pro-max-skill&Date)

View File

@@ -23,6 +23,7 @@ uipro init --ai roocode # Roo Code
uipro init --ai qoder # Qoder uipro init --ai qoder # Qoder
uipro init --ai gemini # Gemini CLI uipro init --ai gemini # Gemini CLI
uipro init --ai trae # Trae uipro init --ai trae # Trae
uipro init --ai opencode # OpenCode
uipro init --ai all # All assistants uipro init --ai all # All assistants
# Options # Options

View File

@@ -1,8 +1,3 @@
---
description: Plan and implement UI
auto_execution_mode: 3
---
# ui-ux-pro-max # ui-ux-pro-max
Comprehensive design guide for web and mobile applications. Contains 50+ styles, 97 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 9 technology stacks. Searchable database with priority-based recommendations. Comprehensive design guide for web and mobile applications. Contains 50+ styles, 97 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 9 technology stacks. Searchable database with priority-based recommendations.
@@ -65,6 +60,31 @@ This command:
python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa" python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa"
``` ```
### Step 2b: Persist Design System (Master + Overrides Pattern)
To save the design system for hierarchical retrieval across sessions, add `--persist`:
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name"
```
This creates:
- `design-system/MASTER.md` — Global Source of Truth with all design rules
- `design-system/pages/` — Folder for page-specific overrides
**With page-specific override:**
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name" --page "dashboard"
```
This also creates:
- `design-system/pages/dashboard.md` — Page-specific deviations from Master
**How hierarchical retrieval works:**
1. When building a specific page (e.g., "Checkout"), first check `design-system/pages/checkout.md`
2. If the page file exists, its rules **override** the Master file
3. If not, use `design-system/MASTER.md` exclusively
### Step 3: Supplement with Detailed Searches (as needed) ### Step 3: Supplement with Detailed Searches (as needed)
After getting the design system, use domain searches to get additional details: After getting the design system, use domain searches to get additional details:
@@ -91,8 +111,8 @@ Get implementation-specific best practices. If user doesn't specify a stack, **d
python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind
``` ```
Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn` Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn`, `jetpack-compose`
, `jetpack-compose`
--- ---
## Search Reference ## Search Reference
@@ -125,6 +145,7 @@ Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`
| `react-native` | Components, Navigation, Lists | | `react-native` | Components, Navigation, Lists |
| `flutter` | Widgets, State, Layout, Theming | | `flutter` | Widgets, State, Layout, Theming |
| `shadcn` | shadcn/ui components, theming, forms, patterns | | `shadcn` | shadcn/ui components, theming, forms, patterns |
| `jetpack-compose` | Composables, Modifiers, State Hoisting, Recomposition |
--- ---

View File

@@ -149,6 +149,40 @@ This command:
python3 .claude/skills/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa" python3 .claude/skills/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa"
``` ```
### Step 2b: Persist Design System (Master + Overrides Pattern)
To save the design system for **hierarchical retrieval across sessions**, add `--persist`:
```bash
python3 .claude/skills/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name"
```
This creates:
- `design-system/MASTER.md` — Global Source of Truth with all design rules
- `design-system/pages/` — Folder for page-specific overrides
**With page-specific override:**
```bash
python3 .claude/skills/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name" --page "dashboard"
```
This also creates:
- `design-system/pages/dashboard.md` — Page-specific deviations from Master
**How hierarchical retrieval works:**
1. When building a specific page (e.g., "Checkout"), first check `design-system/pages/checkout.md`
2. If the page file exists, its rules **override** the Master file
3. If not, use `design-system/MASTER.md` exclusively
**Context-aware retrieval prompt:**
```
I am building the [Page Name] page. Please read design-system/MASTER.md.
Also check if design-system/pages/[page-name].md exists.
If the page file exists, prioritize its rules.
If not, use the Master rules exclusively.
Now, generate the code...
```
### Step 3: Supplement with Detailed Searches (as needed) ### Step 3: Supplement with Detailed Searches (as needed)
After getting the design system, use domain searches to get additional details: After getting the design system, use domain searches to get additional details:
@@ -175,7 +209,7 @@ Get implementation-specific best practices. If user doesn't specify a stack, **d
python3 .claude/skills/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind python3 .claude/skills/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind
``` ```
Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn` Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn`, `jetpack-compose`
--- ---
@@ -209,6 +243,7 @@ Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`
| `react-native` | Components, Navigation, Lists | | `react-native` | Components, Navigation, Lists |
| `flutter` | Widgets, State, Layout, Theming | | `flutter` | Widgets, State, Layout, Theming |
| `shadcn` | shadcn/ui components, theming, forms, patterns | | `shadcn` | shadcn/ui components, theming, forms, patterns |
| `jetpack-compose` | Composables, Modifiers, State Hoisting, Recomposition |
--- ---

View File

@@ -0,0 +1,53 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Composable,Pure UI composables,Composable functions should only render UI,Accept state and callbacks,Calling usecase/repo,Pure UI composable,Business logic in UI,High,https://developer.android.com/jetpack/compose/mental-model
2,Composable,Small composables,Each composable has single responsibility,Split into components,Huge composable,Reusable UI,Monolithic UI,Medium,
3,Composable,Stateless by default,Prefer stateless composables,Hoist state,Local mutable state,Stateless UI,Hidden state,High,https://developer.android.com/jetpack/compose/state#state-hoisting
4,State,Single source of truth,UI state comes from one source,StateFlow from VM,Multiple states,Unified UiState,Scattered state,High,https://developer.android.com/topic/architecture/ui-layer
5,State,Model UI State,Use sealed interface/data class,UiState.Loading,Boolean flags,Explicit state,Flag hell,High,
6,State,remember only UI state,remember for UI-only state,"Scroll, animation",Business state,Correct remember,Misuse remember,High,https://developer.android.com/jetpack/compose/state
7,State,rememberSaveable,Persist state across config,rememberSaveable,remember,State survives,State lost,High,https://developer.android.com/jetpack/compose/state#restore-ui-state
8,State,derivedStateOf,Optimize recomposition,derivedStateOf,Recompute always,Optimized,Jank,Medium,https://developer.android.com/jetpack/compose/performance
9,SideEffect,LaunchedEffect keys,Use correct keys,LaunchedEffect(id),LaunchedEffect(Unit),Scoped effect,Infinite loop,High,https://developer.android.com/jetpack/compose/side-effects
10,SideEffect,rememberUpdatedState,Avoid stale lambdas,rememberUpdatedState,Capture directly,Safe callback,Stale state,Medium,https://developer.android.com/jetpack/compose/side-effects
11,SideEffect,DisposableEffect,Clean up resources,onDispose,No cleanup,No leak,Memory leak,High,
12,Architecture,Unidirectional data flow,UI → VM → State,onEvent,Two-way binding,Predictable flow,Hard debug,High,https://developer.android.com/topic/architecture
13,Architecture,No business logic in UI,Logic belongs to VM,Collect state,Call repo,Clean UI,Fat UI,High,
14,Architecture,Expose immutable state,Expose StateFlow,asStateFlow,Mutable exposed,Safe API,State mutation,High,
15,Lifecycle,Lifecycle-aware collect,Use collectAsStateWithLifecycle,Lifecycle aware,collectAsState,No leak,Leak,High,https://developer.android.com/jetpack/compose/lifecycle
16,Navigation,Event-based navigation,VM emits navigation event,"VM: Channel + receiveAsFlow(), V: Collect with Dispatchers.Main.immediate",Nav in UI,Decoupled nav,Using State / SharedFlow for navigation -> event is replayed and navigation fires again (StateFlow),High,https://developer.android.com/jetpack/compose/navigation
17,Navigation,Typed routes,Use sealed routes,sealed class Route,String routes,Type-safe,Runtime crash,Medium,
18,Performance,Stable parameters,Prefer immutable/stable params,@Immutable,Mutable params,Stable recomposition,Extra recomposition,High,https://developer.android.com/jetpack/compose/performance
19,Performance,Use key in Lazy,Provide stable keys,key=id,No key,Stable list,Item jump,High,
20,Performance,Avoid heavy work,No heavy computation in UI,Precompute in VM,Compute in UI,Smooth UI,Jank,High,
21,Performance,Remember expensive objects,remember heavy objects,remember,Recreate each recomposition,Efficient,Wasteful,Medium,
22,Theming,Design system,Centralized theme,Material3 tokens,Hardcoded values,Consistent UI,Inconsistent,High,https://developer.android.com/jetpack/compose/themes
23,Theming,Dark mode support,Theme-based colors,colorScheme,Fixed color,Adaptive UI,Broken dark,Medium,
24,Layout,Prefer Modifier over extra layouts,Use Modifier to adjust layout instead of adding wrapper composables,Use Modifier.padding(),Wrap content with extra Box,Padding via modifier,Box just for padding,High,https://developer.android.com/jetpack/compose/modifiers
25,Layout,Avoid deep layout nesting,Deep layout trees increase measure & layout cost,Keep layout flat,Box ? Column ? Box ? Row,Flat hierarchy,Deep nested tree,High,
26,Layout,Use Row/Column for linear layout,Linear layouts are simpler and more performant,Use Row / Column,Custom layout for simple cases,Row/Column usage,Over-engineered layout,High,
27,Layout,Use Box only for overlapping content,Box should be used only when children overlap,Stack elements,Use Box as Column,Proper overlay,Misused Box,Medium,
28,Layout,Prefer LazyColumn over Column scroll,Lazy layouts are virtualized and efficient,LazyColumn,Column.verticalScroll(),Lazy list,Scrollable Column,High,https://developer.android.com/jetpack/compose/lists
29,Layout,Avoid nested scroll containers,Nested scrolling causes UX & performance issues,Single scroll container,Scroll inside scroll,One scroll per screen,Nested scroll,High,
30,Layout,Avoid fillMaxSize by default,fillMaxSize may break parent constraints,Use exact size,Fill max everywhere,Constraint-aware size,Overfilled layout,Medium,
31,Layout,Avoid intrinsic size unless necessary,Intrinsic measurement is expensive,Explicit sizing,IntrinsicSize.Min,Predictable layout,Expensive measure,High,https://developer.android.com/jetpack/compose/layout/intrinsics
32,Layout,Use Arrangement and Alignment APIs,Declare layout intent explicitly,Use Arrangement / Alignment,Manual spacing hacks,Declarative spacing,Magic spacing,High,
33,Layout,Extract reusable layout patterns,Repeated layouts should be shared,Create layout composable,Copy-paste layouts,Reusable scaffold,Duplicated layout,High,
34,Theming,No hardcoded text style,Use typography,MaterialTheme.typography,Hardcode sp,Scalable,Inconsistent,Medium,
35,Testing,Stateless UI testing,Composable easy to test,Pass state,Hidden state,Testable,Hard test,High,https://developer.android.com/jetpack/compose/testing
36,Testing,Use testTag,Stable UI selectors,Modifier.testTag,Find by text,Stable tests,Flaky tests,Medium,
37,Preview,Multiple previews,Preview multiple states,@Preview,Single preview,Better dev UX,Misleading,Low,https://developer.android.com/jetpack/compose/tooling/preview
38,DI,Inject VM via Hilt,Use hiltViewModel,@HiltViewModel,Manual VM,Clean DI,Coupling,High,https://developer.android.com/training/dependency-injection/hilt-jetpack
39,DI,No DI in UI,Inject in VM,Constructor inject,Inject composable,Proper scope,Wrong scope,High,
40,Accessibility,Content description,Accessible UI,contentDescription,Ignore a11y,Inclusive,A11y fail,Medium,https://developer.android.com/jetpack/compose/accessibility
41,Accessibility,Semantics,Use semantics API,Modifier.semantics,None,Testable a11y,Invisible,Medium,
42,Animation,Compose animation APIs,Use animate*AsState,AnimatedVisibility,Manual anim,Smooth,Jank,Medium,https://developer.android.com/jetpack/compose/animation
43,Animation,Avoid animation logic in VM,Animation is UI concern,Animate in UI,Animate in VM,Correct layering,Mixed concern,Low,
44,Modularization,Feature-based UI modules,UI per feature,:feature:ui,God module,Scalable,Tight coupling,High,https://developer.android.com/topic/modularization
45,Modularization,Public UI contracts,Expose minimal UI API,Interface/Route,Expose impl,Encapsulated,Leaky module,Medium,
46,State,Snapshot state only,Use Compose state,mutableStateOf,Custom observable,Compose aware,Buggy UI,Medium,
47,State,Avoid mutable collections,Immutable list/map,PersistentList,MutableList,Stable UI,Silent bug,High,
48,Lifecycle,RememberCoroutineScope usage,Only for UI jobs,UI coroutine,Long jobs,Scoped job,Leak,Medium,https://developer.android.com/jetpack/compose/side-effects#remembercoroutinescope
49,Interop,Interop View carefully,Use AndroidView,Isolated usage,Mix everywhere,Safe interop,Messy UI,Low,https://developer.android.com/jetpack/compose/interop
50,Interop,Avoid legacy patterns,No LiveData in UI,StateFlow,LiveData,Modern stack,Legacy debt,Medium,
51,Debug,Use layout inspector,Inspect recomposition,Tools,Blind debug,Fast debug,Guessing,Low,https://developer.android.com/studio/debug/layout-inspector
52,Debug,Enable recomposition counts,Track recomposition,Debug flags,Ignore,Performance aware,Hidden jank,Low,
1 No Category Guideline Description Do Don't Code Good Code Bad Severity Docs URL
2 1 Composable Pure UI composables Composable functions should only render UI Accept state and callbacks Calling usecase/repo Pure UI composable Business logic in UI High https://developer.android.com/jetpack/compose/mental-model
3 2 Composable Small composables Each composable has single responsibility Split into components Huge composable Reusable UI Monolithic UI Medium
4 3 Composable Stateless by default Prefer stateless composables Hoist state Local mutable state Stateless UI Hidden state High https://developer.android.com/jetpack/compose/state#state-hoisting
5 4 State Single source of truth UI state comes from one source StateFlow from VM Multiple states Unified UiState Scattered state High https://developer.android.com/topic/architecture/ui-layer
6 5 State Model UI State Use sealed interface/data class UiState.Loading Boolean flags Explicit state Flag hell High
7 6 State remember only UI state remember for UI-only state Scroll, animation Business state Correct remember Misuse remember High https://developer.android.com/jetpack/compose/state
8 7 State rememberSaveable Persist state across config rememberSaveable remember State survives State lost High https://developer.android.com/jetpack/compose/state#restore-ui-state
9 8 State derivedStateOf Optimize recomposition derivedStateOf Recompute always Optimized Jank Medium https://developer.android.com/jetpack/compose/performance
10 9 SideEffect LaunchedEffect keys Use correct keys LaunchedEffect(id) LaunchedEffect(Unit) Scoped effect Infinite loop High https://developer.android.com/jetpack/compose/side-effects
11 10 SideEffect rememberUpdatedState Avoid stale lambdas rememberUpdatedState Capture directly Safe callback Stale state Medium https://developer.android.com/jetpack/compose/side-effects
12 11 SideEffect DisposableEffect Clean up resources onDispose No cleanup No leak Memory leak High
13 12 Architecture Unidirectional data flow UI → VM → State onEvent Two-way binding Predictable flow Hard debug High https://developer.android.com/topic/architecture
14 13 Architecture No business logic in UI Logic belongs to VM Collect state Call repo Clean UI Fat UI High
15 14 Architecture Expose immutable state Expose StateFlow asStateFlow Mutable exposed Safe API State mutation High
16 15 Lifecycle Lifecycle-aware collect Use collectAsStateWithLifecycle Lifecycle aware collectAsState No leak Leak High https://developer.android.com/jetpack/compose/lifecycle
17 16 Navigation Event-based navigation VM emits navigation event VM: Channel + receiveAsFlow(), V: Collect with Dispatchers.Main.immediate Nav in UI Decoupled nav Using State / SharedFlow for navigation -> event is replayed and navigation fires again (StateFlow) High https://developer.android.com/jetpack/compose/navigation
18 17 Navigation Typed routes Use sealed routes sealed class Route String routes Type-safe Runtime crash Medium
19 18 Performance Stable parameters Prefer immutable/stable params @Immutable Mutable params Stable recomposition Extra recomposition High https://developer.android.com/jetpack/compose/performance
20 19 Performance Use key in Lazy Provide stable keys key=id No key Stable list Item jump High
21 20 Performance Avoid heavy work No heavy computation in UI Precompute in VM Compute in UI Smooth UI Jank High
22 21 Performance Remember expensive objects remember heavy objects remember Recreate each recomposition Efficient Wasteful Medium
23 22 Theming Design system Centralized theme Material3 tokens Hardcoded values Consistent UI Inconsistent High https://developer.android.com/jetpack/compose/themes
24 23 Theming Dark mode support Theme-based colors colorScheme Fixed color Adaptive UI Broken dark Medium
25 24 Layout Prefer Modifier over extra layouts Use Modifier to adjust layout instead of adding wrapper composables Use Modifier.padding() Wrap content with extra Box Padding via modifier Box just for padding High https://developer.android.com/jetpack/compose/modifiers
26 25 Layout Avoid deep layout nesting Deep layout trees increase measure & layout cost Keep layout flat Box ? Column ? Box ? Row Flat hierarchy Deep nested tree High
27 26 Layout Use Row/Column for linear layout Linear layouts are simpler and more performant Use Row / Column Custom layout for simple cases Row/Column usage Over-engineered layout High
28 27 Layout Use Box only for overlapping content Box should be used only when children overlap Stack elements Use Box as Column Proper overlay Misused Box Medium
29 28 Layout Prefer LazyColumn over Column scroll Lazy layouts are virtualized and efficient LazyColumn Column.verticalScroll() Lazy list Scrollable Column High https://developer.android.com/jetpack/compose/lists
30 29 Layout Avoid nested scroll containers Nested scrolling causes UX & performance issues Single scroll container Scroll inside scroll One scroll per screen Nested scroll High
31 30 Layout Avoid fillMaxSize by default fillMaxSize may break parent constraints Use exact size Fill max everywhere Constraint-aware size Overfilled layout Medium
32 31 Layout Avoid intrinsic size unless necessary Intrinsic measurement is expensive Explicit sizing IntrinsicSize.Min Predictable layout Expensive measure High https://developer.android.com/jetpack/compose/layout/intrinsics
33 32 Layout Use Arrangement and Alignment APIs Declare layout intent explicitly Use Arrangement / Alignment Manual spacing hacks Declarative spacing Magic spacing High
34 33 Layout Extract reusable layout patterns Repeated layouts should be shared Create layout composable Copy-paste layouts Reusable scaffold Duplicated layout High
35 34 Theming No hardcoded text style Use typography MaterialTheme.typography Hardcode sp Scalable Inconsistent Medium
36 35 Testing Stateless UI testing Composable easy to test Pass state Hidden state Testable Hard test High https://developer.android.com/jetpack/compose/testing
37 36 Testing Use testTag Stable UI selectors Modifier.testTag Find by text Stable tests Flaky tests Medium
38 37 Preview Multiple previews Preview multiple states @Preview Single preview Better dev UX Misleading Low https://developer.android.com/jetpack/compose/tooling/preview
39 38 DI Inject VM via Hilt Use hiltViewModel @HiltViewModel Manual VM Clean DI Coupling High https://developer.android.com/training/dependency-injection/hilt-jetpack
40 39 DI No DI in UI Inject in VM Constructor inject Inject composable Proper scope Wrong scope High
41 40 Accessibility Content description Accessible UI contentDescription Ignore a11y Inclusive A11y fail Medium https://developer.android.com/jetpack/compose/accessibility
42 41 Accessibility Semantics Use semantics API Modifier.semantics None Testable a11y Invisible Medium
43 42 Animation Compose animation APIs Use animate*AsState AnimatedVisibility Manual anim Smooth Jank Medium https://developer.android.com/jetpack/compose/animation
44 43 Animation Avoid animation logic in VM Animation is UI concern Animate in UI Animate in VM Correct layering Mixed concern Low
45 44 Modularization Feature-based UI modules UI per feature :feature:ui God module Scalable Tight coupling High https://developer.android.com/topic/modularization
46 45 Modularization Public UI contracts Expose minimal UI API Interface/Route Expose impl Encapsulated Leaky module Medium
47 46 State Snapshot state only Use Compose state mutableStateOf Custom observable Compose aware Buggy UI Medium
48 47 State Avoid mutable collections Immutable list/map PersistentList MutableList Stable UI Silent bug High
49 48 Lifecycle RememberCoroutineScope usage Only for UI jobs UI coroutine Long jobs Scoped job Leak Medium https://developer.android.com/jetpack/compose/side-effects#remembercoroutinescope
50 49 Interop Interop View carefully Use AndroidView Isolated usage Mix everywhere Safe interop Messy UI Low https://developer.android.com/jetpack/compose/interop
51 50 Interop Avoid legacy patterns No LiveData in UI StateFlow LiveData Modern stack Legacy debt Medium
52 51 Debug Use layout inspector Inspect recomposition Tools Blind debug Fast debug Guessing Low https://developer.android.com/studio/debug/layout-inspector
53 52 Debug Enable recomposition counts Track recomposition Debug flags Ignore Performance aware Hidden jank Low

View File

@@ -83,7 +83,8 @@ STACK_CONFIG = {
"swiftui": {"file": "stacks/swiftui.csv"}, "swiftui": {"file": "stacks/swiftui.csv"},
"react-native": {"file": "stacks/react-native.csv"}, "react-native": {"file": "stacks/react-native.csv"},
"flutter": {"file": "stacks/flutter.csv"}, "flutter": {"file": "stacks/flutter.csv"},
"shadcn": {"file": "stacks/shadcn.csv"} "shadcn": {"file": "stacks/shadcn.csv"},
"jetpack-compose": {"file": "stacks/jetpack-compose.csv"}
} }
# Common columns for all stacks # Common columns for all stacks

View File

@@ -7,10 +7,16 @@ to generate comprehensive design system recommendations.
Usage: Usage:
from design_system import generate_design_system from design_system import generate_design_system
result = generate_design_system("SaaS dashboard", "My Project") result = generate_design_system("SaaS dashboard", "My Project")
# With persistence (Master + Overrides pattern)
result = generate_design_system("SaaS dashboard", "My Project", persist=True)
result = generate_design_system("SaaS dashboard", "My Project", persist=True, page="dashboard")
""" """
import csv import csv
import json import json
import os
from datetime import datetime
from pathlib import Path from pathlib import Path
from core import search, DATA_DIR from core import search, DATA_DIR
@@ -452,7 +458,8 @@ def format_markdown(design_system: dict) -> str:
# ============ MAIN ENTRY POINT ============ # ============ MAIN ENTRY POINT ============
def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii") -> str: def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii",
persist: bool = False, page: str = None, output_dir: str = None) -> str:
""" """
Main entry point for design system generation. Main entry point for design system generation.
@@ -460,6 +467,9 @@ def generate_design_system(query: str, project_name: str = None, output_format:
query: Search query (e.g., "SaaS dashboard", "e-commerce luxury") query: Search query (e.g., "SaaS dashboard", "e-commerce luxury")
project_name: Optional project name for output header project_name: Optional project name for output header
output_format: "ascii" (default) or "markdown" output_format: "ascii" (default) or "markdown"
persist: If True, save design system to design-system/ folder
page: Optional page name for page-specific override file
output_dir: Optional output directory (defaults to current working directory)
Returns: Returns:
Formatted design system string Formatted design system string
@@ -467,11 +477,580 @@ def generate_design_system(query: str, project_name: str = None, output_format:
generator = DesignSystemGenerator() generator = DesignSystemGenerator()
design_system = generator.generate(query, project_name) design_system = generator.generate(query, project_name)
# Persist to files if requested
if persist:
persist_design_system(design_system, page, output_dir, query)
if output_format == "markdown": if output_format == "markdown":
return format_markdown(design_system) return format_markdown(design_system)
return format_ascii_box(design_system) return format_ascii_box(design_system)
# ============ PERSISTENCE FUNCTIONS ============
def persist_design_system(design_system: dict, page: str = None, output_dir: str = None, page_query: str = None) -> dict:
"""
Persist design system to design-system/<project>/ folder using Master + Overrides pattern.
Args:
design_system: The generated design system dictionary
page: Optional page name for page-specific override file
output_dir: Optional output directory (defaults to current working directory)
page_query: Optional query string for intelligent page override generation
Returns:
dict with created file paths and status
"""
base_dir = Path(output_dir) if output_dir else Path.cwd()
# Use project name for project-specific folder
project_name = design_system.get("project_name", "default")
project_slug = project_name.lower().replace(' ', '-')
design_system_dir = base_dir / "design-system" / project_slug
pages_dir = design_system_dir / "pages"
created_files = []
# Create directories
design_system_dir.mkdir(parents=True, exist_ok=True)
pages_dir.mkdir(parents=True, exist_ok=True)
master_file = design_system_dir / "MASTER.md"
# Generate and write MASTER.md
master_content = format_master_md(design_system)
with open(master_file, 'w', encoding='utf-8') as f:
f.write(master_content)
created_files.append(str(master_file))
# If page is specified, create page override file with intelligent content
if page:
page_file = pages_dir / f"{page.lower().replace(' ', '-')}.md"
page_content = format_page_override_md(design_system, page, page_query)
with open(page_file, 'w', encoding='utf-8') as f:
f.write(page_content)
created_files.append(str(page_file))
return {
"status": "success",
"design_system_dir": str(design_system_dir),
"created_files": created_files
}
def format_master_md(design_system: dict) -> str:
"""Format design system as MASTER.md with hierarchical override logic."""
project = design_system.get("project_name", "PROJECT")
pattern = design_system.get("pattern", {})
style = design_system.get("style", {})
colors = design_system.get("colors", {})
typography = design_system.get("typography", {})
effects = design_system.get("key_effects", "")
anti_patterns = design_system.get("anti_patterns", "")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
lines = []
# Logic header
lines.append("# Design System Master File")
lines.append("")
lines.append("> **LOGIC:** When building a specific page, first check `design-system/pages/[page-name].md`.")
lines.append("> If that file exists, its rules **override** this Master file.")
lines.append("> If not, strictly follow the rules below.")
lines.append("")
lines.append("---")
lines.append("")
lines.append(f"**Project:** {project}")
lines.append(f"**Generated:** {timestamp}")
lines.append(f"**Category:** {design_system.get('category', 'General')}")
lines.append("")
lines.append("---")
lines.append("")
# Global Rules section
lines.append("## Global Rules")
lines.append("")
# Color Palette
lines.append("### Color Palette")
lines.append("")
lines.append("| Role | Hex | CSS Variable |")
lines.append("|------|-----|--------------|")
lines.append(f"| Primary | `{colors.get('primary', '#2563EB')}` | `--color-primary` |")
lines.append(f"| Secondary | `{colors.get('secondary', '#3B82F6')}` | `--color-secondary` |")
lines.append(f"| CTA/Accent | `{colors.get('cta', '#F97316')}` | `--color-cta` |")
lines.append(f"| Background | `{colors.get('background', '#F8FAFC')}` | `--color-background` |")
lines.append(f"| Text | `{colors.get('text', '#1E293B')}` | `--color-text` |")
lines.append("")
if colors.get("notes"):
lines.append(f"**Color Notes:** {colors.get('notes', '')}")
lines.append("")
# Typography
lines.append("### Typography")
lines.append("")
lines.append(f"- **Heading Font:** {typography.get('heading', 'Inter')}")
lines.append(f"- **Body Font:** {typography.get('body', 'Inter')}")
if typography.get("mood"):
lines.append(f"- **Mood:** {typography.get('mood', '')}")
if typography.get("google_fonts_url"):
lines.append(f"- **Google Fonts:** [{typography.get('heading', '')} + {typography.get('body', '')}]({typography.get('google_fonts_url', '')})")
lines.append("")
if typography.get("css_import"):
lines.append("**CSS Import:**")
lines.append("```css")
lines.append(typography.get("css_import", ""))
lines.append("```")
lines.append("")
# Spacing Variables
lines.append("### Spacing Variables")
lines.append("")
lines.append("| Token | Value | Usage |")
lines.append("|-------|-------|-------|")
lines.append("| `--space-xs` | `4px` / `0.25rem` | Tight gaps |")
lines.append("| `--space-sm` | `8px` / `0.5rem` | Icon gaps, inline spacing |")
lines.append("| `--space-md` | `16px` / `1rem` | Standard padding |")
lines.append("| `--space-lg` | `24px` / `1.5rem` | Section padding |")
lines.append("| `--space-xl` | `32px` / `2rem` | Large gaps |")
lines.append("| `--space-2xl` | `48px` / `3rem` | Section margins |")
lines.append("| `--space-3xl` | `64px` / `4rem` | Hero padding |")
lines.append("")
# Shadow Depths
lines.append("### Shadow Depths")
lines.append("")
lines.append("| Level | Value | Usage |")
lines.append("|-------|-------|-------|")
lines.append("| `--shadow-sm` | `0 1px 2px rgba(0,0,0,0.05)` | Subtle lift |")
lines.append("| `--shadow-md` | `0 4px 6px rgba(0,0,0,0.1)` | Cards, buttons |")
lines.append("| `--shadow-lg` | `0 10px 15px rgba(0,0,0,0.1)` | Modals, dropdowns |")
lines.append("| `--shadow-xl` | `0 20px 25px rgba(0,0,0,0.15)` | Hero images, featured cards |")
lines.append("")
# Component Specs section
lines.append("---")
lines.append("")
lines.append("## Component Specs")
lines.append("")
# Buttons
lines.append("### Buttons")
lines.append("")
lines.append("```css")
lines.append("/* Primary Button */")
lines.append(".btn-primary {")
lines.append(f" background: {colors.get('cta', '#F97316')};")
lines.append(" color: white;")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("")
lines.append(".btn-primary:hover {")
lines.append(" opacity: 0.9;")
lines.append(" transform: translateY(-1px);")
lines.append("}")
lines.append("")
lines.append("/* Secondary Button */")
lines.append(".btn-secondary {")
lines.append(f" background: transparent;")
lines.append(f" color: {colors.get('primary', '#2563EB')};")
lines.append(f" border: 2px solid {colors.get('primary', '#2563EB')};")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("```")
lines.append("")
# Cards
lines.append("### Cards")
lines.append("")
lines.append("```css")
lines.append(".card {")
lines.append(f" background: {colors.get('background', '#FFFFFF')};")
lines.append(" border-radius: 12px;")
lines.append(" padding: 24px;")
lines.append(" box-shadow: var(--shadow-md);")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("")
lines.append(".card:hover {")
lines.append(" box-shadow: var(--shadow-lg);")
lines.append(" transform: translateY(-2px);")
lines.append("}")
lines.append("```")
lines.append("")
# Inputs
lines.append("### Inputs")
lines.append("")
lines.append("```css")
lines.append(".input {")
lines.append(" padding: 12px 16px;")
lines.append(" border: 1px solid #E2E8F0;")
lines.append(" border-radius: 8px;")
lines.append(" font-size: 16px;")
lines.append(" transition: border-color 200ms ease;")
lines.append("}")
lines.append("")
lines.append(".input:focus {")
lines.append(f" border-color: {colors.get('primary', '#2563EB')};")
lines.append(" outline: none;")
lines.append(f" box-shadow: 0 0 0 3px {colors.get('primary', '#2563EB')}20;")
lines.append("}")
lines.append("```")
lines.append("")
# Modals
lines.append("### Modals")
lines.append("")
lines.append("```css")
lines.append(".modal-overlay {")
lines.append(" background: rgba(0, 0, 0, 0.5);")
lines.append(" backdrop-filter: blur(4px);")
lines.append("}")
lines.append("")
lines.append(".modal {")
lines.append(" background: white;")
lines.append(" border-radius: 16px;")
lines.append(" padding: 32px;")
lines.append(" box-shadow: var(--shadow-xl);")
lines.append(" max-width: 500px;")
lines.append(" width: 90%;")
lines.append("}")
lines.append("```")
lines.append("")
# Style section
lines.append("---")
lines.append("")
lines.append("## Style Guidelines")
lines.append("")
lines.append(f"**Style:** {style.get('name', 'Minimalism')}")
lines.append("")
if style.get("keywords"):
lines.append(f"**Keywords:** {style.get('keywords', '')}")
lines.append("")
if style.get("best_for"):
lines.append(f"**Best For:** {style.get('best_for', '')}")
lines.append("")
if effects:
lines.append(f"**Key Effects:** {effects}")
lines.append("")
# Layout Pattern
lines.append("### Page Pattern")
lines.append("")
lines.append(f"**Pattern Name:** {pattern.get('name', '')}")
lines.append("")
if pattern.get('conversion'):
lines.append(f"- **Conversion Strategy:** {pattern.get('conversion', '')}")
if pattern.get('cta_placement'):
lines.append(f"- **CTA Placement:** {pattern.get('cta_placement', '')}")
lines.append(f"- **Section Order:** {pattern.get('sections', '')}")
lines.append("")
# Anti-Patterns section
lines.append("---")
lines.append("")
lines.append("## Anti-Patterns (Do NOT Use)")
lines.append("")
if anti_patterns:
anti_list = [a.strip() for a in anti_patterns.split("+")]
for anti in anti_list:
if anti:
lines.append(f"- ❌ {anti}")
lines.append("")
lines.append("### Additional Forbidden Patterns")
lines.append("")
lines.append("- ❌ **Emojis as icons** — Use SVG icons (Heroicons, Lucide, Simple Icons)")
lines.append("- ❌ **Missing cursor:pointer** — All clickable elements must have cursor:pointer")
lines.append("- ❌ **Layout-shifting hovers** — Avoid scale transforms that shift layout")
lines.append("- ❌ **Low contrast text** — Maintain 4.5:1 minimum contrast ratio")
lines.append("- ❌ **Instant state changes** — Always use transitions (150-300ms)")
lines.append("- ❌ **Invisible focus states** — Focus states must be visible for a11y")
lines.append("")
# Pre-Delivery Checklist
lines.append("---")
lines.append("")
lines.append("## Pre-Delivery Checklist")
lines.append("")
lines.append("Before delivering any UI code, verify:")
lines.append("")
lines.append("- [ ] No emojis used as icons (use SVG instead)")
lines.append("- [ ] All icons from consistent icon set (Heroicons/Lucide)")
lines.append("- [ ] `cursor-pointer` on all clickable elements")
lines.append("- [ ] Hover states with smooth transitions (150-300ms)")
lines.append("- [ ] Light mode: text contrast 4.5:1 minimum")
lines.append("- [ ] Focus states visible for keyboard navigation")
lines.append("- [ ] `prefers-reduced-motion` respected")
lines.append("- [ ] Responsive: 375px, 768px, 1024px, 1440px")
lines.append("- [ ] No content hidden behind fixed navbars")
lines.append("- [ ] No horizontal scroll on mobile")
lines.append("")
return "\n".join(lines)
def format_page_override_md(design_system: dict, page_name: str, page_query: str = None) -> str:
"""Format a page-specific override file with intelligent AI-generated content."""
project = design_system.get("project_name", "PROJECT")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
page_title = page_name.replace("-", " ").replace("_", " ").title()
# Detect page type and generate intelligent overrides
page_overrides = _generate_intelligent_overrides(page_name, page_query, design_system)
lines = []
lines.append(f"# {page_title} Page Overrides")
lines.append("")
lines.append(f"> **PROJECT:** {project}")
lines.append(f"> **Generated:** {timestamp}")
lines.append(f"> **Page Type:** {page_overrides.get('page_type', 'General')}")
lines.append("")
lines.append("> ⚠️ **IMPORTANT:** Rules in this file **override** the Master file (`design-system/MASTER.md`).")
lines.append("> Only deviations from the Master are documented here. For all other rules, refer to the Master.")
lines.append("")
lines.append("---")
lines.append("")
# Page-specific rules with actual content
lines.append("## Page-Specific Rules")
lines.append("")
# Layout Overrides
lines.append("### Layout Overrides")
lines.append("")
layout = page_overrides.get("layout", {})
if layout:
for key, value in layout.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master layout")
lines.append("")
# Spacing Overrides
lines.append("### Spacing Overrides")
lines.append("")
spacing = page_overrides.get("spacing", {})
if spacing:
for key, value in spacing.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master spacing")
lines.append("")
# Typography Overrides
lines.append("### Typography Overrides")
lines.append("")
typography = page_overrides.get("typography", {})
if typography:
for key, value in typography.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master typography")
lines.append("")
# Color Overrides
lines.append("### Color Overrides")
lines.append("")
colors = page_overrides.get("colors", {})
if colors:
for key, value in colors.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master colors")
lines.append("")
# Component Overrides
lines.append("### Component Overrides")
lines.append("")
components = page_overrides.get("components", [])
if components:
for comp in components:
lines.append(f"- {comp}")
else:
lines.append("- No overrides — use Master component specs")
lines.append("")
# Page-Specific Components
lines.append("---")
lines.append("")
lines.append("## Page-Specific Components")
lines.append("")
unique_components = page_overrides.get("unique_components", [])
if unique_components:
for comp in unique_components:
lines.append(f"- {comp}")
else:
lines.append("- No unique components for this page")
lines.append("")
# Recommendations
lines.append("---")
lines.append("")
lines.append("## Recommendations")
lines.append("")
recommendations = page_overrides.get("recommendations", [])
if recommendations:
for rec in recommendations:
lines.append(f"- {rec}")
lines.append("")
return "\n".join(lines)
def _generate_intelligent_overrides(page_name: str, page_query: str, design_system: dict) -> dict:
"""
Generate intelligent overrides based on page type using layered search.
Uses the existing search infrastructure to find relevant style, UX, and layout
data instead of hardcoded page types.
"""
from core import search
page_lower = page_name.lower()
query_lower = (page_query or "").lower()
combined_context = f"{page_lower} {query_lower}"
# Search across multiple domains for page-specific guidance
style_search = search(combined_context, "style", max_results=1)
ux_search = search(combined_context, "ux", max_results=3)
landing_search = search(combined_context, "landing", max_results=1)
# Extract results from search response
style_results = style_search.get("results", [])
ux_results = ux_search.get("results", [])
landing_results = landing_search.get("results", [])
# Detect page type from search results or context
page_type = _detect_page_type(combined_context, style_results)
# Build overrides from search results
layout = {}
spacing = {}
typography = {}
colors = {}
components = []
unique_components = []
recommendations = []
# Extract style-based overrides
if style_results:
style = style_results[0]
style_name = style.get("Style Category", "")
keywords = style.get("Keywords", "")
best_for = style.get("Best For", "")
effects = style.get("Effects & Animation", "")
# Infer layout from style keywords
if any(kw in keywords.lower() for kw in ["data", "dense", "dashboard", "grid"]):
layout["Max Width"] = "1400px or full-width"
layout["Grid"] = "12-column grid for data flexibility"
spacing["Content Density"] = "High — optimize for information display"
elif any(kw in keywords.lower() for kw in ["minimal", "simple", "clean", "single"]):
layout["Max Width"] = "800px (narrow, focused)"
layout["Layout"] = "Single column, centered"
spacing["Content Density"] = "Low — focus on clarity"
else:
layout["Max Width"] = "1200px (standard)"
layout["Layout"] = "Full-width sections, centered content"
if effects:
recommendations.append(f"Effects: {effects}")
# Extract UX guidelines as recommendations
for ux in ux_results:
category = ux.get("Category", "")
do_text = ux.get("Do", "")
dont_text = ux.get("Don't", "")
if do_text:
recommendations.append(f"{category}: {do_text}")
if dont_text:
components.append(f"Avoid: {dont_text}")
# Extract landing pattern info for section structure
if landing_results:
landing = landing_results[0]
sections = landing.get("Section Order", "")
cta_placement = landing.get("Primary CTA Placement", "")
color_strategy = landing.get("Color Strategy", "")
if sections:
layout["Sections"] = sections
if cta_placement:
recommendations.append(f"CTA Placement: {cta_placement}")
if color_strategy:
colors["Strategy"] = color_strategy
# Add page-type specific defaults if no search results
if not layout:
layout["Max Width"] = "1200px"
layout["Layout"] = "Responsive grid"
if not recommendations:
recommendations = [
"Refer to MASTER.md for all design rules",
"Add specific overrides as needed for this page"
]
return {
"page_type": page_type,
"layout": layout,
"spacing": spacing,
"typography": typography,
"colors": colors,
"components": components,
"unique_components": unique_components,
"recommendations": recommendations
}
def _detect_page_type(context: str, style_results: list) -> str:
"""Detect page type from context and search results."""
context_lower = context.lower()
# Check for common page type patterns
page_patterns = [
(["dashboard", "admin", "analytics", "data", "metrics", "stats", "monitor", "overview"], "Dashboard / Data View"),
(["checkout", "payment", "cart", "purchase", "order", "billing"], "Checkout / Payment"),
(["settings", "profile", "account", "preferences", "config"], "Settings / Profile"),
(["landing", "marketing", "homepage", "hero", "home", "promo"], "Landing / Marketing"),
(["login", "signin", "signup", "register", "auth", "password"], "Authentication"),
(["pricing", "plans", "subscription", "tiers", "packages"], "Pricing / Plans"),
(["blog", "article", "post", "news", "content", "story"], "Blog / Article"),
(["product", "item", "detail", "pdp", "shop", "store"], "Product Detail"),
(["search", "results", "browse", "filter", "catalog", "list"], "Search Results"),
(["empty", "404", "error", "not found", "zero"], "Empty State"),
]
for keywords, page_type in page_patterns:
if any(kw in context_lower for kw in keywords):
return page_type
# Fallback: try to infer from style results
if style_results:
style_name = style_results[0].get("Style Category", "").lower()
best_for = style_results[0].get("Best For", "").lower()
if "dashboard" in best_for or "data" in best_for:
return "Dashboard / Data View"
elif "landing" in best_for or "marketing" in best_for:
return "Landing / Marketing"
return "General"
# ============ CLI SUPPORT ============ # ============ CLI SUPPORT ============
if __name__ == "__main__": if __name__ == "__main__":
import argparse import argparse

View File

@@ -4,14 +4,19 @@
UI/UX Pro Max Search - BM25 search engine for UI/UX style guides UI/UX Pro Max Search - BM25 search engine for UI/UX style guides
Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3] Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3]
python search.py "<query>" --design-system [-p "Project Name"] python search.py "<query>" --design-system [-p "Project Name"]
python search.py "<query>" --design-system --persist [-p "Project Name"] [--page "dashboard"]
Domains: style, prompt, color, chart, landing, product, ux, typography Domains: style, prompt, color, chart, landing, product, ux, typography
Stacks: html-tailwind, react, nextjs Stacks: html-tailwind, react, nextjs
Persistence (Master + Overrides pattern):
--persist Save design system to design-system/MASTER.md
--page Also create a page-specific override file in design-system/pages/
""" """
import argparse import argparse
from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack
from design_system import generate_design_system from design_system import generate_design_system, persist_design_system
def format_output(result): def format_output(result):
@@ -51,13 +56,38 @@ if __name__ == "__main__":
parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation") parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation")
parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output") parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output")
parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system") parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system")
# Persistence (Master + Overrides pattern)
parser.add_argument("--persist", action="store_true", help="Save design system to design-system/MASTER.md (creates hierarchical structure)")
parser.add_argument("--page", type=str, default=None, help="Create page-specific override file in design-system/pages/")
parser.add_argument("--output-dir", "-o", type=str, default=None, help="Output directory for persisted files (default: current directory)")
args = parser.parse_args() args = parser.parse_args()
# Design system takes priority # Design system takes priority
if args.design_system: if args.design_system:
result = generate_design_system(args.query, args.project_name, args.format) result = generate_design_system(
args.query,
args.project_name,
args.format,
persist=args.persist,
page=args.page,
output_dir=args.output_dir
)
print(result) print(result)
# Print persistence confirmation
if args.persist:
project_slug = args.project_name.lower().replace(' ', '-') if args.project_name else "default"
print("\n" + "=" * 60)
print(f"✅ Design system persisted to design-system/{project_slug}/")
print(f" 📄 design-system/{project_slug}/MASTER.md (Global Source of Truth)")
if args.page:
page_filename = args.page.lower().replace(' ', '-')
print(f" 📄 design-system/{project_slug}/pages/{page_filename}.md (Page Overrides)")
print("")
print(f"📖 Usage: When building a page, check design-system/{project_slug}/pages/[page].md first.")
print(f" If exists, its rules override MASTER.md. Otherwise, use MASTER.md.")
print("=" * 60)
# Stack search # Stack search
elif args.stack: elif args.stack:
result = search_stack(args.query, args.stack, args.max_results) result = search_stack(args.query, args.stack, args.max_results)

View File

@@ -7,10 +7,16 @@ to generate comprehensive design system recommendations.
Usage: Usage:
from design_system import generate_design_system from design_system import generate_design_system
result = generate_design_system("SaaS dashboard", "My Project") result = generate_design_system("SaaS dashboard", "My Project")
# With persistence (Master + Overrides pattern)
result = generate_design_system("SaaS dashboard", "My Project", persist=True)
result = generate_design_system("SaaS dashboard", "My Project", persist=True, page="dashboard")
""" """
import csv import csv
import json import json
import os
from datetime import datetime
from pathlib import Path from pathlib import Path
from core import search, DATA_DIR from core import search, DATA_DIR
@@ -338,6 +344,21 @@ def format_ascii_box(design_system: dict) -> str:
lines.append(line.ljust(BOX_WIDTH) + "|") lines.append(line.ljust(BOX_WIDTH) + "|")
lines.append("|" + " " * BOX_WIDTH + "|") lines.append("|" + " " * BOX_WIDTH + "|")
# Pre-Delivery Checklist section
lines.append("| PRE-DELIVERY CHECKLIST:".ljust(BOX_WIDTH) + "|")
checklist_items = [
"[ ] No emojis as icons (use SVG: Heroicons/Lucide)",
"[ ] cursor-pointer on all clickable elements",
"[ ] Hover states with smooth transitions (150-300ms)",
"[ ] Light mode: text contrast 4.5:1 minimum",
"[ ] Focus states visible for keyboard nav",
"[ ] prefers-reduced-motion respected",
"[ ] Responsive: 375px, 768px, 1024px, 1440px"
]
for item in checklist_items:
lines.append(f"| {item}".ljust(BOX_WIDTH) + "|")
lines.append("|" + " " * BOX_WIDTH + "|")
lines.append("+" + "-" * w + "+") lines.append("+" + "-" * w + "+")
return "\n".join(lines) return "\n".join(lines)
@@ -422,11 +443,23 @@ def format_markdown(design_system: dict) -> str:
lines.append(f"- {anti_patterns.replace(' + ', '\n- ')}") lines.append(f"- {anti_patterns.replace(' + ', '\n- ')}")
lines.append("") lines.append("")
# Pre-Delivery Checklist section
lines.append("### Pre-Delivery Checklist")
lines.append("- [ ] No emojis as icons (use SVG: Heroicons/Lucide)")
lines.append("- [ ] cursor-pointer on all clickable elements")
lines.append("- [ ] Hover states with smooth transitions (150-300ms)")
lines.append("- [ ] Light mode: text contrast 4.5:1 minimum")
lines.append("- [ ] Focus states visible for keyboard nav")
lines.append("- [ ] prefers-reduced-motion respected")
lines.append("- [ ] Responsive: 375px, 768px, 1024px, 1440px")
lines.append("")
return "\n".join(lines) return "\n".join(lines)
# ============ MAIN ENTRY POINT ============ # ============ MAIN ENTRY POINT ============
def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii") -> str: def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii",
persist: bool = False, page: str = None, output_dir: str = None) -> str:
""" """
Main entry point for design system generation. Main entry point for design system generation.
@@ -434,6 +467,9 @@ def generate_design_system(query: str, project_name: str = None, output_format:
query: Search query (e.g., "SaaS dashboard", "e-commerce luxury") query: Search query (e.g., "SaaS dashboard", "e-commerce luxury")
project_name: Optional project name for output header project_name: Optional project name for output header
output_format: "ascii" (default) or "markdown" output_format: "ascii" (default) or "markdown"
persist: If True, save design system to design-system/ folder
page: Optional page name for page-specific override file
output_dir: Optional output directory (defaults to current working directory)
Returns: Returns:
Formatted design system string Formatted design system string
@@ -441,11 +477,580 @@ def generate_design_system(query: str, project_name: str = None, output_format:
generator = DesignSystemGenerator() generator = DesignSystemGenerator()
design_system = generator.generate(query, project_name) design_system = generator.generate(query, project_name)
# Persist to files if requested
if persist:
persist_design_system(design_system, page, output_dir, query)
if output_format == "markdown": if output_format == "markdown":
return format_markdown(design_system) return format_markdown(design_system)
return format_ascii_box(design_system) return format_ascii_box(design_system)
# ============ PERSISTENCE FUNCTIONS ============
def persist_design_system(design_system: dict, page: str = None, output_dir: str = None, page_query: str = None) -> dict:
"""
Persist design system to design-system/<project>/ folder using Master + Overrides pattern.
Args:
design_system: The generated design system dictionary
page: Optional page name for page-specific override file
output_dir: Optional output directory (defaults to current working directory)
page_query: Optional query string for intelligent page override generation
Returns:
dict with created file paths and status
"""
base_dir = Path(output_dir) if output_dir else Path.cwd()
# Use project name for project-specific folder
project_name = design_system.get("project_name", "default")
project_slug = project_name.lower().replace(' ', '-')
design_system_dir = base_dir / "design-system" / project_slug
pages_dir = design_system_dir / "pages"
created_files = []
# Create directories
design_system_dir.mkdir(parents=True, exist_ok=True)
pages_dir.mkdir(parents=True, exist_ok=True)
master_file = design_system_dir / "MASTER.md"
# Generate and write MASTER.md
master_content = format_master_md(design_system)
with open(master_file, 'w', encoding='utf-8') as f:
f.write(master_content)
created_files.append(str(master_file))
# If page is specified, create page override file with intelligent content
if page:
page_file = pages_dir / f"{page.lower().replace(' ', '-')}.md"
page_content = format_page_override_md(design_system, page, page_query)
with open(page_file, 'w', encoding='utf-8') as f:
f.write(page_content)
created_files.append(str(page_file))
return {
"status": "success",
"design_system_dir": str(design_system_dir),
"created_files": created_files
}
def format_master_md(design_system: dict) -> str:
"""Format design system as MASTER.md with hierarchical override logic."""
project = design_system.get("project_name", "PROJECT")
pattern = design_system.get("pattern", {})
style = design_system.get("style", {})
colors = design_system.get("colors", {})
typography = design_system.get("typography", {})
effects = design_system.get("key_effects", "")
anti_patterns = design_system.get("anti_patterns", "")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
lines = []
# Logic header
lines.append("# Design System Master File")
lines.append("")
lines.append("> **LOGIC:** When building a specific page, first check `design-system/pages/[page-name].md`.")
lines.append("> If that file exists, its rules **override** this Master file.")
lines.append("> If not, strictly follow the rules below.")
lines.append("")
lines.append("---")
lines.append("")
lines.append(f"**Project:** {project}")
lines.append(f"**Generated:** {timestamp}")
lines.append(f"**Category:** {design_system.get('category', 'General')}")
lines.append("")
lines.append("---")
lines.append("")
# Global Rules section
lines.append("## Global Rules")
lines.append("")
# Color Palette
lines.append("### Color Palette")
lines.append("")
lines.append("| Role | Hex | CSS Variable |")
lines.append("|------|-----|--------------|")
lines.append(f"| Primary | `{colors.get('primary', '#2563EB')}` | `--color-primary` |")
lines.append(f"| Secondary | `{colors.get('secondary', '#3B82F6')}` | `--color-secondary` |")
lines.append(f"| CTA/Accent | `{colors.get('cta', '#F97316')}` | `--color-cta` |")
lines.append(f"| Background | `{colors.get('background', '#F8FAFC')}` | `--color-background` |")
lines.append(f"| Text | `{colors.get('text', '#1E293B')}` | `--color-text` |")
lines.append("")
if colors.get("notes"):
lines.append(f"**Color Notes:** {colors.get('notes', '')}")
lines.append("")
# Typography
lines.append("### Typography")
lines.append("")
lines.append(f"- **Heading Font:** {typography.get('heading', 'Inter')}")
lines.append(f"- **Body Font:** {typography.get('body', 'Inter')}")
if typography.get("mood"):
lines.append(f"- **Mood:** {typography.get('mood', '')}")
if typography.get("google_fonts_url"):
lines.append(f"- **Google Fonts:** [{typography.get('heading', '')} + {typography.get('body', '')}]({typography.get('google_fonts_url', '')})")
lines.append("")
if typography.get("css_import"):
lines.append("**CSS Import:**")
lines.append("```css")
lines.append(typography.get("css_import", ""))
lines.append("```")
lines.append("")
# Spacing Variables
lines.append("### Spacing Variables")
lines.append("")
lines.append("| Token | Value | Usage |")
lines.append("|-------|-------|-------|")
lines.append("| `--space-xs` | `4px` / `0.25rem` | Tight gaps |")
lines.append("| `--space-sm` | `8px` / `0.5rem` | Icon gaps, inline spacing |")
lines.append("| `--space-md` | `16px` / `1rem` | Standard padding |")
lines.append("| `--space-lg` | `24px` / `1.5rem` | Section padding |")
lines.append("| `--space-xl` | `32px` / `2rem` | Large gaps |")
lines.append("| `--space-2xl` | `48px` / `3rem` | Section margins |")
lines.append("| `--space-3xl` | `64px` / `4rem` | Hero padding |")
lines.append("")
# Shadow Depths
lines.append("### Shadow Depths")
lines.append("")
lines.append("| Level | Value | Usage |")
lines.append("|-------|-------|-------|")
lines.append("| `--shadow-sm` | `0 1px 2px rgba(0,0,0,0.05)` | Subtle lift |")
lines.append("| `--shadow-md` | `0 4px 6px rgba(0,0,0,0.1)` | Cards, buttons |")
lines.append("| `--shadow-lg` | `0 10px 15px rgba(0,0,0,0.1)` | Modals, dropdowns |")
lines.append("| `--shadow-xl` | `0 20px 25px rgba(0,0,0,0.15)` | Hero images, featured cards |")
lines.append("")
# Component Specs section
lines.append("---")
lines.append("")
lines.append("## Component Specs")
lines.append("")
# Buttons
lines.append("### Buttons")
lines.append("")
lines.append("```css")
lines.append("/* Primary Button */")
lines.append(".btn-primary {")
lines.append(f" background: {colors.get('cta', '#F97316')};")
lines.append(" color: white;")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("")
lines.append(".btn-primary:hover {")
lines.append(" opacity: 0.9;")
lines.append(" transform: translateY(-1px);")
lines.append("}")
lines.append("")
lines.append("/* Secondary Button */")
lines.append(".btn-secondary {")
lines.append(f" background: transparent;")
lines.append(f" color: {colors.get('primary', '#2563EB')};")
lines.append(f" border: 2px solid {colors.get('primary', '#2563EB')};")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("```")
lines.append("")
# Cards
lines.append("### Cards")
lines.append("")
lines.append("```css")
lines.append(".card {")
lines.append(f" background: {colors.get('background', '#FFFFFF')};")
lines.append(" border-radius: 12px;")
lines.append(" padding: 24px;")
lines.append(" box-shadow: var(--shadow-md);")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("")
lines.append(".card:hover {")
lines.append(" box-shadow: var(--shadow-lg);")
lines.append(" transform: translateY(-2px);")
lines.append("}")
lines.append("```")
lines.append("")
# Inputs
lines.append("### Inputs")
lines.append("")
lines.append("```css")
lines.append(".input {")
lines.append(" padding: 12px 16px;")
lines.append(" border: 1px solid #E2E8F0;")
lines.append(" border-radius: 8px;")
lines.append(" font-size: 16px;")
lines.append(" transition: border-color 200ms ease;")
lines.append("}")
lines.append("")
lines.append(".input:focus {")
lines.append(f" border-color: {colors.get('primary', '#2563EB')};")
lines.append(" outline: none;")
lines.append(f" box-shadow: 0 0 0 3px {colors.get('primary', '#2563EB')}20;")
lines.append("}")
lines.append("```")
lines.append("")
# Modals
lines.append("### Modals")
lines.append("")
lines.append("```css")
lines.append(".modal-overlay {")
lines.append(" background: rgba(0, 0, 0, 0.5);")
lines.append(" backdrop-filter: blur(4px);")
lines.append("}")
lines.append("")
lines.append(".modal {")
lines.append(" background: white;")
lines.append(" border-radius: 16px;")
lines.append(" padding: 32px;")
lines.append(" box-shadow: var(--shadow-xl);")
lines.append(" max-width: 500px;")
lines.append(" width: 90%;")
lines.append("}")
lines.append("```")
lines.append("")
# Style section
lines.append("---")
lines.append("")
lines.append("## Style Guidelines")
lines.append("")
lines.append(f"**Style:** {style.get('name', 'Minimalism')}")
lines.append("")
if style.get("keywords"):
lines.append(f"**Keywords:** {style.get('keywords', '')}")
lines.append("")
if style.get("best_for"):
lines.append(f"**Best For:** {style.get('best_for', '')}")
lines.append("")
if effects:
lines.append(f"**Key Effects:** {effects}")
lines.append("")
# Layout Pattern
lines.append("### Page Pattern")
lines.append("")
lines.append(f"**Pattern Name:** {pattern.get('name', '')}")
lines.append("")
if pattern.get('conversion'):
lines.append(f"- **Conversion Strategy:** {pattern.get('conversion', '')}")
if pattern.get('cta_placement'):
lines.append(f"- **CTA Placement:** {pattern.get('cta_placement', '')}")
lines.append(f"- **Section Order:** {pattern.get('sections', '')}")
lines.append("")
# Anti-Patterns section
lines.append("---")
lines.append("")
lines.append("## Anti-Patterns (Do NOT Use)")
lines.append("")
if anti_patterns:
anti_list = [a.strip() for a in anti_patterns.split("+")]
for anti in anti_list:
if anti:
lines.append(f"- ❌ {anti}")
lines.append("")
lines.append("### Additional Forbidden Patterns")
lines.append("")
lines.append("- ❌ **Emojis as icons** — Use SVG icons (Heroicons, Lucide, Simple Icons)")
lines.append("- ❌ **Missing cursor:pointer** — All clickable elements must have cursor:pointer")
lines.append("- ❌ **Layout-shifting hovers** — Avoid scale transforms that shift layout")
lines.append("- ❌ **Low contrast text** — Maintain 4.5:1 minimum contrast ratio")
lines.append("- ❌ **Instant state changes** — Always use transitions (150-300ms)")
lines.append("- ❌ **Invisible focus states** — Focus states must be visible for a11y")
lines.append("")
# Pre-Delivery Checklist
lines.append("---")
lines.append("")
lines.append("## Pre-Delivery Checklist")
lines.append("")
lines.append("Before delivering any UI code, verify:")
lines.append("")
lines.append("- [ ] No emojis used as icons (use SVG instead)")
lines.append("- [ ] All icons from consistent icon set (Heroicons/Lucide)")
lines.append("- [ ] `cursor-pointer` on all clickable elements")
lines.append("- [ ] Hover states with smooth transitions (150-300ms)")
lines.append("- [ ] Light mode: text contrast 4.5:1 minimum")
lines.append("- [ ] Focus states visible for keyboard navigation")
lines.append("- [ ] `prefers-reduced-motion` respected")
lines.append("- [ ] Responsive: 375px, 768px, 1024px, 1440px")
lines.append("- [ ] No content hidden behind fixed navbars")
lines.append("- [ ] No horizontal scroll on mobile")
lines.append("")
return "\n".join(lines)
def format_page_override_md(design_system: dict, page_name: str, page_query: str = None) -> str:
"""Format a page-specific override file with intelligent AI-generated content."""
project = design_system.get("project_name", "PROJECT")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
page_title = page_name.replace("-", " ").replace("_", " ").title()
# Detect page type and generate intelligent overrides
page_overrides = _generate_intelligent_overrides(page_name, page_query, design_system)
lines = []
lines.append(f"# {page_title} Page Overrides")
lines.append("")
lines.append(f"> **PROJECT:** {project}")
lines.append(f"> **Generated:** {timestamp}")
lines.append(f"> **Page Type:** {page_overrides.get('page_type', 'General')}")
lines.append("")
lines.append("> ⚠️ **IMPORTANT:** Rules in this file **override** the Master file (`design-system/MASTER.md`).")
lines.append("> Only deviations from the Master are documented here. For all other rules, refer to the Master.")
lines.append("")
lines.append("---")
lines.append("")
# Page-specific rules with actual content
lines.append("## Page-Specific Rules")
lines.append("")
# Layout Overrides
lines.append("### Layout Overrides")
lines.append("")
layout = page_overrides.get("layout", {})
if layout:
for key, value in layout.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master layout")
lines.append("")
# Spacing Overrides
lines.append("### Spacing Overrides")
lines.append("")
spacing = page_overrides.get("spacing", {})
if spacing:
for key, value in spacing.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master spacing")
lines.append("")
# Typography Overrides
lines.append("### Typography Overrides")
lines.append("")
typography = page_overrides.get("typography", {})
if typography:
for key, value in typography.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master typography")
lines.append("")
# Color Overrides
lines.append("### Color Overrides")
lines.append("")
colors = page_overrides.get("colors", {})
if colors:
for key, value in colors.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master colors")
lines.append("")
# Component Overrides
lines.append("### Component Overrides")
lines.append("")
components = page_overrides.get("components", [])
if components:
for comp in components:
lines.append(f"- {comp}")
else:
lines.append("- No overrides — use Master component specs")
lines.append("")
# Page-Specific Components
lines.append("---")
lines.append("")
lines.append("## Page-Specific Components")
lines.append("")
unique_components = page_overrides.get("unique_components", [])
if unique_components:
for comp in unique_components:
lines.append(f"- {comp}")
else:
lines.append("- No unique components for this page")
lines.append("")
# Recommendations
lines.append("---")
lines.append("")
lines.append("## Recommendations")
lines.append("")
recommendations = page_overrides.get("recommendations", [])
if recommendations:
for rec in recommendations:
lines.append(f"- {rec}")
lines.append("")
return "\n".join(lines)
def _generate_intelligent_overrides(page_name: str, page_query: str, design_system: dict) -> dict:
"""
Generate intelligent overrides based on page type using layered search.
Uses the existing search infrastructure to find relevant style, UX, and layout
data instead of hardcoded page types.
"""
from core import search
page_lower = page_name.lower()
query_lower = (page_query or "").lower()
combined_context = f"{page_lower} {query_lower}"
# Search across multiple domains for page-specific guidance
style_search = search(combined_context, "style", max_results=1)
ux_search = search(combined_context, "ux", max_results=3)
landing_search = search(combined_context, "landing", max_results=1)
# Extract results from search response
style_results = style_search.get("results", [])
ux_results = ux_search.get("results", [])
landing_results = landing_search.get("results", [])
# Detect page type from search results or context
page_type = _detect_page_type(combined_context, style_results)
# Build overrides from search results
layout = {}
spacing = {}
typography = {}
colors = {}
components = []
unique_components = []
recommendations = []
# Extract style-based overrides
if style_results:
style = style_results[0]
style_name = style.get("Style Category", "")
keywords = style.get("Keywords", "")
best_for = style.get("Best For", "")
effects = style.get("Effects & Animation", "")
# Infer layout from style keywords
if any(kw in keywords.lower() for kw in ["data", "dense", "dashboard", "grid"]):
layout["Max Width"] = "1400px or full-width"
layout["Grid"] = "12-column grid for data flexibility"
spacing["Content Density"] = "High — optimize for information display"
elif any(kw in keywords.lower() for kw in ["minimal", "simple", "clean", "single"]):
layout["Max Width"] = "800px (narrow, focused)"
layout["Layout"] = "Single column, centered"
spacing["Content Density"] = "Low — focus on clarity"
else:
layout["Max Width"] = "1200px (standard)"
layout["Layout"] = "Full-width sections, centered content"
if effects:
recommendations.append(f"Effects: {effects}")
# Extract UX guidelines as recommendations
for ux in ux_results:
category = ux.get("Category", "")
do_text = ux.get("Do", "")
dont_text = ux.get("Don't", "")
if do_text:
recommendations.append(f"{category}: {do_text}")
if dont_text:
components.append(f"Avoid: {dont_text}")
# Extract landing pattern info for section structure
if landing_results:
landing = landing_results[0]
sections = landing.get("Section Order", "")
cta_placement = landing.get("Primary CTA Placement", "")
color_strategy = landing.get("Color Strategy", "")
if sections:
layout["Sections"] = sections
if cta_placement:
recommendations.append(f"CTA Placement: {cta_placement}")
if color_strategy:
colors["Strategy"] = color_strategy
# Add page-type specific defaults if no search results
if not layout:
layout["Max Width"] = "1200px"
layout["Layout"] = "Responsive grid"
if not recommendations:
recommendations = [
"Refer to MASTER.md for all design rules",
"Add specific overrides as needed for this page"
]
return {
"page_type": page_type,
"layout": layout,
"spacing": spacing,
"typography": typography,
"colors": colors,
"components": components,
"unique_components": unique_components,
"recommendations": recommendations
}
def _detect_page_type(context: str, style_results: list) -> str:
"""Detect page type from context and search results."""
context_lower = context.lower()
# Check for common page type patterns
page_patterns = [
(["dashboard", "admin", "analytics", "data", "metrics", "stats", "monitor", "overview"], "Dashboard / Data View"),
(["checkout", "payment", "cart", "purchase", "order", "billing"], "Checkout / Payment"),
(["settings", "profile", "account", "preferences", "config"], "Settings / Profile"),
(["landing", "marketing", "homepage", "hero", "home", "promo"], "Landing / Marketing"),
(["login", "signin", "signup", "register", "auth", "password"], "Authentication"),
(["pricing", "plans", "subscription", "tiers", "packages"], "Pricing / Plans"),
(["blog", "article", "post", "news", "content", "story"], "Blog / Article"),
(["product", "item", "detail", "pdp", "shop", "store"], "Product Detail"),
(["search", "results", "browse", "filter", "catalog", "list"], "Search Results"),
(["empty", "404", "error", "not found", "zero"], "Empty State"),
]
for keywords, page_type in page_patterns:
if any(kw in context_lower for kw in keywords):
return page_type
# Fallback: try to infer from style results
if style_results:
style_name = style_results[0].get("Style Category", "").lower()
best_for = style_results[0].get("Best For", "").lower()
if "dashboard" in best_for or "data" in best_for:
return "Dashboard / Data View"
elif "landing" in best_for or "marketing" in best_for:
return "Landing / Marketing"
return "General"
# ============ CLI SUPPORT ============ # ============ CLI SUPPORT ============
if __name__ == "__main__": if __name__ == "__main__":
import argparse import argparse

View File

@@ -4,14 +4,19 @@
UI/UX Pro Max Search - BM25 search engine for UI/UX style guides UI/UX Pro Max Search - BM25 search engine for UI/UX style guides
Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3] Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3]
python search.py "<query>" --design-system [-p "Project Name"] python search.py "<query>" --design-system [-p "Project Name"]
python search.py "<query>" --design-system --persist [-p "Project Name"] [--page "dashboard"]
Domains: style, prompt, color, chart, landing, product, ux, typography Domains: style, prompt, color, chart, landing, product, ux, typography
Stacks: html-tailwind, react, nextjs Stacks: html-tailwind, react, nextjs
Persistence (Master + Overrides pattern):
--persist Save design system to design-system/MASTER.md
--page Also create a page-specific override file in design-system/pages/
""" """
import argparse import argparse
from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack
from design_system import generate_design_system from design_system import generate_design_system, persist_design_system
def format_output(result): def format_output(result):
@@ -51,13 +56,38 @@ if __name__ == "__main__":
parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation") parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation")
parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output") parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output")
parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system") parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system")
# Persistence (Master + Overrides pattern)
parser.add_argument("--persist", action="store_true", help="Save design system to design-system/MASTER.md (creates hierarchical structure)")
parser.add_argument("--page", type=str, default=None, help="Create page-specific override file in design-system/pages/")
parser.add_argument("--output-dir", "-o", type=str, default=None, help="Output directory for persisted files (default: current directory)")
args = parser.parse_args() args = parser.parse_args()
# Design system takes priority # Design system takes priority
if args.design_system: if args.design_system:
result = generate_design_system(args.query, args.project_name, args.format) result = generate_design_system(
args.query,
args.project_name,
args.format,
persist=args.persist,
page=args.page,
output_dir=args.output_dir
)
print(result) print(result)
# Print persistence confirmation
if args.persist:
project_slug = args.project_name.lower().replace(' ', '-') if args.project_name else "default"
print("\n" + "=" * 60)
print(f"✅ Design system persisted to design-system/{project_slug}/")
print(f" 📄 design-system/{project_slug}/MASTER.md (Global Source of Truth)")
if args.page:
page_filename = args.page.lower().replace(' ', '-')
print(f" 📄 design-system/{project_slug}/pages/{page_filename}.md (Page Overrides)")
print("")
print(f"📖 Usage: When building a page, check design-system/{project_slug}/pages/[page].md first.")
print(f" If exists, its rules override MASTER.md. Otherwise, use MASTER.md.")
print("=" * 60)
# Stack search # Stack search
elif args.stack: elif args.stack:
result = search_stack(args.query, args.stack, args.max_results) result = search_stack(args.query, args.stack, args.max_results)

View File

@@ -60,6 +60,31 @@ This command:
python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa" python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa"
``` ```
### Step 2b: Persist Design System (Master + Overrides Pattern)
To save the design system for hierarchical retrieval across sessions, add `--persist`:
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name"
```
This creates:
- `design-system/MASTER.md` — Global Source of Truth with all design rules
- `design-system/pages/` — Folder for page-specific overrides
**With page-specific override:**
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name" --page "dashboard"
```
This also creates:
- `design-system/pages/dashboard.md` — Page-specific deviations from Master
**How hierarchical retrieval works:**
1. When building a specific page (e.g., "Checkout"), first check `design-system/pages/checkout.md`
2. If the page file exists, its rules **override** the Master file
3. If not, use `design-system/MASTER.md` exclusively
### Step 3: Supplement with Detailed Searches (as needed) ### Step 3: Supplement with Detailed Searches (as needed)
After getting the design system, use domain searches to get additional details: After getting the design system, use domain searches to get additional details:
@@ -86,8 +111,8 @@ Get implementation-specific best practices. If user doesn't specify a stack, **d
python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind
``` ```
Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn` Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn`, `jetpack-compose`
, `jetpack-compose`
--- ---
## Search Reference ## Search Reference
@@ -120,6 +145,7 @@ Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`
| `react-native` | Components, Navigation, Lists | | `react-native` | Components, Navigation, Lists |
| `flutter` | Widgets, State, Layout, Theming | | `flutter` | Widgets, State, Layout, Theming |
| `shadcn` | shadcn/ui components, theming, forms, patterns | | `shadcn` | shadcn/ui components, theming, forms, patterns |
| `jetpack-compose` | Composables, Modifiers, State Hoisting, Recomposition |
--- ---

View File

@@ -7,10 +7,16 @@ to generate comprehensive design system recommendations.
Usage: Usage:
from design_system import generate_design_system from design_system import generate_design_system
result = generate_design_system("SaaS dashboard", "My Project") result = generate_design_system("SaaS dashboard", "My Project")
# With persistence (Master + Overrides pattern)
result = generate_design_system("SaaS dashboard", "My Project", persist=True)
result = generate_design_system("SaaS dashboard", "My Project", persist=True, page="dashboard")
""" """
import csv import csv
import json import json
import os
from datetime import datetime
from pathlib import Path from pathlib import Path
from core import search, DATA_DIR from core import search, DATA_DIR
@@ -452,7 +458,8 @@ def format_markdown(design_system: dict) -> str:
# ============ MAIN ENTRY POINT ============ # ============ MAIN ENTRY POINT ============
def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii") -> str: def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii",
persist: bool = False, page: str = None, output_dir: str = None) -> str:
""" """
Main entry point for design system generation. Main entry point for design system generation.
@@ -460,6 +467,9 @@ def generate_design_system(query: str, project_name: str = None, output_format:
query: Search query (e.g., "SaaS dashboard", "e-commerce luxury") query: Search query (e.g., "SaaS dashboard", "e-commerce luxury")
project_name: Optional project name for output header project_name: Optional project name for output header
output_format: "ascii" (default) or "markdown" output_format: "ascii" (default) or "markdown"
persist: If True, save design system to design-system/ folder
page: Optional page name for page-specific override file
output_dir: Optional output directory (defaults to current working directory)
Returns: Returns:
Formatted design system string Formatted design system string
@@ -467,11 +477,580 @@ def generate_design_system(query: str, project_name: str = None, output_format:
generator = DesignSystemGenerator() generator = DesignSystemGenerator()
design_system = generator.generate(query, project_name) design_system = generator.generate(query, project_name)
# Persist to files if requested
if persist:
persist_design_system(design_system, page, output_dir, query)
if output_format == "markdown": if output_format == "markdown":
return format_markdown(design_system) return format_markdown(design_system)
return format_ascii_box(design_system) return format_ascii_box(design_system)
# ============ PERSISTENCE FUNCTIONS ============
def persist_design_system(design_system: dict, page: str = None, output_dir: str = None, page_query: str = None) -> dict:
"""
Persist design system to design-system/<project>/ folder using Master + Overrides pattern.
Args:
design_system: The generated design system dictionary
page: Optional page name for page-specific override file
output_dir: Optional output directory (defaults to current working directory)
page_query: Optional query string for intelligent page override generation
Returns:
dict with created file paths and status
"""
base_dir = Path(output_dir) if output_dir else Path.cwd()
# Use project name for project-specific folder
project_name = design_system.get("project_name", "default")
project_slug = project_name.lower().replace(' ', '-')
design_system_dir = base_dir / "design-system" / project_slug
pages_dir = design_system_dir / "pages"
created_files = []
# Create directories
design_system_dir.mkdir(parents=True, exist_ok=True)
pages_dir.mkdir(parents=True, exist_ok=True)
master_file = design_system_dir / "MASTER.md"
# Generate and write MASTER.md
master_content = format_master_md(design_system)
with open(master_file, 'w', encoding='utf-8') as f:
f.write(master_content)
created_files.append(str(master_file))
# If page is specified, create page override file with intelligent content
if page:
page_file = pages_dir / f"{page.lower().replace(' ', '-')}.md"
page_content = format_page_override_md(design_system, page, page_query)
with open(page_file, 'w', encoding='utf-8') as f:
f.write(page_content)
created_files.append(str(page_file))
return {
"status": "success",
"design_system_dir": str(design_system_dir),
"created_files": created_files
}
def format_master_md(design_system: dict) -> str:
"""Format design system as MASTER.md with hierarchical override logic."""
project = design_system.get("project_name", "PROJECT")
pattern = design_system.get("pattern", {})
style = design_system.get("style", {})
colors = design_system.get("colors", {})
typography = design_system.get("typography", {})
effects = design_system.get("key_effects", "")
anti_patterns = design_system.get("anti_patterns", "")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
lines = []
# Logic header
lines.append("# Design System Master File")
lines.append("")
lines.append("> **LOGIC:** When building a specific page, first check `design-system/pages/[page-name].md`.")
lines.append("> If that file exists, its rules **override** this Master file.")
lines.append("> If not, strictly follow the rules below.")
lines.append("")
lines.append("---")
lines.append("")
lines.append(f"**Project:** {project}")
lines.append(f"**Generated:** {timestamp}")
lines.append(f"**Category:** {design_system.get('category', 'General')}")
lines.append("")
lines.append("---")
lines.append("")
# Global Rules section
lines.append("## Global Rules")
lines.append("")
# Color Palette
lines.append("### Color Palette")
lines.append("")
lines.append("| Role | Hex | CSS Variable |")
lines.append("|------|-----|--------------|")
lines.append(f"| Primary | `{colors.get('primary', '#2563EB')}` | `--color-primary` |")
lines.append(f"| Secondary | `{colors.get('secondary', '#3B82F6')}` | `--color-secondary` |")
lines.append(f"| CTA/Accent | `{colors.get('cta', '#F97316')}` | `--color-cta` |")
lines.append(f"| Background | `{colors.get('background', '#F8FAFC')}` | `--color-background` |")
lines.append(f"| Text | `{colors.get('text', '#1E293B')}` | `--color-text` |")
lines.append("")
if colors.get("notes"):
lines.append(f"**Color Notes:** {colors.get('notes', '')}")
lines.append("")
# Typography
lines.append("### Typography")
lines.append("")
lines.append(f"- **Heading Font:** {typography.get('heading', 'Inter')}")
lines.append(f"- **Body Font:** {typography.get('body', 'Inter')}")
if typography.get("mood"):
lines.append(f"- **Mood:** {typography.get('mood', '')}")
if typography.get("google_fonts_url"):
lines.append(f"- **Google Fonts:** [{typography.get('heading', '')} + {typography.get('body', '')}]({typography.get('google_fonts_url', '')})")
lines.append("")
if typography.get("css_import"):
lines.append("**CSS Import:**")
lines.append("```css")
lines.append(typography.get("css_import", ""))
lines.append("```")
lines.append("")
# Spacing Variables
lines.append("### Spacing Variables")
lines.append("")
lines.append("| Token | Value | Usage |")
lines.append("|-------|-------|-------|")
lines.append("| `--space-xs` | `4px` / `0.25rem` | Tight gaps |")
lines.append("| `--space-sm` | `8px` / `0.5rem` | Icon gaps, inline spacing |")
lines.append("| `--space-md` | `16px` / `1rem` | Standard padding |")
lines.append("| `--space-lg` | `24px` / `1.5rem` | Section padding |")
lines.append("| `--space-xl` | `32px` / `2rem` | Large gaps |")
lines.append("| `--space-2xl` | `48px` / `3rem` | Section margins |")
lines.append("| `--space-3xl` | `64px` / `4rem` | Hero padding |")
lines.append("")
# Shadow Depths
lines.append("### Shadow Depths")
lines.append("")
lines.append("| Level | Value | Usage |")
lines.append("|-------|-------|-------|")
lines.append("| `--shadow-sm` | `0 1px 2px rgba(0,0,0,0.05)` | Subtle lift |")
lines.append("| `--shadow-md` | `0 4px 6px rgba(0,0,0,0.1)` | Cards, buttons |")
lines.append("| `--shadow-lg` | `0 10px 15px rgba(0,0,0,0.1)` | Modals, dropdowns |")
lines.append("| `--shadow-xl` | `0 20px 25px rgba(0,0,0,0.15)` | Hero images, featured cards |")
lines.append("")
# Component Specs section
lines.append("---")
lines.append("")
lines.append("## Component Specs")
lines.append("")
# Buttons
lines.append("### Buttons")
lines.append("")
lines.append("```css")
lines.append("/* Primary Button */")
lines.append(".btn-primary {")
lines.append(f" background: {colors.get('cta', '#F97316')};")
lines.append(" color: white;")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("")
lines.append(".btn-primary:hover {")
lines.append(" opacity: 0.9;")
lines.append(" transform: translateY(-1px);")
lines.append("}")
lines.append("")
lines.append("/* Secondary Button */")
lines.append(".btn-secondary {")
lines.append(f" background: transparent;")
lines.append(f" color: {colors.get('primary', '#2563EB')};")
lines.append(f" border: 2px solid {colors.get('primary', '#2563EB')};")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("```")
lines.append("")
# Cards
lines.append("### Cards")
lines.append("")
lines.append("```css")
lines.append(".card {")
lines.append(f" background: {colors.get('background', '#FFFFFF')};")
lines.append(" border-radius: 12px;")
lines.append(" padding: 24px;")
lines.append(" box-shadow: var(--shadow-md);")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("")
lines.append(".card:hover {")
lines.append(" box-shadow: var(--shadow-lg);")
lines.append(" transform: translateY(-2px);")
lines.append("}")
lines.append("```")
lines.append("")
# Inputs
lines.append("### Inputs")
lines.append("")
lines.append("```css")
lines.append(".input {")
lines.append(" padding: 12px 16px;")
lines.append(" border: 1px solid #E2E8F0;")
lines.append(" border-radius: 8px;")
lines.append(" font-size: 16px;")
lines.append(" transition: border-color 200ms ease;")
lines.append("}")
lines.append("")
lines.append(".input:focus {")
lines.append(f" border-color: {colors.get('primary', '#2563EB')};")
lines.append(" outline: none;")
lines.append(f" box-shadow: 0 0 0 3px {colors.get('primary', '#2563EB')}20;")
lines.append("}")
lines.append("```")
lines.append("")
# Modals
lines.append("### Modals")
lines.append("")
lines.append("```css")
lines.append(".modal-overlay {")
lines.append(" background: rgba(0, 0, 0, 0.5);")
lines.append(" backdrop-filter: blur(4px);")
lines.append("}")
lines.append("")
lines.append(".modal {")
lines.append(" background: white;")
lines.append(" border-radius: 16px;")
lines.append(" padding: 32px;")
lines.append(" box-shadow: var(--shadow-xl);")
lines.append(" max-width: 500px;")
lines.append(" width: 90%;")
lines.append("}")
lines.append("```")
lines.append("")
# Style section
lines.append("---")
lines.append("")
lines.append("## Style Guidelines")
lines.append("")
lines.append(f"**Style:** {style.get('name', 'Minimalism')}")
lines.append("")
if style.get("keywords"):
lines.append(f"**Keywords:** {style.get('keywords', '')}")
lines.append("")
if style.get("best_for"):
lines.append(f"**Best For:** {style.get('best_for', '')}")
lines.append("")
if effects:
lines.append(f"**Key Effects:** {effects}")
lines.append("")
# Layout Pattern
lines.append("### Page Pattern")
lines.append("")
lines.append(f"**Pattern Name:** {pattern.get('name', '')}")
lines.append("")
if pattern.get('conversion'):
lines.append(f"- **Conversion Strategy:** {pattern.get('conversion', '')}")
if pattern.get('cta_placement'):
lines.append(f"- **CTA Placement:** {pattern.get('cta_placement', '')}")
lines.append(f"- **Section Order:** {pattern.get('sections', '')}")
lines.append("")
# Anti-Patterns section
lines.append("---")
lines.append("")
lines.append("## Anti-Patterns (Do NOT Use)")
lines.append("")
if anti_patterns:
anti_list = [a.strip() for a in anti_patterns.split("+")]
for anti in anti_list:
if anti:
lines.append(f"- ❌ {anti}")
lines.append("")
lines.append("### Additional Forbidden Patterns")
lines.append("")
lines.append("- ❌ **Emojis as icons** — Use SVG icons (Heroicons, Lucide, Simple Icons)")
lines.append("- ❌ **Missing cursor:pointer** — All clickable elements must have cursor:pointer")
lines.append("- ❌ **Layout-shifting hovers** — Avoid scale transforms that shift layout")
lines.append("- ❌ **Low contrast text** — Maintain 4.5:1 minimum contrast ratio")
lines.append("- ❌ **Instant state changes** — Always use transitions (150-300ms)")
lines.append("- ❌ **Invisible focus states** — Focus states must be visible for a11y")
lines.append("")
# Pre-Delivery Checklist
lines.append("---")
lines.append("")
lines.append("## Pre-Delivery Checklist")
lines.append("")
lines.append("Before delivering any UI code, verify:")
lines.append("")
lines.append("- [ ] No emojis used as icons (use SVG instead)")
lines.append("- [ ] All icons from consistent icon set (Heroicons/Lucide)")
lines.append("- [ ] `cursor-pointer` on all clickable elements")
lines.append("- [ ] Hover states with smooth transitions (150-300ms)")
lines.append("- [ ] Light mode: text contrast 4.5:1 minimum")
lines.append("- [ ] Focus states visible for keyboard navigation")
lines.append("- [ ] `prefers-reduced-motion` respected")
lines.append("- [ ] Responsive: 375px, 768px, 1024px, 1440px")
lines.append("- [ ] No content hidden behind fixed navbars")
lines.append("- [ ] No horizontal scroll on mobile")
lines.append("")
return "\n".join(lines)
def format_page_override_md(design_system: dict, page_name: str, page_query: str = None) -> str:
"""Format a page-specific override file with intelligent AI-generated content."""
project = design_system.get("project_name", "PROJECT")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
page_title = page_name.replace("-", " ").replace("_", " ").title()
# Detect page type and generate intelligent overrides
page_overrides = _generate_intelligent_overrides(page_name, page_query, design_system)
lines = []
lines.append(f"# {page_title} Page Overrides")
lines.append("")
lines.append(f"> **PROJECT:** {project}")
lines.append(f"> **Generated:** {timestamp}")
lines.append(f"> **Page Type:** {page_overrides.get('page_type', 'General')}")
lines.append("")
lines.append("> ⚠️ **IMPORTANT:** Rules in this file **override** the Master file (`design-system/MASTER.md`).")
lines.append("> Only deviations from the Master are documented here. For all other rules, refer to the Master.")
lines.append("")
lines.append("---")
lines.append("")
# Page-specific rules with actual content
lines.append("## Page-Specific Rules")
lines.append("")
# Layout Overrides
lines.append("### Layout Overrides")
lines.append("")
layout = page_overrides.get("layout", {})
if layout:
for key, value in layout.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master layout")
lines.append("")
# Spacing Overrides
lines.append("### Spacing Overrides")
lines.append("")
spacing = page_overrides.get("spacing", {})
if spacing:
for key, value in spacing.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master spacing")
lines.append("")
# Typography Overrides
lines.append("### Typography Overrides")
lines.append("")
typography = page_overrides.get("typography", {})
if typography:
for key, value in typography.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master typography")
lines.append("")
# Color Overrides
lines.append("### Color Overrides")
lines.append("")
colors = page_overrides.get("colors", {})
if colors:
for key, value in colors.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master colors")
lines.append("")
# Component Overrides
lines.append("### Component Overrides")
lines.append("")
components = page_overrides.get("components", [])
if components:
for comp in components:
lines.append(f"- {comp}")
else:
lines.append("- No overrides — use Master component specs")
lines.append("")
# Page-Specific Components
lines.append("---")
lines.append("")
lines.append("## Page-Specific Components")
lines.append("")
unique_components = page_overrides.get("unique_components", [])
if unique_components:
for comp in unique_components:
lines.append(f"- {comp}")
else:
lines.append("- No unique components for this page")
lines.append("")
# Recommendations
lines.append("---")
lines.append("")
lines.append("## Recommendations")
lines.append("")
recommendations = page_overrides.get("recommendations", [])
if recommendations:
for rec in recommendations:
lines.append(f"- {rec}")
lines.append("")
return "\n".join(lines)
def _generate_intelligent_overrides(page_name: str, page_query: str, design_system: dict) -> dict:
"""
Generate intelligent overrides based on page type using layered search.
Uses the existing search infrastructure to find relevant style, UX, and layout
data instead of hardcoded page types.
"""
from core import search
page_lower = page_name.lower()
query_lower = (page_query or "").lower()
combined_context = f"{page_lower} {query_lower}"
# Search across multiple domains for page-specific guidance
style_search = search(combined_context, "style", max_results=1)
ux_search = search(combined_context, "ux", max_results=3)
landing_search = search(combined_context, "landing", max_results=1)
# Extract results from search response
style_results = style_search.get("results", [])
ux_results = ux_search.get("results", [])
landing_results = landing_search.get("results", [])
# Detect page type from search results or context
page_type = _detect_page_type(combined_context, style_results)
# Build overrides from search results
layout = {}
spacing = {}
typography = {}
colors = {}
components = []
unique_components = []
recommendations = []
# Extract style-based overrides
if style_results:
style = style_results[0]
style_name = style.get("Style Category", "")
keywords = style.get("Keywords", "")
best_for = style.get("Best For", "")
effects = style.get("Effects & Animation", "")
# Infer layout from style keywords
if any(kw in keywords.lower() for kw in ["data", "dense", "dashboard", "grid"]):
layout["Max Width"] = "1400px or full-width"
layout["Grid"] = "12-column grid for data flexibility"
spacing["Content Density"] = "High — optimize for information display"
elif any(kw in keywords.lower() for kw in ["minimal", "simple", "clean", "single"]):
layout["Max Width"] = "800px (narrow, focused)"
layout["Layout"] = "Single column, centered"
spacing["Content Density"] = "Low — focus on clarity"
else:
layout["Max Width"] = "1200px (standard)"
layout["Layout"] = "Full-width sections, centered content"
if effects:
recommendations.append(f"Effects: {effects}")
# Extract UX guidelines as recommendations
for ux in ux_results:
category = ux.get("Category", "")
do_text = ux.get("Do", "")
dont_text = ux.get("Don't", "")
if do_text:
recommendations.append(f"{category}: {do_text}")
if dont_text:
components.append(f"Avoid: {dont_text}")
# Extract landing pattern info for section structure
if landing_results:
landing = landing_results[0]
sections = landing.get("Section Order", "")
cta_placement = landing.get("Primary CTA Placement", "")
color_strategy = landing.get("Color Strategy", "")
if sections:
layout["Sections"] = sections
if cta_placement:
recommendations.append(f"CTA Placement: {cta_placement}")
if color_strategy:
colors["Strategy"] = color_strategy
# Add page-type specific defaults if no search results
if not layout:
layout["Max Width"] = "1200px"
layout["Layout"] = "Responsive grid"
if not recommendations:
recommendations = [
"Refer to MASTER.md for all design rules",
"Add specific overrides as needed for this page"
]
return {
"page_type": page_type,
"layout": layout,
"spacing": spacing,
"typography": typography,
"colors": colors,
"components": components,
"unique_components": unique_components,
"recommendations": recommendations
}
def _detect_page_type(context: str, style_results: list) -> str:
"""Detect page type from context and search results."""
context_lower = context.lower()
# Check for common page type patterns
page_patterns = [
(["dashboard", "admin", "analytics", "data", "metrics", "stats", "monitor", "overview"], "Dashboard / Data View"),
(["checkout", "payment", "cart", "purchase", "order", "billing"], "Checkout / Payment"),
(["settings", "profile", "account", "preferences", "config"], "Settings / Profile"),
(["landing", "marketing", "homepage", "hero", "home", "promo"], "Landing / Marketing"),
(["login", "signin", "signup", "register", "auth", "password"], "Authentication"),
(["pricing", "plans", "subscription", "tiers", "packages"], "Pricing / Plans"),
(["blog", "article", "post", "news", "content", "story"], "Blog / Article"),
(["product", "item", "detail", "pdp", "shop", "store"], "Product Detail"),
(["search", "results", "browse", "filter", "catalog", "list"], "Search Results"),
(["empty", "404", "error", "not found", "zero"], "Empty State"),
]
for keywords, page_type in page_patterns:
if any(kw in context_lower for kw in keywords):
return page_type
# Fallback: try to infer from style results
if style_results:
style_name = style_results[0].get("Style Category", "").lower()
best_for = style_results[0].get("Best For", "").lower()
if "dashboard" in best_for or "data" in best_for:
return "Dashboard / Data View"
elif "landing" in best_for or "marketing" in best_for:
return "Landing / Marketing"
return "General"
# ============ CLI SUPPORT ============ # ============ CLI SUPPORT ============
if __name__ == "__main__": if __name__ == "__main__":
import argparse import argparse

View File

@@ -4,14 +4,19 @@
UI/UX Pro Max Search - BM25 search engine for UI/UX style guides UI/UX Pro Max Search - BM25 search engine for UI/UX style guides
Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3] Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3]
python search.py "<query>" --design-system [-p "Project Name"] python search.py "<query>" --design-system [-p "Project Name"]
python search.py "<query>" --design-system --persist [-p "Project Name"] [--page "dashboard"]
Domains: style, prompt, color, chart, landing, product, ux, typography Domains: style, prompt, color, chart, landing, product, ux, typography
Stacks: html-tailwind, react, nextjs Stacks: html-tailwind, react, nextjs
Persistence (Master + Overrides pattern):
--persist Save design system to design-system/MASTER.md
--page Also create a page-specific override file in design-system/pages/
""" """
import argparse import argparse
from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack
from design_system import generate_design_system from design_system import generate_design_system, persist_design_system
def format_output(result): def format_output(result):
@@ -51,13 +56,38 @@ if __name__ == "__main__":
parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation") parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation")
parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output") parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output")
parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system") parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system")
# Persistence (Master + Overrides pattern)
parser.add_argument("--persist", action="store_true", help="Save design system to design-system/MASTER.md (creates hierarchical structure)")
parser.add_argument("--page", type=str, default=None, help="Create page-specific override file in design-system/pages/")
parser.add_argument("--output-dir", "-o", type=str, default=None, help="Output directory for persisted files (default: current directory)")
args = parser.parse_args() args = parser.parse_args()
# Design system takes priority # Design system takes priority
if args.design_system: if args.design_system:
result = generate_design_system(args.query, args.project_name, args.format) result = generate_design_system(
args.query,
args.project_name,
args.format,
persist=args.persist,
page=args.page,
output_dir=args.output_dir
)
print(result) print(result)
# Print persistence confirmation
if args.persist:
project_slug = args.project_name.lower().replace(' ', '-') if args.project_name else "default"
print("\n" + "=" * 60)
print(f"✅ Design system persisted to design-system/{project_slug}/")
print(f" 📄 design-system/{project_slug}/MASTER.md (Global Source of Truth)")
if args.page:
page_filename = args.page.lower().replace(' ', '-')
print(f" 📄 design-system/{project_slug}/pages/{page_filename}.md (Page Overrides)")
print("")
print(f"📖 Usage: When building a page, check design-system/{project_slug}/pages/[page].md first.")
print(f" If exists, its rules override MASTER.md. Otherwise, use MASTER.md.")
print("=" * 60)
# Stack search # Stack search
elif args.stack: elif args.stack:
result = search_stack(args.query, args.stack, args.max_results) result = search_stack(args.query, args.stack, args.max_results)

View File

@@ -86,7 +86,7 @@ Get implementation-specific best practices. If user doesn't specify a stack, **d
python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind
``` ```
Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn` Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`,`jetpack-compose`, `shadcn`
--- ---
@@ -120,6 +120,7 @@ Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`
| `react-native` | Components, Navigation, Lists | | `react-native` | Components, Navigation, Lists |
| `flutter` | Widgets, State, Layout, Theming | | `flutter` | Widgets, State, Layout, Theming |
| `shadcn` | shadcn/ui components, theming, forms, patterns | | `shadcn` | shadcn/ui components, theming, forms, patterns |
| `jetpack-compose` | Composables, Modifiers, State Hoisting, Recomposition |
--- ---

View File

@@ -60,6 +60,31 @@ This command:
python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa" python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa"
``` ```
### Step 2b: Persist Design System (Master + Overrides Pattern)
To save the design system for hierarchical retrieval across sessions, add `--persist`:
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name"
```
This creates:
- `design-system/MASTER.md` — Global Source of Truth with all design rules
- `design-system/pages/` — Folder for page-specific overrides
**With page-specific override:**
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name" --page "dashboard"
```
This also creates:
- `design-system/pages/dashboard.md` — Page-specific deviations from Master
**How hierarchical retrieval works:**
1. When building a specific page (e.g., "Checkout"), first check `design-system/pages/checkout.md`
2. If the page file exists, its rules **override** the Master file
3. If not, use `design-system/MASTER.md` exclusively
### Step 3: Supplement with Detailed Searches (as needed) ### Step 3: Supplement with Detailed Searches (as needed)
After getting the design system, use domain searches to get additional details: After getting the design system, use domain searches to get additional details:
@@ -86,8 +111,8 @@ Get implementation-specific best practices. If user doesn't specify a stack, **d
python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind
``` ```
Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn` Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn`, `jetpack-compose`
, `jetpack-compose`
--- ---
## Search Reference ## Search Reference
@@ -120,6 +145,7 @@ Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`
| `react-native` | Components, Navigation, Lists | | `react-native` | Components, Navigation, Lists |
| `flutter` | Widgets, State, Layout, Theming | | `flutter` | Widgets, State, Layout, Theming |
| `shadcn` | shadcn/ui components, theming, forms, patterns | | `shadcn` | shadcn/ui components, theming, forms, patterns |
| `jetpack-compose` | Composables, Modifiers, State Hoisting, Recomposition |
--- ---

View File

@@ -0,0 +1,253 @@
---
name: ui-ux-pro-max
description: "UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 9 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind, shadcn/ui). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient. Integrations: shadcn/ui MCP for component search and examples."
compatibility: "OpenCode with Python 3.x"
---
# UI/UX Pro Max - Design Intelligence
Searchable database of UI styles, color palettes, font pairings, chart types, product recommendations, UX guidelines, and stack-specific best practices.
## OpenCode Tool Mapping
| Claude Code Tool | OpenCode Equivalent |
|---------------------|------------------------|
| `TodoWrite` | `update_plan` |
| `Task` subagents | `@mention` system |
| `Skill` tool | `use_skill` tool |
| File operations | Native OpenCode tools |
## When to Use
Use this skill when:
- User requests UI/UX work (design, build, create, implement, review, fix, improve)
- User wants to build frontend pages (landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app)
- User asks about design patterns, color palettes, typography, or UI components
## Prerequisites
Check if Python is installed:
```bash
python3 --version || python --version
```
If Python is not installed, install it based on user's OS:
**macOS:**
```bash
brew install python3
```
**Ubuntu/Debian:**
```bash
sudo apt update && sudo apt install python3
```
**Windows:**
```powershell
winget install Python.Python.3.12
```
---
## How to Use
### Step 1: Analyze User Requirements
Extract key information from user request:
- **Product type**: SaaS, e-commerce, portfolio, dashboard, landing page, etc.
- **Style keywords**: minimal, playful, professional, elegant, dark mode, etc.
- **Industry**: healthcare, fintech, gaming, education, etc.
- **Stack**: React, Vue, Next.js, or default to `html-tailwind`
### Step 2: Search Relevant Domains
Use `update_plan` to track your search tasks, then execute multiple searches to gather comprehensive information.
```bash
python3 ../../.shared/ui-ux-pro-max/scripts/search.py "<keyword>" --domain <domain> [-n <max_results>]
```
**Recommended search order:**
1. **Product** - Get style recommendations for product type
2. **Style** - Get detailed style guide (colors, effects, frameworks)
3. **Typography** - Get font pairings with Google Fonts imports
4. **Color** - Get color palette (Primary, Secondary, CTA, Background, Text, Border)
5. **Landing** - Get page structure (if landing page)
6. **Chart** - Get chart recommendations (if dashboard/analytics)
7. **UX** - Get best practices and anti-patterns
8. **Stack** - Get stack-specific guidelines (default: html-tailwind)
### Step 3: Stack Guidelines (Default: html-tailwind)
If user doesn't specify a stack, **default to `html-tailwind`**.
```bash
python3 ../../.shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind
```
Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn`
---
## Search Reference
### Available Domains
| Domain | Use For | Example Keywords |
| ------------ | ------------------------------------ | -------------------------------------------------------- |
| `product` | Product type recommendations | SaaS, e-commerce, portfolio, healthcare, beauty, service |
| `style` | UI styles, colors, effects | glassmorphism, minimalism, dark mode, brutalism |
| `typography` | Font pairings, Google Fonts | elegant, playful, professional, modern |
| `color` | Color palettes by product type | saas, ecommerce, healthcare, beauty, fintech, service |
| `landing` | Page structure, CTA strategies | hero, hero-centric, testimonial, pricing, social-proof |
| `chart` | Chart types, library recommendations | trend, comparison, timeline, funnel, pie |
| `ux` | Best practices, anti-patterns | animation, accessibility, z-index, loading |
| `prompt` | AI prompts, CSS keywords | (style name) |
### Available Stacks
| Stack | Focus |
| --------------- | ---------------------------------------------- |
| `html-tailwind` | Tailwind utilities, responsive, a11y (DEFAULT) |
| `react` | State, hooks, performance, patterns |
| `nextjs` | SSR, routing, images, API routes |
| `vue` | Composition API, Pinia, Vue Router |
| `svelte` | Runes, stores, SvelteKit |
| `swiftui` | Views, State, Navigation, Animation |
| `react-native` | Components, Navigation, Lists |
| `flutter` | Widgets, State, Layout, Theming |
| `shadcn` | shadcn/ui components, theming, forms, patterns |
---
## Example Workflow
**User request:** "Build a landing page for a professional skincare service."
**AI should:**
```bash
# 1. Search product type
python3 ../../.shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --domain product
# 2. Search style (based on industry: beauty, elegant)
python3 ../../.shared/ui-ux-pro-max/scripts/search.py "elegant minimal soft" --domain style
# 3. Search typography
python3 ../../.shared/ui-ux-pro-max/scripts/search.py "elegant luxury" --domain typography
# 4. Search color palette
python3 ../../.shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness" --domain color
# 5. Search landing page structure
python3 ../../.shared/ui-ux-pro-max/scripts/search.py "hero-centric social-proof" --domain landing
# 6. Search UX guidelines
python3 ../../.shared/ui-ux-pro-max/scripts/search.py "animation" --domain ux
python3 ../../.shared/ui-ux-pro-max/scripts/search.py "accessibility" --domain ux
# 7. Search stack guidelines (default: html-tailwind)
python3 ../../.shared/ui-ux-pro-max/scripts/search.py "layout responsive" --stack html-tailwind
```
**Then:** Synthesize all search results and implement the design.
---
## Tips for Better Results
1. **Be specific with keywords** - "healthcare SaaS dashboard" > "app"
2. **Search multiple times** - Different keywords reveal different insights
3. **Combine domains** - Style + Typography + Color = Complete design system
4. **Always check UX** - Search "animation", "z-index", "accessibility" for common issues
5. **Use stack flag** - Get implementation-specific best practices
6. **Iterate** - If first search doesn't match, try different keywords
---
## Common Rules for Professional UI
These are frequently overlooked issues that make UI look unprofessional:
### Icons & Visual Elements
| Rule | Do | Don't |
| -------------------------- | ----------------------------------------------- | -------------------------------------- |
| **No emoji icons** | Use SVG icons (Heroicons, Lucide, Simple Icons) | Use emojis like 🎨 🚀 ⚙️ as UI icons |
| **Stable hover states** | Use color/opacity transitions on hover | Use scale transforms that shift layout |
| **Correct brand logos** | Research official SVG from Simple Icons | Guess or use incorrect logo paths |
| **Consistent icon sizing** | Use fixed viewBox (24x24) with w-6 h-6 | Mix different icon sizes randomly |
### Interaction & Cursor
| Rule | Do | Don't |
| ---------------------- | ----------------------------------------------------- | -------------------------------------------- |
| **Cursor pointer** | Add `cursor-pointer` to all clickable/hoverable cards | Leave default cursor on interactive elements |
| **Hover feedback** | Provide visual feedback (color, shadow, border) | No indication element is interactive |
| **Smooth transitions** | Use `transition-colors duration-200` | Instant state changes or too slow (>500ms) |
### Light/Dark Mode Contrast
| Rule | Do | Don't |
| ------------------------- | ----------------------------------- | --------------------------------------- |
| **Glass card light mode** | Use `bg-white/80` or higher opacity | Use `bg-white/10` (too transparent) |
| **Text contrast light** | Use `#0F172A` (slate-900) for text | Use `#94A3B8` (slate-400) for body text |
| **Muted text light** | Use `#475569` (slate-600) minimum | Use gray-400 or lighter |
| **Border visibility** | Use `border-gray-200` in light mode | Use `border-white/10` (invisible) |
### Layout & Spacing
| Rule | Do | Don't |
| ------------------------ | ----------------------------------- | -------------------------------------- |
| **Floating navbar** | Add `top-4 left-4 right-4` spacing | Stick navbar to `top-0 left-0 right-0` |
| **Content padding** | Account for fixed navbar height | Let content hide behind fixed elements |
| **Consistent max-width** | Use same `max-w-6xl` or `max-w-7xl` | Mix different container widths |
---
## Pre-Delivery Checklist
Before delivering UI code, verify these items:
### Visual Quality
- [ ] No emojis used as icons (use SVG instead)
- [ ] All icons from consistent icon set (Heroicons/Lucide)
- [ ] Brand logos are correct (verified from Simple Icons)
- [ ] Hover states don't cause layout shift
- [ ] Use theme colors directly (bg-primary) not var() wrapper
### Interaction
- [ ] All clickable elements have `cursor-pointer`
- [ ] Hover states provide clear visual feedback
- [ ] Transitions are smooth (150-300ms)
- [ ] Focus states visible for keyboard navigation
### Light/Dark Mode
- [ ] Light mode text has sufficient contrast (4.5:1 minimum)
- [ ] Glass/transparent elements visible in light mode
- [ ] Borders visible in both modes
- [ ] Test both modes before delivery
### Layout
- [ ] Floating elements have proper spacing from edges
- [ ] No content hidden behind fixed navbars
- [ ] Responsive at 320px, 768px, 1024px, 1440px
- [ ] No horizontal scroll on mobile
### Accessibility
- [ ] All images have alt text
- [ ] Form inputs have labels
- [ ] Color is not the only indicator
- [ ] `prefers-reduced-motion` respected

View File

@@ -60,6 +60,31 @@ This command:
python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa" python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa"
``` ```
### Step 2b: Persist Design System (Master + Overrides Pattern)
To save the design system for hierarchical retrieval across sessions, add `--persist`:
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name"
```
This creates:
- `design-system/MASTER.md` — Global Source of Truth with all design rules
- `design-system/pages/` — Folder for page-specific overrides
**With page-specific override:**
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name" --page "dashboard"
```
This also creates:
- `design-system/pages/dashboard.md` — Page-specific deviations from Master
**How hierarchical retrieval works:**
1. When building a specific page (e.g., "Checkout"), first check `design-system/pages/checkout.md`
2. If the page file exists, its rules **override** the Master file
3. If not, use `design-system/MASTER.md` exclusively
### Step 3: Supplement with Detailed Searches (as needed) ### Step 3: Supplement with Detailed Searches (as needed)
After getting the design system, use domain searches to get additional details: After getting the design system, use domain searches to get additional details:
@@ -86,8 +111,8 @@ Get implementation-specific best practices. If user doesn't specify a stack, **d
python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind
``` ```
Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn` Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn`, `jetpack-compose`
, `jetpack-compose`
--- ---
## Search Reference ## Search Reference
@@ -120,6 +145,7 @@ Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`
| `react-native` | Components, Navigation, Lists | | `react-native` | Components, Navigation, Lists |
| `flutter` | Widgets, State, Layout, Theming | | `flutter` | Widgets, State, Layout, Theming |
| `shadcn` | shadcn/ui components, theming, forms, patterns | | `shadcn` | shadcn/ui components, theming, forms, patterns |
| `jetpack-compose` | Composables, Modifiers, State Hoisting, Recomposition |
--- ---

View File

@@ -60,6 +60,31 @@ This command:
python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa" python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa"
``` ```
### Step 2b: Persist Design System (Master + Overrides Pattern)
To save the design system for hierarchical retrieval across sessions, add `--persist`:
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name"
```
This creates:
- `design-system/MASTER.md` — Global Source of Truth with all design rules
- `design-system/pages/` — Folder for page-specific overrides
**With page-specific override:**
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name" --page "dashboard"
```
This also creates:
- `design-system/pages/dashboard.md` — Page-specific deviations from Master
**How hierarchical retrieval works:**
1. When building a specific page (e.g., "Checkout"), first check `design-system/pages/checkout.md`
2. If the page file exists, its rules **override** the Master file
3. If not, use `design-system/MASTER.md` exclusively
### Step 3: Supplement with Detailed Searches (as needed) ### Step 3: Supplement with Detailed Searches (as needed)
After getting the design system, use domain searches to get additional details: After getting the design system, use domain searches to get additional details:
@@ -86,8 +111,8 @@ Get implementation-specific best practices. If user doesn't specify a stack, **d
python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind
``` ```
Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn` Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn`, `jetpack-compose`
, `jetpack-compose`
--- ---
## Search Reference ## Search Reference
@@ -120,6 +145,7 @@ Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`
| `react-native` | Components, Navigation, Lists | | `react-native` | Components, Navigation, Lists |
| `flutter` | Widgets, State, Layout, Theming | | `flutter` | Widgets, State, Layout, Theming |
| `shadcn` | shadcn/ui components, theming, forms, patterns | | `shadcn` | shadcn/ui components, theming, forms, patterns |
| `jetpack-compose` | Composables, Modifiers, State Hoisting, Recomposition |
--- ---

View File

@@ -0,0 +1,53 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Composable,Pure UI composables,Composable functions should only render UI,Accept state and callbacks,Calling usecase/repo,Pure UI composable,Business logic in UI,High,https://developer.android.com/jetpack/compose/mental-model
2,Composable,Small composables,Each composable has single responsibility,Split into components,Huge composable,Reusable UI,Monolithic UI,Medium,
3,Composable,Stateless by default,Prefer stateless composables,Hoist state,Local mutable state,Stateless UI,Hidden state,High,https://developer.android.com/jetpack/compose/state#state-hoisting
4,State,Single source of truth,UI state comes from one source,StateFlow from VM,Multiple states,Unified UiState,Scattered state,High,https://developer.android.com/topic/architecture/ui-layer
5,State,Model UI State,Use sealed interface/data class,UiState.Loading,Boolean flags,Explicit state,Flag hell,High,
6,State,remember only UI state,remember for UI-only state,"Scroll, animation",Business state,Correct remember,Misuse remember,High,https://developer.android.com/jetpack/compose/state
7,State,rememberSaveable,Persist state across config,rememberSaveable,remember,State survives,State lost,High,https://developer.android.com/jetpack/compose/state#restore-ui-state
8,State,derivedStateOf,Optimize recomposition,derivedStateOf,Recompute always,Optimized,Jank,Medium,https://developer.android.com/jetpack/compose/performance
9,SideEffect,LaunchedEffect keys,Use correct keys,LaunchedEffect(id),LaunchedEffect(Unit),Scoped effect,Infinite loop,High,https://developer.android.com/jetpack/compose/side-effects
10,SideEffect,rememberUpdatedState,Avoid stale lambdas,rememberUpdatedState,Capture directly,Safe callback,Stale state,Medium,https://developer.android.com/jetpack/compose/side-effects
11,SideEffect,DisposableEffect,Clean up resources,onDispose,No cleanup,No leak,Memory leak,High,
12,Architecture,Unidirectional data flow,UI → VM → State,onEvent,Two-way binding,Predictable flow,Hard debug,High,https://developer.android.com/topic/architecture
13,Architecture,No business logic in UI,Logic belongs to VM,Collect state,Call repo,Clean UI,Fat UI,High,
14,Architecture,Expose immutable state,Expose StateFlow,asStateFlow,Mutable exposed,Safe API,State mutation,High,
15,Lifecycle,Lifecycle-aware collect,Use collectAsStateWithLifecycle,Lifecycle aware,collectAsState,No leak,Leak,High,https://developer.android.com/jetpack/compose/lifecycle
16,Navigation,Event-based navigation,VM emits navigation event,"VM: Channel + receiveAsFlow(), V: Collect with Dispatchers.Main.immediate",Nav in UI,Decoupled nav,Using State / SharedFlow for navigation -> event is replayed and navigation fires again (StateFlow),High,https://developer.android.com/jetpack/compose/navigation
17,Navigation,Typed routes,Use sealed routes,sealed class Route,String routes,Type-safe,Runtime crash,Medium,
18,Performance,Stable parameters,Prefer immutable/stable params,@Immutable,Mutable params,Stable recomposition,Extra recomposition,High,https://developer.android.com/jetpack/compose/performance
19,Performance,Use key in Lazy,Provide stable keys,key=id,No key,Stable list,Item jump,High,
20,Performance,Avoid heavy work,No heavy computation in UI,Precompute in VM,Compute in UI,Smooth UI,Jank,High,
21,Performance,Remember expensive objects,remember heavy objects,remember,Recreate each recomposition,Efficient,Wasteful,Medium,
22,Theming,Design system,Centralized theme,Material3 tokens,Hardcoded values,Consistent UI,Inconsistent,High,https://developer.android.com/jetpack/compose/themes
23,Theming,Dark mode support,Theme-based colors,colorScheme,Fixed color,Adaptive UI,Broken dark,Medium,
24,Layout,Prefer Modifier over extra layouts,Use Modifier to adjust layout instead of adding wrapper composables,Use Modifier.padding(),Wrap content with extra Box,Padding via modifier,Box just for padding,High,https://developer.android.com/jetpack/compose/modifiers
25,Layout,Avoid deep layout nesting,Deep layout trees increase measure & layout cost,Keep layout flat,Box ? Column ? Box ? Row,Flat hierarchy,Deep nested tree,High,
26,Layout,Use Row/Column for linear layout,Linear layouts are simpler and more performant,Use Row / Column,Custom layout for simple cases,Row/Column usage,Over-engineered layout,High,
27,Layout,Use Box only for overlapping content,Box should be used only when children overlap,Stack elements,Use Box as Column,Proper overlay,Misused Box,Medium,
28,Layout,Prefer LazyColumn over Column scroll,Lazy layouts are virtualized and efficient,LazyColumn,Column.verticalScroll(),Lazy list,Scrollable Column,High,https://developer.android.com/jetpack/compose/lists
29,Layout,Avoid nested scroll containers,Nested scrolling causes UX & performance issues,Single scroll container,Scroll inside scroll,One scroll per screen,Nested scroll,High,
30,Layout,Avoid fillMaxSize by default,fillMaxSize may break parent constraints,Use exact size,Fill max everywhere,Constraint-aware size,Overfilled layout,Medium,
31,Layout,Avoid intrinsic size unless necessary,Intrinsic measurement is expensive,Explicit sizing,IntrinsicSize.Min,Predictable layout,Expensive measure,High,https://developer.android.com/jetpack/compose/layout/intrinsics
32,Layout,Use Arrangement and Alignment APIs,Declare layout intent explicitly,Use Arrangement / Alignment,Manual spacing hacks,Declarative spacing,Magic spacing,High,
33,Layout,Extract reusable layout patterns,Repeated layouts should be shared,Create layout composable,Copy-paste layouts,Reusable scaffold,Duplicated layout,High,
34,Theming,No hardcoded text style,Use typography,MaterialTheme.typography,Hardcode sp,Scalable,Inconsistent,Medium,
35,Testing,Stateless UI testing,Composable easy to test,Pass state,Hidden state,Testable,Hard test,High,https://developer.android.com/jetpack/compose/testing
36,Testing,Use testTag,Stable UI selectors,Modifier.testTag,Find by text,Stable tests,Flaky tests,Medium,
37,Preview,Multiple previews,Preview multiple states,@Preview,Single preview,Better dev UX,Misleading,Low,https://developer.android.com/jetpack/compose/tooling/preview
38,DI,Inject VM via Hilt,Use hiltViewModel,@HiltViewModel,Manual VM,Clean DI,Coupling,High,https://developer.android.com/training/dependency-injection/hilt-jetpack
39,DI,No DI in UI,Inject in VM,Constructor inject,Inject composable,Proper scope,Wrong scope,High,
40,Accessibility,Content description,Accessible UI,contentDescription,Ignore a11y,Inclusive,A11y fail,Medium,https://developer.android.com/jetpack/compose/accessibility
41,Accessibility,Semantics,Use semantics API,Modifier.semantics,None,Testable a11y,Invisible,Medium,
42,Animation,Compose animation APIs,Use animate*AsState,AnimatedVisibility,Manual anim,Smooth,Jank,Medium,https://developer.android.com/jetpack/compose/animation
43,Animation,Avoid animation logic in VM,Animation is UI concern,Animate in UI,Animate in VM,Correct layering,Mixed concern,Low,
44,Modularization,Feature-based UI modules,UI per feature,:feature:ui,God module,Scalable,Tight coupling,High,https://developer.android.com/topic/modularization
45,Modularization,Public UI contracts,Expose minimal UI API,Interface/Route,Expose impl,Encapsulated,Leaky module,Medium,
46,State,Snapshot state only,Use Compose state,mutableStateOf,Custom observable,Compose aware,Buggy UI,Medium,
47,State,Avoid mutable collections,Immutable list/map,PersistentList,MutableList,Stable UI,Silent bug,High,
48,Lifecycle,RememberCoroutineScope usage,Only for UI jobs,UI coroutine,Long jobs,Scoped job,Leak,Medium,https://developer.android.com/jetpack/compose/side-effects#remembercoroutinescope
49,Interop,Interop View carefully,Use AndroidView,Isolated usage,Mix everywhere,Safe interop,Messy UI,Low,https://developer.android.com/jetpack/compose/interop
50,Interop,Avoid legacy patterns,No LiveData in UI,StateFlow,LiveData,Modern stack,Legacy debt,Medium,
51,Debug,Use layout inspector,Inspect recomposition,Tools,Blind debug,Fast debug,Guessing,Low,https://developer.android.com/studio/debug/layout-inspector
52,Debug,Enable recomposition counts,Track recomposition,Debug flags,Ignore,Performance aware,Hidden jank,Low,
1 No Category Guideline Description Do Don't Code Good Code Bad Severity Docs URL
2 1 Composable Pure UI composables Composable functions should only render UI Accept state and callbacks Calling usecase/repo Pure UI composable Business logic in UI High https://developer.android.com/jetpack/compose/mental-model
3 2 Composable Small composables Each composable has single responsibility Split into components Huge composable Reusable UI Monolithic UI Medium
4 3 Composable Stateless by default Prefer stateless composables Hoist state Local mutable state Stateless UI Hidden state High https://developer.android.com/jetpack/compose/state#state-hoisting
5 4 State Single source of truth UI state comes from one source StateFlow from VM Multiple states Unified UiState Scattered state High https://developer.android.com/topic/architecture/ui-layer
6 5 State Model UI State Use sealed interface/data class UiState.Loading Boolean flags Explicit state Flag hell High
7 6 State remember only UI state remember for UI-only state Scroll, animation Business state Correct remember Misuse remember High https://developer.android.com/jetpack/compose/state
8 7 State rememberSaveable Persist state across config rememberSaveable remember State survives State lost High https://developer.android.com/jetpack/compose/state#restore-ui-state
9 8 State derivedStateOf Optimize recomposition derivedStateOf Recompute always Optimized Jank Medium https://developer.android.com/jetpack/compose/performance
10 9 SideEffect LaunchedEffect keys Use correct keys LaunchedEffect(id) LaunchedEffect(Unit) Scoped effect Infinite loop High https://developer.android.com/jetpack/compose/side-effects
11 10 SideEffect rememberUpdatedState Avoid stale lambdas rememberUpdatedState Capture directly Safe callback Stale state Medium https://developer.android.com/jetpack/compose/side-effects
12 11 SideEffect DisposableEffect Clean up resources onDispose No cleanup No leak Memory leak High
13 12 Architecture Unidirectional data flow UI → VM → State onEvent Two-way binding Predictable flow Hard debug High https://developer.android.com/topic/architecture
14 13 Architecture No business logic in UI Logic belongs to VM Collect state Call repo Clean UI Fat UI High
15 14 Architecture Expose immutable state Expose StateFlow asStateFlow Mutable exposed Safe API State mutation High
16 15 Lifecycle Lifecycle-aware collect Use collectAsStateWithLifecycle Lifecycle aware collectAsState No leak Leak High https://developer.android.com/jetpack/compose/lifecycle
17 16 Navigation Event-based navigation VM emits navigation event VM: Channel + receiveAsFlow(), V: Collect with Dispatchers.Main.immediate Nav in UI Decoupled nav Using State / SharedFlow for navigation -> event is replayed and navigation fires again (StateFlow) High https://developer.android.com/jetpack/compose/navigation
18 17 Navigation Typed routes Use sealed routes sealed class Route String routes Type-safe Runtime crash Medium
19 18 Performance Stable parameters Prefer immutable/stable params @Immutable Mutable params Stable recomposition Extra recomposition High https://developer.android.com/jetpack/compose/performance
20 19 Performance Use key in Lazy Provide stable keys key=id No key Stable list Item jump High
21 20 Performance Avoid heavy work No heavy computation in UI Precompute in VM Compute in UI Smooth UI Jank High
22 21 Performance Remember expensive objects remember heavy objects remember Recreate each recomposition Efficient Wasteful Medium
23 22 Theming Design system Centralized theme Material3 tokens Hardcoded values Consistent UI Inconsistent High https://developer.android.com/jetpack/compose/themes
24 23 Theming Dark mode support Theme-based colors colorScheme Fixed color Adaptive UI Broken dark Medium
25 24 Layout Prefer Modifier over extra layouts Use Modifier to adjust layout instead of adding wrapper composables Use Modifier.padding() Wrap content with extra Box Padding via modifier Box just for padding High https://developer.android.com/jetpack/compose/modifiers
26 25 Layout Avoid deep layout nesting Deep layout trees increase measure & layout cost Keep layout flat Box ? Column ? Box ? Row Flat hierarchy Deep nested tree High
27 26 Layout Use Row/Column for linear layout Linear layouts are simpler and more performant Use Row / Column Custom layout for simple cases Row/Column usage Over-engineered layout High
28 27 Layout Use Box only for overlapping content Box should be used only when children overlap Stack elements Use Box as Column Proper overlay Misused Box Medium
29 28 Layout Prefer LazyColumn over Column scroll Lazy layouts are virtualized and efficient LazyColumn Column.verticalScroll() Lazy list Scrollable Column High https://developer.android.com/jetpack/compose/lists
30 29 Layout Avoid nested scroll containers Nested scrolling causes UX & performance issues Single scroll container Scroll inside scroll One scroll per screen Nested scroll High
31 30 Layout Avoid fillMaxSize by default fillMaxSize may break parent constraints Use exact size Fill max everywhere Constraint-aware size Overfilled layout Medium
32 31 Layout Avoid intrinsic size unless necessary Intrinsic measurement is expensive Explicit sizing IntrinsicSize.Min Predictable layout Expensive measure High https://developer.android.com/jetpack/compose/layout/intrinsics
33 32 Layout Use Arrangement and Alignment APIs Declare layout intent explicitly Use Arrangement / Alignment Manual spacing hacks Declarative spacing Magic spacing High
34 33 Layout Extract reusable layout patterns Repeated layouts should be shared Create layout composable Copy-paste layouts Reusable scaffold Duplicated layout High
35 34 Theming No hardcoded text style Use typography MaterialTheme.typography Hardcode sp Scalable Inconsistent Medium
36 35 Testing Stateless UI testing Composable easy to test Pass state Hidden state Testable Hard test High https://developer.android.com/jetpack/compose/testing
37 36 Testing Use testTag Stable UI selectors Modifier.testTag Find by text Stable tests Flaky tests Medium
38 37 Preview Multiple previews Preview multiple states @Preview Single preview Better dev UX Misleading Low https://developer.android.com/jetpack/compose/tooling/preview
39 38 DI Inject VM via Hilt Use hiltViewModel @HiltViewModel Manual VM Clean DI Coupling High https://developer.android.com/training/dependency-injection/hilt-jetpack
40 39 DI No DI in UI Inject in VM Constructor inject Inject composable Proper scope Wrong scope High
41 40 Accessibility Content description Accessible UI contentDescription Ignore a11y Inclusive A11y fail Medium https://developer.android.com/jetpack/compose/accessibility
42 41 Accessibility Semantics Use semantics API Modifier.semantics None Testable a11y Invisible Medium
43 42 Animation Compose animation APIs Use animate*AsState AnimatedVisibility Manual anim Smooth Jank Medium https://developer.android.com/jetpack/compose/animation
44 43 Animation Avoid animation logic in VM Animation is UI concern Animate in UI Animate in VM Correct layering Mixed concern Low
45 44 Modularization Feature-based UI modules UI per feature :feature:ui God module Scalable Tight coupling High https://developer.android.com/topic/modularization
46 45 Modularization Public UI contracts Expose minimal UI API Interface/Route Expose impl Encapsulated Leaky module Medium
47 46 State Snapshot state only Use Compose state mutableStateOf Custom observable Compose aware Buggy UI Medium
48 47 State Avoid mutable collections Immutable list/map PersistentList MutableList Stable UI Silent bug High
49 48 Lifecycle RememberCoroutineScope usage Only for UI jobs UI coroutine Long jobs Scoped job Leak Medium https://developer.android.com/jetpack/compose/side-effects#remembercoroutinescope
50 49 Interop Interop View carefully Use AndroidView Isolated usage Mix everywhere Safe interop Messy UI Low https://developer.android.com/jetpack/compose/interop
51 50 Interop Avoid legacy patterns No LiveData in UI StateFlow LiveData Modern stack Legacy debt Medium
52 51 Debug Use layout inspector Inspect recomposition Tools Blind debug Fast debug Guessing Low https://developer.android.com/studio/debug/layout-inspector
53 52 Debug Enable recomposition counts Track recomposition Debug flags Ignore Performance aware Hidden jank Low

View File

@@ -83,7 +83,8 @@ STACK_CONFIG = {
"swiftui": {"file": "stacks/swiftui.csv"}, "swiftui": {"file": "stacks/swiftui.csv"},
"react-native": {"file": "stacks/react-native.csv"}, "react-native": {"file": "stacks/react-native.csv"},
"flutter": {"file": "stacks/flutter.csv"}, "flutter": {"file": "stacks/flutter.csv"},
"shadcn": {"file": "stacks/shadcn.csv"} "shadcn": {"file": "stacks/shadcn.csv"},
"jetpack-compose": {"file": "stacks/jetpack-compose.csv"}
} }
# Common columns for all stacks # Common columns for all stacks

View File

@@ -7,10 +7,16 @@ to generate comprehensive design system recommendations.
Usage: Usage:
from design_system import generate_design_system from design_system import generate_design_system
result = generate_design_system("SaaS dashboard", "My Project") result = generate_design_system("SaaS dashboard", "My Project")
# With persistence (Master + Overrides pattern)
result = generate_design_system("SaaS dashboard", "My Project", persist=True)
result = generate_design_system("SaaS dashboard", "My Project", persist=True, page="dashboard")
""" """
import csv import csv
import json import json
import os
from datetime import datetime
from pathlib import Path from pathlib import Path
from core import search, DATA_DIR from core import search, DATA_DIR
@@ -452,7 +458,8 @@ def format_markdown(design_system: dict) -> str:
# ============ MAIN ENTRY POINT ============ # ============ MAIN ENTRY POINT ============
def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii") -> str: def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii",
persist: bool = False, page: str = None, output_dir: str = None) -> str:
""" """
Main entry point for design system generation. Main entry point for design system generation.
@@ -460,6 +467,9 @@ def generate_design_system(query: str, project_name: str = None, output_format:
query: Search query (e.g., "SaaS dashboard", "e-commerce luxury") query: Search query (e.g., "SaaS dashboard", "e-commerce luxury")
project_name: Optional project name for output header project_name: Optional project name for output header
output_format: "ascii" (default) or "markdown" output_format: "ascii" (default) or "markdown"
persist: If True, save design system to design-system/ folder
page: Optional page name for page-specific override file
output_dir: Optional output directory (defaults to current working directory)
Returns: Returns:
Formatted design system string Formatted design system string
@@ -467,11 +477,580 @@ def generate_design_system(query: str, project_name: str = None, output_format:
generator = DesignSystemGenerator() generator = DesignSystemGenerator()
design_system = generator.generate(query, project_name) design_system = generator.generate(query, project_name)
# Persist to files if requested
if persist:
persist_design_system(design_system, page, output_dir, query)
if output_format == "markdown": if output_format == "markdown":
return format_markdown(design_system) return format_markdown(design_system)
return format_ascii_box(design_system) return format_ascii_box(design_system)
# ============ PERSISTENCE FUNCTIONS ============
def persist_design_system(design_system: dict, page: str = None, output_dir: str = None, page_query: str = None) -> dict:
"""
Persist design system to design-system/<project>/ folder using Master + Overrides pattern.
Args:
design_system: The generated design system dictionary
page: Optional page name for page-specific override file
output_dir: Optional output directory (defaults to current working directory)
page_query: Optional query string for intelligent page override generation
Returns:
dict with created file paths and status
"""
base_dir = Path(output_dir) if output_dir else Path.cwd()
# Use project name for project-specific folder
project_name = design_system.get("project_name", "default")
project_slug = project_name.lower().replace(' ', '-')
design_system_dir = base_dir / "design-system" / project_slug
pages_dir = design_system_dir / "pages"
created_files = []
# Create directories
design_system_dir.mkdir(parents=True, exist_ok=True)
pages_dir.mkdir(parents=True, exist_ok=True)
master_file = design_system_dir / "MASTER.md"
# Generate and write MASTER.md
master_content = format_master_md(design_system)
with open(master_file, 'w', encoding='utf-8') as f:
f.write(master_content)
created_files.append(str(master_file))
# If page is specified, create page override file with intelligent content
if page:
page_file = pages_dir / f"{page.lower().replace(' ', '-')}.md"
page_content = format_page_override_md(design_system, page, page_query)
with open(page_file, 'w', encoding='utf-8') as f:
f.write(page_content)
created_files.append(str(page_file))
return {
"status": "success",
"design_system_dir": str(design_system_dir),
"created_files": created_files
}
def format_master_md(design_system: dict) -> str:
"""Format design system as MASTER.md with hierarchical override logic."""
project = design_system.get("project_name", "PROJECT")
pattern = design_system.get("pattern", {})
style = design_system.get("style", {})
colors = design_system.get("colors", {})
typography = design_system.get("typography", {})
effects = design_system.get("key_effects", "")
anti_patterns = design_system.get("anti_patterns", "")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
lines = []
# Logic header
lines.append("# Design System Master File")
lines.append("")
lines.append("> **LOGIC:** When building a specific page, first check `design-system/pages/[page-name].md`.")
lines.append("> If that file exists, its rules **override** this Master file.")
lines.append("> If not, strictly follow the rules below.")
lines.append("")
lines.append("---")
lines.append("")
lines.append(f"**Project:** {project}")
lines.append(f"**Generated:** {timestamp}")
lines.append(f"**Category:** {design_system.get('category', 'General')}")
lines.append("")
lines.append("---")
lines.append("")
# Global Rules section
lines.append("## Global Rules")
lines.append("")
# Color Palette
lines.append("### Color Palette")
lines.append("")
lines.append("| Role | Hex | CSS Variable |")
lines.append("|------|-----|--------------|")
lines.append(f"| Primary | `{colors.get('primary', '#2563EB')}` | `--color-primary` |")
lines.append(f"| Secondary | `{colors.get('secondary', '#3B82F6')}` | `--color-secondary` |")
lines.append(f"| CTA/Accent | `{colors.get('cta', '#F97316')}` | `--color-cta` |")
lines.append(f"| Background | `{colors.get('background', '#F8FAFC')}` | `--color-background` |")
lines.append(f"| Text | `{colors.get('text', '#1E293B')}` | `--color-text` |")
lines.append("")
if colors.get("notes"):
lines.append(f"**Color Notes:** {colors.get('notes', '')}")
lines.append("")
# Typography
lines.append("### Typography")
lines.append("")
lines.append(f"- **Heading Font:** {typography.get('heading', 'Inter')}")
lines.append(f"- **Body Font:** {typography.get('body', 'Inter')}")
if typography.get("mood"):
lines.append(f"- **Mood:** {typography.get('mood', '')}")
if typography.get("google_fonts_url"):
lines.append(f"- **Google Fonts:** [{typography.get('heading', '')} + {typography.get('body', '')}]({typography.get('google_fonts_url', '')})")
lines.append("")
if typography.get("css_import"):
lines.append("**CSS Import:**")
lines.append("```css")
lines.append(typography.get("css_import", ""))
lines.append("```")
lines.append("")
# Spacing Variables
lines.append("### Spacing Variables")
lines.append("")
lines.append("| Token | Value | Usage |")
lines.append("|-------|-------|-------|")
lines.append("| `--space-xs` | `4px` / `0.25rem` | Tight gaps |")
lines.append("| `--space-sm` | `8px` / `0.5rem` | Icon gaps, inline spacing |")
lines.append("| `--space-md` | `16px` / `1rem` | Standard padding |")
lines.append("| `--space-lg` | `24px` / `1.5rem` | Section padding |")
lines.append("| `--space-xl` | `32px` / `2rem` | Large gaps |")
lines.append("| `--space-2xl` | `48px` / `3rem` | Section margins |")
lines.append("| `--space-3xl` | `64px` / `4rem` | Hero padding |")
lines.append("")
# Shadow Depths
lines.append("### Shadow Depths")
lines.append("")
lines.append("| Level | Value | Usage |")
lines.append("|-------|-------|-------|")
lines.append("| `--shadow-sm` | `0 1px 2px rgba(0,0,0,0.05)` | Subtle lift |")
lines.append("| `--shadow-md` | `0 4px 6px rgba(0,0,0,0.1)` | Cards, buttons |")
lines.append("| `--shadow-lg` | `0 10px 15px rgba(0,0,0,0.1)` | Modals, dropdowns |")
lines.append("| `--shadow-xl` | `0 20px 25px rgba(0,0,0,0.15)` | Hero images, featured cards |")
lines.append("")
# Component Specs section
lines.append("---")
lines.append("")
lines.append("## Component Specs")
lines.append("")
# Buttons
lines.append("### Buttons")
lines.append("")
lines.append("```css")
lines.append("/* Primary Button */")
lines.append(".btn-primary {")
lines.append(f" background: {colors.get('cta', '#F97316')};")
lines.append(" color: white;")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("")
lines.append(".btn-primary:hover {")
lines.append(" opacity: 0.9;")
lines.append(" transform: translateY(-1px);")
lines.append("}")
lines.append("")
lines.append("/* Secondary Button */")
lines.append(".btn-secondary {")
lines.append(f" background: transparent;")
lines.append(f" color: {colors.get('primary', '#2563EB')};")
lines.append(f" border: 2px solid {colors.get('primary', '#2563EB')};")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("```")
lines.append("")
# Cards
lines.append("### Cards")
lines.append("")
lines.append("```css")
lines.append(".card {")
lines.append(f" background: {colors.get('background', '#FFFFFF')};")
lines.append(" border-radius: 12px;")
lines.append(" padding: 24px;")
lines.append(" box-shadow: var(--shadow-md);")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("")
lines.append(".card:hover {")
lines.append(" box-shadow: var(--shadow-lg);")
lines.append(" transform: translateY(-2px);")
lines.append("}")
lines.append("```")
lines.append("")
# Inputs
lines.append("### Inputs")
lines.append("")
lines.append("```css")
lines.append(".input {")
lines.append(" padding: 12px 16px;")
lines.append(" border: 1px solid #E2E8F0;")
lines.append(" border-radius: 8px;")
lines.append(" font-size: 16px;")
lines.append(" transition: border-color 200ms ease;")
lines.append("}")
lines.append("")
lines.append(".input:focus {")
lines.append(f" border-color: {colors.get('primary', '#2563EB')};")
lines.append(" outline: none;")
lines.append(f" box-shadow: 0 0 0 3px {colors.get('primary', '#2563EB')}20;")
lines.append("}")
lines.append("```")
lines.append("")
# Modals
lines.append("### Modals")
lines.append("")
lines.append("```css")
lines.append(".modal-overlay {")
lines.append(" background: rgba(0, 0, 0, 0.5);")
lines.append(" backdrop-filter: blur(4px);")
lines.append("}")
lines.append("")
lines.append(".modal {")
lines.append(" background: white;")
lines.append(" border-radius: 16px;")
lines.append(" padding: 32px;")
lines.append(" box-shadow: var(--shadow-xl);")
lines.append(" max-width: 500px;")
lines.append(" width: 90%;")
lines.append("}")
lines.append("```")
lines.append("")
# Style section
lines.append("---")
lines.append("")
lines.append("## Style Guidelines")
lines.append("")
lines.append(f"**Style:** {style.get('name', 'Minimalism')}")
lines.append("")
if style.get("keywords"):
lines.append(f"**Keywords:** {style.get('keywords', '')}")
lines.append("")
if style.get("best_for"):
lines.append(f"**Best For:** {style.get('best_for', '')}")
lines.append("")
if effects:
lines.append(f"**Key Effects:** {effects}")
lines.append("")
# Layout Pattern
lines.append("### Page Pattern")
lines.append("")
lines.append(f"**Pattern Name:** {pattern.get('name', '')}")
lines.append("")
if pattern.get('conversion'):
lines.append(f"- **Conversion Strategy:** {pattern.get('conversion', '')}")
if pattern.get('cta_placement'):
lines.append(f"- **CTA Placement:** {pattern.get('cta_placement', '')}")
lines.append(f"- **Section Order:** {pattern.get('sections', '')}")
lines.append("")
# Anti-Patterns section
lines.append("---")
lines.append("")
lines.append("## Anti-Patterns (Do NOT Use)")
lines.append("")
if anti_patterns:
anti_list = [a.strip() for a in anti_patterns.split("+")]
for anti in anti_list:
if anti:
lines.append(f"- ❌ {anti}")
lines.append("")
lines.append("### Additional Forbidden Patterns")
lines.append("")
lines.append("- ❌ **Emojis as icons** — Use SVG icons (Heroicons, Lucide, Simple Icons)")
lines.append("- ❌ **Missing cursor:pointer** — All clickable elements must have cursor:pointer")
lines.append("- ❌ **Layout-shifting hovers** — Avoid scale transforms that shift layout")
lines.append("- ❌ **Low contrast text** — Maintain 4.5:1 minimum contrast ratio")
lines.append("- ❌ **Instant state changes** — Always use transitions (150-300ms)")
lines.append("- ❌ **Invisible focus states** — Focus states must be visible for a11y")
lines.append("")
# Pre-Delivery Checklist
lines.append("---")
lines.append("")
lines.append("## Pre-Delivery Checklist")
lines.append("")
lines.append("Before delivering any UI code, verify:")
lines.append("")
lines.append("- [ ] No emojis used as icons (use SVG instead)")
lines.append("- [ ] All icons from consistent icon set (Heroicons/Lucide)")
lines.append("- [ ] `cursor-pointer` on all clickable elements")
lines.append("- [ ] Hover states with smooth transitions (150-300ms)")
lines.append("- [ ] Light mode: text contrast 4.5:1 minimum")
lines.append("- [ ] Focus states visible for keyboard navigation")
lines.append("- [ ] `prefers-reduced-motion` respected")
lines.append("- [ ] Responsive: 375px, 768px, 1024px, 1440px")
lines.append("- [ ] No content hidden behind fixed navbars")
lines.append("- [ ] No horizontal scroll on mobile")
lines.append("")
return "\n".join(lines)
def format_page_override_md(design_system: dict, page_name: str, page_query: str = None) -> str:
"""Format a page-specific override file with intelligent AI-generated content."""
project = design_system.get("project_name", "PROJECT")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
page_title = page_name.replace("-", " ").replace("_", " ").title()
# Detect page type and generate intelligent overrides
page_overrides = _generate_intelligent_overrides(page_name, page_query, design_system)
lines = []
lines.append(f"# {page_title} Page Overrides")
lines.append("")
lines.append(f"> **PROJECT:** {project}")
lines.append(f"> **Generated:** {timestamp}")
lines.append(f"> **Page Type:** {page_overrides.get('page_type', 'General')}")
lines.append("")
lines.append("> ⚠️ **IMPORTANT:** Rules in this file **override** the Master file (`design-system/MASTER.md`).")
lines.append("> Only deviations from the Master are documented here. For all other rules, refer to the Master.")
lines.append("")
lines.append("---")
lines.append("")
# Page-specific rules with actual content
lines.append("## Page-Specific Rules")
lines.append("")
# Layout Overrides
lines.append("### Layout Overrides")
lines.append("")
layout = page_overrides.get("layout", {})
if layout:
for key, value in layout.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master layout")
lines.append("")
# Spacing Overrides
lines.append("### Spacing Overrides")
lines.append("")
spacing = page_overrides.get("spacing", {})
if spacing:
for key, value in spacing.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master spacing")
lines.append("")
# Typography Overrides
lines.append("### Typography Overrides")
lines.append("")
typography = page_overrides.get("typography", {})
if typography:
for key, value in typography.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master typography")
lines.append("")
# Color Overrides
lines.append("### Color Overrides")
lines.append("")
colors = page_overrides.get("colors", {})
if colors:
for key, value in colors.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master colors")
lines.append("")
# Component Overrides
lines.append("### Component Overrides")
lines.append("")
components = page_overrides.get("components", [])
if components:
for comp in components:
lines.append(f"- {comp}")
else:
lines.append("- No overrides — use Master component specs")
lines.append("")
# Page-Specific Components
lines.append("---")
lines.append("")
lines.append("## Page-Specific Components")
lines.append("")
unique_components = page_overrides.get("unique_components", [])
if unique_components:
for comp in unique_components:
lines.append(f"- {comp}")
else:
lines.append("- No unique components for this page")
lines.append("")
# Recommendations
lines.append("---")
lines.append("")
lines.append("## Recommendations")
lines.append("")
recommendations = page_overrides.get("recommendations", [])
if recommendations:
for rec in recommendations:
lines.append(f"- {rec}")
lines.append("")
return "\n".join(lines)
def _generate_intelligent_overrides(page_name: str, page_query: str, design_system: dict) -> dict:
"""
Generate intelligent overrides based on page type using layered search.
Uses the existing search infrastructure to find relevant style, UX, and layout
data instead of hardcoded page types.
"""
from core import search
page_lower = page_name.lower()
query_lower = (page_query or "").lower()
combined_context = f"{page_lower} {query_lower}"
# Search across multiple domains for page-specific guidance
style_search = search(combined_context, "style", max_results=1)
ux_search = search(combined_context, "ux", max_results=3)
landing_search = search(combined_context, "landing", max_results=1)
# Extract results from search response
style_results = style_search.get("results", [])
ux_results = ux_search.get("results", [])
landing_results = landing_search.get("results", [])
# Detect page type from search results or context
page_type = _detect_page_type(combined_context, style_results)
# Build overrides from search results
layout = {}
spacing = {}
typography = {}
colors = {}
components = []
unique_components = []
recommendations = []
# Extract style-based overrides
if style_results:
style = style_results[0]
style_name = style.get("Style Category", "")
keywords = style.get("Keywords", "")
best_for = style.get("Best For", "")
effects = style.get("Effects & Animation", "")
# Infer layout from style keywords
if any(kw in keywords.lower() for kw in ["data", "dense", "dashboard", "grid"]):
layout["Max Width"] = "1400px or full-width"
layout["Grid"] = "12-column grid for data flexibility"
spacing["Content Density"] = "High — optimize for information display"
elif any(kw in keywords.lower() for kw in ["minimal", "simple", "clean", "single"]):
layout["Max Width"] = "800px (narrow, focused)"
layout["Layout"] = "Single column, centered"
spacing["Content Density"] = "Low — focus on clarity"
else:
layout["Max Width"] = "1200px (standard)"
layout["Layout"] = "Full-width sections, centered content"
if effects:
recommendations.append(f"Effects: {effects}")
# Extract UX guidelines as recommendations
for ux in ux_results:
category = ux.get("Category", "")
do_text = ux.get("Do", "")
dont_text = ux.get("Don't", "")
if do_text:
recommendations.append(f"{category}: {do_text}")
if dont_text:
components.append(f"Avoid: {dont_text}")
# Extract landing pattern info for section structure
if landing_results:
landing = landing_results[0]
sections = landing.get("Section Order", "")
cta_placement = landing.get("Primary CTA Placement", "")
color_strategy = landing.get("Color Strategy", "")
if sections:
layout["Sections"] = sections
if cta_placement:
recommendations.append(f"CTA Placement: {cta_placement}")
if color_strategy:
colors["Strategy"] = color_strategy
# Add page-type specific defaults if no search results
if not layout:
layout["Max Width"] = "1200px"
layout["Layout"] = "Responsive grid"
if not recommendations:
recommendations = [
"Refer to MASTER.md for all design rules",
"Add specific overrides as needed for this page"
]
return {
"page_type": page_type,
"layout": layout,
"spacing": spacing,
"typography": typography,
"colors": colors,
"components": components,
"unique_components": unique_components,
"recommendations": recommendations
}
def _detect_page_type(context: str, style_results: list) -> str:
"""Detect page type from context and search results."""
context_lower = context.lower()
# Check for common page type patterns
page_patterns = [
(["dashboard", "admin", "analytics", "data", "metrics", "stats", "monitor", "overview"], "Dashboard / Data View"),
(["checkout", "payment", "cart", "purchase", "order", "billing"], "Checkout / Payment"),
(["settings", "profile", "account", "preferences", "config"], "Settings / Profile"),
(["landing", "marketing", "homepage", "hero", "home", "promo"], "Landing / Marketing"),
(["login", "signin", "signup", "register", "auth", "password"], "Authentication"),
(["pricing", "plans", "subscription", "tiers", "packages"], "Pricing / Plans"),
(["blog", "article", "post", "news", "content", "story"], "Blog / Article"),
(["product", "item", "detail", "pdp", "shop", "store"], "Product Detail"),
(["search", "results", "browse", "filter", "catalog", "list"], "Search Results"),
(["empty", "404", "error", "not found", "zero"], "Empty State"),
]
for keywords, page_type in page_patterns:
if any(kw in context_lower for kw in keywords):
return page_type
# Fallback: try to infer from style results
if style_results:
style_name = style_results[0].get("Style Category", "").lower()
best_for = style_results[0].get("Best For", "").lower()
if "dashboard" in best_for or "data" in best_for:
return "Dashboard / Data View"
elif "landing" in best_for or "marketing" in best_for:
return "Landing / Marketing"
return "General"
# ============ CLI SUPPORT ============ # ============ CLI SUPPORT ============
if __name__ == "__main__": if __name__ == "__main__":
import argparse import argparse

View File

@@ -4,14 +4,19 @@
UI/UX Pro Max Search - BM25 search engine for UI/UX style guides UI/UX Pro Max Search - BM25 search engine for UI/UX style guides
Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3] Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3]
python search.py "<query>" --design-system [-p "Project Name"] python search.py "<query>" --design-system [-p "Project Name"]
python search.py "<query>" --design-system --persist [-p "Project Name"] [--page "dashboard"]
Domains: style, prompt, color, chart, landing, product, ux, typography Domains: style, prompt, color, chart, landing, product, ux, typography
Stacks: html-tailwind, react, nextjs Stacks: html-tailwind, react, nextjs
Persistence (Master + Overrides pattern):
--persist Save design system to design-system/MASTER.md
--page Also create a page-specific override file in design-system/pages/
""" """
import argparse import argparse
from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack
from design_system import generate_design_system from design_system import generate_design_system, persist_design_system
def format_output(result): def format_output(result):
@@ -51,13 +56,38 @@ if __name__ == "__main__":
parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation") parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation")
parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output") parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output")
parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system") parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system")
# Persistence (Master + Overrides pattern)
parser.add_argument("--persist", action="store_true", help="Save design system to design-system/MASTER.md (creates hierarchical structure)")
parser.add_argument("--page", type=str, default=None, help="Create page-specific override file in design-system/pages/")
parser.add_argument("--output-dir", "-o", type=str, default=None, help="Output directory for persisted files (default: current directory)")
args = parser.parse_args() args = parser.parse_args()
# Design system takes priority # Design system takes priority
if args.design_system: if args.design_system:
result = generate_design_system(args.query, args.project_name, args.format) result = generate_design_system(
args.query,
args.project_name,
args.format,
persist=args.persist,
page=args.page,
output_dir=args.output_dir
)
print(result) print(result)
# Print persistence confirmation
if args.persist:
project_slug = args.project_name.lower().replace(' ', '-') if args.project_name else "default"
print("\n" + "=" * 60)
print(f"✅ Design system persisted to design-system/{project_slug}/")
print(f" 📄 design-system/{project_slug}/MASTER.md (Global Source of Truth)")
if args.page:
page_filename = args.page.lower().replace(' ', '-')
print(f" 📄 design-system/{project_slug}/pages/{page_filename}.md (Page Overrides)")
print("")
print(f"📖 Usage: When building a page, check design-system/{project_slug}/pages/[page].md first.")
print(f" If exists, its rules override MASTER.md. Otherwise, use MASTER.md.")
print("=" * 60)
# Stack search # Stack search
elif args.stack: elif args.stack:
result = search_stack(args.query, args.stack, args.max_results) result = search_stack(args.query, args.stack, args.max_results)

View File

@@ -7,10 +7,16 @@ to generate comprehensive design system recommendations.
Usage: Usage:
from design_system import generate_design_system from design_system import generate_design_system
result = generate_design_system("SaaS dashboard", "My Project") result = generate_design_system("SaaS dashboard", "My Project")
# With persistence (Master + Overrides pattern)
result = generate_design_system("SaaS dashboard", "My Project", persist=True)
result = generate_design_system("SaaS dashboard", "My Project", persist=True, page="dashboard")
""" """
import csv import csv
import json import json
import os
from datetime import datetime
from pathlib import Path from pathlib import Path
from core import search, DATA_DIR from core import search, DATA_DIR
@@ -338,6 +344,21 @@ def format_ascii_box(design_system: dict) -> str:
lines.append(line.ljust(BOX_WIDTH) + "|") lines.append(line.ljust(BOX_WIDTH) + "|")
lines.append("|" + " " * BOX_WIDTH + "|") lines.append("|" + " " * BOX_WIDTH + "|")
# Pre-Delivery Checklist section
lines.append("| PRE-DELIVERY CHECKLIST:".ljust(BOX_WIDTH) + "|")
checklist_items = [
"[ ] No emojis as icons (use SVG: Heroicons/Lucide)",
"[ ] cursor-pointer on all clickable elements",
"[ ] Hover states with smooth transitions (150-300ms)",
"[ ] Light mode: text contrast 4.5:1 minimum",
"[ ] Focus states visible for keyboard nav",
"[ ] prefers-reduced-motion respected",
"[ ] Responsive: 375px, 768px, 1024px, 1440px"
]
for item in checklist_items:
lines.append(f"| {item}".ljust(BOX_WIDTH) + "|")
lines.append("|" + " " * BOX_WIDTH + "|")
lines.append("+" + "-" * w + "+") lines.append("+" + "-" * w + "+")
return "\n".join(lines) return "\n".join(lines)
@@ -422,11 +443,23 @@ def format_markdown(design_system: dict) -> str:
lines.append(f"- {anti_patterns.replace(' + ', '\n- ')}") lines.append(f"- {anti_patterns.replace(' + ', '\n- ')}")
lines.append("") lines.append("")
# Pre-Delivery Checklist section
lines.append("### Pre-Delivery Checklist")
lines.append("- [ ] No emojis as icons (use SVG: Heroicons/Lucide)")
lines.append("- [ ] cursor-pointer on all clickable elements")
lines.append("- [ ] Hover states with smooth transitions (150-300ms)")
lines.append("- [ ] Light mode: text contrast 4.5:1 minimum")
lines.append("- [ ] Focus states visible for keyboard nav")
lines.append("- [ ] prefers-reduced-motion respected")
lines.append("- [ ] Responsive: 375px, 768px, 1024px, 1440px")
lines.append("")
return "\n".join(lines) return "\n".join(lines)
# ============ MAIN ENTRY POINT ============ # ============ MAIN ENTRY POINT ============
def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii") -> str: def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii",
persist: bool = False, page: str = None, output_dir: str = None) -> str:
""" """
Main entry point for design system generation. Main entry point for design system generation.
@@ -434,6 +467,9 @@ def generate_design_system(query: str, project_name: str = None, output_format:
query: Search query (e.g., "SaaS dashboard", "e-commerce luxury") query: Search query (e.g., "SaaS dashboard", "e-commerce luxury")
project_name: Optional project name for output header project_name: Optional project name for output header
output_format: "ascii" (default) or "markdown" output_format: "ascii" (default) or "markdown"
persist: If True, save design system to design-system/ folder
page: Optional page name for page-specific override file
output_dir: Optional output directory (defaults to current working directory)
Returns: Returns:
Formatted design system string Formatted design system string
@@ -441,11 +477,580 @@ def generate_design_system(query: str, project_name: str = None, output_format:
generator = DesignSystemGenerator() generator = DesignSystemGenerator()
design_system = generator.generate(query, project_name) design_system = generator.generate(query, project_name)
# Persist to files if requested
if persist:
persist_design_system(design_system, page, output_dir, query)
if output_format == "markdown": if output_format == "markdown":
return format_markdown(design_system) return format_markdown(design_system)
return format_ascii_box(design_system) return format_ascii_box(design_system)
# ============ PERSISTENCE FUNCTIONS ============
def persist_design_system(design_system: dict, page: str = None, output_dir: str = None, page_query: str = None) -> dict:
"""
Persist design system to design-system/<project>/ folder using Master + Overrides pattern.
Args:
design_system: The generated design system dictionary
page: Optional page name for page-specific override file
output_dir: Optional output directory (defaults to current working directory)
page_query: Optional query string for intelligent page override generation
Returns:
dict with created file paths and status
"""
base_dir = Path(output_dir) if output_dir else Path.cwd()
# Use project name for project-specific folder
project_name = design_system.get("project_name", "default")
project_slug = project_name.lower().replace(' ', '-')
design_system_dir = base_dir / "design-system" / project_slug
pages_dir = design_system_dir / "pages"
created_files = []
# Create directories
design_system_dir.mkdir(parents=True, exist_ok=True)
pages_dir.mkdir(parents=True, exist_ok=True)
master_file = design_system_dir / "MASTER.md"
# Generate and write MASTER.md
master_content = format_master_md(design_system)
with open(master_file, 'w', encoding='utf-8') as f:
f.write(master_content)
created_files.append(str(master_file))
# If page is specified, create page override file with intelligent content
if page:
page_file = pages_dir / f"{page.lower().replace(' ', '-')}.md"
page_content = format_page_override_md(design_system, page, page_query)
with open(page_file, 'w', encoding='utf-8') as f:
f.write(page_content)
created_files.append(str(page_file))
return {
"status": "success",
"design_system_dir": str(design_system_dir),
"created_files": created_files
}
def format_master_md(design_system: dict) -> str:
"""Format design system as MASTER.md with hierarchical override logic."""
project = design_system.get("project_name", "PROJECT")
pattern = design_system.get("pattern", {})
style = design_system.get("style", {})
colors = design_system.get("colors", {})
typography = design_system.get("typography", {})
effects = design_system.get("key_effects", "")
anti_patterns = design_system.get("anti_patterns", "")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
lines = []
# Logic header
lines.append("# Design System Master File")
lines.append("")
lines.append("> **LOGIC:** When building a specific page, first check `design-system/pages/[page-name].md`.")
lines.append("> If that file exists, its rules **override** this Master file.")
lines.append("> If not, strictly follow the rules below.")
lines.append("")
lines.append("---")
lines.append("")
lines.append(f"**Project:** {project}")
lines.append(f"**Generated:** {timestamp}")
lines.append(f"**Category:** {design_system.get('category', 'General')}")
lines.append("")
lines.append("---")
lines.append("")
# Global Rules section
lines.append("## Global Rules")
lines.append("")
# Color Palette
lines.append("### Color Palette")
lines.append("")
lines.append("| Role | Hex | CSS Variable |")
lines.append("|------|-----|--------------|")
lines.append(f"| Primary | `{colors.get('primary', '#2563EB')}` | `--color-primary` |")
lines.append(f"| Secondary | `{colors.get('secondary', '#3B82F6')}` | `--color-secondary` |")
lines.append(f"| CTA/Accent | `{colors.get('cta', '#F97316')}` | `--color-cta` |")
lines.append(f"| Background | `{colors.get('background', '#F8FAFC')}` | `--color-background` |")
lines.append(f"| Text | `{colors.get('text', '#1E293B')}` | `--color-text` |")
lines.append("")
if colors.get("notes"):
lines.append(f"**Color Notes:** {colors.get('notes', '')}")
lines.append("")
# Typography
lines.append("### Typography")
lines.append("")
lines.append(f"- **Heading Font:** {typography.get('heading', 'Inter')}")
lines.append(f"- **Body Font:** {typography.get('body', 'Inter')}")
if typography.get("mood"):
lines.append(f"- **Mood:** {typography.get('mood', '')}")
if typography.get("google_fonts_url"):
lines.append(f"- **Google Fonts:** [{typography.get('heading', '')} + {typography.get('body', '')}]({typography.get('google_fonts_url', '')})")
lines.append("")
if typography.get("css_import"):
lines.append("**CSS Import:**")
lines.append("```css")
lines.append(typography.get("css_import", ""))
lines.append("```")
lines.append("")
# Spacing Variables
lines.append("### Spacing Variables")
lines.append("")
lines.append("| Token | Value | Usage |")
lines.append("|-------|-------|-------|")
lines.append("| `--space-xs` | `4px` / `0.25rem` | Tight gaps |")
lines.append("| `--space-sm` | `8px` / `0.5rem` | Icon gaps, inline spacing |")
lines.append("| `--space-md` | `16px` / `1rem` | Standard padding |")
lines.append("| `--space-lg` | `24px` / `1.5rem` | Section padding |")
lines.append("| `--space-xl` | `32px` / `2rem` | Large gaps |")
lines.append("| `--space-2xl` | `48px` / `3rem` | Section margins |")
lines.append("| `--space-3xl` | `64px` / `4rem` | Hero padding |")
lines.append("")
# Shadow Depths
lines.append("### Shadow Depths")
lines.append("")
lines.append("| Level | Value | Usage |")
lines.append("|-------|-------|-------|")
lines.append("| `--shadow-sm` | `0 1px 2px rgba(0,0,0,0.05)` | Subtle lift |")
lines.append("| `--shadow-md` | `0 4px 6px rgba(0,0,0,0.1)` | Cards, buttons |")
lines.append("| `--shadow-lg` | `0 10px 15px rgba(0,0,0,0.1)` | Modals, dropdowns |")
lines.append("| `--shadow-xl` | `0 20px 25px rgba(0,0,0,0.15)` | Hero images, featured cards |")
lines.append("")
# Component Specs section
lines.append("---")
lines.append("")
lines.append("## Component Specs")
lines.append("")
# Buttons
lines.append("### Buttons")
lines.append("")
lines.append("```css")
lines.append("/* Primary Button */")
lines.append(".btn-primary {")
lines.append(f" background: {colors.get('cta', '#F97316')};")
lines.append(" color: white;")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("")
lines.append(".btn-primary:hover {")
lines.append(" opacity: 0.9;")
lines.append(" transform: translateY(-1px);")
lines.append("}")
lines.append("")
lines.append("/* Secondary Button */")
lines.append(".btn-secondary {")
lines.append(f" background: transparent;")
lines.append(f" color: {colors.get('primary', '#2563EB')};")
lines.append(f" border: 2px solid {colors.get('primary', '#2563EB')};")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("```")
lines.append("")
# Cards
lines.append("### Cards")
lines.append("")
lines.append("```css")
lines.append(".card {")
lines.append(f" background: {colors.get('background', '#FFFFFF')};")
lines.append(" border-radius: 12px;")
lines.append(" padding: 24px;")
lines.append(" box-shadow: var(--shadow-md);")
lines.append(" transition: all 200ms ease;")
lines.append(" cursor: pointer;")
lines.append("}")
lines.append("")
lines.append(".card:hover {")
lines.append(" box-shadow: var(--shadow-lg);")
lines.append(" transform: translateY(-2px);")
lines.append("}")
lines.append("```")
lines.append("")
# Inputs
lines.append("### Inputs")
lines.append("")
lines.append("```css")
lines.append(".input {")
lines.append(" padding: 12px 16px;")
lines.append(" border: 1px solid #E2E8F0;")
lines.append(" border-radius: 8px;")
lines.append(" font-size: 16px;")
lines.append(" transition: border-color 200ms ease;")
lines.append("}")
lines.append("")
lines.append(".input:focus {")
lines.append(f" border-color: {colors.get('primary', '#2563EB')};")
lines.append(" outline: none;")
lines.append(f" box-shadow: 0 0 0 3px {colors.get('primary', '#2563EB')}20;")
lines.append("}")
lines.append("```")
lines.append("")
# Modals
lines.append("### Modals")
lines.append("")
lines.append("```css")
lines.append(".modal-overlay {")
lines.append(" background: rgba(0, 0, 0, 0.5);")
lines.append(" backdrop-filter: blur(4px);")
lines.append("}")
lines.append("")
lines.append(".modal {")
lines.append(" background: white;")
lines.append(" border-radius: 16px;")
lines.append(" padding: 32px;")
lines.append(" box-shadow: var(--shadow-xl);")
lines.append(" max-width: 500px;")
lines.append(" width: 90%;")
lines.append("}")
lines.append("```")
lines.append("")
# Style section
lines.append("---")
lines.append("")
lines.append("## Style Guidelines")
lines.append("")
lines.append(f"**Style:** {style.get('name', 'Minimalism')}")
lines.append("")
if style.get("keywords"):
lines.append(f"**Keywords:** {style.get('keywords', '')}")
lines.append("")
if style.get("best_for"):
lines.append(f"**Best For:** {style.get('best_for', '')}")
lines.append("")
if effects:
lines.append(f"**Key Effects:** {effects}")
lines.append("")
# Layout Pattern
lines.append("### Page Pattern")
lines.append("")
lines.append(f"**Pattern Name:** {pattern.get('name', '')}")
lines.append("")
if pattern.get('conversion'):
lines.append(f"- **Conversion Strategy:** {pattern.get('conversion', '')}")
if pattern.get('cta_placement'):
lines.append(f"- **CTA Placement:** {pattern.get('cta_placement', '')}")
lines.append(f"- **Section Order:** {pattern.get('sections', '')}")
lines.append("")
# Anti-Patterns section
lines.append("---")
lines.append("")
lines.append("## Anti-Patterns (Do NOT Use)")
lines.append("")
if anti_patterns:
anti_list = [a.strip() for a in anti_patterns.split("+")]
for anti in anti_list:
if anti:
lines.append(f"- ❌ {anti}")
lines.append("")
lines.append("### Additional Forbidden Patterns")
lines.append("")
lines.append("- ❌ **Emojis as icons** — Use SVG icons (Heroicons, Lucide, Simple Icons)")
lines.append("- ❌ **Missing cursor:pointer** — All clickable elements must have cursor:pointer")
lines.append("- ❌ **Layout-shifting hovers** — Avoid scale transforms that shift layout")
lines.append("- ❌ **Low contrast text** — Maintain 4.5:1 minimum contrast ratio")
lines.append("- ❌ **Instant state changes** — Always use transitions (150-300ms)")
lines.append("- ❌ **Invisible focus states** — Focus states must be visible for a11y")
lines.append("")
# Pre-Delivery Checklist
lines.append("---")
lines.append("")
lines.append("## Pre-Delivery Checklist")
lines.append("")
lines.append("Before delivering any UI code, verify:")
lines.append("")
lines.append("- [ ] No emojis used as icons (use SVG instead)")
lines.append("- [ ] All icons from consistent icon set (Heroicons/Lucide)")
lines.append("- [ ] `cursor-pointer` on all clickable elements")
lines.append("- [ ] Hover states with smooth transitions (150-300ms)")
lines.append("- [ ] Light mode: text contrast 4.5:1 minimum")
lines.append("- [ ] Focus states visible for keyboard navigation")
lines.append("- [ ] `prefers-reduced-motion` respected")
lines.append("- [ ] Responsive: 375px, 768px, 1024px, 1440px")
lines.append("- [ ] No content hidden behind fixed navbars")
lines.append("- [ ] No horizontal scroll on mobile")
lines.append("")
return "\n".join(lines)
def format_page_override_md(design_system: dict, page_name: str, page_query: str = None) -> str:
"""Format a page-specific override file with intelligent AI-generated content."""
project = design_system.get("project_name", "PROJECT")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
page_title = page_name.replace("-", " ").replace("_", " ").title()
# Detect page type and generate intelligent overrides
page_overrides = _generate_intelligent_overrides(page_name, page_query, design_system)
lines = []
lines.append(f"# {page_title} Page Overrides")
lines.append("")
lines.append(f"> **PROJECT:** {project}")
lines.append(f"> **Generated:** {timestamp}")
lines.append(f"> **Page Type:** {page_overrides.get('page_type', 'General')}")
lines.append("")
lines.append("> ⚠️ **IMPORTANT:** Rules in this file **override** the Master file (`design-system/MASTER.md`).")
lines.append("> Only deviations from the Master are documented here. For all other rules, refer to the Master.")
lines.append("")
lines.append("---")
lines.append("")
# Page-specific rules with actual content
lines.append("## Page-Specific Rules")
lines.append("")
# Layout Overrides
lines.append("### Layout Overrides")
lines.append("")
layout = page_overrides.get("layout", {})
if layout:
for key, value in layout.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master layout")
lines.append("")
# Spacing Overrides
lines.append("### Spacing Overrides")
lines.append("")
spacing = page_overrides.get("spacing", {})
if spacing:
for key, value in spacing.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master spacing")
lines.append("")
# Typography Overrides
lines.append("### Typography Overrides")
lines.append("")
typography = page_overrides.get("typography", {})
if typography:
for key, value in typography.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master typography")
lines.append("")
# Color Overrides
lines.append("### Color Overrides")
lines.append("")
colors = page_overrides.get("colors", {})
if colors:
for key, value in colors.items():
lines.append(f"- **{key}:** {value}")
else:
lines.append("- No overrides — use Master colors")
lines.append("")
# Component Overrides
lines.append("### Component Overrides")
lines.append("")
components = page_overrides.get("components", [])
if components:
for comp in components:
lines.append(f"- {comp}")
else:
lines.append("- No overrides — use Master component specs")
lines.append("")
# Page-Specific Components
lines.append("---")
lines.append("")
lines.append("## Page-Specific Components")
lines.append("")
unique_components = page_overrides.get("unique_components", [])
if unique_components:
for comp in unique_components:
lines.append(f"- {comp}")
else:
lines.append("- No unique components for this page")
lines.append("")
# Recommendations
lines.append("---")
lines.append("")
lines.append("## Recommendations")
lines.append("")
recommendations = page_overrides.get("recommendations", [])
if recommendations:
for rec in recommendations:
lines.append(f"- {rec}")
lines.append("")
return "\n".join(lines)
def _generate_intelligent_overrides(page_name: str, page_query: str, design_system: dict) -> dict:
"""
Generate intelligent overrides based on page type using layered search.
Uses the existing search infrastructure to find relevant style, UX, and layout
data instead of hardcoded page types.
"""
from core import search
page_lower = page_name.lower()
query_lower = (page_query or "").lower()
combined_context = f"{page_lower} {query_lower}"
# Search across multiple domains for page-specific guidance
style_search = search(combined_context, "style", max_results=1)
ux_search = search(combined_context, "ux", max_results=3)
landing_search = search(combined_context, "landing", max_results=1)
# Extract results from search response
style_results = style_search.get("results", [])
ux_results = ux_search.get("results", [])
landing_results = landing_search.get("results", [])
# Detect page type from search results or context
page_type = _detect_page_type(combined_context, style_results)
# Build overrides from search results
layout = {}
spacing = {}
typography = {}
colors = {}
components = []
unique_components = []
recommendations = []
# Extract style-based overrides
if style_results:
style = style_results[0]
style_name = style.get("Style Category", "")
keywords = style.get("Keywords", "")
best_for = style.get("Best For", "")
effects = style.get("Effects & Animation", "")
# Infer layout from style keywords
if any(kw in keywords.lower() for kw in ["data", "dense", "dashboard", "grid"]):
layout["Max Width"] = "1400px or full-width"
layout["Grid"] = "12-column grid for data flexibility"
spacing["Content Density"] = "High — optimize for information display"
elif any(kw in keywords.lower() for kw in ["minimal", "simple", "clean", "single"]):
layout["Max Width"] = "800px (narrow, focused)"
layout["Layout"] = "Single column, centered"
spacing["Content Density"] = "Low — focus on clarity"
else:
layout["Max Width"] = "1200px (standard)"
layout["Layout"] = "Full-width sections, centered content"
if effects:
recommendations.append(f"Effects: {effects}")
# Extract UX guidelines as recommendations
for ux in ux_results:
category = ux.get("Category", "")
do_text = ux.get("Do", "")
dont_text = ux.get("Don't", "")
if do_text:
recommendations.append(f"{category}: {do_text}")
if dont_text:
components.append(f"Avoid: {dont_text}")
# Extract landing pattern info for section structure
if landing_results:
landing = landing_results[0]
sections = landing.get("Section Order", "")
cta_placement = landing.get("Primary CTA Placement", "")
color_strategy = landing.get("Color Strategy", "")
if sections:
layout["Sections"] = sections
if cta_placement:
recommendations.append(f"CTA Placement: {cta_placement}")
if color_strategy:
colors["Strategy"] = color_strategy
# Add page-type specific defaults if no search results
if not layout:
layout["Max Width"] = "1200px"
layout["Layout"] = "Responsive grid"
if not recommendations:
recommendations = [
"Refer to MASTER.md for all design rules",
"Add specific overrides as needed for this page"
]
return {
"page_type": page_type,
"layout": layout,
"spacing": spacing,
"typography": typography,
"colors": colors,
"components": components,
"unique_components": unique_components,
"recommendations": recommendations
}
def _detect_page_type(context: str, style_results: list) -> str:
"""Detect page type from context and search results."""
context_lower = context.lower()
# Check for common page type patterns
page_patterns = [
(["dashboard", "admin", "analytics", "data", "metrics", "stats", "monitor", "overview"], "Dashboard / Data View"),
(["checkout", "payment", "cart", "purchase", "order", "billing"], "Checkout / Payment"),
(["settings", "profile", "account", "preferences", "config"], "Settings / Profile"),
(["landing", "marketing", "homepage", "hero", "home", "promo"], "Landing / Marketing"),
(["login", "signin", "signup", "register", "auth", "password"], "Authentication"),
(["pricing", "plans", "subscription", "tiers", "packages"], "Pricing / Plans"),
(["blog", "article", "post", "news", "content", "story"], "Blog / Article"),
(["product", "item", "detail", "pdp", "shop", "store"], "Product Detail"),
(["search", "results", "browse", "filter", "catalog", "list"], "Search Results"),
(["empty", "404", "error", "not found", "zero"], "Empty State"),
]
for keywords, page_type in page_patterns:
if any(kw in context_lower for kw in keywords):
return page_type
# Fallback: try to infer from style results
if style_results:
style_name = style_results[0].get("Style Category", "").lower()
best_for = style_results[0].get("Best For", "").lower()
if "dashboard" in best_for or "data" in best_for:
return "Dashboard / Data View"
elif "landing" in best_for or "marketing" in best_for:
return "Landing / Marketing"
return "General"
# ============ CLI SUPPORT ============ # ============ CLI SUPPORT ============
if __name__ == "__main__": if __name__ == "__main__":
import argparse import argparse

View File

@@ -4,14 +4,19 @@
UI/UX Pro Max Search - BM25 search engine for UI/UX style guides UI/UX Pro Max Search - BM25 search engine for UI/UX style guides
Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3] Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3]
python search.py "<query>" --design-system [-p "Project Name"] python search.py "<query>" --design-system [-p "Project Name"]
python search.py "<query>" --design-system --persist [-p "Project Name"] [--page "dashboard"]
Domains: style, prompt, color, chart, landing, product, ux, typography Domains: style, prompt, color, chart, landing, product, ux, typography
Stacks: html-tailwind, react, nextjs Stacks: html-tailwind, react, nextjs
Persistence (Master + Overrides pattern):
--persist Save design system to design-system/MASTER.md
--page Also create a page-specific override file in design-system/pages/
""" """
import argparse import argparse
from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack
from design_system import generate_design_system from design_system import generate_design_system, persist_design_system
def format_output(result): def format_output(result):
@@ -51,13 +56,38 @@ if __name__ == "__main__":
parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation") parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation")
parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output") parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output")
parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system") parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system")
# Persistence (Master + Overrides pattern)
parser.add_argument("--persist", action="store_true", help="Save design system to design-system/MASTER.md (creates hierarchical structure)")
parser.add_argument("--page", type=str, default=None, help="Create page-specific override file in design-system/pages/")
parser.add_argument("--output-dir", "-o", type=str, default=None, help="Output directory for persisted files (default: current directory)")
args = parser.parse_args() args = parser.parse_args()
# Design system takes priority # Design system takes priority
if args.design_system: if args.design_system:
result = generate_design_system(args.query, args.project_name, args.format) result = generate_design_system(
args.query,
args.project_name,
args.format,
persist=args.persist,
page=args.page,
output_dir=args.output_dir
)
print(result) print(result)
# Print persistence confirmation
if args.persist:
project_slug = args.project_name.lower().replace(' ', '-') if args.project_name else "default"
print("\n" + "=" * 60)
print(f"✅ Design system persisted to design-system/{project_slug}/")
print(f" 📄 design-system/{project_slug}/MASTER.md (Global Source of Truth)")
if args.page:
page_filename = args.page.lower().replace(' ', '-')
print(f" 📄 design-system/{project_slug}/pages/{page_filename}.md (Page Overrides)")
print("")
print(f"📖 Usage: When building a page, check design-system/{project_slug}/pages/[page].md first.")
print(f" If exists, its rules override MASTER.md. Otherwise, use MASTER.md.")
print("=" * 60)
# Stack search # Stack search
elif args.stack: elif args.stack:
result = search_stack(args.query, args.stack, args.max_results) result = search_stack(args.query, args.stack, args.max_results)

View File

@@ -60,6 +60,31 @@ This command:
python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa" python3 .shared/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa"
``` ```
### Step 2b: Persist Design System (Master + Overrides Pattern)
To save the design system for hierarchical retrieval across sessions, add `--persist`:
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name"
```
This creates:
- `design-system/MASTER.md` — Global Source of Truth with all design rules
- `design-system/pages/` — Folder for page-specific overrides
**With page-specific override:**
```bash
python3 .shared/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name" --page "dashboard"
```
This also creates:
- `design-system/pages/dashboard.md` — Page-specific deviations from Master
**How hierarchical retrieval works:**
1. When building a specific page (e.g., "Checkout"), first check `design-system/pages/checkout.md`
2. If the page file exists, its rules **override** the Master file
3. If not, use `design-system/MASTER.md` exclusively
### Step 3: Supplement with Detailed Searches (as needed) ### Step 3: Supplement with Detailed Searches (as needed)
After getting the design system, use domain searches to get additional details: After getting the design system, use domain searches to get additional details:
@@ -86,8 +111,8 @@ Get implementation-specific best practices. If user doesn't specify a stack, **d
python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind python3 .shared/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind
``` ```
Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn` Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn`, `jetpack-compose`
, `jetpack-compose`
--- ---
## Search Reference ## Search Reference
@@ -120,6 +145,7 @@ Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`
| `react-native` | Components, Navigation, Lists | | `react-native` | Components, Navigation, Lists |
| `flutter` | Widgets, State, Layout, Theming | | `flutter` | Widgets, State, Layout, Theming |
| `shadcn` | shadcn/ui components, theming, forms, patterns | | `shadcn` | shadcn/ui components, theming, forms, patterns |
| `jetpack-compose` | Composables, Modifiers, State Hoisting, Recomposition |
--- ---

View File

@@ -1,6 +1,6 @@
{ {
"name": "uipro-cli", "name": "uipro-cli",
"version": "2.0.0", "version": "2.1.0",
"description": "CLI to install UI/UX Pro Max skill for AI coding assistants", "description": "CLI to install UI/UX Pro Max skill for AI coding assistants",
"type": "module", "type": "module",
"bin": { "bin": {

View File

@@ -1,18 +1,25 @@
#!/usr/bin/env node #!/usr/bin/env node
import { Command } from 'commander'; import { Command } from 'commander';
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { initCommand } from './commands/init.js'; import { initCommand } from './commands/init.js';
import { versionsCommand } from './commands/versions.js'; import { versionsCommand } from './commands/versions.js';
import { updateCommand } from './commands/update.js'; import { updateCommand } from './commands/update.js';
import type { AIType } from './types/index.js'; import type { AIType } from './types/index.js';
import { AI_TYPES } from './types/index.js'; import { AI_TYPES } from './types/index.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
const program = new Command(); const program = new Command();
program program
.name('uipro') .name('uipro')
.description('CLI to install UI/UX Pro Max skill for AI coding assistants') .description('CLI to install UI/UX Pro Max skill for AI coding assistants')
.version('1.9.0'); .version(pkg.version);
program program
.command('init') .command('init')

View File

@@ -1,4 +1,4 @@
export type AIType = 'claude' | 'cursor' | 'windsurf' | 'antigravity' | 'copilot' | 'kiro' | 'roocode' | 'codex' | 'qoder' | 'gemini' | 'trae' | 'all'; export type AIType = 'claude' | 'cursor' | 'windsurf' | 'antigravity' | 'copilot' | 'kiro' | 'roocode' | 'codex' | 'qoder' | 'gemini' | 'trae' | 'opencode' | 'all';
export interface Release { export interface Release {
tag_name: string; tag_name: string;
@@ -20,7 +20,7 @@ export interface InstallConfig {
force?: boolean; force?: boolean;
} }
export const AI_TYPES: AIType[] = ['claude', 'cursor', 'windsurf', 'antigravity', 'copilot', 'roocode', 'kiro', 'codex', 'qoder', 'gemini', 'trae', 'all']; export const AI_TYPES: AIType[] = ['claude', 'cursor', 'windsurf', 'antigravity', 'copilot', 'roocode', 'kiro', 'codex', 'qoder', 'gemini', 'trae', 'opencode', 'all'];
export const AI_FOLDERS: Record<Exclude<AIType, 'all'>, string[]> = { export const AI_FOLDERS: Record<Exclude<AIType, 'all'>, string[]> = {
claude: ['.claude'], claude: ['.claude'],
@@ -34,4 +34,5 @@ export const AI_FOLDERS: Record<Exclude<AIType, 'all'>, string[]> = {
qoder: ['.qoder', '.shared'], qoder: ['.qoder', '.shared'],
gemini: ['.gemini', '.shared'], gemini: ['.gemini', '.shared'],
trae: ['.trae', '.shared'], trae: ['.trae', '.shared'],
opencode: ['.opencode', '.shared'],
}; };

View File

@@ -43,7 +43,9 @@ export function detectAIType(cwd: string = process.cwd()): DetectionResult {
if (existsSync(join(cwd, '.trae'))) { if (existsSync(join(cwd, '.trae'))) {
detected.push('trae'); detected.push('trae');
} }
if (existsSync(join(cwd, '.opencode'))) {
detected.push('opencode');
}
// Suggest based on what's detected // Suggest based on what's detected
let suggested: AIType | null = null; let suggested: AIType | null = null;
@@ -80,6 +82,8 @@ export function getAITypeDescription(aiType: AIType): string {
return 'Gemini CLI (.gemini/skills/ + .shared/)'; return 'Gemini CLI (.gemini/skills/ + .shared/)';
case 'trae': case 'trae':
return 'Trae (.trae/skills/ + .shared/)'; return 'Trae (.trae/skills/ + .shared/)';
case 'opencode':
return 'OpenCode (.opencode/skills/ + .shared/)';
case 'all': case 'all':
return 'All AI assistants'; return 'All AI assistants';
} }

View File

@@ -40,7 +40,7 @@ export async function copyFolders(
const copiedFolders: string[] = []; const copiedFolders: string[] = [];
const foldersToCopy = aiType === 'all' const foldersToCopy = aiType === 'all'
? ['.claude', '.cursor', '.windsurf', '.agent', '.github', '.kiro', '.roo','.codex', '.gemini', '.trae', '.shared'] ? Object.values(AI_FOLDERS).flat()
: AI_FOLDERS[aiType]; : AI_FOLDERS[aiType];
// Deduplicate folders (e.g., .shared might be listed multiple times) // Deduplicate folders (e.g., .shared might be listed multiple times)

View File

@@ -135,7 +135,7 @@ Extract key information from user request:
**Always start with `--design-system`** to get comprehensive recommendations with reasoning: **Always start with `--design-system`** to get comprehensive recommendations with reasoning:
```bash ```bash
python3 .claude/skills/ui-ux-pro-max/scripts/search.py "<product_type> <industry> <keywords>" --design-system [-p "Project Name"] python3 skills/ui-ux-pro-max/scripts/search.py "<product_type> <industry> <keywords>" --design-system [-p "Project Name"]
``` ```
This command: This command:
@@ -146,7 +146,41 @@ This command:
**Example:** **Example:**
```bash ```bash
python3 .claude/skills/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa" python3 skills/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa"
```
### Step 2b: Persist Design System (Master + Overrides Pattern)
To save the design system for **hierarchical retrieval across sessions**, add `--persist`:
```bash
python3 skills/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name"
```
This creates:
- `design-system/MASTER.md` — Global Source of Truth with all design rules
- `design-system/pages/` — Folder for page-specific overrides
**With page-specific override:**
```bash
python3 skills/ui-ux-pro-max/scripts/search.py "<query>" --design-system --persist -p "Project Name" --page "dashboard"
```
This also creates:
- `design-system/pages/dashboard.md` — Page-specific deviations from Master
**How hierarchical retrieval works:**
1. When building a specific page (e.g., "Checkout"), first check `design-system/pages/checkout.md`
2. If the page file exists, its rules **override** the Master file
3. If not, use `design-system/MASTER.md` exclusively
**Context-aware retrieval prompt:**
```
I am building the [Page Name] page. Please read design-system/MASTER.md.
Also check if design-system/pages/[page-name].md exists.
If the page file exists, prioritize its rules.
If not, use the Master rules exclusively.
Now, generate the code...
``` ```
### Step 3: Supplement with Detailed Searches (as needed) ### Step 3: Supplement with Detailed Searches (as needed)
@@ -154,7 +188,7 @@ python3 .claude/skills/ui-ux-pro-max/scripts/search.py "beauty spa wellness serv
After getting the design system, use domain searches to get additional details: After getting the design system, use domain searches to get additional details:
```bash ```bash
python3 .claude/skills/ui-ux-pro-max/scripts/search.py "<keyword>" --domain <domain> [-n <max_results>] python3 skills/ui-ux-pro-max/scripts/search.py "<keyword>" --domain <domain> [-n <max_results>]
``` ```
**When to use detailed searches:** **When to use detailed searches:**
@@ -172,10 +206,10 @@ python3 .claude/skills/ui-ux-pro-max/scripts/search.py "<keyword>" --domain <dom
Get implementation-specific best practices. If user doesn't specify a stack, **default to `html-tailwind`**. Get implementation-specific best practices. If user doesn't specify a stack, **default to `html-tailwind`**.
```bash ```bash
python3 .claude/skills/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind python3 skills/ui-ux-pro-max/scripts/search.py "<keyword>" --stack html-tailwind
``` ```
Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn` Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`, `react-native`, `flutter`, `shadcn`, `jetpack-compose`
--- ---
@@ -209,6 +243,7 @@ Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`
| `react-native` | Components, Navigation, Lists | | `react-native` | Components, Navigation, Lists |
| `flutter` | Widgets, State, Layout, Theming | | `flutter` | Widgets, State, Layout, Theming |
| `shadcn` | shadcn/ui components, theming, forms, patterns | | `shadcn` | shadcn/ui components, theming, forms, patterns |
| `jetpack-compose` | Composables, Modifiers, State Hoisting, Recomposition |
--- ---
@@ -225,7 +260,7 @@ Available stacks: `html-tailwind`, `react`, `nextjs`, `vue`, `svelte`, `swiftui`
### Step 2: Generate Design System (REQUIRED) ### Step 2: Generate Design System (REQUIRED)
```bash ```bash
python3 .claude/skills/ui-ux-pro-max/scripts/search.py "beauty spa wellness service elegant" --design-system -p "Serenity Spa" python3 skills/ui-ux-pro-max/scripts/search.py "beauty spa wellness service elegant" --design-system -p "Serenity Spa"
``` ```
**Output:** Complete design system with pattern, style, colors, typography, effects, and anti-patterns. **Output:** Complete design system with pattern, style, colors, typography, effects, and anti-patterns.
@@ -234,16 +269,16 @@ python3 .claude/skills/ui-ux-pro-max/scripts/search.py "beauty spa wellness serv
```bash ```bash
# Get UX guidelines for animation and accessibility # Get UX guidelines for animation and accessibility
python3 .claude/skills/ui-ux-pro-max/scripts/search.py "animation accessibility" --domain ux python3 skills/ui-ux-pro-max/scripts/search.py "animation accessibility" --domain ux
# Get alternative typography options if needed # Get alternative typography options if needed
python3 .claude/skills/ui-ux-pro-max/scripts/search.py "elegant luxury serif" --domain typography python3 skills/ui-ux-pro-max/scripts/search.py "elegant luxury serif" --domain typography
``` ```
### Step 4: Stack Guidelines ### Step 4: Stack Guidelines
```bash ```bash
python3 .claude/skills/ui-ux-pro-max/scripts/search.py "layout responsive form" --stack html-tailwind python3 skills/ui-ux-pro-max/scripts/search.py "layout responsive form" --stack html-tailwind
``` ```
**Then:** Synthesize design system + detailed searches and implement the design. **Then:** Synthesize design system + detailed searches and implement the design.
@@ -256,10 +291,10 @@ The `--design-system` flag supports two output formats:
```bash ```bash
# ASCII box (default) - best for terminal display # ASCII box (default) - best for terminal display
python3 .claude/skills/ui-ux-pro-max/scripts/search.py "fintech crypto" --design-system python3 skills/ui-ux-pro-max/scripts/search.py "fintech crypto" --design-system
# Markdown - best for documentation # Markdown - best for documentation
python3 .claude/skills/ui-ux-pro-max/scripts/search.py "fintech crypto" --design-system -f markdown python3 skills/ui-ux-pro-max/scripts/search.py "fintech crypto" --design-system -f markdown
``` ```
--- ---

View File

@@ -0,0 +1,53 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Composable,Pure UI composables,Composable functions should only render UI,Accept state and callbacks,Calling usecase/repo,Pure UI composable,Business logic in UI,High,https://developer.android.com/jetpack/compose/mental-model
2,Composable,Small composables,Each composable has single responsibility,Split into components,Huge composable,Reusable UI,Monolithic UI,Medium,
3,Composable,Stateless by default,Prefer stateless composables,Hoist state,Local mutable state,Stateless UI,Hidden state,High,https://developer.android.com/jetpack/compose/state#state-hoisting
4,State,Single source of truth,UI state comes from one source,StateFlow from VM,Multiple states,Unified UiState,Scattered state,High,https://developer.android.com/topic/architecture/ui-layer
5,State,Model UI State,Use sealed interface/data class,UiState.Loading,Boolean flags,Explicit state,Flag hell,High,
6,State,remember only UI state,remember for UI-only state,"Scroll, animation",Business state,Correct remember,Misuse remember,High,https://developer.android.com/jetpack/compose/state
7,State,rememberSaveable,Persist state across config,rememberSaveable,remember,State survives,State lost,High,https://developer.android.com/jetpack/compose/state#restore-ui-state
8,State,derivedStateOf,Optimize recomposition,derivedStateOf,Recompute always,Optimized,Jank,Medium,https://developer.android.com/jetpack/compose/performance
9,SideEffect,LaunchedEffect keys,Use correct keys,LaunchedEffect(id),LaunchedEffect(Unit),Scoped effect,Infinite loop,High,https://developer.android.com/jetpack/compose/side-effects
10,SideEffect,rememberUpdatedState,Avoid stale lambdas,rememberUpdatedState,Capture directly,Safe callback,Stale state,Medium,https://developer.android.com/jetpack/compose/side-effects
11,SideEffect,DisposableEffect,Clean up resources,onDispose,No cleanup,No leak,Memory leak,High,
12,Architecture,Unidirectional data flow,UI → VM → State,onEvent,Two-way binding,Predictable flow,Hard debug,High,https://developer.android.com/topic/architecture
13,Architecture,No business logic in UI,Logic belongs to VM,Collect state,Call repo,Clean UI,Fat UI,High,
14,Architecture,Expose immutable state,Expose StateFlow,asStateFlow,Mutable exposed,Safe API,State mutation,High,
15,Lifecycle,Lifecycle-aware collect,Use collectAsStateWithLifecycle,Lifecycle aware,collectAsState,No leak,Leak,High,https://developer.android.com/jetpack/compose/lifecycle
16,Navigation,Event-based navigation,VM emits navigation event,"VM: Channel + receiveAsFlow(), V: Collect with Dispatchers.Main.immediate",Nav in UI,Decoupled nav,Using State / SharedFlow for navigation -> event is replayed and navigation fires again (StateFlow),High,https://developer.android.com/jetpack/compose/navigation
17,Navigation,Typed routes,Use sealed routes,sealed class Route,String routes,Type-safe,Runtime crash,Medium,
18,Performance,Stable parameters,Prefer immutable/stable params,@Immutable,Mutable params,Stable recomposition,Extra recomposition,High,https://developer.android.com/jetpack/compose/performance
19,Performance,Use key in Lazy,Provide stable keys,key=id,No key,Stable list,Item jump,High,
20,Performance,Avoid heavy work,No heavy computation in UI,Precompute in VM,Compute in UI,Smooth UI,Jank,High,
21,Performance,Remember expensive objects,remember heavy objects,remember,Recreate each recomposition,Efficient,Wasteful,Medium,
22,Theming,Design system,Centralized theme,Material3 tokens,Hardcoded values,Consistent UI,Inconsistent,High,https://developer.android.com/jetpack/compose/themes
23,Theming,Dark mode support,Theme-based colors,colorScheme,Fixed color,Adaptive UI,Broken dark,Medium,
24,Layout,Prefer Modifier over extra layouts,Use Modifier to adjust layout instead of adding wrapper composables,Use Modifier.padding(),Wrap content with extra Box,Padding via modifier,Box just for padding,High,https://developer.android.com/jetpack/compose/modifiers
25,Layout,Avoid deep layout nesting,Deep layout trees increase measure & layout cost,Keep layout flat,Box ? Column ? Box ? Row,Flat hierarchy,Deep nested tree,High,
26,Layout,Use Row/Column for linear layout,Linear layouts are simpler and more performant,Use Row / Column,Custom layout for simple cases,Row/Column usage,Over-engineered layout,High,
27,Layout,Use Box only for overlapping content,Box should be used only when children overlap,Stack elements,Use Box as Column,Proper overlay,Misused Box,Medium,
28,Layout,Prefer LazyColumn over Column scroll,Lazy layouts are virtualized and efficient,LazyColumn,Column.verticalScroll(),Lazy list,Scrollable Column,High,https://developer.android.com/jetpack/compose/lists
29,Layout,Avoid nested scroll containers,Nested scrolling causes UX & performance issues,Single scroll container,Scroll inside scroll,One scroll per screen,Nested scroll,High,
30,Layout,Avoid fillMaxSize by default,fillMaxSize may break parent constraints,Use exact size,Fill max everywhere,Constraint-aware size,Overfilled layout,Medium,
31,Layout,Avoid intrinsic size unless necessary,Intrinsic measurement is expensive,Explicit sizing,IntrinsicSize.Min,Predictable layout,Expensive measure,High,https://developer.android.com/jetpack/compose/layout/intrinsics
32,Layout,Use Arrangement and Alignment APIs,Declare layout intent explicitly,Use Arrangement / Alignment,Manual spacing hacks,Declarative spacing,Magic spacing,High,
33,Layout,Extract reusable layout patterns,Repeated layouts should be shared,Create layout composable,Copy-paste layouts,Reusable scaffold,Duplicated layout,High,
34,Theming,No hardcoded text style,Use typography,MaterialTheme.typography,Hardcode sp,Scalable,Inconsistent,Medium,
35,Testing,Stateless UI testing,Composable easy to test,Pass state,Hidden state,Testable,Hard test,High,https://developer.android.com/jetpack/compose/testing
36,Testing,Use testTag,Stable UI selectors,Modifier.testTag,Find by text,Stable tests,Flaky tests,Medium,
37,Preview,Multiple previews,Preview multiple states,@Preview,Single preview,Better dev UX,Misleading,Low,https://developer.android.com/jetpack/compose/tooling/preview
38,DI,Inject VM via Hilt,Use hiltViewModel,@HiltViewModel,Manual VM,Clean DI,Coupling,High,https://developer.android.com/training/dependency-injection/hilt-jetpack
39,DI,No DI in UI,Inject in VM,Constructor inject,Inject composable,Proper scope,Wrong scope,High,
40,Accessibility,Content description,Accessible UI,contentDescription,Ignore a11y,Inclusive,A11y fail,Medium,https://developer.android.com/jetpack/compose/accessibility
41,Accessibility,Semantics,Use semantics API,Modifier.semantics,None,Testable a11y,Invisible,Medium,
42,Animation,Compose animation APIs,Use animate*AsState,AnimatedVisibility,Manual anim,Smooth,Jank,Medium,https://developer.android.com/jetpack/compose/animation
43,Animation,Avoid animation logic in VM,Animation is UI concern,Animate in UI,Animate in VM,Correct layering,Mixed concern,Low,
44,Modularization,Feature-based UI modules,UI per feature,:feature:ui,God module,Scalable,Tight coupling,High,https://developer.android.com/topic/modularization
45,Modularization,Public UI contracts,Expose minimal UI API,Interface/Route,Expose impl,Encapsulated,Leaky module,Medium,
46,State,Snapshot state only,Use Compose state,mutableStateOf,Custom observable,Compose aware,Buggy UI,Medium,
47,State,Avoid mutable collections,Immutable list/map,PersistentList,MutableList,Stable UI,Silent bug,High,
48,Lifecycle,RememberCoroutineScope usage,Only for UI jobs,UI coroutine,Long jobs,Scoped job,Leak,Medium,https://developer.android.com/jetpack/compose/side-effects#remembercoroutinescope
49,Interop,Interop View carefully,Use AndroidView,Isolated usage,Mix everywhere,Safe interop,Messy UI,Low,https://developer.android.com/jetpack/compose/interop
50,Interop,Avoid legacy patterns,No LiveData in UI,StateFlow,LiveData,Modern stack,Legacy debt,Medium,
51,Debug,Use layout inspector,Inspect recomposition,Tools,Blind debug,Fast debug,Guessing,Low,https://developer.android.com/studio/debug/layout-inspector
52,Debug,Enable recomposition counts,Track recomposition,Debug flags,Ignore,Performance aware,Hidden jank,Low,
1 No Category Guideline Description Do Don't Code Good Code Bad Severity Docs URL
2 1 Composable Pure UI composables Composable functions should only render UI Accept state and callbacks Calling usecase/repo Pure UI composable Business logic in UI High https://developer.android.com/jetpack/compose/mental-model
3 2 Composable Small composables Each composable has single responsibility Split into components Huge composable Reusable UI Monolithic UI Medium
4 3 Composable Stateless by default Prefer stateless composables Hoist state Local mutable state Stateless UI Hidden state High https://developer.android.com/jetpack/compose/state#state-hoisting
5 4 State Single source of truth UI state comes from one source StateFlow from VM Multiple states Unified UiState Scattered state High https://developer.android.com/topic/architecture/ui-layer
6 5 State Model UI State Use sealed interface/data class UiState.Loading Boolean flags Explicit state Flag hell High
7 6 State remember only UI state remember for UI-only state Scroll, animation Business state Correct remember Misuse remember High https://developer.android.com/jetpack/compose/state
8 7 State rememberSaveable Persist state across config rememberSaveable remember State survives State lost High https://developer.android.com/jetpack/compose/state#restore-ui-state
9 8 State derivedStateOf Optimize recomposition derivedStateOf Recompute always Optimized Jank Medium https://developer.android.com/jetpack/compose/performance
10 9 SideEffect LaunchedEffect keys Use correct keys LaunchedEffect(id) LaunchedEffect(Unit) Scoped effect Infinite loop High https://developer.android.com/jetpack/compose/side-effects
11 10 SideEffect rememberUpdatedState Avoid stale lambdas rememberUpdatedState Capture directly Safe callback Stale state Medium https://developer.android.com/jetpack/compose/side-effects
12 11 SideEffect DisposableEffect Clean up resources onDispose No cleanup No leak Memory leak High
13 12 Architecture Unidirectional data flow UI → VM → State onEvent Two-way binding Predictable flow Hard debug High https://developer.android.com/topic/architecture
14 13 Architecture No business logic in UI Logic belongs to VM Collect state Call repo Clean UI Fat UI High
15 14 Architecture Expose immutable state Expose StateFlow asStateFlow Mutable exposed Safe API State mutation High
16 15 Lifecycle Lifecycle-aware collect Use collectAsStateWithLifecycle Lifecycle aware collectAsState No leak Leak High https://developer.android.com/jetpack/compose/lifecycle
17 16 Navigation Event-based navigation VM emits navigation event VM: Channel + receiveAsFlow(), V: Collect with Dispatchers.Main.immediate Nav in UI Decoupled nav Using State / SharedFlow for navigation -> event is replayed and navigation fires again (StateFlow) High https://developer.android.com/jetpack/compose/navigation
18 17 Navigation Typed routes Use sealed routes sealed class Route String routes Type-safe Runtime crash Medium
19 18 Performance Stable parameters Prefer immutable/stable params @Immutable Mutable params Stable recomposition Extra recomposition High https://developer.android.com/jetpack/compose/performance
20 19 Performance Use key in Lazy Provide stable keys key=id No key Stable list Item jump High
21 20 Performance Avoid heavy work No heavy computation in UI Precompute in VM Compute in UI Smooth UI Jank High
22 21 Performance Remember expensive objects remember heavy objects remember Recreate each recomposition Efficient Wasteful Medium
23 22 Theming Design system Centralized theme Material3 tokens Hardcoded values Consistent UI Inconsistent High https://developer.android.com/jetpack/compose/themes
24 23 Theming Dark mode support Theme-based colors colorScheme Fixed color Adaptive UI Broken dark Medium
25 24 Layout Prefer Modifier over extra layouts Use Modifier to adjust layout instead of adding wrapper composables Use Modifier.padding() Wrap content with extra Box Padding via modifier Box just for padding High https://developer.android.com/jetpack/compose/modifiers
26 25 Layout Avoid deep layout nesting Deep layout trees increase measure & layout cost Keep layout flat Box ? Column ? Box ? Row Flat hierarchy Deep nested tree High
27 26 Layout Use Row/Column for linear layout Linear layouts are simpler and more performant Use Row / Column Custom layout for simple cases Row/Column usage Over-engineered layout High
28 27 Layout Use Box only for overlapping content Box should be used only when children overlap Stack elements Use Box as Column Proper overlay Misused Box Medium
29 28 Layout Prefer LazyColumn over Column scroll Lazy layouts are virtualized and efficient LazyColumn Column.verticalScroll() Lazy list Scrollable Column High https://developer.android.com/jetpack/compose/lists
30 29 Layout Avoid nested scroll containers Nested scrolling causes UX & performance issues Single scroll container Scroll inside scroll One scroll per screen Nested scroll High
31 30 Layout Avoid fillMaxSize by default fillMaxSize may break parent constraints Use exact size Fill max everywhere Constraint-aware size Overfilled layout Medium
32 31 Layout Avoid intrinsic size unless necessary Intrinsic measurement is expensive Explicit sizing IntrinsicSize.Min Predictable layout Expensive measure High https://developer.android.com/jetpack/compose/layout/intrinsics
33 32 Layout Use Arrangement and Alignment APIs Declare layout intent explicitly Use Arrangement / Alignment Manual spacing hacks Declarative spacing Magic spacing High
34 33 Layout Extract reusable layout patterns Repeated layouts should be shared Create layout composable Copy-paste layouts Reusable scaffold Duplicated layout High
35 34 Theming No hardcoded text style Use typography MaterialTheme.typography Hardcode sp Scalable Inconsistent Medium
36 35 Testing Stateless UI testing Composable easy to test Pass state Hidden state Testable Hard test High https://developer.android.com/jetpack/compose/testing
37 36 Testing Use testTag Stable UI selectors Modifier.testTag Find by text Stable tests Flaky tests Medium
38 37 Preview Multiple previews Preview multiple states @Preview Single preview Better dev UX Misleading Low https://developer.android.com/jetpack/compose/tooling/preview
39 38 DI Inject VM via Hilt Use hiltViewModel @HiltViewModel Manual VM Clean DI Coupling High https://developer.android.com/training/dependency-injection/hilt-jetpack
40 39 DI No DI in UI Inject in VM Constructor inject Inject composable Proper scope Wrong scope High
41 40 Accessibility Content description Accessible UI contentDescription Ignore a11y Inclusive A11y fail Medium https://developer.android.com/jetpack/compose/accessibility
42 41 Accessibility Semantics Use semantics API Modifier.semantics None Testable a11y Invisible Medium
43 42 Animation Compose animation APIs Use animate*AsState AnimatedVisibility Manual anim Smooth Jank Medium https://developer.android.com/jetpack/compose/animation
44 43 Animation Avoid animation logic in VM Animation is UI concern Animate in UI Animate in VM Correct layering Mixed concern Low
45 44 Modularization Feature-based UI modules UI per feature :feature:ui God module Scalable Tight coupling High https://developer.android.com/topic/modularization
46 45 Modularization Public UI contracts Expose minimal UI API Interface/Route Expose impl Encapsulated Leaky module Medium
47 46 State Snapshot state only Use Compose state mutableStateOf Custom observable Compose aware Buggy UI Medium
48 47 State Avoid mutable collections Immutable list/map PersistentList MutableList Stable UI Silent bug High
49 48 Lifecycle RememberCoroutineScope usage Only for UI jobs UI coroutine Long jobs Scoped job Leak Medium https://developer.android.com/jetpack/compose/side-effects#remembercoroutinescope
50 49 Interop Interop View carefully Use AndroidView Isolated usage Mix everywhere Safe interop Messy UI Low https://developer.android.com/jetpack/compose/interop
51 50 Interop Avoid legacy patterns No LiveData in UI StateFlow LiveData Modern stack Legacy debt Medium
52 51 Debug Use layout inspector Inspect recomposition Tools Blind debug Fast debug Guessing Low https://developer.android.com/studio/debug/layout-inspector
53 52 Debug Enable recomposition counts Track recomposition Debug flags Ignore Performance aware Hidden jank Low

View File

Can't render this file because it contains an unexpected character in line 6 and column 94.

View File

Can't render this file because it contains an unexpected character in line 8 and column 193.

View File

Can't render this file because it contains an unexpected character in line 4 and column 188.

View File

@@ -83,7 +83,8 @@ STACK_CONFIG = {
"swiftui": {"file": "stacks/swiftui.csv"}, "swiftui": {"file": "stacks/swiftui.csv"},
"react-native": {"file": "stacks/react-native.csv"}, "react-native": {"file": "stacks/react-native.csv"},
"flutter": {"file": "stacks/flutter.csv"}, "flutter": {"file": "stacks/flutter.csv"},
"shadcn": {"file": "stacks/shadcn.csv"} "shadcn": {"file": "stacks/shadcn.csv"},
"jetpack-compose": {"file": "stacks/jetpack-compose.csv"}
} }
# Common columns for all stacks # Common columns for all stacks

File diff suppressed because it is too large Load Diff

View File

@@ -4,14 +4,19 @@
UI/UX Pro Max Search - BM25 search engine for UI/UX style guides UI/UX Pro Max Search - BM25 search engine for UI/UX style guides
Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3] Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3]
python search.py "<query>" --design-system [-p "Project Name"] python search.py "<query>" --design-system [-p "Project Name"]
python search.py "<query>" --design-system --persist [-p "Project Name"] [--page "dashboard"]
Domains: style, prompt, color, chart, landing, product, ux, typography Domains: style, prompt, color, chart, landing, product, ux, typography
Stacks: html-tailwind, react, nextjs Stacks: html-tailwind, react, nextjs
Persistence (Master + Overrides pattern):
--persist Save design system to design-system/MASTER.md
--page Also create a page-specific override file in design-system/pages/
""" """
import argparse import argparse
from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack
from design_system import generate_design_system from design_system import generate_design_system, persist_design_system
def format_output(result): def format_output(result):
@@ -51,13 +56,38 @@ if __name__ == "__main__":
parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation") parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation")
parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output") parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output")
parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system") parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system")
# Persistence (Master + Overrides pattern)
parser.add_argument("--persist", action="store_true", help="Save design system to design-system/MASTER.md (creates hierarchical structure)")
parser.add_argument("--page", type=str, default=None, help="Create page-specific override file in design-system/pages/")
parser.add_argument("--output-dir", "-o", type=str, default=None, help="Output directory for persisted files (default: current directory)")
args = parser.parse_args() args = parser.parse_args()
# Design system takes priority # Design system takes priority
if args.design_system: if args.design_system:
result = generate_design_system(args.query, args.project_name, args.format) result = generate_design_system(
args.query,
args.project_name,
args.format,
persist=args.persist,
page=args.page,
output_dir=args.output_dir
)
print(result) print(result)
# Print persistence confirmation
if args.persist:
project_slug = args.project_name.lower().replace(' ', '-') if args.project_name else "default"
print("\n" + "=" * 60)
print(f"✅ Design system persisted to design-system/{project_slug}/")
print(f" 📄 design-system/{project_slug}/MASTER.md (Global Source of Truth)")
if args.page:
page_filename = args.page.lower().replace(' ', '-')
print(f" 📄 design-system/{project_slug}/pages/{page_filename}.md (Page Overrides)")
print("")
print(f"📖 Usage: When building a page, check design-system/{project_slug}/pages/[page].md first.")
print(f" If exists, its rules override MASTER.md. Otherwise, use MASTER.md.")
print("=" * 60)
# Stack search # Stack search
elif args.stack: elif args.stack:
result = search_stack(args.query, args.stack, args.max_results) result = search_stack(args.query, args.stack, args.max_results)