mirror of
https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git
synced 2026-07-21 21:47:45 +00:00
Merge pull request #402 from Abraham040203/feat/motion-domain-design-dials
Add motion domain and design dials (variance/motion/density)
This commit is contained in:
commit
bf5c3cfb61
@ -413,6 +413,29 @@ If not, use the Master rules exclusively.
|
||||
Now, generate the code...
|
||||
```
|
||||
|
||||
### Step 2c: Design Dials (optional)
|
||||
|
||||
Three optional 1-10 sliders that tune `--design-system` output without changing your query. Add any combination of them to the same command:
|
||||
|
||||
```bash
|
||||
python3 skills/ui-ux-pro-max/scripts/search.py "<query>" --design-system --variance <1-10> --motion <1-10> --density <1-10>
|
||||
```
|
||||
|
||||
| Dial | Low (1-3) | Mid (4-7) | High (8-10) |
|
||||
|------|-----------|-----------|-------------|
|
||||
| `--variance` | Centered / minimal (biases toward Minimalism-style categories) | Balanced / modern | Bold / asymmetric (biases toward Brutalism, Bento Grids) |
|
||||
| `--motion` | Subtle micro-interactions | Standard scroll/stagger motion | Complex choreography (pin, Flip, SplitText) |
|
||||
| `--density` | Spacious (24-96px spacing scale) | Standard (16-64px, current default) | Dense/dashboard (8-32px spacing scale) |
|
||||
|
||||
- `--motion` attaches a ready-to-use GSAP snippet (with framework notes, Do/Don't, and performance notes) pulled from `--domain gsap`, matched to the resolved tier (Subtle/Standard/Complex).
|
||||
- `--density` overrides the `--space-*` CSS variable table in the ASCII/markdown/MASTER.md output — use it for dashboards (high) vs. marketing pages (low) without hand-editing tokens.
|
||||
- Leaving a dial unset keeps that part of the output exactly as it was before (no behavior change).
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
python3 skills/ui-ux-pro-max/scripts/search.py "internal analytics dashboard" --design-system --variance 8 --motion 7 --density 8 -p "Ops Console"
|
||||
```
|
||||
|
||||
### Step 3: Supplement with Detailed Searches (as needed)
|
||||
|
||||
After getting the design system, use domain searches to get additional details:
|
||||
@ -463,6 +486,7 @@ python3 skills/ui-ux-pro-max/scripts/search.py "<keyword>" --stack <your-stack>
|
||||
| `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 |
|
||||
| `gsap` | GSAP animation skeletons by intensity tier | scroll reveal, stagger, magnetic cursor, page transition |
|
||||
| `google-fonts` | Individual Google Fonts lookup | sans serif, monospace, japanese, variable font, popular |
|
||||
| `react` | React/Next.js performance | waterfall, bundle, suspense, memo, rerender, cache |
|
||||
| `web` | App interface guidelines (iOS/Android/React Native) | accessibilityLabel, touch targets, safe areas, Dynamic Type |
|
||||
|
||||
17
.claude/skills/ui-ux-pro-max/data/motion.csv
Normal file
17
.claude/skills/ui-ux-pro-max/data/motion.csv
Normal file
@ -0,0 +1,17 @@
|
||||
No,Category,Intensity Tier,Keywords,Trigger,Duration,Easing,GSAP Snippet,Framework Notes,Do,Don't,Performance Notes
|
||||
1,Hover Micro-interaction,Subtle,"hover, button, opacity, lift, press feedback",hover,150-200ms,power1.out,"gsap.to(el, { y: -1, opacity: 0.9, duration: 0.15, ease: 'power1.out' });",Bind on mouseenter/mouseleave; in React wrap in a ref + useEffect (or onMouseEnter/onMouseLeave props directly calling gsap.to),Keep displacement under 2px so it reads as feedback not motion,Don't animate layout-affecting props (width/height/margin) on hover,Runs on transform/opacity only so it stays on the compositor thread
|
||||
2,Hover Micro-interaction,Standard,"hover, card, scale, tilt, cursor feedback",hover,200-300ms,power2.out,"gsap.to(el, { y: -4, scale: 1.02, boxShadow: '0 12px 24px rgba(0,0,0,0.12)', duration: 0.25, ease: 'power2.out' });","Use gsap.quickTo(el, 'y') for cards with many hover targets to avoid re-creating tweens every event",Pair with a matching mouseleave tween that reverses the same properties,Don't leave the hover state stuck if the pointer leaves fast; always attach the reverse tween,quickTo() avoids GC churn on lists with 20+ hoverable cards
|
||||
3,Hover Micro-interaction,Complex,"hover, magnetic, cursor follow, 3d tilt",hover + mousemove,300-500ms,"elastic.out(1,0.4)","const xTo = gsap.quickTo(el, 'x', { duration: 0.4, ease: 'elastic.out(1,0.4)' }); const yTo = gsap.quickTo(el, 'y', { duration: 0.4, ease: 'elastic.out(1,0.4)' }); el.addEventListener('mousemove', (e) => { const r = el.getBoundingClientRect(); xTo((e.clientX - r.left - r.width/2) * 0.3); yTo((e.clientY - r.top - r.height/2) * 0.3); });",Debounce is not needed since quickTo interpolates; remove listeners on component unmount in React/Vue to avoid leaks,Clamp the pull strength (e.g. * 0.3) so the element never fully leaves its hit box,Don't apply magnetic effect to more than 1-2 focal elements per screen; it becomes noisy,Use will-change: transform on the target element for smoother compositing
|
||||
4,Scroll Reveal,Subtle,"scroll, fade in, reveal, on view",scroll (viewport enter),300-400ms,power1.out,"gsap.from(el, { opacity: 0, y: 12, duration: 0.35, ease: 'power1.out', scrollTrigger: { trigger: el, start: 'top 90%', toggleActions: 'play none none reverse' } });",Requires the ScrollTrigger plugin registered once via gsap.registerPlugin(ScrollTrigger),"Keep the y offset small (8-16px) so it reads as a fade, not a slide",Don't reveal below-the-fold content needed for SEO/crawlers as invisible-by-default without a no-JS fallback,toggleActions 'play none none reverse' avoids re-triggering on every scroll direction change
|
||||
5,Scroll Reveal,Standard,"scroll, slide up, staggered section, reveal",scroll (viewport enter),400-600ms,power2.out,"gsap.from(el.children, { opacity: 0, y: 24, duration: 0.5, stagger: 0.08, ease: 'power2.out', scrollTrigger: { trigger: el, start: 'top 85%' } });","In React use useGSAP(() => {...}, { scope: containerRef }) from @gsap/react to auto-cleanup on unmount",Scope the ScrollTrigger to the section container so it doesn't re-scan the whole page,Don't stagger more than ~8 children; beyond that the last items feel laggy,Set scroller/markers: false in production; markers is dev-only
|
||||
6,Scroll Reveal,Complex,"scroll, pin, scrub, storytelling, scrollytelling",scroll (continuous scrub),tied to scroll position,none (scrub-driven),"gsap.timeline({ scrollTrigger: { trigger: section, start: 'top top', end: '+=150%', scrub: 1, pin: true } }).from('.headline', { opacity: 0, y: 40 }).to('.bg-layer', { yPercent: -20 }, '<');",Pinning needs the section to have deterministic height; recalc ScrollTrigger.refresh() after images/fonts load,Use scrub: true or a small number (0.5-1.5) instead of instant jumps so it feels tied to the scrollbar,Don't pin more than 1-2 sections per page; excessive pinning fights native scroll feel and hurts mobile UX,"Pinning forces layout reflow; test on mid-tier mobile devices, not just desktop"
|
||||
7,Stagger List,Subtle,"list, stagger, cards, grid entrance",load or scroll,250-350ms,power1.out,"gsap.from('.list-item', { opacity: 0, y: 8, duration: 0.3, stagger: 0.03 });",Select items with a stable class/data-attribute (not array index) so re-renders in React don't break targeting,Keep per-item stagger delay small (0.02-0.04s) for lists longer than 10 items,Don't stagger by more than 0.1s per item on long lists; total reveal time becomes sluggish,"For virtualized lists, only animate items currently mounted in the DOM"
|
||||
8,Stagger List,Standard,"grid, bento, cards, staggered scale",load or scroll,300-450ms,back.out(1.4),"gsap.from('.grid-item', { opacity: 0, scale: 0.92, y: 16, duration: 0.4, stagger: { each: 0.06, from: 'start', grid: 'auto' }, ease: 'back.out(1.4)' });",grid: 'auto' lets GSAP infer rows/columns from a CSS grid layout for a natural wave stagger,Combine with from: 'center' for a bento-grid layout to draw the eye inward first,Don't use back.out on dense data tables; the overshoot reads as sloppy on informational UI,Group DOM writes; avoid interleaving layout reads (getBoundingClientRect) between staggered tweens
|
||||
9,Stagger List,Complex,"stagger, wave, text reveal, split text",load or scroll,400-700ms,expo.out,"const split = new SplitText(headline, { type: 'chars' }); gsap.from(split.chars, { opacity: 0, y: 20, rotateX: -40, duration: 0.6, stagger: 0.015, ease: 'expo.out' });",SplitText is a GSAP Club/paid plugin; confirm license before shipping and provide a plain fade fallback if unavailable,Revert SplitText on unmount/cleanup (split.revert()) to restore original text nodes for accessibility tools,Don't split-animate long paragraphs; reserve for short headlines (under ~8 words),Splitting text creates one element per character; keep it to headline-length copy only for DOM size
|
||||
10,Page Transition,Subtle,"route change, fade, page transition",route change,200-300ms,power1.inOut,"gsap.to(main, { opacity: 0, duration: 0.2, onComplete: () => { navigate(); gsap.fromTo(main, { opacity: 0 }, { opacity: 1, duration: 0.2 }); } });","Pair with the router's transition hooks (Next.js App Router transitions, React Router's useNavigate, Vue Router's beforeEach/afterEach)",Preload the destination route's critical assets before the exit tween finishes,Don't block navigation on animation; cap exit duration at ~250ms so the app never feels unresponsive,Exit animation should always resolve faster than entrance (asymmetric timing) so back/forward feels snappy
|
||||
11,Page Transition,Standard,"route change, slide, overlay wipe",route change,400-600ms,power2.inOut,"const tl = gsap.timeline(); tl.to('.transition-overlay', { yPercent: 0, duration: 0.4, ease: 'power2.inOut' }).call(navigate).to('.transition-overlay', { yPercent: -100, duration: 0.4, ease: 'power2.inOut', delay: 0.1 });",Keep the overlay element mounted at the layout root (outside the page component) so it survives the route swap,Show a lightweight loading indicator if the destination route's data fetch outlasts the overlay,Don't tie the overlay's reveal directly to data-fetch completion without a max-wait timeout; a slow API stalls the whole transition,Prefer CSS transform (yPercent) over top/left to keep the overlay animation on the compositor thread
|
||||
12,Page Transition,Complex,"shared element, morph, hero transition",route change,500-800ms,expo.inOut,"const state = Flip.getState('.hero-image'); navigate(); Flip.from(state, { duration: 0.6, ease: 'expo.inOut', absolute: true, zIndex: 100 });",Requires the GSAP Flip plugin; the 'from' and 'to' route must render the same element with a shared data-flip-id,Verify the shared element exists in both DOM states before calling Flip.from to avoid a silent no-op,Don't use shared-element transitions across more than one element pair per navigation; compounding Flips are hard to time correctly,Flip recalculates layout (FLIP technique) so test on low-end devices for jank
|
||||
13,Parallax Scroll,Subtle,"parallax, background, depth, scroll speed",scroll (continuous),tied to scroll position,linear (scrub),"gsap.to('.bg-layer', { yPercent: 10, ease: 'none', scrollTrigger: { trigger: section, scrub: true } });","Apply parallax to background/decorative layers only, never to text or interactive controls",Keep the yPercent delta small (5-15) so foreground and background never desync distractingly,Don't parallax body copy; it hurts reading comfort and can trigger motion sickness,will-change: transform on the parallax layer only; remove it after scroll settles to free GPU memory
|
||||
14,Parallax Scroll,Standard,"multi-layer parallax, depth, hero background",scroll (continuous),tied to scroll position,linear (scrub),"gsap.utils.toArray('.parallax-layer').forEach((layer, i) => { gsap.to(layer, { yPercent: (i + 1) * -8, ease: 'none', scrollTrigger: { trigger: layer.parentElement, scrub: 0.5 } }); });",Layer count beyond 3-4 has diminishing visual return and multiplies scroll-listener cost,"Vary speed per layer (background slowest, foreground fastest) to sell the depth illusion",Don't let parallax layers overflow their container; clip with overflow: hidden on the wrapper,Batch all layers under one ScrollTrigger container where possible instead of one per layer
|
||||
15,Loading / Skeleton,Subtle,"loading, skeleton, shimmer, pulse",on mount / async wait,1200-1600ms loop,sine.inOut,"gsap.to('.skeleton', { backgroundPosition: '200% 0', duration: 1.4, ease: 'sine.inOut', repeat: -1 });",Kill the loop tween (tween.kill()) as soon as real content mounts to avoid orphaned repeating animations,Use a CSS gradient background-position sweep rather than opacity pulsing; reads as 'loading' more clearly,Don't run more than one shimmer loop per skeleton group; sync them under one timeline so the wave reads as a single unit,repeat: -1 tweens are cheap but must be explicitly killed on unmount or they leak in SPA route changes
|
||||
16,Loading / Skeleton,Standard,"progress, spinner, morphing loader",on mount / async wait,800-1200ms loop,power1.inOut,"gsap.timeline({ repeat: -1 }).to('.loader-dot', { y: -8, duration: 0.4, stagger: { each: 0.15, yoyo: true, repeat: 1 } });",Wrap the whole loop timeline in useGSAP with { revertOnUpdate: false } in React so it isn't rebuilt every render,Cap total loop duration under ~1.5s so long waits don't feel like the UI froze on a single beat,Don't use elaborate loaders for sub-300ms waits; they flash and feel worse than no indicator,Pause the timeline (tl.pause()) when the loading tab/view is not visible to save CPU on background tabs
|
||||
|
@ -55,6 +55,11 @@ CSV_CONFIG = {
|
||||
"search_cols": ["Category", "Icon Name", "Keywords", "Best For"],
|
||||
"output_cols": ["Category", "Icon Name", "Keywords", "Library", "Import Code", "Usage", "Best For", "Style"]
|
||||
},
|
||||
"gsap": {
|
||||
"file": "motion.csv",
|
||||
"search_cols": ["Category", "Intensity Tier", "Keywords", "Trigger"],
|
||||
"output_cols": ["Category", "Intensity Tier", "Trigger", "Duration", "Easing", "GSAP Snippet", "Framework Notes", "Do", "Don't", "Performance Notes"]
|
||||
},
|
||||
"react": {
|
||||
"file": "react-performance.csv",
|
||||
"search_cols": ["Category", "Issue", "Keywords", "Description"],
|
||||
@ -89,6 +94,12 @@ STACK_CONFIG = {
|
||||
"threejs": {"file": "stacks/threejs.csv"},
|
||||
"angular": {"file": "stacks/angular.csv"},
|
||||
"laravel": {"file": "stacks/laravel.csv"},
|
||||
"javafx": {"file": "stacks/javafx.csv"},
|
||||
"wpf": {"file": "stacks/wpf.csv"},
|
||||
"winui": {"file": "stacks/winui.csv"},
|
||||
"avalonia": {"file": "stacks/avalonia.csv"},
|
||||
"uno": {"file": "stacks/uno.csv"},
|
||||
"uwp": {"file": "stacks/uwp.csv"},
|
||||
}
|
||||
|
||||
# Common columns for all stacks
|
||||
@ -117,7 +128,7 @@ class BM25:
|
||||
def tokenize(self, text):
|
||||
"""Lowercase, split, remove punctuation, filter short words"""
|
||||
text = re.sub(r'[^\w\s]', ' ', str(text).lower())
|
||||
return [w for w in text.split() if len(w) > 2]
|
||||
return [w for w in text.split() if len(w) >= 2]
|
||||
|
||||
def fit(self, documents):
|
||||
"""Build BM25 index from documents"""
|
||||
@ -209,6 +220,7 @@ def detect_domain(query):
|
||||
"typography": ["font pairing", "typography pairing", "heading font", "body font"],
|
||||
"google-fonts": ["google font", "font family", "font weight", "font style", "variable font", "noto", "font for", "find font", "font subset", "font language", "monospace font", "serif font", "sans serif font", "display font", "handwriting font", "font", "typography", "serif", "sans"],
|
||||
"icons": ["icon", "icons", "lucide", "heroicons", "symbol", "glyph", "pictogram", "svg icon"],
|
||||
"gsap": ["gsap", "quickto", "scrolltrigger", "stagger", "magnetic cursor", "parallax", "page transition", "scroll reveal", "scroll-triggered", "scrollytelling", "flip plugin", "splittext", "shimmer", "skeleton loader"],
|
||||
"react": ["react", "next.js", "nextjs", "suspense", "memo", "usecallback", "useeffect", "rerender", "bundle", "waterfall", "barrel", "dynamic import", "rsc", "server component"],
|
||||
"web": ["aria", "focus", "outline", "semantic", "virtualize", "autocomplete", "form", "input type", "preconnect"]
|
||||
}
|
||||
|
||||
@ -16,10 +16,18 @@ Usage:
|
||||
import csv
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import io
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from core import search, DATA_DIR
|
||||
|
||||
# Force UTF-8 for stdout/stderr to handle emojis/box-drawing chars on Windows (cp1252 default)
|
||||
if sys.stdout.encoding and sys.stdout.encoding.lower() != 'utf-8':
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||||
if sys.stderr.encoding and sys.stderr.encoding.lower() != 'utf-8':
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
|
||||
|
||||
|
||||
# ============ CONFIGURATION ============
|
||||
REASONING_FILE = "ui-reasoning.csv"
|
||||
@ -32,6 +40,39 @@ SEARCH_CONFIG = {
|
||||
"typography": {"max_results": 2}
|
||||
}
|
||||
|
||||
# ============ DESIGN DIALS (1-10) ============
|
||||
# Inspired by taste-skill's DESIGN_VARIANCE / MOTION_INTENSITY / VISUAL_DENSITY
|
||||
# knobs: three optional 1-10 sliders that bias the existing query-based search
|
||||
# instead of replacing it. Each dial buckets into a low/mid/high tier.
|
||||
DIAL_TIERS = {
|
||||
"variance": [
|
||||
(1, 3, {"label": "Centered / Minimal", "style_keywords": ["Minimalism", "Exaggerated Minimalism", "centered", "symmetric", "grid-based"]}),
|
||||
(4, 7, {"label": "Balanced / Modern", "style_keywords": ["modern", "structured", "balanced"]}),
|
||||
(8, 10, {"label": "Bold / Asymmetric", "style_keywords": ["Brutalism", "Bento Grids", "asymmetric", "experimental"]}),
|
||||
],
|
||||
"motion": [
|
||||
(1, 3, {"label": "Subtle", "tier": "Subtle"}),
|
||||
(4, 7, {"label": "Standard", "tier": "Standard"}),
|
||||
(8, 10, {"label": "Complex", "tier": "Complex"}),
|
||||
],
|
||||
"density": [
|
||||
(1, 3, {"label": "Spacious", "spacing": {"xs": "4px", "sm": "8px", "md": "24px", "lg": "32px", "xl": "48px", "2xl": "64px", "3xl": "96px"}}),
|
||||
(4, 7, {"label": "Standard", "spacing": {"xs": "4px", "sm": "8px", "md": "16px", "lg": "24px", "xl": "32px", "2xl": "48px", "3xl": "64px"}}),
|
||||
(8, 10, {"label": "Dense / Dashboard", "spacing": {"xs": "2px", "sm": "4px", "md": "8px", "lg": "12px", "xl": "16px", "2xl": "24px", "3xl": "32px"}}),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _resolve_dial(dial_name: str, value) -> dict:
|
||||
"""Bucket a 1-10 dial value into its tier config. Returns None if value is None."""
|
||||
if value is None:
|
||||
return None
|
||||
value = max(1, min(10, int(value)))
|
||||
for lo, hi, info in DIAL_TIERS[dial_name]:
|
||||
if lo <= value <= hi:
|
||||
return {**info, "value": value}
|
||||
return None
|
||||
|
||||
|
||||
# ============ DESIGN SYSTEM GENERATOR ============
|
||||
class DesignSystemGenerator:
|
||||
@ -160,8 +201,18 @@ class DesignSystemGenerator:
|
||||
"""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."""
|
||||
def generate(self, query: str, project_name: str = None,
|
||||
variance: int = None, motion: int = None, density: int = None) -> dict:
|
||||
"""Generate complete design system recommendation.
|
||||
|
||||
variance/motion/density are optional 1-10 dials (see DIAL_TIERS) that bias
|
||||
style selection, pull in a matching motion.csv snippet, and override the
|
||||
spacing scale, without changing behavior when left unset.
|
||||
"""
|
||||
variance_info = _resolve_dial("variance", variance)
|
||||
motion_info = _resolve_dial("motion", motion)
|
||||
density_info = _resolve_dial("density", density)
|
||||
|
||||
# Step 1: First search product to get category
|
||||
product_result = search(query, "product", 1)
|
||||
product_results = product_result.get("results", [])
|
||||
@ -173,8 +224,14 @@ class DesignSystemGenerator:
|
||||
reasoning = self._apply_reasoning(category, {})
|
||||
style_priority = reasoning.get("style_priority", [])
|
||||
|
||||
# DESIGN_VARIANCE dial: bias style retrieval/selection toward
|
||||
# centered-minimal (low) or bold-asymmetric (high) keywords.
|
||||
effective_style_priority = style_priority
|
||||
if variance_info:
|
||||
effective_style_priority = variance_info["style_keywords"] + style_priority
|
||||
|
||||
# Step 3: Multi-domain search with style priority hints
|
||||
search_results = self._multi_domain_search(query, style_priority)
|
||||
search_results = self._multi_domain_search(query, effective_style_priority)
|
||||
search_results["product"] = product_result # Reuse product search
|
||||
|
||||
# Step 4: Select best matches from each domain using priority
|
||||
@ -183,11 +240,24 @@ class DesignSystemGenerator:
|
||||
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_style = self._select_best_match(style_results, effective_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 {}
|
||||
|
||||
# MOTION_INTENSITY dial: pull a matching GSAP skeleton from motion.csv
|
||||
# (domain key is "gsap", not "motion" - PR #296 already owns the "motion"
|
||||
# domain for Emil Kowalski's motion-design principles, motion-principles.csv).
|
||||
motion_snippet = {}
|
||||
if motion_info:
|
||||
motion_result = search(f"{query} {motion_info['tier']}", "gsap", 5)
|
||||
motion_matches = motion_result.get("results", [])
|
||||
tiered = [m for m in motion_matches if m.get("Intensity Tier") == motion_info["tier"]]
|
||||
if tiered:
|
||||
motion_snippet = tiered[0]
|
||||
elif motion_matches:
|
||||
motion_snippet = motion_matches[0]
|
||||
|
||||
# Step 5: Build final recommendation
|
||||
# Combine effects from both reasoning and style search
|
||||
style_effects = best_style.get("Effects & Animation", "")
|
||||
@ -242,7 +312,17 @@ class DesignSystemGenerator:
|
||||
"key_effects": combined_effects,
|
||||
"anti_patterns": reasoning.get("anti_patterns", ""),
|
||||
"decision_rules": reasoning.get("decision_rules", {}),
|
||||
"severity": reasoning.get("severity", "MEDIUM")
|
||||
"severity": reasoning.get("severity", "MEDIUM"),
|
||||
"dials": {
|
||||
"variance": variance_info["value"] if variance_info else None,
|
||||
"variance_label": variance_info["label"] if variance_info else None,
|
||||
"motion": motion_info["value"] if motion_info else None,
|
||||
"motion_label": motion_info["label"] if motion_info else None,
|
||||
"density": density_info["value"] if density_info else None,
|
||||
"density_label": density_info["label"] if density_info else None,
|
||||
},
|
||||
"motion_snippet": motion_snippet,
|
||||
"spacing_scale": density_info["spacing"] if density_info else None,
|
||||
}
|
||||
|
||||
|
||||
@ -288,6 +368,8 @@ def format_ascii_box(design_system: dict) -> str:
|
||||
typography = design_system.get("typography", {})
|
||||
effects = design_system.get("key_effects", "")
|
||||
anti_patterns = design_system.get("anti_patterns", "")
|
||||
dials = design_system.get("dials", {})
|
||||
motion_snippet = design_system.get("motion_snippet", {})
|
||||
|
||||
def wrap_text(text: str, prefix: str, width: int) -> list:
|
||||
"""Wrap long text into multiple lines."""
|
||||
@ -321,6 +403,16 @@ def format_ascii_box(design_system: dict) -> str:
|
||||
lines.append("╚" + "═" * w + "╝")
|
||||
lines.append("┌" + "─" * w + "┐")
|
||||
|
||||
# Design Dials section (only if at least one dial was set)
|
||||
if any(dials.get(k) is not None for k in ("variance", "motion", "density")):
|
||||
lines.append(section_header("DESIGN DIALS", BOX_WIDTH + 1))
|
||||
if dials.get("variance") is not None:
|
||||
lines.append(f"│ Variance: {dials['variance']}/10 — {dials['variance_label']}".ljust(BOX_WIDTH) + "│")
|
||||
if dials.get("motion") is not None:
|
||||
lines.append(f"│ Motion: {dials['motion']}/10 — {dials['motion_label']}".ljust(BOX_WIDTH) + "│")
|
||||
if dials.get("density") is not None:
|
||||
lines.append(f"│ Density: {dials['density']}/10 — {dials['density_label']}".ljust(BOX_WIDTH) + "│")
|
||||
|
||||
# Pattern section
|
||||
lines.append(section_header("PATTERN", BOX_WIDTH + 1))
|
||||
lines.append(f"│ Name: {pattern.get('name', '')}".ljust(BOX_WIDTH) + "│")
|
||||
@ -394,6 +486,17 @@ def format_ascii_box(design_system: dict) -> str:
|
||||
for line in wrap_text(effects, "│ ", BOX_WIDTH):
|
||||
lines.append(line.ljust(BOX_WIDTH) + "│")
|
||||
|
||||
# Motion section (GSAP skeleton, only if --motion dial was set)
|
||||
if motion_snippet:
|
||||
lines.append(section_header("MOTION", BOX_WIDTH + 1))
|
||||
lines.append(f"│ {motion_snippet.get('Category', '')} ({motion_snippet.get('Intensity Tier', '')})".ljust(BOX_WIDTH) + "│")
|
||||
lines.append(f"│ Trigger: {motion_snippet.get('Trigger', '')} | Duration: {motion_snippet.get('Duration', '')} | Easing: {motion_snippet.get('Easing', '')}".ljust(BOX_WIDTH) + "│")
|
||||
for line in wrap_text(f"GSAP: {motion_snippet.get('GSAP Snippet', '')}", "│ ", BOX_WIDTH):
|
||||
lines.append(line.ljust(BOX_WIDTH) + "│")
|
||||
if motion_snippet.get("Framework Notes"):
|
||||
for line in wrap_text(f"Framework: {motion_snippet.get('Framework Notes', '')}", "│ ", BOX_WIDTH):
|
||||
lines.append(line.ljust(BOX_WIDTH) + "│")
|
||||
|
||||
# Anti-patterns section
|
||||
if anti_patterns:
|
||||
lines.append(section_header("AVOID", BOX_WIDTH + 1))
|
||||
@ -428,11 +531,24 @@ def format_markdown(design_system: dict) -> str:
|
||||
typography = design_system.get("typography", {})
|
||||
effects = design_system.get("key_effects", "")
|
||||
anti_patterns = design_system.get("anti_patterns", "")
|
||||
dials = design_system.get("dials", {})
|
||||
motion_snippet = design_system.get("motion_snippet", {})
|
||||
|
||||
lines = []
|
||||
lines.append(f"## Design System: {project}")
|
||||
lines.append("")
|
||||
|
||||
# Design Dials section (only if at least one dial was set)
|
||||
if any(dials.get(k) is not None for k in ("variance", "motion", "density")):
|
||||
lines.append("### Design Dials")
|
||||
if dials.get("variance") is not None:
|
||||
lines.append(f"- **Variance:** {dials['variance']}/10 — {dials['variance_label']}")
|
||||
if dials.get("motion") is not None:
|
||||
lines.append(f"- **Motion:** {dials['motion']}/10 — {dials['motion_label']}")
|
||||
if dials.get("density") is not None:
|
||||
lines.append(f"- **Density:** {dials['density']}/10 — {dials['density_label']}")
|
||||
lines.append("")
|
||||
|
||||
# Pattern section
|
||||
lines.append("### Pattern")
|
||||
lines.append(f"- **Name:** {pattern.get('name', '')}")
|
||||
@ -507,6 +623,23 @@ def format_markdown(design_system: dict) -> str:
|
||||
lines.append(f"{effects}")
|
||||
lines.append("")
|
||||
|
||||
# Motion section (GSAP skeleton, only if --motion dial was set)
|
||||
if motion_snippet:
|
||||
lines.append("### Motion")
|
||||
lines.append(f"**{motion_snippet.get('Category', '')}** ({motion_snippet.get('Intensity Tier', '')}) — Trigger: {motion_snippet.get('Trigger', '')} | Duration: {motion_snippet.get('Duration', '')} | Easing: `{motion_snippet.get('Easing', '')}`")
|
||||
lines.append("```js")
|
||||
lines.append(motion_snippet.get("GSAP Snippet", ""))
|
||||
lines.append("```")
|
||||
if motion_snippet.get("Framework Notes"):
|
||||
lines.append(f"*Framework notes: {motion_snippet.get('Framework Notes', '')}*")
|
||||
motion_do = motion_snippet.get("Do", "")
|
||||
motion_dont = motion_snippet.get("Don't", "")
|
||||
if motion_do:
|
||||
lines.append(f"- ✅ {motion_do}")
|
||||
if motion_dont:
|
||||
lines.append(f"- ❌ {motion_dont}")
|
||||
lines.append("")
|
||||
|
||||
# Anti-patterns section
|
||||
if anti_patterns:
|
||||
lines.append("### Avoid (Anti-patterns)")
|
||||
@ -529,8 +662,9 @@ def format_markdown(design_system: dict) -> str:
|
||||
|
||||
|
||||
# ============ MAIN ENTRY POINT ============
|
||||
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:
|
||||
def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii",
|
||||
persist: bool = False, page: str = None, output_dir: str = None,
|
||||
variance: int = None, motion: int = None, density: int = None) -> str:
|
||||
"""
|
||||
Main entry point for design system generation.
|
||||
|
||||
@ -541,13 +675,16 @@ def generate_design_system(query: str, project_name: str = None, output_format:
|
||||
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)
|
||||
variance: Optional 1-10 DESIGN_VARIANCE dial (1=centered/minimal, 10=bold/asymmetric)
|
||||
motion: Optional 1-10 MOTION_INTENSITY dial, pulls a matching GSAP snippet from motion.csv
|
||||
density: Optional 1-10 VISUAL_DENSITY dial, overrides the spacing scale (1=spacious, 10=dense)
|
||||
|
||||
Returns:
|
||||
Formatted design system string
|
||||
"""
|
||||
generator = DesignSystemGenerator()
|
||||
design_system = generator.generate(query, project_name)
|
||||
|
||||
design_system = generator.generate(query, project_name, variance=variance, motion=motion, density=density)
|
||||
|
||||
# Persist to files if requested
|
||||
if persist:
|
||||
persist_design_system(design_system, page, output_dir, query)
|
||||
@ -573,8 +710,9 @@ def persist_design_system(design_system: dict, page: str = None, output_dir: str
|
||||
"""
|
||||
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")
|
||||
# Use project name for project-specific folder. Coalesce falsy values
|
||||
# (missing key, explicit None, or "") so the .lower() below can't crash.
|
||||
project_name = design_system.get("project_name") or "default"
|
||||
project_slug = project_name.lower().replace(' ', '-')
|
||||
|
||||
design_system_dir = base_dir / "design-system" / project_slug
|
||||
@ -618,11 +756,14 @@ def format_master_md(design_system: dict) -> str:
|
||||
typography = design_system.get("typography", {})
|
||||
effects = design_system.get("key_effects", "")
|
||||
anti_patterns = design_system.get("anti_patterns", "")
|
||||
|
||||
dials = design_system.get("dials", {})
|
||||
motion_snippet = design_system.get("motion_snippet", {})
|
||||
spacing_scale = design_system.get("spacing_scale")
|
||||
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
|
||||
lines = []
|
||||
|
||||
|
||||
# Logic header
|
||||
lines.append("# Design System Master File")
|
||||
lines.append("")
|
||||
@ -635,6 +776,15 @@ def format_master_md(design_system: dict) -> str:
|
||||
lines.append(f"**Project:** {project}")
|
||||
lines.append(f"**Generated:** {timestamp}")
|
||||
lines.append(f"**Category:** {design_system.get('category', 'General')}")
|
||||
if any(dials.get(k) is not None for k in ("variance", "motion", "density")):
|
||||
dial_parts = []
|
||||
if dials.get("variance") is not None:
|
||||
dial_parts.append(f"Variance {dials['variance']}/10 ({dials['variance_label']})")
|
||||
if dials.get("motion") is not None:
|
||||
dial_parts.append(f"Motion {dials['motion']}/10 ({dials['motion_label']})")
|
||||
if dials.get("density") is not None:
|
||||
dial_parts.append(f"Density {dials['density']}/10 ({dials['density_label']})")
|
||||
lines.append(f"**Design Dials:** {' | '.join(dial_parts)}")
|
||||
lines.append("")
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
@ -686,18 +836,24 @@ def format_master_md(design_system: dict) -> str:
|
||||
lines.append("```")
|
||||
lines.append("")
|
||||
|
||||
# Spacing Variables
|
||||
# Spacing Variables (overridden by the VISUAL_DENSITY dial when set)
|
||||
default_spacing = DIAL_TIERS["density"][1][2]["spacing"] # mid-tier = the historical defaults
|
||||
scale = spacing_scale or default_spacing
|
||||
spacing_usage = {
|
||||
"xs": "Tight gaps", "sm": "Icon gaps, inline spacing", "md": "Standard padding",
|
||||
"lg": "Section padding", "xl": "Large gaps", "2xl": "Section margins", "3xl": "Hero padding",
|
||||
}
|
||||
lines.append("### Spacing Variables")
|
||||
lines.append("")
|
||||
if spacing_scale:
|
||||
lines.append(f"*Density: {dials.get('density')}/10 — {dials.get('density_label')}*")
|
||||
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 |")
|
||||
for token in ("xs", "sm", "md", "lg", "xl", "2xl", "3xl"):
|
||||
px_value = scale[token]
|
||||
rem_value = f"{int(px_value.rstrip('px')) / 16:g}rem"
|
||||
lines.append(f"| `--space-{token}` | `{px_value}` / `{rem_value}` | {spacing_usage[token]} |")
|
||||
lines.append("")
|
||||
|
||||
# Shadow Depths
|
||||
@ -839,7 +995,32 @@ def format_master_md(design_system: dict) -> str:
|
||||
lines.append(f"- **CTA Placement:** {pattern.get('cta_placement', '')}")
|
||||
lines.append(f"- **Section Order:** {pattern.get('sections', '')}")
|
||||
lines.append("")
|
||||
|
||||
|
||||
# Motion section (GSAP skeleton, only if --motion dial was set)
|
||||
if motion_snippet:
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
lines.append("## Motion")
|
||||
lines.append("")
|
||||
lines.append(f"**{motion_snippet.get('Category', '')}** ({motion_snippet.get('Intensity Tier', '')}) — Trigger: {motion_snippet.get('Trigger', '')} | Duration: {motion_snippet.get('Duration', '')} | Easing: `{motion_snippet.get('Easing', '')}`")
|
||||
lines.append("")
|
||||
lines.append("```js")
|
||||
lines.append(motion_snippet.get("GSAP Snippet", ""))
|
||||
lines.append("```")
|
||||
lines.append("")
|
||||
if motion_snippet.get("Framework Notes"):
|
||||
lines.append(f"**Framework notes:** {motion_snippet.get('Framework Notes', '')}")
|
||||
lines.append("")
|
||||
motion_do = motion_snippet.get("Do", "")
|
||||
motion_dont = motion_snippet.get("Don't", "")
|
||||
if motion_do:
|
||||
lines.append(f"- ✅ {motion_do}")
|
||||
if motion_dont:
|
||||
lines.append(f"- ❌ {motion_dont}")
|
||||
if motion_snippet.get("Performance Notes"):
|
||||
lines.append(f"- ⚡ {motion_snippet.get('Performance Notes', '')}")
|
||||
lines.append("")
|
||||
|
||||
# Anti-Patterns section
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
|
||||
@ -1,114 +1,127 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
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]
|
||||
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, google-fonts
|
||||
Stacks: react, nextjs, vue, svelte, astro, swiftui, react-native, flutter, nuxtjs, nuxt-ui, html-tailwind, shadcn, jetpack-compose, threejs
|
||||
|
||||
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 sys
|
||||
import io
|
||||
from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack
|
||||
from design_system import generate_design_system, persist_design_system
|
||||
|
||||
# Force UTF-8 for stdout/stderr to handle emojis on Windows (cp1252 default)
|
||||
if sys.stdout.encoding and sys.stdout.encoding.lower() != 'utf-8':
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||||
if sys.stderr.encoding and sys.stderr.encoding.lower() != 'utf-8':
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
|
||||
|
||||
|
||||
def format_output(result):
|
||||
"""Format results for Claude consumption (token-optimized)"""
|
||||
if "error" in result:
|
||||
return f"Error: {result['error']}"
|
||||
|
||||
output = []
|
||||
if result.get("stack"):
|
||||
output.append(f"## UI Pro Max Stack Guidelines")
|
||||
output.append(f"**Stack:** {result['stack']} | **Query:** {result['query']}")
|
||||
else:
|
||||
output.append(f"## UI Pro Max Search Results")
|
||||
output.append(f"**Domain:** {result['domain']} | **Query:** {result['query']}")
|
||||
output.append(f"**Source:** {result['file']} | **Found:** {result['count']} results\n")
|
||||
|
||||
for i, row in enumerate(result['results'], 1):
|
||||
output.append(f"### Result {i}")
|
||||
for key, value in row.items():
|
||||
value_str = str(value)
|
||||
if len(value_str) > 300:
|
||||
value_str = value_str[:300] + "..."
|
||||
output.append(f"- **{key}:** {value_str}")
|
||||
output.append("")
|
||||
|
||||
return "\n".join(output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="UI Pro Max Search")
|
||||
parser.add_argument("query", help="Search query")
|
||||
parser.add_argument("--domain", "-d", choices=list(CSV_CONFIG.keys()), help="Search domain")
|
||||
parser.add_argument("--stack", "-s", choices=AVAILABLE_STACKS, help=f"Stack-specific search. Available: {', '.join(AVAILABLE_STACKS)}")
|
||||
parser.add_argument("--max-results", "-n", type=int, default=MAX_RESULTS, help="Max results (default: 3)")
|
||||
parser.add_argument("--json", action="store_true", help="Output as JSON")
|
||||
# Design system generation
|
||||
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("--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()
|
||||
|
||||
# Design system takes priority
|
||||
if args.design_system:
|
||||
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 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
|
||||
elif args.stack:
|
||||
result = search_stack(args.query, args.stack, args.max_results)
|
||||
if args.json:
|
||||
import json
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
else:
|
||||
print(format_output(result))
|
||||
# Domain search
|
||||
else:
|
||||
result = search(args.query, args.domain, args.max_results)
|
||||
if args.json:
|
||||
import json
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
else:
|
||||
print(format_output(result))
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
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]
|
||||
python search.py "<query>" --design-system [-p "Project Name"]
|
||||
python search.py "<query>" --design-system --persist [-p "Project Name"] [--page "dashboard"]
|
||||
python search.py "<query>" --design-system --variance 8 --motion 9 --density 7
|
||||
|
||||
Domains: style, prompt, color, chart, landing, product, ux, typography, google-fonts, gsap
|
||||
Stacks: react, nextjs, vue, svelte, astro, swiftui, react-native, flutter, nuxtjs, nuxt-ui, html-tailwind, shadcn, jetpack-compose, threejs, angular, laravel, javafx, wpf, winui, avalonia, uno, uwp
|
||||
|
||||
Design dials (1-10, only with --design-system):
|
||||
--variance DESIGN_VARIANCE: 1=centered/minimal, 10=bold/asymmetric
|
||||
--motion MOTION_INTENSITY: 1=subtle, 10=complex; attaches a GSAP snippet from motion.csv
|
||||
--density VISUAL_DENSITY: 1=spacious, 10=dense/dashboard; overrides the spacing scale
|
||||
|
||||
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 sys
|
||||
import io
|
||||
from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack
|
||||
from design_system import generate_design_system, persist_design_system
|
||||
|
||||
# Force UTF-8 for stdout/stderr to handle emojis on Windows (cp1252 default)
|
||||
if sys.stdout.encoding and sys.stdout.encoding.lower() != 'utf-8':
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||||
if sys.stderr.encoding and sys.stderr.encoding.lower() != 'utf-8':
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
|
||||
|
||||
|
||||
def format_output(result):
|
||||
"""Format results for Claude consumption (token-optimized)"""
|
||||
if "error" in result:
|
||||
return f"Error: {result['error']}"
|
||||
|
||||
output = []
|
||||
if result.get("stack"):
|
||||
output.append(f"## UI Pro Max Stack Guidelines")
|
||||
output.append(f"**Stack:** {result['stack']} | **Query:** {result['query']}")
|
||||
else:
|
||||
output.append(f"## UI Pro Max Search Results")
|
||||
output.append(f"**Domain:** {result['domain']} | **Query:** {result['query']}")
|
||||
output.append(f"**Source:** {result['file']} | **Found:** {result['count']} results\n")
|
||||
|
||||
for i, row in enumerate(result['results'], 1):
|
||||
output.append(f"### Result {i}")
|
||||
for key, value in row.items():
|
||||
value_str = str(value)
|
||||
if len(value_str) > 300:
|
||||
value_str = value_str[:300] + "..."
|
||||
output.append(f"- **{key}:** {value_str}")
|
||||
output.append("")
|
||||
|
||||
return "\n".join(output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="UI Pro Max Search")
|
||||
parser.add_argument("query", help="Search query")
|
||||
parser.add_argument("--domain", "-d", choices=list(CSV_CONFIG.keys()), help="Search domain")
|
||||
parser.add_argument("--stack", "-s", choices=AVAILABLE_STACKS, help=f"Stack-specific search. Available: {', '.join(AVAILABLE_STACKS)}")
|
||||
parser.add_argument("--max-results", "-n", type=int, default=MAX_RESULTS, help="Max results (default: 3)")
|
||||
parser.add_argument("--json", action="store_true", help="Output as JSON")
|
||||
# Design system generation
|
||||
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("--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)")
|
||||
# Design dials (1-10), only applied with --design-system
|
||||
parser.add_argument("--variance", type=int, choices=range(1, 11), metavar="1-10", help="DESIGN_VARIANCE dial: 1=centered/minimal, 10=bold/asymmetric (only with --design-system)")
|
||||
parser.add_argument("--motion", type=int, choices=range(1, 11), metavar="1-10", help="MOTION_INTENSITY dial: 1=subtle, 10=complex; pulls a matching GSAP snippet from motion.csv (only with --design-system)")
|
||||
parser.add_argument("--density", type=int, choices=range(1, 11), metavar="1-10", help="VISUAL_DENSITY dial: 1=spacious, 10=dense/dashboard; overrides the spacing scale (only with --design-system)")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Design system takes priority
|
||||
if args.design_system:
|
||||
result = generate_design_system(
|
||||
args.query,
|
||||
args.project_name,
|
||||
args.format,
|
||||
persist=args.persist,
|
||||
page=args.page,
|
||||
output_dir=args.output_dir,
|
||||
variance=args.variance,
|
||||
motion=args.motion,
|
||||
density=args.density
|
||||
)
|
||||
print(result)
|
||||
|
||||
# Print persistence confirmation
|
||||
if args.persist:
|
||||
project_slug = (args.project_name or args.query).lower().replace(' ', '-')
|
||||
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
|
||||
elif args.stack:
|
||||
result = search_stack(args.query, args.stack, args.max_results)
|
||||
if args.json:
|
||||
import json
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
else:
|
||||
print(format_output(result))
|
||||
# Domain search
|
||||
else:
|
||||
result = search(args.query, args.domain, args.max_results)
|
||||
if args.json:
|
||||
import json
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
else:
|
||||
print(format_output(result))
|
||||
|
||||
@ -20,6 +20,13 @@ python3 src/ui-ux-pro-max/scripts/search.py "<query>" --domain <domain> [-n <max
|
||||
- `landing` - Page structure and CTA strategies
|
||||
- `chart` - Chart types and library recommendations
|
||||
- `ux` - Best practices and anti-patterns
|
||||
- `gsap` - GSAP animation skeletons by intensity tier (hover, scroll reveal, stagger, page transition, parallax, loading)
|
||||
|
||||
**Design dials (optional, only with `--design-system`):**
|
||||
```bash
|
||||
python3 src/ui-ux-pro-max/scripts/search.py "<query>" --design-system --variance <1-10> --motion <1-10> --density <1-10>
|
||||
```
|
||||
`--variance` biases style selection (centered/minimal → bold/asymmetric), `--motion` attaches a matching GSAP snippet from `motion.csv`, `--density` overrides the spacing-scale tokens (spacious → dense/dashboard). Any dial left unset behaves exactly as before.
|
||||
|
||||
**Stack search:**
|
||||
```bash
|
||||
|
||||
@ -159,4 +159,35 @@ No,Product Type,Primary,On Primary,Secondary,On Secondary,Accent,On Accent,Backg
|
||||
158,Emergency SOS & Safety,#DC2626,#FFFFFF,#EF4444,#FFFFFF,#2563EB,#FFFFFF,#FFF1F2,#0F172A,#FFFFFF,#0F172A,#FCF1F1,#64748B,#FAE4E4,#DC2626,#FFFFFF,#DC2626,Alert red + safety blue
|
||||
159,Wallpaper & Theme App,#7C3AED,#FFFFFF,#EC4899,#FFFFFF,#2563EB,#FFFFFF,#FAF5FF,#0F172A,#FFFFFF,#0F172A,#F7F3FD,#64748B,#EFE7FC,#DC2626,#FFFFFF,#7C3AED,Aesthetic purple + trending pink
|
||||
160,White Noise & Ambient Sound,#475569,#FFFFFF,#334155,#FFFFFF,#4338CA,#FFFFFF,#0F172A,#FFFFFF,#192134,#FFFFFF,#131B2F,#94A3B8,"rgba(255,255,255,0.08)",#DC2626,#FFFFFF,#475569,Ambient grey + deep indigo on dark
|
||||
161,Home Decoration & Interior Design,#78716C,#FFFFFF,#A8A29E,#FFFFFF,#D97706,#FFFFFF,#FAF5F2,#0F172A,#FFFFFF,#0F172A,#F6F6F6,#64748B,#EEEDED,#DC2626,#FFFFFF,#78716C,Interior warm grey + gold accent
|
||||
161,Home Decoration & Interior Design,#78716C,#FFFFFF,#A8A29E,#FFFFFF,#D97706,#FFFFFF,#FAF5F2,#0F172A,#FFFFFF,#0F172A,#F6F6F6,#64748B,#EEEDED,#DC2626,#FFFFFF,#78716C,Interior warm grey + gold accent
|
||||
162,Academic Journal / Scholarly Publishing,#1E3A5F,#FFFFFF,#334155,#FFFFFF,#B45309,#FFFFFF,#F8FAFC,#0F172A,#FFFFFF,#0F172A,#E9EEF5,#64748B,#CBD5E1,#DC2626,#FFFFFF,#1E3A5F,Scholarly navy + citation gold + serif accent
|
||||
163,API Developer Portal,#0F172A,#FFFFFF,#1E293B,#FFFFFF,#22C55E,#0F172A,#020617,#F8FAFC,#0E1223,#F8FAFC,#1A1E2F,#94A3B8,#334155,#EF4444,#FFFFFF,#0F172A,Code dark + endpoint green + syntax colors
|
||||
164,Forum / Discussion Board,#475569,#FFFFFF,#64748B,#FFFFFF,#2563EB,#FFFFFF,#F8FAFC,#1E293B,#FFFFFF,#1E293B,#EAEFF3,#64748B,#E2E8F0,#DC2626,#FFFFFF,#475569,Neutral grey + topic accent + unread indicator
|
||||
165,Directory / Listing Site,#059669,#FFFFFF,#10B981,#0F172A,#D97706,#FFFFFF,#ECFDF5,#064E3B,#FFFFFF,#064E3B,#E8F1F3,#64748B,#A7F3D0,#DC2626,#FFFFFF,#059669,Category green + verified badge + map accent
|
||||
166,Status Page / Incident Management,#16A34A,#FFFFFF,#22C55E,#0F172A,#DC2626,#FFFFFF,#F0FDF4,#14532D,#FFFFFF,#14532D,#E8F0F1,#64748B,#BBF7D0,#DC2626,#FFFFFF,#16A34A,Operational green + incident red + maintenance amber
|
||||
167,Wiki / Encyclopedia,#1E3A8A,#FFFFFF,#3B82F6,#FFFFFF,#7C3AED,#FFFFFF,#F8FAFC,#1E40AF,#FFFFFF,#1E40AF,#E9EEF5,#64748B,#BFDBFE,#DC2626,#FFFFFF,#1E3A8A,Knowledge blue + link purple + clean white
|
||||
168,Auction Platform,#0F172A,#FFFFFF,#1E293B,#FFFFFF,#16A34A,#0F172A,#020617,#F8FAFC,#0E1223,#F8FAFC,#1A1E2F,#94A3B8,#334155,#DC2626,#FFFFFF,#0F172A,Dark luxury + bid green + outbid red + urgency
|
||||
169,Changelog / Release Notes,#475569,#FFFFFF,#64748B,#FFFFFF,#059669,#FFFFFF,#F8FAFC,#1E293B,#FFFFFF,#1E293B,#EAEFF3,#64748B,#E2E8F0,#DC2626,#FFFFFF,#475569,Feature green + fix blue + breaking red badges
|
||||
170,Citizen Science Platform,#15803D,#FFFFFF,#22C55E,#0F172A,#D97706,#FFFFFF,#F0FDF4,#14532D,#FFFFFF,#14532D,#E8F0F1,#64748B,#BBF7D0,#DC2626,#FFFFFF,#15803D,Discovery green + volunteer badge + data neutral
|
||||
171,Classifieds / Buy-Sell,#2563EB,#FFFFFF,#3B82F6,#FFFFFF,#16A34A,#FFFFFF,#EFF6FF,#1E40AF,#FFFFFF,#1E40AF,#E9EFF8,#64748B,#BFDBFE,#DC2626,#FFFFFF,#2563EB,Listing blue + price green + seller badge
|
||||
172,Conference / Symposium Landing Page,#1E3A5F,#FFFFFF,#2563EB,#FFFFFF,#A16207,#FFFFFF,#F8FAFC,#0F172A,#FFFFFF,#0F172A,#E9EEF5,#64748B,#CBD5E1,#DC2626,#FFFFFF,#1E3A5F,Academic navy + gold keynote + track chips
|
||||
173,Crowdfunding Platform,#D97706,#FFFFFF,#F59E0B,#0F172A,#16A34A,#FFFFFF,#FFFBEB,#0F172A,#FFFFFF,#0F172A,#FCF6F0,#64748B,#FAEEE1,#DC2626,#FFFFFF,#D97706,Funding progress amber + goal green + urgency
|
||||
174,Digital Signage / Kiosk,#0F172A,#FFFFFF,#1E293B,#FFFFFF,#EF4444,#FFFFFF,#020617,#F8FAFC,#0E1223,#F8FAFC,#1A1E2F,#94A3B8,#334155,#EF4444,#FFFFFF,#0F172A,High contrast dark + brand accent + large touch targets
|
||||
175,E-signature / Document Workflow,#1E3A5F,#FFFFFF,#2563EB,#FFFFFF,#16A34A,#FFFFFF,#F8FAFC,#0F172A,#FFFFFF,#0F172A,#E9EEF5,#64748B,#CBD5E1,#DC2626,#FFFFFF,#1E3A5F,Trust navy + signature green + audit trail
|
||||
176,Feature Flag / Config Management,#0F172A,#FFFFFF,#1E293B,#FFFFFF,#16A34A,#0F172A,#020617,#F8FAFC,#0E1223,#F8FAFC,#1A1E2F,#94A3B8,#334155,#DC2626,#FFFFFF,#0F172A,Enabled green + disabled grey + experimental amber
|
||||
177,Government Portal / Civic Services,#1E40AF,#FFFFFF,#3B82F6,#FFFFFF,#16A34A,#FFFFFF,#EFF6FF,#1E3A8A,#FFFFFF,#1E3A8A,#E9EFF5,#64748B,#BFDBFE,#DC2626,#FFFFFF,#1E40AF,Professional blue + service green + accessibility
|
||||
178,Grant / Funding Portal,#1E3A5F,#FFFFFF,#2563EB,#FFFFFF,#16A34A,#FFFFFF,#F8FAFC,#0F172A,#FFFFFF,#0F172A,#E9EEF5,#64748B,#CBD5E1,#DC2626,#FFFFFF,#1E3A5F,Institution navy + funding green + deadline urgency
|
||||
179,LMS (Learning Management System),#0D9488,#FFFFFF,#2DD4BF,#0F172A,#D97706,#FFFFFF,#F0FDFA,#134E4A,#FFFFFF,#134E4A,#E8F1F4,#64748B,#5EEAD4,#DC2626,#FFFFFF,#0D9488,Education teal + course amber + grade green
|
||||
180,No-code / Low-code Builder,#7C3AED,#FFFFFF,#A78BFA,#0F172A,#EC4899,#FFFFFF,#FAF5FF,#4C1D95,#FFFFFF,#4C1D95,#ECEEF9,#64748B,#DDD6FE,#DC2626,#FFFFFF,#7C3AED,Builder purple + component pink + canvas neutral
|
||||
181,Open Source Project Landing,#0F172A,#FFFFFF,#1E293B,#FFFFFF,#A16207,#FFFFFF,#020617,#F8FAFC,#0E1223,#F8FAFC,#1A1E2F,#94A3B8,#334155,#22C55E,#FFFFFF,#0F172A,Dark code + star gold + fork silver + sponsor purple
|
||||
182,Patient Portal / Health Records,#0284C7,#FFFFFF,#0891B2,#FFFFFF,#16A34A,#FFFFFF,#F0F9FF,#0C4A6E,#FFFFFF,#0C4A6E,#E8F2F8,#64748B,#BAE6FD,#DC2626,#FFFFFF,#0284C7,Clinical blue + health green + alert red
|
||||
183,Patent / IP Database,#475569,#FFFFFF,#64748B,#FFFFFF,#A16207,#FFFFFF,#F8FAFC,#1E293B,#FFFFFF,#1E293B,#EAEFF3,#64748B,#E2E8F0,#DC2626,#FFFFFF,#475569,Formal neutral + patent type chips + status badges
|
||||
184,Q&A Community Platform,#2563EB,#FFFFFF,#0891B2,#FFFFFF,#D97706,#FFFFFF,#F8FAFC,#0F172A,#FFFFFF,#0F172A,#F1F5FD,#64748B,#E4ECFC,#DC2626,#FFFFFF,#2563EB,Knowledge blue + accepted green + reputation gold
|
||||
185,Research Lab / University Department,#1E3A5F,#FFFFFF,#2563EB,#FFFFFF,#A16207,#FFFFFF,#F8FAFC,#0F172A,#FFFFFF,#0F172A,#E9EEF5,#64748B,#CBD5E1,#DC2626,#FFFFFF,#1E3A5F,Institutional navy + research accent + serif headings
|
||||
186,Resume / CV Builder,#1E3A5F,#FFFFFF,#2563EB,#FFFFFF,#16A34A,#FFFFFF,#F8FAFC,#0F172A,#FFFFFF,#0F172A,#E9EEF5,#64748B,#CBD5E1,#DC2626,#FFFFFF,#1E3A5F,Professional navy + section accent + success green
|
||||
187,Review Platform,#F59E0B,#0F172A,#FBBF24,#0F172A,#16A34A,#FFFFFF,#FFFBEB,#0F172A,#FFFFFF,#0F172A,#FCF6F0,#64748B,#FAEEE1,#DC2626,#FFFFFF,#F59E0B,Star gold + positive green + negative red
|
||||
188,RPA / Automation Dashboard,#0F172A,#FFFFFF,#1E293B,#FFFFFF,#16A34A,#0F172A,#020617,#F8FAFC,#0E1223,#F8FAFC,#1A1E2F,#94A3B8,#334155,#DC2626,#FFFFFF,#0F172A,Dark terminal + running green + failed red + queued amber
|
||||
189,Survey / Form Builder,#0D9488,#FFFFFF,#2DD4BF,#0F172A,#D97706,#FFFFFF,#F0FDFA,#134E4A,#FFFFFF,#134E4A,#E8F1F4,#64748B,#5EEAD4,#DC2626,#FFFFFF,#0D9488,Question teal + progress green + submit blue
|
||||
190,Telemedicine Platform,#0891B2,#FFFFFF,#22D3EE,#0F172A,#16A34A,#FFFFFF,#F0FDFA,#134E4A,#FFFFFF,#134E4A,#E8F1F6,#64748B,#CCFBF1,#DC2626,#FFFFFF,#0891B2,Medical teal + video green + waiting amber
|
||||
191,Testimonial & Social Proof Widget,#7C3AED,#FFFFFF,#A78BFA,#0F172A,#F59E0B,#0F172A,#FAF5FF,#4C1D95,#FFFFFF,#4C1D95,#ECEEF9,#64748B,#DDD6FE,#DC2626,#FFFFFF,#7C3AED,Trust purple + quote gold + verified blue
|
||||
192,Ticketing / Box Office,#0F172A,#FFFFFF,#1E293B,#FFFFFF,#16A34A,#0F172A,#020617,#F8FAFC,#0E1223,#F8FAFC,#1A1E2F,#94A3B8,#334155,#DC2626,#FFFFFF,#0F172A,Event theme colors + available green + sold-out red
|
||||
|
||||
|
0
cli/assets/data/draft.csv
Executable file → Normal file
0
cli/assets/data/draft.csv
Executable file → Normal file
|
Can't render this file because it contains an unexpected character in line 13 and column 56.
|
0
cli/assets/data/google-fonts.csv
Executable file → Normal file
0
cli/assets/data/google-fonts.csv
Executable file → Normal file
|
Can't render this file because it is too large.
|
17
cli/assets/data/motion.csv
Normal file
17
cli/assets/data/motion.csv
Normal file
@ -0,0 +1,17 @@
|
||||
No,Category,Intensity Tier,Keywords,Trigger,Duration,Easing,GSAP Snippet,Framework Notes,Do,Don't,Performance Notes
|
||||
1,Hover Micro-interaction,Subtle,"hover, button, opacity, lift, press feedback",hover,150-200ms,power1.out,"gsap.to(el, { y: -1, opacity: 0.9, duration: 0.15, ease: 'power1.out' });",Bind on mouseenter/mouseleave; in React wrap in a ref + useEffect (or onMouseEnter/onMouseLeave props directly calling gsap.to),Keep displacement under 2px so it reads as feedback not motion,Don't animate layout-affecting props (width/height/margin) on hover,Runs on transform/opacity only so it stays on the compositor thread
|
||||
2,Hover Micro-interaction,Standard,"hover, card, scale, tilt, cursor feedback",hover,200-300ms,power2.out,"gsap.to(el, { y: -4, scale: 1.02, boxShadow: '0 12px 24px rgba(0,0,0,0.12)', duration: 0.25, ease: 'power2.out' });","Use gsap.quickTo(el, 'y') for cards with many hover targets to avoid re-creating tweens every event",Pair with a matching mouseleave tween that reverses the same properties,Don't leave the hover state stuck if the pointer leaves fast; always attach the reverse tween,quickTo() avoids GC churn on lists with 20+ hoverable cards
|
||||
3,Hover Micro-interaction,Complex,"hover, magnetic, cursor follow, 3d tilt",hover + mousemove,300-500ms,"elastic.out(1,0.4)","const xTo = gsap.quickTo(el, 'x', { duration: 0.4, ease: 'elastic.out(1,0.4)' }); const yTo = gsap.quickTo(el, 'y', { duration: 0.4, ease: 'elastic.out(1,0.4)' }); el.addEventListener('mousemove', (e) => { const r = el.getBoundingClientRect(); xTo((e.clientX - r.left - r.width/2) * 0.3); yTo((e.clientY - r.top - r.height/2) * 0.3); });",Debounce is not needed since quickTo interpolates; remove listeners on component unmount in React/Vue to avoid leaks,Clamp the pull strength (e.g. * 0.3) so the element never fully leaves its hit box,Don't apply magnetic effect to more than 1-2 focal elements per screen; it becomes noisy,Use will-change: transform on the target element for smoother compositing
|
||||
4,Scroll Reveal,Subtle,"scroll, fade in, reveal, on view",scroll (viewport enter),300-400ms,power1.out,"gsap.from(el, { opacity: 0, y: 12, duration: 0.35, ease: 'power1.out', scrollTrigger: { trigger: el, start: 'top 90%', toggleActions: 'play none none reverse' } });",Requires the ScrollTrigger plugin registered once via gsap.registerPlugin(ScrollTrigger),"Keep the y offset small (8-16px) so it reads as a fade, not a slide",Don't reveal below-the-fold content needed for SEO/crawlers as invisible-by-default without a no-JS fallback,toggleActions 'play none none reverse' avoids re-triggering on every scroll direction change
|
||||
5,Scroll Reveal,Standard,"scroll, slide up, staggered section, reveal",scroll (viewport enter),400-600ms,power2.out,"gsap.from(el.children, { opacity: 0, y: 24, duration: 0.5, stagger: 0.08, ease: 'power2.out', scrollTrigger: { trigger: el, start: 'top 85%' } });","In React use useGSAP(() => {...}, { scope: containerRef }) from @gsap/react to auto-cleanup on unmount",Scope the ScrollTrigger to the section container so it doesn't re-scan the whole page,Don't stagger more than ~8 children; beyond that the last items feel laggy,Set scroller/markers: false in production; markers is dev-only
|
||||
6,Scroll Reveal,Complex,"scroll, pin, scrub, storytelling, scrollytelling",scroll (continuous scrub),tied to scroll position,none (scrub-driven),"gsap.timeline({ scrollTrigger: { trigger: section, start: 'top top', end: '+=150%', scrub: 1, pin: true } }).from('.headline', { opacity: 0, y: 40 }).to('.bg-layer', { yPercent: -20 }, '<');",Pinning needs the section to have deterministic height; recalc ScrollTrigger.refresh() after images/fonts load,Use scrub: true or a small number (0.5-1.5) instead of instant jumps so it feels tied to the scrollbar,Don't pin more than 1-2 sections per page; excessive pinning fights native scroll feel and hurts mobile UX,"Pinning forces layout reflow; test on mid-tier mobile devices, not just desktop"
|
||||
7,Stagger List,Subtle,"list, stagger, cards, grid entrance",load or scroll,250-350ms,power1.out,"gsap.from('.list-item', { opacity: 0, y: 8, duration: 0.3, stagger: 0.03 });",Select items with a stable class/data-attribute (not array index) so re-renders in React don't break targeting,Keep per-item stagger delay small (0.02-0.04s) for lists longer than 10 items,Don't stagger by more than 0.1s per item on long lists; total reveal time becomes sluggish,"For virtualized lists, only animate items currently mounted in the DOM"
|
||||
8,Stagger List,Standard,"grid, bento, cards, staggered scale",load or scroll,300-450ms,back.out(1.4),"gsap.from('.grid-item', { opacity: 0, scale: 0.92, y: 16, duration: 0.4, stagger: { each: 0.06, from: 'start', grid: 'auto' }, ease: 'back.out(1.4)' });",grid: 'auto' lets GSAP infer rows/columns from a CSS grid layout for a natural wave stagger,Combine with from: 'center' for a bento-grid layout to draw the eye inward first,Don't use back.out on dense data tables; the overshoot reads as sloppy on informational UI,Group DOM writes; avoid interleaving layout reads (getBoundingClientRect) between staggered tweens
|
||||
9,Stagger List,Complex,"stagger, wave, text reveal, split text",load or scroll,400-700ms,expo.out,"const split = new SplitText(headline, { type: 'chars' }); gsap.from(split.chars, { opacity: 0, y: 20, rotateX: -40, duration: 0.6, stagger: 0.015, ease: 'expo.out' });",SplitText is a GSAP Club/paid plugin; confirm license before shipping and provide a plain fade fallback if unavailable,Revert SplitText on unmount/cleanup (split.revert()) to restore original text nodes for accessibility tools,Don't split-animate long paragraphs; reserve for short headlines (under ~8 words),Splitting text creates one element per character; keep it to headline-length copy only for DOM size
|
||||
10,Page Transition,Subtle,"route change, fade, page transition",route change,200-300ms,power1.inOut,"gsap.to(main, { opacity: 0, duration: 0.2, onComplete: () => { navigate(); gsap.fromTo(main, { opacity: 0 }, { opacity: 1, duration: 0.2 }); } });","Pair with the router's transition hooks (Next.js App Router transitions, React Router's useNavigate, Vue Router's beforeEach/afterEach)",Preload the destination route's critical assets before the exit tween finishes,Don't block navigation on animation; cap exit duration at ~250ms so the app never feels unresponsive,Exit animation should always resolve faster than entrance (asymmetric timing) so back/forward feels snappy
|
||||
11,Page Transition,Standard,"route change, slide, overlay wipe",route change,400-600ms,power2.inOut,"const tl = gsap.timeline(); tl.to('.transition-overlay', { yPercent: 0, duration: 0.4, ease: 'power2.inOut' }).call(navigate).to('.transition-overlay', { yPercent: -100, duration: 0.4, ease: 'power2.inOut', delay: 0.1 });",Keep the overlay element mounted at the layout root (outside the page component) so it survives the route swap,Show a lightweight loading indicator if the destination route's data fetch outlasts the overlay,Don't tie the overlay's reveal directly to data-fetch completion without a max-wait timeout; a slow API stalls the whole transition,Prefer CSS transform (yPercent) over top/left to keep the overlay animation on the compositor thread
|
||||
12,Page Transition,Complex,"shared element, morph, hero transition",route change,500-800ms,expo.inOut,"const state = Flip.getState('.hero-image'); navigate(); Flip.from(state, { duration: 0.6, ease: 'expo.inOut', absolute: true, zIndex: 100 });",Requires the GSAP Flip plugin; the 'from' and 'to' route must render the same element with a shared data-flip-id,Verify the shared element exists in both DOM states before calling Flip.from to avoid a silent no-op,Don't use shared-element transitions across more than one element pair per navigation; compounding Flips are hard to time correctly,Flip recalculates layout (FLIP technique) so test on low-end devices for jank
|
||||
13,Parallax Scroll,Subtle,"parallax, background, depth, scroll speed",scroll (continuous),tied to scroll position,linear (scrub),"gsap.to('.bg-layer', { yPercent: 10, ease: 'none', scrollTrigger: { trigger: section, scrub: true } });","Apply parallax to background/decorative layers only, never to text or interactive controls",Keep the yPercent delta small (5-15) so foreground and background never desync distractingly,Don't parallax body copy; it hurts reading comfort and can trigger motion sickness,will-change: transform on the parallax layer only; remove it after scroll settles to free GPU memory
|
||||
14,Parallax Scroll,Standard,"multi-layer parallax, depth, hero background",scroll (continuous),tied to scroll position,linear (scrub),"gsap.utils.toArray('.parallax-layer').forEach((layer, i) => { gsap.to(layer, { yPercent: (i + 1) * -8, ease: 'none', scrollTrigger: { trigger: layer.parentElement, scrub: 0.5 } }); });",Layer count beyond 3-4 has diminishing visual return and multiplies scroll-listener cost,"Vary speed per layer (background slowest, foreground fastest) to sell the depth illusion",Don't let parallax layers overflow their container; clip with overflow: hidden on the wrapper,Batch all layers under one ScrollTrigger container where possible instead of one per layer
|
||||
15,Loading / Skeleton,Subtle,"loading, skeleton, shimmer, pulse",on mount / async wait,1200-1600ms loop,sine.inOut,"gsap.to('.skeleton', { backgroundPosition: '200% 0', duration: 1.4, ease: 'sine.inOut', repeat: -1 });",Kill the loop tween (tween.kill()) as soon as real content mounts to avoid orphaned repeating animations,Use a CSS gradient background-position sweep rather than opacity pulsing; reads as 'loading' more clearly,Don't run more than one shimmer loop per skeleton group; sync them under one timeline so the wave reads as a single unit,repeat: -1 tweens are cheap but must be explicitly killed on unmount or they leak in SPA route changes
|
||||
16,Loading / Skeleton,Standard,"progress, spinner, morphing loader",on mount / async wait,800-1200ms loop,power1.inOut,"gsap.timeline({ repeat: -1 }).to('.loader-dot', { y: -8, duration: 0.4, stagger: { each: 0.15, yoyo: true, repeat: 1 } });",Wrap the whole loop timeline in useGSAP with { revertOnUpdate: false } in React so it isn't rebuilt every render,Cap total loop duration under ~1.5s so long waits don't feel like the UI froze on a single beat,Don't use elaborate loaders for sub-300ms waits; they flash and feel worse than no indicator,Pause the timeline (tl.pause()) when the loading tab/view is not visible to save CPU on background tabs
|
||||
|
@ -1,17 +1,17 @@
|
||||
No,Product Type,Keywords,Primary Style Recommendation,Secondary Styles,Landing Page Pattern,Dashboard Style (if applicable),Color Palette Focus,Key Considerations
|
||||
1,SaaS (General),"app, b2b, cloud, general, saas, software, subscription",Glassmorphism + Flat Design,"Soft UI Evolution, Minimalism",Hero + Features + CTA,Data-Dense + Real-Time Monitoring,Trust blue + accent contrast,Balance modern feel with clarity. Focus on CTAs.
|
||||
2,Micro SaaS,"app, b2b, cloud, indie, micro, micro-saas, niche, saas, small, software, solo, subscription",Flat Design + Vibrant & Block,"Motion-Driven, Micro-interactions",Minimal & Direct + Demo,Executive Dashboard,Vibrant primary + white space,"Keep simple, show product quickly. Speed is key."
|
||||
2,Micro SaaS,"indie, micro-saas, niche, solo, bootstrap, micro, side-project, solopreneur, small-team, indie-hacker, product-hunt",Flat Design + Vibrant & Block,"Motion-Driven, Micro-interactions",Minimal & Direct + Demo,Executive Dashboard,Vibrant primary + white space,"Keep simple, show product quickly. Speed is key."
|
||||
3,E-commerce,"buy, commerce, e, ecommerce, products, retail, sell, shop, store",Vibrant & Block-based,"Aurora UI, Motion-Driven",Feature-Rich Showcase,Sales Intelligence Dashboard,Brand primary + success green,Engagement & conversions. High visual hierarchy.
|
||||
4,E-commerce Luxury,"buy, commerce, e, ecommerce, elegant, exclusive, high-end, luxury, premium, products, retail, sell, shop, store",Liquid Glass + Glassmorphism,"3D & Hyperrealism, Aurora UI",Feature-Rich Showcase,Sales Intelligence Dashboard,Premium colors + minimal accent,Elegance & sophistication. Premium materials.
|
||||
5,B2B Service,"appointment, b, b2b, booking, business, consultation, corporate, enterprise, service",Trust & Authority + Minimal,"Feature-Rich, Conversion-Optimized",Feature-Rich Showcase,Sales Intelligence Dashboard,Professional blue + neutral grey,Credibility essential. Clear ROI messaging.
|
||||
6,Financial Dashboard,"admin, analytics, dashboard, data, financial, panel",Dark Mode (OLED) + Data-Dense,"Minimalism, Accessible & Ethical",N/A - Dashboard focused,Financial Dashboard,Dark bg + red/green alerts + trust blue,"High contrast, real-time updates, accuracy paramount."
|
||||
7,Analytics Dashboard,"admin, analytics, dashboard, data, panel",Data-Dense + Heat Map & Heatmap,"Minimalism, Dark Mode (OLED)",N/A - Analytics focused,Drill-Down Analytics + Comparative,Cool→Hot gradients + neutral grey,Clarity > aesthetics. Color-coded data priority.
|
||||
5,B2B Service,"b2b, enterprise, consulting, professional, solution, contract, corporate, strategy, advisory, roi, deliverable, whitepaper",Trust & Authority + Minimal,"Feature-Rich, Conversion-Optimized",Feature-Rich Showcase,Sales Intelligence Dashboard,Professional blue + neutral grey,Credibility essential. Clear ROI messaging.
|
||||
6,Financial Dashboard,"portfolio, trading, pnl, budget, revenue, expense, cashflow, balance-sheet, investment, bank, accounting, fintech",Dark Mode (OLED) + Data-Dense,"Minimalism, Accessible & Ethical",N/A - Dashboard focused,Financial Dashboard,Dark bg + red/green alerts + trust blue,"High contrast, real-time updates, accuracy paramount."
|
||||
7,Analytics Dashboard,"kpi, metric, funnel, conversion, cohort, retention, segment, attribution, ab-test, dashboard-data, business-intelligence",Data-Dense + Heat Map & Heatmap,"Minimalism, Dark Mode (OLED)",N/A - Analytics focused,Drill-Down Analytics + Comparative,Cool→Hot gradients + neutral grey,Clarity > aesthetics. Color-coded data priority.
|
||||
8,Healthcare App,"app, clinic, health, healthcare, medical, patient",Neumorphism + Accessible & Ethical,"Soft UI Evolution, Claymorphism (for patients)",Social Proof-Focused,User Behavior Analytics,Calm blue + health green + trust,Accessibility mandatory. Calming aesthetic.
|
||||
9,Educational App,"app, course, education, educational, learning, school, training",Claymorphism + Micro-interactions,"Vibrant & Block-based, Flat Design",Storytelling-Driven,User Behavior Analytics,Playful colors + clear hierarchy,Engagement & ease of use. Age-appropriate design.
|
||||
10,Creative Agency,"agency, creative, design, marketing, studio",Brutalism + Motion-Driven,"Retro-Futurism, Storytelling-Driven",Storytelling-Driven,N/A - Portfolio focused,Bold primaries + artistic freedom,Differentiation key. Wow-factor necessary.
|
||||
10,Creative Agency,"branding, identity, portfolio, logo, visual, rebrand, creative-director, campaign, awards, award-winning, showreel",Brutalism + Motion-Driven,"Retro-Futurism, Storytelling-Driven",Storytelling-Driven,N/A - Portfolio focused,Bold primaries + artistic freedom,Differentiation key. Wow-factor necessary.
|
||||
11,Portfolio/Personal,"creative, personal, portfolio, projects, showcase, work",Motion-Driven + Minimalism,"Brutalism, Aurora UI",Storytelling-Driven,N/A - Personal branding,Brand primary + artistic interpretation,Showcase work. Personality shine through.
|
||||
12,Gaming,"entertainment, esports, game, gaming, play",3D & Hyperrealism + Retro-Futurism,"Motion-Driven, Vibrant & Block",Feature-Rich Showcase,N/A - Game focused,Vibrant + neon + immersive colors,Immersion priority. Performance critical.
|
||||
13,Government/Public Service,"appointment, booking, consultation, government, public, service",Accessible & Ethical + Minimalism,"Flat Design, Inclusive Design",Minimal & Direct,Executive Dashboard,Professional blue + high contrast,WCAG AAA mandatory. Trust paramount.
|
||||
13,Government/Public Service,"government, civic, municipal, federal, citizen, public, administration, permit, tax, voter, transparency, regulation",Accessible & Ethical + Minimalism,"Flat Design, Inclusive Design",Minimal & Direct,Executive Dashboard,Professional blue + high contrast,WCAG AAA mandatory. Trust paramount.
|
||||
14,Fintech/Crypto,"banking, blockchain, crypto, defi, finance, fintech, money, nft, payment, web3",Glassmorphism + Dark Mode (OLED),"Retro-Futurism, Motion-Driven",Conversion-Optimized,Real-Time Monitoring + Predictive,Dark tech colors + trust + vibrant accents,Security perception. Real-time data critical.
|
||||
15,Social Media App,"app, community, content, entertainment, media, network, sharing, social, streaming, users, video",Vibrant & Block-based + Motion-Driven,"Aurora UI, Micro-interactions",Feature-Rich Showcase,User Behavior Analytics,Vibrant + engagement colors,Engagement & retention. Addictive design ethics.
|
||||
16,Productivity Tool,"collaboration, productivity, project, task, tool, workflow",Flat Design + Micro-interactions,"Minimalism, Soft UI Evolution",Interactive Product Demo,Drill-Down Analytics,Clear hierarchy + functional colors,Ease of use. Speed & efficiency focus.
|
||||
@ -24,21 +24,21 @@ No,Product Type,Keywords,Primary Style Recommendation,Secondary Styles,Landing P
|
||||
23,Pet Tech App,"app, pet, tech",Claymorphism + Vibrant & Block-based,"Micro-interactions, Flat Design",Storytelling-Driven,User Behavior Analytics,Playful + Warm colors,Pet profiles. Health tracking. Playful UI. Photo galleries. Vet integration.
|
||||
24,Smart Home/IoT Dashboard,"admin, analytics, dashboard, data, home, iot, panel, smart",Glassmorphism + Dark Mode (OLED),"Minimalism, AI-Native UI",Interactive Product Demo,Real-Time Monitoring,Dark + Status indicator colors,Device status. Real-time controls. Energy monitoring. Automation rules. Quick actions.
|
||||
25,EV/Charging Ecosystem,"charging, ecosystem, ev",Minimalism + Aurora UI,"Glassmorphism, Organic Biophilic",Hero-Centric Design,Energy/Utilities Dashboard,Electric Blue (#009CD1) + Green,Charging station maps. Range estimation. Cost calculation. Environmental impact.
|
||||
26,Subscription Box Service,"appointment, booking, box, consultation, membership, plan, recurring, service, subscription",Vibrant & Block-based + Motion-Driven,"Claymorphism, Aurora UI",Feature-Rich Showcase,E-commerce Analytics,Brand + Excitement colors,Unboxing experience. Personalization quiz. Subscription management. Product reveals.
|
||||
26,Subscription Box Service,"subscription, box, recurring, membership, unboxing, curated, plan, monthly, surprise, product-box",Vibrant & Block-based + Motion-Driven,"Claymorphism, Aurora UI",Feature-Rich Showcase,E-commerce Analytics,Brand + Excitement colors,Unboxing experience. Personalization quiz. Subscription management. Product reveals.
|
||||
27,Podcast Platform,"platform, podcast",Dark Mode (OLED) + Minimalism,"Motion-Driven, Vibrant & Block-based",Storytelling-Driven,Media/Entertainment Dashboard,Dark + Audio waveform accents,Audio player UX. Episode discovery. Creator tools. Analytics for podcasters.
|
||||
28,Dating App,"app, dating",Vibrant & Block-based + Motion-Driven,"Aurora UI, Glassmorphism",Social Proof-Focused,User Behavior Analytics,Warm + Romantic (Pink/Red gradients),Profile cards. Swipe interactions. Match animations. Safety features. Video chat.
|
||||
29,Micro-Credentials/Badges Platform,"badges, credentials, micro, platform",Minimalism + Flat Design,"Accessible & Ethical, Swiss Modernism 2.0",Trust & Authority,Education Dashboard,Trust Blue + Gold (#FFD700),Credential verification. Badge display. Progress tracking. Issuer trust. LinkedIn integration.
|
||||
30,Knowledge Base/Documentation,"base, documentation, knowledge",Minimalism + Accessible & Ethical,"Swiss Modernism 2.0, Flat Design",FAQ/Documentation,N/A - Documentation focused,Clean hierarchy + minimal color,Search-first. Clear navigation. Code highlighting. Version switching. Feedback system.
|
||||
31,Hyperlocal Services,"appointment, booking, consultation, hyperlocal, service, services",Minimalism + Vibrant & Block-based,"Micro-interactions, Flat Design",Conversion-Optimized,Drill-Down Analytics + Map,Location markers + Trust colors,Map integration. Service categories. Provider profiles. Booking system. Reviews.
|
||||
32,Beauty/Spa/Wellness Service,"appointment, beauty, booking, consultation, service, spa, wellness",Soft UI Evolution + Neumorphism,"Glassmorphism, Minimalism",Hero-Centric Design + Social Proof,User Behavior Analytics,Soft pastels (Pink #FFB6C1 Sage #90EE90) + Cream + Gold accents,Calming aesthetic. Booking system. Service menu. Before/after gallery. Testimonials. Relaxing imagery.
|
||||
31,Hyperlocal Services,"hyperlocal, local, neighborhood, nearby, community, nearby, zip, map, local-business, geo-target, city",Minimalism + Vibrant & Block-based,"Micro-interactions, Flat Design",Conversion-Optimized,Drill-Down Analytics + Map,Location markers + Trust colors,Map integration. Service categories. Provider profiles. Booking system. Reviews.
|
||||
32,Beauty/Spa/Wellness Service,"spa, beauty, salon, wellness, treatment, relaxation, massage, skincare, facial, aesthetic, self-care, pamper",Soft UI Evolution + Neumorphism,"Glassmorphism, Minimalism",Hero-Centric Design + Social Proof,User Behavior Analytics,Soft pastels (Pink #FFB6C1 Sage #90EE90) + Cream + Gold accents,Calming aesthetic. Booking system. Service menu. Before/after gallery. Testimonials. Relaxing imagery.
|
||||
33,Luxury/Premium Brand,"brand, elegant, exclusive, high-end, luxury, premium",Liquid Glass + Glassmorphism,"Minimalism, 3D & Hyperrealism",Storytelling-Driven + Feature-Rich,Sales Intelligence Dashboard,Black + Gold (#FFD700) + White + Minimal accent,Elegance paramount. Premium imagery. Storytelling. High-quality visuals. Exclusive feel.
|
||||
34,Restaurant/Food Service,"appointment, booking, consultation, delivery, food, menu, order, restaurant, service",Vibrant & Block-based + Motion-Driven,"Claymorphism, Flat Design",Hero-Centric Design + Conversion,N/A - Booking focused,Warm colors (Orange Red Brown) + appetizing imagery,Menu display. Online ordering. Reservation system. Food photography. Location/hours prominent.
|
||||
34,Restaurant/Food Service,"restaurant, menu, order, food, dining, reservation, delivery, cuisine, chef, table, takeaway, eatery",Vibrant & Block-based + Motion-Driven,"Claymorphism, Flat Design",Hero-Centric Design + Conversion,N/A - Booking focused,Warm colors (Orange Red Brown) + appetizing imagery,Menu display. Online ordering. Reservation system. Food photography. Location/hours prominent.
|
||||
35,Fitness/Gym App,"app, exercise, fitness, gym, health, workout",Vibrant & Block-based + Dark Mode (OLED),"Motion-Driven, Neumorphism",Feature-Rich Showcase,User Behavior Analytics,Energetic (Orange #FF6B35 Electric Blue) + Dark bg,Progress tracking. Workout plans. Community features. Achievements. Motivational design.
|
||||
36,Real Estate/Property,"buy, estate, housing, property, real, real-estate, rent",Glassmorphism + Minimalism,"Motion-Driven, 3D & Hyperrealism",Hero-Centric Design + Feature-Rich,Sales Intelligence Dashboard,Trust Blue (#0077B6) + Gold accents + White,Property listings. Virtual tours. Map integration. Agent profiles. Mortgage calculator. High-quality imagery.
|
||||
37,Travel/Tourism Agency,"agency, booking, creative, design, flight, hotel, marketing, studio, tourism, travel, vacation",Aurora UI + Motion-Driven,"Vibrant & Block-based, Glassmorphism",Storytelling-Driven + Hero-Centric,Booking Analytics,Vibrant destination colors + Sky Blue + Warm accents,Destination showcase. Booking system. Itinerary builder. Reviews. Inspiration galleries. Mobile-first.
|
||||
37,Travel/Tourism Agency,"travel, tourism, vacation, flight, hotel, destination, adventure, cruise, safari, backpacking, guided-tour, holiday-package",Aurora UI + Motion-Driven,"Vibrant & Block-based, Glassmorphism",Storytelling-Driven + Hero-Centric,Booking Analytics,Vibrant destination colors + Sky Blue + Warm accents,Destination showcase. Booking system. Itinerary builder. Reviews. Inspiration galleries. Mobile-first.
|
||||
38,Hotel/Hospitality,"hospitality, hotel",Liquid Glass + Minimalism,"Glassmorphism, Soft UI Evolution",Hero-Centric Design + Social Proof,Revenue Management Dashboard,Warm neutrals + Gold (#D4AF37) + Brand accent,Room booking. Amenities showcase. Location maps. Guest reviews. Seasonal pricing. Luxury imagery.
|
||||
39,Wedding/Event Planning,"conference, event, meetup, planning, registration, ticket, wedding",Soft UI Evolution + Aurora UI,"Glassmorphism, Motion-Driven",Storytelling-Driven + Social Proof,N/A - Planning focused,Soft Pink (#FFD6E0) + Gold + Cream + Sage,Portfolio gallery. Vendor directory. Planning tools. Timeline. Budget tracker. Romantic aesthetic.
|
||||
40,Legal Services,"appointment, attorney, booking, compliance, consultation, contract, law, legal, service, services",Trust & Authority + Minimalism,"Accessible & Ethical, Swiss Modernism 2.0",Trust & Authority + Minimal,Case Management Dashboard,Navy Blue (#1E3A5F) + Gold + White,Credibility paramount. Practice areas. Attorney profiles. Case results. Contact forms. Professional imagery.
|
||||
40,Legal Services,"law, attorney, legal, case, compliance, contract, court, firm, counsel, litigation, practice-area, jurisdiction",Trust & Authority + Minimalism,"Accessible & Ethical, Swiss Modernism 2.0",Trust & Authority + Minimal,Case Management Dashboard,Navy Blue (#1E3A5F) + Gold + White,Credibility paramount. Practice areas. Attorney profiles. Case results. Contact forms. Professional imagery.
|
||||
41,Insurance Platform,"insurance, platform",Trust & Authority + Flat Design,"Accessible & Ethical, Minimalism",Conversion-Optimized + Trust,Claims Analytics Dashboard,Trust Blue (#0066CC) + Green (security) + Neutral,Quote calculator. Policy comparison. Claims process. Trust signals. Clear pricing. Security badges.
|
||||
42,Banking/Traditional Finance,"banking, finance, traditional",Minimalism + Accessible & Ethical,"Trust & Authority, Dark Mode (OLED)",Trust & Authority + Feature-Rich,Financial Dashboard,Navy (#0A1628) + Trust Blue + Gold accents,Security-first. Account overview. Transaction history. Mobile banking. Accessibility critical. Trust paramount.
|
||||
43,Online Course/E-learning,"course, e, learning, online",Claymorphism + Vibrant & Block-based,"Motion-Driven, Flat Design",Feature-Rich Showcase + Social Proof,Education Dashboard,Vibrant learning colors + Progress green,Course catalog. Progress tracking. Video player. Quizzes. Certificates. Community forums. Gamification.
|
||||
@ -53,7 +53,7 @@ No,Product Type,Keywords,Primary Style Recommendation,Secondary Styles,Landing P
|
||||
52,Automotive/Car Dealership,"automotive, car, dealership",Motion-Driven + 3D & Hyperrealism,"Dark Mode (OLED), Glassmorphism",Hero-Centric Design + Feature-Rich,Sales Intelligence Dashboard,Brand colors + Metallic accents + Dark/Light,Vehicle showcase. 360° views. Comparison tools. Financing calculator. Test drive booking. High-quality imagery.
|
||||
53,Photography Studio,"photography, studio",Motion-Driven + Minimalism,"Aurora UI, Glassmorphism",Storytelling-Driven + Hero-Centric,N/A - Portfolio focused,Black + White + Minimal accent,Portfolio gallery. Before/after. Service packages. Booking system. Client galleries. Full-bleed imagery.
|
||||
54,Coworking Space,"coworking, space",Vibrant & Block-based + Glassmorphism,"Minimalism, Motion-Driven",Hero-Centric Design + Feature-Rich,Occupancy Dashboard,Energetic colors + Wood tones + Brand accent,Space tour. Membership plans. Booking system. Amenities. Community events. Virtual tour.
|
||||
55,Home Services (Plumber/Electrician),"appointment, booking, consultation, electrician, home, plumber, service, services",Flat Design + Trust & Authority,"Minimalism, Accessible & Ethical",Conversion-Optimized + Trust,Service Analytics,Trust Blue + Safety Orange + Professional grey,Service list. Emergency contact. Booking. Price transparency. Certifications. Local trust signals.
|
||||
55,Home Services (Plumber/Electrician),"plumber, electrician, hvac, handyman, repair, maintenance, home, emergency, leak, wiring, inspection, licensed",Flat Design + Trust & Authority,"Minimalism, Accessible & Ethical",Conversion-Optimized + Trust,Service Analytics,Trust Blue + Safety Orange + Professional grey,Service list. Emergency contact. Booking. Price transparency. Certifications. Local trust signals.
|
||||
56,Childcare/Daycare,"childcare, daycare",Claymorphism + Vibrant & Block-based,"Soft UI Evolution, Accessible & Ethical",Social Proof-Focused + Trust,Parent Dashboard,Playful pastels + Safe colors + Warm accents,Programs. Staff profiles. Safety certifications. Parent portal. Activity updates. Cheerful imagery.
|
||||
57,Senior Care/Elderly,"care, elderly, senior",Accessible & Ethical + Soft UI Evolution,"Minimalism, Neumorphism",Trust & Authority + Social Proof,Healthcare Analytics,Calm Blue + Warm neutrals + Large text,Care services. Staff qualifications. Facility tour. Family portal. Large touch targets. High contrast. Accessibility-first.
|
||||
58,Medical Clinic,"clinic, medical",Accessible & Ethical + Minimalism,"Neumorphism, Trust & Authority",Trust & Authority + Conversion,Healthcare Analytics,Medical Blue (#0077B6) + Trust White + Calm Green,Services. Doctor profiles. Online booking. Patient portal. Insurance info. HIPAA compliant. Trust signals.
|
||||
@ -67,7 +67,7 @@ No,Product Type,Keywords,Primary Style Recommendation,Secondary Styles,Landing P
|
||||
66,News/Media Platform,"content, entertainment, media, news, platform, streaming, video",Minimalism + Flat Design,"Dark Mode (OLED), Accessible & Ethical",Hero-Centric Design + Feature-Rich,Media Analytics Dashboard,Brand colors + High contrast + Category colors,Article layout. Breaking news. Categories. Search. Subscription. Mobile reading. Fast loading.
|
||||
67,Magazine/Blog,"articles, blog, content, magazine, posts, writing",Swiss Modernism 2.0 + Motion-Driven,"Minimalism, Aurora UI",Storytelling-Driven + Hero-Centric,Content Analytics,Editorial colors + Brand primary + Clean white,Article showcase. Category navigation. Author profiles. Newsletter signup. Related content. Typography-focused.
|
||||
68,Freelancer Platform,"freelancer, platform",Flat Design + Minimalism,"Vibrant & Block-based, Micro-interactions",Feature-Rich Showcase + Conversion,Marketplace Analytics,Professional Blue + Success Green + Neutral,Profile creation. Portfolio. Skill matching. Messaging. Payment. Reviews. Project management.
|
||||
69,Marketing Agency,"agency, creative, design, marketing, studio",Brutalism + Motion-Driven,"Vibrant & Block-based, Aurora UI",Storytelling-Driven + Feature-Rich,Campaign Analytics,Bold brand colors + Creative freedom,Portfolio. Case studies. Services. Team. Creative showcase. Results-focused. Bold aesthetic.
|
||||
69,Marketing Agency,"campaign, ads, growth, roi, seo, sem, ppc, social-media, conversion-funnel, ab-test, attribution, performance-marketing",Brutalism + Motion-Driven,"Vibrant & Block-based, Aurora UI",Storytelling-Driven + Feature-Rich,Campaign Analytics,Bold brand colors + Creative freedom,Portfolio. Case studies. Services. Team. Creative showcase. Results-focused. Bold aesthetic.
|
||||
70,Event Management,"conference, event, management, meetup, registration, ticket",Vibrant & Block-based + Motion-Driven,"Glassmorphism, Aurora UI",Hero-Centric Design + Feature-Rich,Event Analytics,Event theme colors + Excitement accents,Event showcase. Registration. Agenda. Speakers. Sponsors. Ticket sales. Countdown timer.
|
||||
71,Membership/Community,"community, membership",Vibrant & Block-based + Soft UI Evolution,"Bento Box Grid, Micro-interactions",Social Proof-Focused + Conversion,Community Analytics,Community brand colors + Engagement accents,Member benefits. Pricing tiers. Community showcase. Events. Member directory. Exclusive content.
|
||||
72,Newsletter Platform,"newsletter, platform",Minimalism + Flat Design,"Swiss Modernism 2.0, Accessible & Ethical",Minimal & Direct + Conversion,Email Analytics,Brand primary + Clean white + CTA accent,Subscribe form. Archive. About. Social proof. Sample content. Simple conversion.
|
||||
@ -160,3 +160,34 @@ No,Product Type,Keywords,Primary Style Recommendation,Secondary Styles,Landing P
|
||||
159,Wallpaper & Theme App,"wallpaper, theme, background, customize, aesthetic, home-screen, lock-screen, widget, design, zedge",Vibrant & Block-based + Aurora UI,"Glassmorphism, Motion-Driven",Feature-Rich Showcase + Social Proof,N/A - Gallery focused,Content-driven + trending aesthetic palettes + download accent,Category browsing. Preview on device. Daily wallpaper auto-set. Widget matching. Creator uploads. Resolution auto-fit.
|
||||
160,White Noise & Ambient Sound,"white noise, ambient, sound, sleep, focus, rain, nature, relax, concentration, background, noisli",Minimalism + Dark Mode (OLED),"Neumorphism, Organic Biophilic",Minimal & Direct + Social Proof,N/A - Player focused,Calming dark + ambient texture visual + subtle sound wave + sleep blue,Sound mixer with multiple simultaneous layers. Sleep timer with fade. Custom soundscapes. Offline. Background audio.
|
||||
161,Home Decoration & Interior Design,"home, interior, decor, design, furniture, room, renovation, ar, plan, inspire, 3d, houzz",Minimalism + 3D Product Preview,"Organic Biophilic, Aurora UI",Storytelling-Driven + Feature-Rich,N/A - Project focused,Neutral interior palette + material texture accent + AR blue,AR room visualization. Style quiz. Product catalog with purchase links. 3D room planner. Mood board. Before/after.
|
||||
162,Academic Journal / Scholarly Publishing,"academic, journal, paper, research, peer-review, open-access, scholarly, publication, citation, manuscript, issn, doi",Swiss Modernism 2.0 + Minimalism,"Trust & Authority, Accessible & Ethical",Content-Index + Search,N/A - Publication focused,Trust navy + White + Citation blue + Serif accents,"Prioritize readability (serif body text). Clear article hierarchy. Abstract/DOI prominence. WCAG AAA. Minimal visual noise. Trust signals: ISSN, indexing badges."
|
||||
163,API Developer Portal,"api, developer, documentation, sdk, endpoint, integration, rest, graphql, webhook, reference, getting-started, auth",Trust & Authority + Minimalism,"Glassmorphism, Dark Mode (OLED)",Quick Start + Interactive Docs,N/A - Documentation focused,Dark code theme + Brand accent + Syntax colors,"Endpoint discoverability. Copy-paste code samples. Auth flow clarity. Version switching. Interactive playground. Rate limit visibility."
|
||||
164,Forum / Discussion Board,"forum, discussion, thread, post, reply, community, comment, moderation, subreddit, stackexchange, topic",Dark Mode (OLED) + Minimalism,"Flat Design, Vibrant & Block-based",Feed + Thread View,N/A - Discussion focused,Dark neutral + topic accent colors + unread indicator + reputation badge,Thread list with pagination. Rich text editor. Quote/mention system. Upvote/downvote. User badges. Moderation tools.
|
||||
165,Directory / Listing Site,"directory, listing, classifieds, catalogue, business-directory, yellow-pages, venue, find, search, filter, map",Flat Design + Vibrant & Block-based,"Minimalism, Trust & Authority",Filter-Heavy Grid + Map,N/A - Listing focused,Neutral bg + category color chips + map accent + verified badge,Category tree. Multi-filter sidebar. Map/list toggle. Verified badges. Reviews. Claim listing flow.
|
||||
166,Status Page / Incident Management,"status, incident, outage, uptime, downtime, statuspage, monitoring, sla, maintenance, sev1, postmortem",Data-Dense + Trust & Authority,"Minimalism, Dark Mode (OLED)",Timeline + Severity Indicators,Real-Time Monitoring + Timeline,Status green + incident red + maintenance amber + neutral dark,Service status matrix. Incident timeline. Severity badges. Maintenance schedule. SLA uptime history. Email/SMS subscribe.
|
||||
167,Wiki / Encyclopedia,"wiki, encyclopedia, knowledge, article, reference, wikipedia, documentation, collaborative, edit, version, citation",Minimalism + Flat Design,"Swiss Modernism 2.0, Accessible & Ethical",Search-First + Hierarchical Navigation,N/A - Reference focused,Clean white + link blue + heading hierarchy + citation grey,Full-text search bar. Table of contents sidebar. Edit history. Inter-page linking. Mobile responsive. Print-friendly.
|
||||
168,Auction Platform,"auction, bid, hammer, lot, live-auction, bidding-war, estate-sale, proxibid, gavel, lot-number, reserve-price",Dark Mode (OLED) + Motion-Driven,"Vibrant & Block-based, Real-Time Monitor",Live Auction Feed + Countdown,N/A - Auction focused,Dark bg + bid green + outbid red + countdown amber,Real-time bid updates. Countdown timer urgency. Auto-bid ceiling. Outbid notifications. Bid history. Reserve price indicator.
|
||||
169,Changelog / Release Notes,"changelog, release-notes, version-history, whats-new, product-updates, semver, patch-notes, release-tracker",Minimalism + Flat Design,"Swiss Modernism 2.0, Trust & Authority",Timeline + Version List,N/A - Documentation focused,Neutral bg + version badge colors (feat=green, fix=blue, breaking=red) + date grey,Chronological release feed. Semver badges. Breaking change warnings. Copy-paste install commands. Subscribe to feed. Search by version.
|
||||
170,Citizen Science Platform,"citizen-science, zooniverse, crowdsourced-research, volunteer-science, public-participation, distributed-research, citizen-researcher",Organic Biophilic + Vibrant & Block-based,"Claymorphism, Motion-Driven",Storytelling-Driven + Social Proof,Project Participation Dashboard,Earth green + discovery orange + volunteer badge blue + data neutral,Project cards with impact metrics. Contribution tracker. Beginner-friendly onboarding. Data quality feedback loop. Leaderboards. Community forums.
|
||||
171,Classifieds / Buy-Sell,"classifieds, buy-sell, craigslist, secondhand, marketplace-listing, for-sale, trade, flea-market, thrift, resell",Flat Design + Vibrant & Block-based,"Minimalism, Trust & Authority",Filter-Heavy Grid + Map,N/A - Listing focused,Neutral bg + price green + category chips + verified seller badge,Category tree. Photo-first listing cards. Price negotiation. Location radius filter. Saved searches. Seller reputation. Flag/report.
|
||||
172,Conference / Symposium Landing Page,"conference, symposium, summit, cfp, call-for-papers, speaker-lineup, registration, venue, proceedings, keynote, track",Swiss Modernism 2.0 + Minimalism,"Trust & Authority, Accessible & Ethical",Hero + Agenda + CFP,N/A - Event focused,Academic navy + track color chips + gold keynote + neutral white,Speaker grid. Multi-track agenda. CFP deadline countdown. Venue map. Sponsor tiers. Early-bird pricing. Proceedings download.
|
||||
173,Crowdfunding Platform,"crowdfunding, kickstarter, indiegogo, campaign, backer, pledge, funding-goal, stretch-goal, reward-tier, all-or-nothing",Vibrant & Block-based + Motion-Driven,"Claymorphism, Storytelling-Driven",Storytelling-Driven + Social Proof,Campaign Analytics Dashboard,Brand primary + funding progress green + urgency amber + reward tier colors,Funding progress bar with % goal. Reward tier selector. Backer count. Countdown timer. Updates feed. Creator profile. Risk/disclaimer section.
|
||||
174,Digital Signage / Kiosk,"digital-signage, kiosk, interactive-display, touchscreen, wayfinding, lobby-display, menu-board, point-of-sale-display",Minimalism + Dark Mode (OLED),"Flat Design, Motion-Driven",Full-Screen Immersive,N/A - Display focused,High contrast + brand accent + touch target emphasis (56px min),Full-screen single-purpose layout. Touch targets ≥56px. Auto-rotate content. Offline fallback. Brightness-aware color palette. No scroll.
|
||||
175,E-signature / Document Workflow,"esignature, e-sign, docusign, digital-signature, document-workflow, approval-chain, contract-signing, signing-ceremony",Trust & Authority + Minimalism,"Accessible & Ethical, Flat Design",Feature-Rich Showcase + Conversion,Document Pipeline Dashboard,Trust navy + signature green + pending amber + neutral grey,Document preview with annotation. Signature placement UI. Multi-signer workflow. Audit trail. Compliance badges. Mobile signing. Expiry reminders.
|
||||
176,Feature Flag / Config Management,"feature-flag, config, launchdarkly, feature-toggle, experiment, rollout, kill-switch, a-b-test-config, percentage-rollout",Dark Mode (OLED) + Data-Dense,"Minimalism, Trust & Authority",Feature List + Toggle Panel,N/A - Config focused,Dark bg + enabled green + disabled grey + experimental amber + kill-switch red,Feature list with on/off toggles. Percentage rollout slider. Environment selector (prod/staging). User targeting rules. Kill switch. Audit log.
|
||||
177,Government Portal / Civic Services,"government-portal, civic-services, city-hall, permit-application, tax-payment, voter-registration, public-records, municipal-online",Accessible & Ethical + Trust & Authority,"Flat Design, Inclusive Design",Service Directory + Search,N/A - Service focused,Professional blue + accessibility high contrast + service category colors,Multilingual toggle. Service A-Z index. Form wizard with save-progress. Document upload. Appointment booking. Status tracker. WCAG AAA. Plain language.
|
||||
178,Grant / Funding Portal,"grant, funding, rfp, proposal, research-grant, foundation, fellowship, award, application-portal, funding-opportunity",Trust & Authority + Minimalism,"Accessible & Ethical, Swiss Modernism 2.0",Opportunity Grid + Search,Application Tracking Dashboard,Institution navy + funding green + deadline red + neutral white,Funding opportunity cards. Eligibility checker. Deadline countdown. Application form wizard. Document checklist. Review status tracker. Award announcement feed.
|
||||
179,LMS (Learning Management System),"lms, course-management, learning-management, canvas, moodle, blackboard, enrollment, gradebook, syllabus, assignment-submit",Flat Design + Accessible & Ethical,"Minimalism, Vibrant & Block-based",Dashboard + Course Grid,Education Analytics Dashboard,Calm blue + course category colors + grade green + alert red,Dashboard with enrolled courses. Assignment deadlines. Gradebook view. Discussion forums. File upload. Calendar integration. Mobile offline sync.
|
||||
180,No-code / Low-code Builder,"no-code, low-code, builder, bubble, webflow, drag-drop, visual-builder, app-builder, workflow-builder, logic-blocks",Vibrant & Block-based + Bento Box Grid,"Motion-Driven, Glassmorphism",Interactive Product Demo,App Builder Workspace,Brand primary + component palette colors + canvas neutral + connect blue,Drag-drop canvas. Component library sidebar. Logic flow visual editor. Preview pane. Template gallery. Publish button. Version history.
|
||||
181,Open Source Project Landing,"open-source, github-project, oss, contributor, star, fork, pull-request, maintainer, sponsoring, readme, repository",Dark Mode (OLED) + Minimalism,"Trust & Authority, Flat Design",Hero + Install + Contribute,Contributor Analytics Dashboard,Dark bg + language color bar + star gold + fork silver + sponsor purple,Star/fork count badges. Install command (copy-paste). Language breakdown bar. Top contributors grid. Sponsor CTA. Documentation link. Issue/pr status.
|
||||
182,Patient Portal / Health Records,"patient-portal, health-records, ehr, emr, mychart, lab-results, prescription-refill, medical-history, test-results, care-team",Trust & Authority + Accessible & Ethical,"Minimalism, Flat Design",Health Summary Dashboard,Healthcare Analytics,Clinical blue + health green + alert red + calm white + accessible contrast,Labs and results timeline. Medication list with refill. Appointment scheduling. Message care team. Immunization records. Allergy alerts. Family access proxy.
|
||||
183,Patent / IP Database,"patent, intellectual-property, trademark, prior-art, uspto, wipo, invention, ip-portfolio, patent-search, claims",Swiss Modernism 2.0 + Minimalism,"Trust & Authority, Data-Dense",Search-First + Results Grid,N/A - Search focused,Formal neutral + patent type chips + status badges (granted/pending/rejected),Full-text patent search. Classification tree. Citation graph. Prior art comparison. Patent family view. PDF download. Legal status tracker.
|
||||
184,Q&A Community Platform,"qa, stack-overflow, question-answer, knowledge-sharing, community-qa, expert-answer, upvote, accepted-answer, reputation",Minimalism + Flat Design,"Dark Mode (OLED), Accessible & Ethical",Feed + Thread View,Community Analytics Dashboard,Clean white + upvote orange + accepted green + reputation gold + tag colors,Question list with vote count. Rich code blocks. Tag filter. Reputation system. Accepted answer highlight. Comment threads. Bookmark/save.
|
||||
185,Research Lab / University Department,"research-lab, university-department, academic-lab, principal-investigator, lab-members, publications, research-group, pi-page",Swiss Modernism 2.0 + Minimalism,"Trust & Authority, Accessible & Ethical",Overview + People + Publications,N/A - Academic focused,Institutional navy + white + research area accent colors + serif headings,PI bio and research focus. Current members grid. Publication list with links. Open positions. Lab facilities photos. Funding acknowledgments.
|
||||
186,Resume / CV Builder,"resume, cv, builder, job-search, curriculum-vitae, portfolio-resume, cover-letter, career-builder, ats-friendly",Minimalism + Flat Design,"Swiss Modernism 2.0, Trust & Authority",Interactive Product Demo + CTA,Template Selection Gallery,Professional navy + section accent + success green + clean white,Template picker. Section-by-section editor. Real-time preview. ATS score indicator. PDF export. Cover letter generator. Import from LinkedIn.
|
||||
187,Review Platform,"review, rating, yelp, trustpilot, testimonial, customer-review, star-rating, verified-purchase, pros-cons",Flat Design + Vibrant & Block-based,"Trust & Authority, Minimalism",Hero + Rating Summary + Review Feed,Review Analytics Dashboard,Brand primary + star gold + positive green + negative red + verified blue,Star rating summary with distribution. Verified purchase badge. Photo/video reviews. Helpful/upvote. Filter by rating. Response from business. Sort by recency.
|
||||
188,RPA / Automation Dashboard,"rpa, robotic-process-automation, uipath, automation-anywhere, bot-orchestrator, process-discovery, attended-bot, unattended-bot",Dark Mode (OLED) + Data-Dense,"Minimalism, Trust & Authority",Bot Fleet Dashboard,Real-Time Monitoring + Process Analytics,Dark bg + running green + failed red + queued amber + completed blue,Bot status grid (running/idle/failed). Queue depth. Process flow visualization. Exception handling alert. ROI metrics. Bot scheduling calendar. Audit trail.
|
||||
189,Survey / Form Builder,"survey, form-builder, questionnaire, typeform, survey-monkey, poll, feedback-form, multi-step-form, nps-survey, logic-jump",Minimalism + Micro-interactions,"Claymorphism, Flat Design",Interactive Product Demo,Response Analytics Dashboard,Clean white + question accent + progress green + submit blue,Drag-drop form builder. Question type library. Conditional logic visualizer. Theme picker. Response dashboard with charts. Export CSV. Share link/QR/embed.
|
||||
190,Telemedicine Platform,"telemedicine, telehealth, virtual-visit, remote-consultation, video-doctor, remote-patient-monitoring, telehealth-app",Neumorphism + Accessible & Ethical,"Trust & Authority, Soft UI Evolution",Trust & Authority + Conversion,Healthcare Analytics,Calm medical blue + video green + waiting amber + trust white,Video call UI with screen share. Appointment queue. Symptom intake form. Prescription e-delivery. Waiting room with ETA. Post-visit summary. Insurance verification.
|
||||
191,Testimonial & Social Proof Widget,"testimonial, social-proof, wall-of-love, customer-quote, case-study, review-widget, trust-signal, user-story",Vibrant & Block-based + Flat Design,"Motion-Driven, Minimalism",Wall-of-Love Grid,Engagement Analytics Dashboard,Brand primary + quote accent + star gold + verified blue,Testimonial cards with photo. Star ratings. Video testimonials. Case study summaries. Filter by industry/product. Embeddable widget code. Auto-rotate carousel.
|
||||
192,Ticketing / Box Office,"ticketing, box-office, eventbrite, ticket-sales, seat-selection, will-call, qr-ticket, venue-capacity, will-call-pickup",Vibrant & Block-based + Motion-Driven,"Dark Mode (OLED), Glassmorphism",Event Grid + Seat Map,Sales Analytics Dashboard,Event theme colors + available green + sold-out red + seat map neutral,Event cards with date/venue. Interactive seat map. Cart with countdown. QR code ticket. Will-call pickup. Group discounts. Refund policy.
|
||||
|
||||
|
Can't render this file because it has a wrong number of fields in line 170.
|
@ -55,6 +55,11 @@ CSV_CONFIG = {
|
||||
"search_cols": ["Category", "Icon Name", "Keywords", "Best For"],
|
||||
"output_cols": ["Category", "Icon Name", "Keywords", "Library", "Import Code", "Usage", "Best For", "Style"]
|
||||
},
|
||||
"gsap": {
|
||||
"file": "motion.csv",
|
||||
"search_cols": ["Category", "Intensity Tier", "Keywords", "Trigger"],
|
||||
"output_cols": ["Category", "Intensity Tier", "Trigger", "Duration", "Easing", "GSAP Snippet", "Framework Notes", "Do", "Don't", "Performance Notes"]
|
||||
},
|
||||
"react": {
|
||||
"file": "react-performance.csv",
|
||||
"search_cols": ["Category", "Issue", "Keywords", "Description"],
|
||||
@ -90,6 +95,11 @@ STACK_CONFIG = {
|
||||
"angular": {"file": "stacks/angular.csv"},
|
||||
"laravel": {"file": "stacks/laravel.csv"},
|
||||
"javafx": {"file": "stacks/javafx.csv"},
|
||||
"wpf": {"file": "stacks/wpf.csv"},
|
||||
"winui": {"file": "stacks/winui.csv"},
|
||||
"avalonia": {"file": "stacks/avalonia.csv"},
|
||||
"uno": {"file": "stacks/uno.csv"},
|
||||
"uwp": {"file": "stacks/uwp.csv"},
|
||||
}
|
||||
|
||||
# Common columns for all stacks
|
||||
@ -210,6 +220,7 @@ def detect_domain(query):
|
||||
"typography": ["font pairing", "typography pairing", "heading font", "body font"],
|
||||
"google-fonts": ["google font", "font family", "font weight", "font style", "variable font", "noto", "font for", "find font", "font subset", "font language", "monospace font", "serif font", "sans serif font", "display font", "handwriting font", "font", "typography", "serif", "sans"],
|
||||
"icons": ["icon", "icons", "lucide", "heroicons", "symbol", "glyph", "pictogram", "svg icon"],
|
||||
"gsap": ["gsap", "quickto", "scrolltrigger", "stagger", "magnetic cursor", "parallax", "page transition", "scroll reveal", "scroll-triggered", "scrollytelling", "flip plugin", "splittext", "shimmer", "skeleton loader"],
|
||||
"react": ["react", "next.js", "nextjs", "suspense", "memo", "usecallback", "useeffect", "rerender", "bundle", "waterfall", "barrel", "dynamic import", "rsc", "server component"],
|
||||
"web": ["aria", "focus", "outline", "semantic", "virtualize", "autocomplete", "form", "input type", "preconnect"]
|
||||
}
|
||||
|
||||
@ -40,6 +40,39 @@ SEARCH_CONFIG = {
|
||||
"typography": {"max_results": 2}
|
||||
}
|
||||
|
||||
# ============ DESIGN DIALS (1-10) ============
|
||||
# Inspired by taste-skill's DESIGN_VARIANCE / MOTION_INTENSITY / VISUAL_DENSITY
|
||||
# knobs: three optional 1-10 sliders that bias the existing query-based search
|
||||
# instead of replacing it. Each dial buckets into a low/mid/high tier.
|
||||
DIAL_TIERS = {
|
||||
"variance": [
|
||||
(1, 3, {"label": "Centered / Minimal", "style_keywords": ["Minimalism", "Exaggerated Minimalism", "centered", "symmetric", "grid-based"]}),
|
||||
(4, 7, {"label": "Balanced / Modern", "style_keywords": ["modern", "structured", "balanced"]}),
|
||||
(8, 10, {"label": "Bold / Asymmetric", "style_keywords": ["Brutalism", "Bento Grids", "asymmetric", "experimental"]}),
|
||||
],
|
||||
"motion": [
|
||||
(1, 3, {"label": "Subtle", "tier": "Subtle"}),
|
||||
(4, 7, {"label": "Standard", "tier": "Standard"}),
|
||||
(8, 10, {"label": "Complex", "tier": "Complex"}),
|
||||
],
|
||||
"density": [
|
||||
(1, 3, {"label": "Spacious", "spacing": {"xs": "4px", "sm": "8px", "md": "24px", "lg": "32px", "xl": "48px", "2xl": "64px", "3xl": "96px"}}),
|
||||
(4, 7, {"label": "Standard", "spacing": {"xs": "4px", "sm": "8px", "md": "16px", "lg": "24px", "xl": "32px", "2xl": "48px", "3xl": "64px"}}),
|
||||
(8, 10, {"label": "Dense / Dashboard", "spacing": {"xs": "2px", "sm": "4px", "md": "8px", "lg": "12px", "xl": "16px", "2xl": "24px", "3xl": "32px"}}),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _resolve_dial(dial_name: str, value) -> dict:
|
||||
"""Bucket a 1-10 dial value into its tier config. Returns None if value is None."""
|
||||
if value is None:
|
||||
return None
|
||||
value = max(1, min(10, int(value)))
|
||||
for lo, hi, info in DIAL_TIERS[dial_name]:
|
||||
if lo <= value <= hi:
|
||||
return {**info, "value": value}
|
||||
return None
|
||||
|
||||
|
||||
# ============ DESIGN SYSTEM GENERATOR ============
|
||||
class DesignSystemGenerator:
|
||||
@ -168,8 +201,18 @@ class DesignSystemGenerator:
|
||||
"""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."""
|
||||
def generate(self, query: str, project_name: str = None,
|
||||
variance: int = None, motion: int = None, density: int = None) -> dict:
|
||||
"""Generate complete design system recommendation.
|
||||
|
||||
variance/motion/density are optional 1-10 dials (see DIAL_TIERS) that bias
|
||||
style selection, pull in a matching motion.csv snippet, and override the
|
||||
spacing scale, without changing behavior when left unset.
|
||||
"""
|
||||
variance_info = _resolve_dial("variance", variance)
|
||||
motion_info = _resolve_dial("motion", motion)
|
||||
density_info = _resolve_dial("density", density)
|
||||
|
||||
# Step 1: First search product to get category
|
||||
product_result = search(query, "product", 1)
|
||||
product_results = product_result.get("results", [])
|
||||
@ -181,8 +224,14 @@ class DesignSystemGenerator:
|
||||
reasoning = self._apply_reasoning(category, {})
|
||||
style_priority = reasoning.get("style_priority", [])
|
||||
|
||||
# DESIGN_VARIANCE dial: bias style retrieval/selection toward
|
||||
# centered-minimal (low) or bold-asymmetric (high) keywords.
|
||||
effective_style_priority = style_priority
|
||||
if variance_info:
|
||||
effective_style_priority = variance_info["style_keywords"] + style_priority
|
||||
|
||||
# Step 3: Multi-domain search with style priority hints
|
||||
search_results = self._multi_domain_search(query, style_priority)
|
||||
search_results = self._multi_domain_search(query, effective_style_priority)
|
||||
search_results["product"] = product_result # Reuse product search
|
||||
|
||||
# Step 4: Select best matches from each domain using priority
|
||||
@ -191,11 +240,24 @@ class DesignSystemGenerator:
|
||||
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_style = self._select_best_match(style_results, effective_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 {}
|
||||
|
||||
# MOTION_INTENSITY dial: pull a matching GSAP skeleton from motion.csv
|
||||
# (domain key is "gsap", not "motion" - PR #296 already owns the "motion"
|
||||
# domain for Emil Kowalski's motion-design principles, motion-principles.csv).
|
||||
motion_snippet = {}
|
||||
if motion_info:
|
||||
motion_result = search(f"{query} {motion_info['tier']}", "gsap", 5)
|
||||
motion_matches = motion_result.get("results", [])
|
||||
tiered = [m for m in motion_matches if m.get("Intensity Tier") == motion_info["tier"]]
|
||||
if tiered:
|
||||
motion_snippet = tiered[0]
|
||||
elif motion_matches:
|
||||
motion_snippet = motion_matches[0]
|
||||
|
||||
# Step 5: Build final recommendation
|
||||
# Combine effects from both reasoning and style search
|
||||
style_effects = best_style.get("Effects & Animation", "")
|
||||
@ -250,7 +312,17 @@ class DesignSystemGenerator:
|
||||
"key_effects": combined_effects,
|
||||
"anti_patterns": reasoning.get("anti_patterns", ""),
|
||||
"decision_rules": reasoning.get("decision_rules", {}),
|
||||
"severity": reasoning.get("severity", "MEDIUM")
|
||||
"severity": reasoning.get("severity", "MEDIUM"),
|
||||
"dials": {
|
||||
"variance": variance_info["value"] if variance_info else None,
|
||||
"variance_label": variance_info["label"] if variance_info else None,
|
||||
"motion": motion_info["value"] if motion_info else None,
|
||||
"motion_label": motion_info["label"] if motion_info else None,
|
||||
"density": density_info["value"] if density_info else None,
|
||||
"density_label": density_info["label"] if density_info else None,
|
||||
},
|
||||
"motion_snippet": motion_snippet,
|
||||
"spacing_scale": density_info["spacing"] if density_info else None,
|
||||
}
|
||||
|
||||
|
||||
@ -296,6 +368,8 @@ def format_ascii_box(design_system: dict) -> str:
|
||||
typography = design_system.get("typography", {})
|
||||
effects = design_system.get("key_effects", "")
|
||||
anti_patterns = design_system.get("anti_patterns", "")
|
||||
dials = design_system.get("dials", {})
|
||||
motion_snippet = design_system.get("motion_snippet", {})
|
||||
|
||||
def wrap_text(text: str, prefix: str, width: int) -> list:
|
||||
"""Wrap long text into multiple lines."""
|
||||
@ -329,6 +403,16 @@ def format_ascii_box(design_system: dict) -> str:
|
||||
lines.append("╚" + "═" * w + "╝")
|
||||
lines.append("┌" + "─" * w + "┐")
|
||||
|
||||
# Design Dials section (only if at least one dial was set)
|
||||
if any(dials.get(k) is not None for k in ("variance", "motion", "density")):
|
||||
lines.append(section_header("DESIGN DIALS", BOX_WIDTH + 1))
|
||||
if dials.get("variance") is not None:
|
||||
lines.append(f"│ Variance: {dials['variance']}/10 — {dials['variance_label']}".ljust(BOX_WIDTH) + "│")
|
||||
if dials.get("motion") is not None:
|
||||
lines.append(f"│ Motion: {dials['motion']}/10 — {dials['motion_label']}".ljust(BOX_WIDTH) + "│")
|
||||
if dials.get("density") is not None:
|
||||
lines.append(f"│ Density: {dials['density']}/10 — {dials['density_label']}".ljust(BOX_WIDTH) + "│")
|
||||
|
||||
# Pattern section
|
||||
lines.append(section_header("PATTERN", BOX_WIDTH + 1))
|
||||
lines.append(f"│ Name: {pattern.get('name', '')}".ljust(BOX_WIDTH) + "│")
|
||||
@ -402,6 +486,17 @@ def format_ascii_box(design_system: dict) -> str:
|
||||
for line in wrap_text(effects, "│ ", BOX_WIDTH):
|
||||
lines.append(line.ljust(BOX_WIDTH) + "│")
|
||||
|
||||
# Motion section (GSAP skeleton, only if --motion dial was set)
|
||||
if motion_snippet:
|
||||
lines.append(section_header("MOTION", BOX_WIDTH + 1))
|
||||
lines.append(f"│ {motion_snippet.get('Category', '')} ({motion_snippet.get('Intensity Tier', '')})".ljust(BOX_WIDTH) + "│")
|
||||
lines.append(f"│ Trigger: {motion_snippet.get('Trigger', '')} | Duration: {motion_snippet.get('Duration', '')} | Easing: {motion_snippet.get('Easing', '')}".ljust(BOX_WIDTH) + "│")
|
||||
for line in wrap_text(f"GSAP: {motion_snippet.get('GSAP Snippet', '')}", "│ ", BOX_WIDTH):
|
||||
lines.append(line.ljust(BOX_WIDTH) + "│")
|
||||
if motion_snippet.get("Framework Notes"):
|
||||
for line in wrap_text(f"Framework: {motion_snippet.get('Framework Notes', '')}", "│ ", BOX_WIDTH):
|
||||
lines.append(line.ljust(BOX_WIDTH) + "│")
|
||||
|
||||
# Anti-patterns section
|
||||
if anti_patterns:
|
||||
lines.append(section_header("AVOID", BOX_WIDTH + 1))
|
||||
@ -436,11 +531,24 @@ def format_markdown(design_system: dict) -> str:
|
||||
typography = design_system.get("typography", {})
|
||||
effects = design_system.get("key_effects", "")
|
||||
anti_patterns = design_system.get("anti_patterns", "")
|
||||
dials = design_system.get("dials", {})
|
||||
motion_snippet = design_system.get("motion_snippet", {})
|
||||
|
||||
lines = []
|
||||
lines.append(f"## Design System: {project}")
|
||||
lines.append("")
|
||||
|
||||
# Design Dials section (only if at least one dial was set)
|
||||
if any(dials.get(k) is not None for k in ("variance", "motion", "density")):
|
||||
lines.append("### Design Dials")
|
||||
if dials.get("variance") is not None:
|
||||
lines.append(f"- **Variance:** {dials['variance']}/10 — {dials['variance_label']}")
|
||||
if dials.get("motion") is not None:
|
||||
lines.append(f"- **Motion:** {dials['motion']}/10 — {dials['motion_label']}")
|
||||
if dials.get("density") is not None:
|
||||
lines.append(f"- **Density:** {dials['density']}/10 — {dials['density_label']}")
|
||||
lines.append("")
|
||||
|
||||
# Pattern section
|
||||
lines.append("### Pattern")
|
||||
lines.append(f"- **Name:** {pattern.get('name', '')}")
|
||||
@ -515,6 +623,23 @@ def format_markdown(design_system: dict) -> str:
|
||||
lines.append(f"{effects}")
|
||||
lines.append("")
|
||||
|
||||
# Motion section (GSAP skeleton, only if --motion dial was set)
|
||||
if motion_snippet:
|
||||
lines.append("### Motion")
|
||||
lines.append(f"**{motion_snippet.get('Category', '')}** ({motion_snippet.get('Intensity Tier', '')}) — Trigger: {motion_snippet.get('Trigger', '')} | Duration: {motion_snippet.get('Duration', '')} | Easing: `{motion_snippet.get('Easing', '')}`")
|
||||
lines.append("```js")
|
||||
lines.append(motion_snippet.get("GSAP Snippet", ""))
|
||||
lines.append("```")
|
||||
if motion_snippet.get("Framework Notes"):
|
||||
lines.append(f"*Framework notes: {motion_snippet.get('Framework Notes', '')}*")
|
||||
motion_do = motion_snippet.get("Do", "")
|
||||
motion_dont = motion_snippet.get("Don't", "")
|
||||
if motion_do:
|
||||
lines.append(f"- ✅ {motion_do}")
|
||||
if motion_dont:
|
||||
lines.append(f"- ❌ {motion_dont}")
|
||||
lines.append("")
|
||||
|
||||
# Anti-patterns section
|
||||
if anti_patterns:
|
||||
lines.append("### Avoid (Anti-patterns)")
|
||||
@ -537,8 +662,9 @@ def format_markdown(design_system: dict) -> str:
|
||||
|
||||
|
||||
# ============ MAIN ENTRY POINT ============
|
||||
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:
|
||||
def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii",
|
||||
persist: bool = False, page: str = None, output_dir: str = None,
|
||||
variance: int = None, motion: int = None, density: int = None) -> str:
|
||||
"""
|
||||
Main entry point for design system generation.
|
||||
|
||||
@ -549,13 +675,16 @@ def generate_design_system(query: str, project_name: str = None, output_format:
|
||||
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)
|
||||
variance: Optional 1-10 DESIGN_VARIANCE dial (1=centered/minimal, 10=bold/asymmetric)
|
||||
motion: Optional 1-10 MOTION_INTENSITY dial, pulls a matching GSAP snippet from motion.csv
|
||||
density: Optional 1-10 VISUAL_DENSITY dial, overrides the spacing scale (1=spacious, 10=dense)
|
||||
|
||||
Returns:
|
||||
Formatted design system string
|
||||
"""
|
||||
generator = DesignSystemGenerator()
|
||||
design_system = generator.generate(query, project_name)
|
||||
|
||||
design_system = generator.generate(query, project_name, variance=variance, motion=motion, density=density)
|
||||
|
||||
# Persist to files if requested
|
||||
if persist:
|
||||
persist_design_system(design_system, page, output_dir, query)
|
||||
@ -627,11 +756,14 @@ def format_master_md(design_system: dict) -> str:
|
||||
typography = design_system.get("typography", {})
|
||||
effects = design_system.get("key_effects", "")
|
||||
anti_patterns = design_system.get("anti_patterns", "")
|
||||
|
||||
dials = design_system.get("dials", {})
|
||||
motion_snippet = design_system.get("motion_snippet", {})
|
||||
spacing_scale = design_system.get("spacing_scale")
|
||||
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
|
||||
lines = []
|
||||
|
||||
|
||||
# Logic header
|
||||
lines.append("# Design System Master File")
|
||||
lines.append("")
|
||||
@ -644,6 +776,15 @@ def format_master_md(design_system: dict) -> str:
|
||||
lines.append(f"**Project:** {project}")
|
||||
lines.append(f"**Generated:** {timestamp}")
|
||||
lines.append(f"**Category:** {design_system.get('category', 'General')}")
|
||||
if any(dials.get(k) is not None for k in ("variance", "motion", "density")):
|
||||
dial_parts = []
|
||||
if dials.get("variance") is not None:
|
||||
dial_parts.append(f"Variance {dials['variance']}/10 ({dials['variance_label']})")
|
||||
if dials.get("motion") is not None:
|
||||
dial_parts.append(f"Motion {dials['motion']}/10 ({dials['motion_label']})")
|
||||
if dials.get("density") is not None:
|
||||
dial_parts.append(f"Density {dials['density']}/10 ({dials['density_label']})")
|
||||
lines.append(f"**Design Dials:** {' | '.join(dial_parts)}")
|
||||
lines.append("")
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
@ -695,18 +836,24 @@ def format_master_md(design_system: dict) -> str:
|
||||
lines.append("```")
|
||||
lines.append("")
|
||||
|
||||
# Spacing Variables
|
||||
# Spacing Variables (overridden by the VISUAL_DENSITY dial when set)
|
||||
default_spacing = DIAL_TIERS["density"][1][2]["spacing"] # mid-tier = the historical defaults
|
||||
scale = spacing_scale or default_spacing
|
||||
spacing_usage = {
|
||||
"xs": "Tight gaps", "sm": "Icon gaps, inline spacing", "md": "Standard padding",
|
||||
"lg": "Section padding", "xl": "Large gaps", "2xl": "Section margins", "3xl": "Hero padding",
|
||||
}
|
||||
lines.append("### Spacing Variables")
|
||||
lines.append("")
|
||||
if spacing_scale:
|
||||
lines.append(f"*Density: {dials.get('density')}/10 — {dials.get('density_label')}*")
|
||||
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 |")
|
||||
for token in ("xs", "sm", "md", "lg", "xl", "2xl", "3xl"):
|
||||
px_value = scale[token]
|
||||
rem_value = f"{int(px_value.rstrip('px')) / 16:g}rem"
|
||||
lines.append(f"| `--space-{token}` | `{px_value}` / `{rem_value}` | {spacing_usage[token]} |")
|
||||
lines.append("")
|
||||
|
||||
# Shadow Depths
|
||||
@ -848,7 +995,32 @@ def format_master_md(design_system: dict) -> str:
|
||||
lines.append(f"- **CTA Placement:** {pattern.get('cta_placement', '')}")
|
||||
lines.append(f"- **Section Order:** {pattern.get('sections', '')}")
|
||||
lines.append("")
|
||||
|
||||
|
||||
# Motion section (GSAP skeleton, only if --motion dial was set)
|
||||
if motion_snippet:
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
lines.append("## Motion")
|
||||
lines.append("")
|
||||
lines.append(f"**{motion_snippet.get('Category', '')}** ({motion_snippet.get('Intensity Tier', '')}) — Trigger: {motion_snippet.get('Trigger', '')} | Duration: {motion_snippet.get('Duration', '')} | Easing: `{motion_snippet.get('Easing', '')}`")
|
||||
lines.append("")
|
||||
lines.append("```js")
|
||||
lines.append(motion_snippet.get("GSAP Snippet", ""))
|
||||
lines.append("```")
|
||||
lines.append("")
|
||||
if motion_snippet.get("Framework Notes"):
|
||||
lines.append(f"**Framework notes:** {motion_snippet.get('Framework Notes', '')}")
|
||||
lines.append("")
|
||||
motion_do = motion_snippet.get("Do", "")
|
||||
motion_dont = motion_snippet.get("Don't", "")
|
||||
if motion_do:
|
||||
lines.append(f"- ✅ {motion_do}")
|
||||
if motion_dont:
|
||||
lines.append(f"- ❌ {motion_dont}")
|
||||
if motion_snippet.get("Performance Notes"):
|
||||
lines.append(f"- ⚡ {motion_snippet.get('Performance Notes', '')}")
|
||||
lines.append("")
|
||||
|
||||
# Anti-Patterns section
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
|
||||
@ -5,9 +5,15 @@ 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]
|
||||
python search.py "<query>" --design-system [-p "Project Name"]
|
||||
python search.py "<query>" --design-system --persist [-p "Project Name"] [--page "dashboard"]
|
||||
python search.py "<query>" --design-system --variance 8 --motion 9 --density 7
|
||||
|
||||
Domains: style, prompt, color, chart, landing, product, ux, typography, google-fonts
|
||||
Stacks: react, nextjs, vue, svelte, astro, swiftui, react-native, flutter, nuxtjs, nuxt-ui, html-tailwind, shadcn, jetpack-compose, threejs, angular, laravel, javafx
|
||||
Domains: style, prompt, color, chart, landing, product, ux, typography, google-fonts, gsap
|
||||
Stacks: react, nextjs, vue, svelte, astro, swiftui, react-native, flutter, nuxtjs, nuxt-ui, html-tailwind, shadcn, jetpack-compose, threejs, angular, laravel, javafx, wpf, winui, avalonia, uno, uwp
|
||||
|
||||
Design dials (1-10, only with --design-system):
|
||||
--variance DESIGN_VARIANCE: 1=centered/minimal, 10=bold/asymmetric
|
||||
--motion MOTION_INTENSITY: 1=subtle, 10=complex; attaches a GSAP snippet from motion.csv
|
||||
--density VISUAL_DENSITY: 1=spacious, 10=dense/dashboard; overrides the spacing scale
|
||||
|
||||
Persistence (Master + Overrides pattern):
|
||||
--persist Save design system to design-system/MASTER.md
|
||||
@ -68,18 +74,25 @@ if __name__ == "__main__":
|
||||
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)")
|
||||
# Design dials (1-10), only applied with --design-system
|
||||
parser.add_argument("--variance", type=int, choices=range(1, 11), metavar="1-10", help="DESIGN_VARIANCE dial: 1=centered/minimal, 10=bold/asymmetric (only with --design-system)")
|
||||
parser.add_argument("--motion", type=int, choices=range(1, 11), metavar="1-10", help="MOTION_INTENSITY dial: 1=subtle, 10=complex; pulls a matching GSAP snippet from motion.csv (only with --design-system)")
|
||||
parser.add_argument("--density", type=int, choices=range(1, 11), metavar="1-10", help="VISUAL_DENSITY dial: 1=spacious, 10=dense/dashboard; overrides the spacing scale (only with --design-system)")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Design system takes priority
|
||||
if args.design_system:
|
||||
result = generate_design_system(
|
||||
args.query,
|
||||
args.project_name,
|
||||
args.query,
|
||||
args.project_name,
|
||||
args.format,
|
||||
persist=args.persist,
|
||||
page=args.page,
|
||||
output_dir=args.output_dir
|
||||
output_dir=args.output_dir,
|
||||
variance=args.variance,
|
||||
motion=args.motion,
|
||||
density=args.density
|
||||
)
|
||||
print(result)
|
||||
|
||||
|
||||
@ -29,61 +29,47 @@ function extractColorsFromMarkdown(content) {
|
||||
accent: { name: 'accent', shades: {} }
|
||||
};
|
||||
|
||||
// Extract primary color name and hex from Quick Reference table
|
||||
const quickRefMatch = content.match(/Primary Color\s*\|\s*#([A-Fa-f0-9]{6})\s*\(([^)]+)\)/);
|
||||
if (quickRefMatch) {
|
||||
colors.primary.name = quickRefMatch[2].toLowerCase().replace(/\s+/g, '-');
|
||||
colors.primary.base = `#${quickRefMatch[1]}`;
|
||||
// Match a "| Label | #hex |" markdown table row. Bold around the label
|
||||
// (**Label**) is optional, so this handles both the bundled starter template
|
||||
// ("| Primary Blue | #2563EB |") and bolded variants.
|
||||
const rowRe = /\|\s*\*{0,2}([^*|]+?)\*{0,2}\s*\|\s*#([A-Fa-f0-9]{6})\b/g;
|
||||
|
||||
// 1) Quick Reference table — hex only, no parenthesized name required.
|
||||
const quickRef = {
|
||||
primary: /Primary Color\s*\|\s*#([A-Fa-f0-9]{6})/i,
|
||||
secondary: /Secondary Color\s*\|\s*#([A-Fa-f0-9]{6})/i,
|
||||
accent: /Accent Color\s*\|\s*#([A-Fa-f0-9]{6})/i
|
||||
};
|
||||
for (const key of Object.keys(quickRef)) {
|
||||
const m = content.match(quickRef[key]);
|
||||
if (m) colors[key].base = `#${m[1]}`;
|
||||
}
|
||||
|
||||
const secondaryMatch = content.match(/Secondary Color\s*\|\s*#([A-Fa-f0-9]{6})\s*\(([^)]+)\)/);
|
||||
if (secondaryMatch) {
|
||||
colors.secondary.name = secondaryMatch[2].toLowerCase().replace(/\s+/g, '-');
|
||||
colors.secondary.base = `#${secondaryMatch[1]}`;
|
||||
}
|
||||
|
||||
const accentMatch = content.match(/Accent Color\s*\|\s*#([A-Fa-f0-9]{6})\s*\(([^)]+)\)/);
|
||||
if (accentMatch) {
|
||||
colors.accent.name = accentMatch[2].toLowerCase().replace(/\s+/g, '-');
|
||||
colors.accent.base = `#${accentMatch[1]}`;
|
||||
}
|
||||
|
||||
// Extract all shades from Primary Colors table
|
||||
const primarySection = content.match(/### Primary Colors[\s\S]*?\|[\s\S]*?(?=###|$)/i);
|
||||
if (primarySection) {
|
||||
const hexMatches = primarySection[0].matchAll(/\*\*([^*]+)\*\*\s*\|\s*#([A-Fa-f0-9]{6})/g);
|
||||
for (const match of hexMatches) {
|
||||
const name = match[1].trim().toLowerCase();
|
||||
const hex = `#${match[2]}`;
|
||||
if (name.includes('dark')) colors.primary.dark = hex;
|
||||
else if (name.includes('light')) colors.primary.light = hex;
|
||||
else colors.primary.base = hex;
|
||||
// 2) Dedicated "### <Role> Colors" tables — assign base/dark/light by the
|
||||
// row label keyword.
|
||||
const assignFromSection = (heading, target) => {
|
||||
const section = content.match(new RegExp(`### ${heading}[\\s\\S]*?(?=\\n###|$)`, 'i'));
|
||||
if (!section) return;
|
||||
for (const m of section[0].matchAll(rowRe)) {
|
||||
const label = m[1].trim().toLowerCase();
|
||||
const hex = `#${m[2]}`;
|
||||
if (label.includes('dark')) target.dark = hex;
|
||||
else if (label.includes('light')) target.light = hex;
|
||||
else if (!target.base) target.base = hex;
|
||||
}
|
||||
}
|
||||
};
|
||||
assignFromSection('Primary Colors', colors.primary);
|
||||
assignFromSection('Secondary Colors', colors.secondary);
|
||||
assignFromSection('Accent Colors', colors.accent);
|
||||
|
||||
// Extract secondary shades
|
||||
const secondarySection = content.match(/### Secondary Colors[\s\S]*?\|[\s\S]*?(?=###|$)/i);
|
||||
if (secondarySection) {
|
||||
const hexMatches = secondarySection[0].matchAll(/\*\*([^*]+)\*\*\s*\|\s*#([A-Fa-f0-9]{6})/g);
|
||||
for (const match of hexMatches) {
|
||||
const name = match[1].trim().toLowerCase();
|
||||
const hex = `#${match[2]}`;
|
||||
if (name.includes('dark')) colors.secondary.dark = hex;
|
||||
else if (name.includes('light')) colors.secondary.light = hex;
|
||||
else colors.secondary.base = hex;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract accent shades
|
||||
const accentSection = content.match(/### Accent Colors[\s\S]*?\|[\s\S]*?(?=###|$)/i);
|
||||
if (accentSection) {
|
||||
const hexMatches = accentSection[0].matchAll(/\*\*([^*]+)\*\*\s*\|\s*#([A-Fa-f0-9]{6})/g);
|
||||
for (const match of hexMatches) {
|
||||
const name = match[1].trim().toLowerCase();
|
||||
const hex = `#${match[2]}`;
|
||||
if (name.includes('dark')) colors.accent.dark = hex;
|
||||
else if (name.includes('light')) colors.accent.light = hex;
|
||||
else colors.accent.base = hex;
|
||||
// 3) Fallback: an accent swatch may live in another table (the starter
|
||||
// lists "Accent Green" under Secondary Colors).
|
||||
if (!colors.accent.base) {
|
||||
for (const m of content.matchAll(rowRe)) {
|
||||
if (m[1].trim().toLowerCase().includes('accent')) {
|
||||
colors.accent.base = `#${m[2]}`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,6 +99,7 @@ function generateColorScale(baseHex, darkHex, lightHex) {
|
||||
* Adjust hex color brightness
|
||||
*/
|
||||
function adjustBrightness(hex, percent) {
|
||||
if (typeof hex !== 'string') return '#000000';
|
||||
const num = parseInt(hex.replace('#', ''), 16);
|
||||
const r = Math.min(255, Math.max(0, (num >> 16) + Math.round(255 * percent)));
|
||||
const g = Math.min(255, Math.max(0, ((num >> 8) & 0x00FF) + Math.round(255 * percent)));
|
||||
@ -129,29 +116,24 @@ function updateDesignTokens(tokens, colors) {
|
||||
tokens.brand = brandName;
|
||||
|
||||
// Update primitive colors with new names
|
||||
const primitiveColors = tokens.primitive?.color || {};
|
||||
tokens.primitive = tokens.primitive || {};
|
||||
const primitiveColors = tokens.primitive.color || {};
|
||||
|
||||
// Remove old color keys, add new ones
|
||||
delete primitiveColors.coral;
|
||||
delete primitiveColors.purple;
|
||||
delete primitiveColors.mint;
|
||||
|
||||
// Add new named colors
|
||||
primitiveColors[colors.primary.name] = generateColorScale(
|
||||
colors.primary.base,
|
||||
colors.primary.dark,
|
||||
colors.primary.light
|
||||
);
|
||||
primitiveColors[colors.secondary.name] = generateColorScale(
|
||||
colors.secondary.base,
|
||||
colors.secondary.dark,
|
||||
colors.secondary.light
|
||||
);
|
||||
primitiveColors[colors.accent.name] = generateColorScale(
|
||||
colors.accent.base,
|
||||
colors.accent.dark,
|
||||
colors.accent.light
|
||||
);
|
||||
// Add new named colors. Skip any role with no base hex rather than crashing
|
||||
// on an unexpected guidelines format.
|
||||
for (const role of ['primary', 'secondary', 'accent']) {
|
||||
const c = colors[role];
|
||||
if (!c.base) {
|
||||
console.warn(`⚠️ No base hex found for ${role} color — skipping its token scale.`);
|
||||
continue;
|
||||
}
|
||||
primitiveColors[c.name] = generateColorScale(c.base, c.dark, c.light);
|
||||
}
|
||||
|
||||
tokens.primitive.color = primitiveColors;
|
||||
|
||||
@ -191,7 +173,7 @@ function updateDesignTokens(tokens, colors) {
|
||||
}
|
||||
|
||||
// Update component references (button uses primary color with opacity)
|
||||
if (tokens.component?.button?.secondary) {
|
||||
if (tokens.component?.button?.secondary && colors.primary.base) {
|
||||
const primaryBase = colors.primary.base;
|
||||
tokens.component.button.secondary['bg-hover'] = {
|
||||
"$value": `${primaryBase}1A`,
|
||||
|
||||
@ -0,0 +1,52 @@
|
||||
"""Regression test for sync-brand-to-tokens.cjs.
|
||||
|
||||
The color parser required a parenthesized name in the Quick Reference row
|
||||
(`#2563EB (name)`) and a bolded label in the color tables (`**Primary Blue**`),
|
||||
neither of which the bundled starter template uses. As a result the base hex
|
||||
came back `undefined` and `adjustBrightness(undefined)` threw a TypeError —
|
||||
i.e. the script crashed on its own documented happy path. This test runs the
|
||||
sync against the bundled starter template and asserts it completes and writes
|
||||
the expected base colors. It is pytest-based so the existing pytest CI runs it.
|
||||
"""
|
||||
|
||||
import json
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
SCRIPTS = Path(__file__).resolve().parent.parent
|
||||
SCRIPT = SCRIPTS / "sync-brand-to-tokens.cjs"
|
||||
BRAND_STARTER = SCRIPTS.parent / "templates" / "brand-guidelines-starter.md"
|
||||
TOKENS_STARTER = (
|
||||
SCRIPTS.parent.parent / "design-system" / "templates" / "design-tokens-starter.json"
|
||||
)
|
||||
|
||||
|
||||
def test_sync_parses_bundled_starter_template(tmp_path):
|
||||
node = shutil.which("node")
|
||||
if not node:
|
||||
pytest.skip("node not available")
|
||||
|
||||
(tmp_path / "docs").mkdir()
|
||||
(tmp_path / "assets").mkdir()
|
||||
shutil.copy(BRAND_STARTER, tmp_path / "docs" / "brand-guidelines.md")
|
||||
shutil.copy(TOKENS_STARTER, tmp_path / "assets" / "design-tokens.json")
|
||||
|
||||
result = subprocess.run(
|
||||
[node, str(SCRIPT)],
|
||||
cwd=tmp_path,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
# Must not crash (the bug raised an unhandled TypeError).
|
||||
assert "TypeError" not in result.stderr, result.stderr
|
||||
assert result.returncode == 0, result.stderr + result.stdout
|
||||
|
||||
tokens = json.loads((tmp_path / "assets" / "design-tokens.json").read_text())
|
||||
primitive = tokens["primitive"]["color"]
|
||||
assert primitive["primary"]["500"]["$value"] == "#2563EB"
|
||||
assert primitive["secondary"]["500"]["$value"] == "#8B5CF6"
|
||||
assert primitive["accent"]["500"]["$value"] == "#10B981"
|
||||
@ -0,0 +1,48 @@
|
||||
"""Regression tests for validate-tokens.cjs.
|
||||
|
||||
The validator used to skip any line containing ``var(--`` outright, so a
|
||||
hardcoded value sharing a line with a token reference (extremely common in
|
||||
real CSS, and universal in minified CSS where everything is one line) went
|
||||
undetected. These tests drive the CLI via ``node`` and assert it flags such
|
||||
cases. They are pytest-based so the repository's existing pytest CI runs them.
|
||||
"""
|
||||
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
SCRIPT = Path(__file__).resolve().parent.parent / "validate-tokens.cjs"
|
||||
|
||||
|
||||
def _run(tmp_path: Path, css: str) -> subprocess.CompletedProcess:
|
||||
node = shutil.which("node")
|
||||
if not node:
|
||||
pytest.skip("node not available")
|
||||
(tmp_path / "sample.css").write_text(css)
|
||||
return subprocess.run(
|
||||
[node, str(SCRIPT), "--dir", str(tmp_path)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
|
||||
def test_flags_hardcoded_hex_sharing_line_with_token(tmp_path):
|
||||
"""A hardcoded hex on the same line as a var() token is still a violation."""
|
||||
result = _run(
|
||||
tmp_path,
|
||||
".btn { background: #FF6B6B; color: var(--color-primary); }\n",
|
||||
)
|
||||
assert "#FF6B6B" in result.stdout, result.stdout
|
||||
assert result.returncode == 1
|
||||
|
||||
|
||||
def test_token_only_line_reports_no_violation(tmp_path):
|
||||
"""A line that references only tokens produces no false positives."""
|
||||
result = _run(
|
||||
tmp_path,
|
||||
".btn { background: var(--color-bg); color: var(--color-primary); }\n",
|
||||
)
|
||||
assert "No token violations" in result.stdout, result.stdout
|
||||
assert result.returncode == 0
|
||||
@ -137,11 +137,6 @@ function scanFile(filePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip lines that already use CSS variables
|
||||
if (line.includes('var(--')) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [name, pattern] of Object.entries(patterns)) {
|
||||
const matches = line.match(pattern.regex);
|
||||
if (matches) {
|
||||
|
||||
@ -213,7 +213,7 @@ class TailwindConfigGenerator:
|
||||
return f"""import type {{ Config }} from 'tailwindcss'
|
||||
|
||||
const config: Config = {{
|
||||
{self._indent_json(config_json, 1)}
|
||||
{self._indent_json(config_json, 1)},
|
||||
plugins: [{plugins_str}],
|
||||
}}
|
||||
|
||||
@ -230,7 +230,7 @@ export default config
|
||||
|
||||
return f"""/** @type {{import('tailwindcss').Config}} */
|
||||
module.exports = {{
|
||||
{self._indent_json(config_json, 1)}
|
||||
{self._indent_json(config_json, 1)},
|
||||
plugins: [{plugins_str}],
|
||||
}}
|
||||
"""
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
"""Tests for tailwind_config_gen.py"""
|
||||
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
@ -334,3 +336,59 @@ class TestTailwindConfigGenerator:
|
||||
assert "module.exports" in content
|
||||
assert "primary" in content
|
||||
assert "@tailwindcss/forms" in content
|
||||
|
||||
|
||||
def _strip_to_object(config_str: str) -> str:
|
||||
"""Reduce a generated TS/JS config to a bare assignable object so it can be
|
||||
handed to `node --check` without a TypeScript loader."""
|
||||
lines = []
|
||||
for line in config_str.splitlines():
|
||||
if line.startswith("import type"):
|
||||
continue
|
||||
if line.strip() == "export default config":
|
||||
continue
|
||||
line = line.replace("const config: Config =", "const config =")
|
||||
line = line.replace("module.exports =", "const config =")
|
||||
lines.append(line)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
class TestGeneratedConfigIsValidJs:
|
||||
"""Regression guard for the missing-comma bug between the ``theme`` block and
|
||||
``plugins`` that produced syntactically invalid config files. The data-shape
|
||||
tests above all passed while the emitted string was unparseable, so these
|
||||
tests validate the serialized output itself."""
|
||||
|
||||
@pytest.mark.parametrize("typescript", [True, False])
|
||||
def test_property_before_plugins_is_comma_terminated(self, typescript):
|
||||
"""The property preceding ``plugins`` must end with a comma (pure-Python
|
||||
check, so the regression is caught even where node is unavailable)."""
|
||||
generator = TailwindConfigGenerator(typescript=typescript)
|
||||
generator.add_colors({"brand": "#6366F1"})
|
||||
generator.add_breakpoints({"3xl": "1920px"})
|
||||
config = generator.generate_config_string()
|
||||
|
||||
assert "}\n plugins:" not in config, "missing comma before plugins"
|
||||
assert "},\n plugins:" in config
|
||||
|
||||
@pytest.mark.parametrize("typescript", [True, False])
|
||||
def test_node_check_parses_generated_config(self, typescript, tmp_path):
|
||||
"""The emitted config parses as valid JS via ``node --check``."""
|
||||
node = shutil.which("node")
|
||||
if not node:
|
||||
pytest.skip("node not available")
|
||||
|
||||
generator = TailwindConfigGenerator(typescript=typescript)
|
||||
generator.add_colors({"brand": "#6366F1", "accent": "#10B981"})
|
||||
generator.add_fonts({"sans": ["Inter"]})
|
||||
generator.add_breakpoints({"3xl": "1920px"})
|
||||
generator.add_plugins(["tailwindcss-animate"])
|
||||
|
||||
snippet = _strip_to_object(generator.generate_config_string())
|
||||
path = tmp_path / "config.cjs"
|
||||
path.write_text(snippet)
|
||||
|
||||
result = subprocess.run(
|
||||
[node, "--check", str(path)], capture_output=True, text=True
|
||||
)
|
||||
assert result.returncode == 0, result.stderr
|
||||
|
||||
@ -110,6 +110,29 @@ If not, use the Master rules exclusively.
|
||||
Now, generate the code...
|
||||
```
|
||||
|
||||
### Step 2c: Design Dials (optional)
|
||||
|
||||
Three optional 1-10 sliders that tune `--design-system` output without changing your query. Add any combination of them to the same command:
|
||||
|
||||
```bash
|
||||
python3 skills/ui-ux-pro-max/scripts/search.py "<query>" --design-system --variance <1-10> --motion <1-10> --density <1-10>
|
||||
```
|
||||
|
||||
| Dial | Low (1-3) | Mid (4-7) | High (8-10) |
|
||||
|------|-----------|-----------|-------------|
|
||||
| `--variance` | Centered / minimal (biases toward Minimalism-style categories) | Balanced / modern | Bold / asymmetric (biases toward Brutalism, Bento Grids) |
|
||||
| `--motion` | Subtle micro-interactions | Standard scroll/stagger motion | Complex choreography (pin, Flip, SplitText) |
|
||||
| `--density` | Spacious (24-96px spacing scale) | Standard (16-64px, current default) | Dense/dashboard (8-32px spacing scale) |
|
||||
|
||||
- `--motion` attaches a ready-to-use GSAP snippet (with framework notes, Do/Don't, and performance notes) pulled from `--domain gsap`, matched to the resolved tier (Subtle/Standard/Complex).
|
||||
- `--density` overrides the `--space-*` CSS variable table in the ASCII/markdown/MASTER.md output — use it for dashboards (high) vs. marketing pages (low) without hand-editing tokens.
|
||||
- Leaving a dial unset keeps that part of the output exactly as it was before (no behavior change).
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
python3 skills/ui-ux-pro-max/scripts/search.py "internal analytics dashboard" --design-system --variance 8 --motion 7 --density 8 -p "Ops Console"
|
||||
```
|
||||
|
||||
### Step 3: Supplement with Detailed Searches (as needed)
|
||||
|
||||
After getting the design system, use domain searches to get additional details:
|
||||
@ -156,6 +179,7 @@ python3 skills/ui-ux-pro-max/scripts/search.py "<keyword>" --stack <stack>
|
||||
| `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 |
|
||||
| `gsap` | GSAP animation skeletons by intensity tier | scroll reveal, stagger, magnetic cursor, page transition |
|
||||
| `react` | React/Next.js performance | waterfall, bundle, suspense, memo, rerender, cache |
|
||||
| `web` | App interface guidelines (iOS/Android/React Native) | accessibilityLabel, touch targets, safe areas, Dynamic Type |
|
||||
| `prompt` | AI prompts, CSS keywords | (style name) |
|
||||
|
||||
4
cli/assets/templates/platforms/agent.json
Executable file → Normal file
4
cli/assets/templates/platforms/agent.json
Executable file → Normal file
@ -10,12 +10,12 @@
|
||||
"scriptPath": "skills/ui-ux-pro-max/scripts/search.py",
|
||||
"frontmatter": {
|
||||
"name": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks."
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks."
|
||||
},
|
||||
"sections": {
|
||||
"quickReference": false
|
||||
},
|
||||
"title": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"skillOrWorkflow": "Skill"
|
||||
}
|
||||
|
||||
@ -13,6 +13,6 @@
|
||||
"quickReference": false
|
||||
},
|
||||
"title": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"skillOrWorkflow": "Skill"
|
||||
}
|
||||
|
||||
@ -10,12 +10,12 @@
|
||||
"scriptPath": "skills/ui-ux-pro-max/scripts/search.py",
|
||||
"frontmatter": {
|
||||
"name": "ui-ux-pro-max",
|
||||
"description": "UI/UX design intelligence. 67 styles, 161 palettes, 57 font pairings, 25 charts, 16 stacks (React, Next.js, Vue, Svelte, Astro, SwiftUI, React Native, Flutter, Nuxt, Nuxt UI, Tailwind, shadcn/ui, Jetpack Compose, Three.js, Angular, Laravel). 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."
|
||||
"description": "UI/UX design intelligence. 67 styles, 161 palettes, 57 font pairings, 25 charts, 21 stacks (React, Next.js, Vue, Svelte, Astro, SwiftUI, React Native, Flutter, WPF, WinUI 3, UWP, Avalonia, Uno Platform, Nuxt, Nuxt UI, Tailwind, shadcn/ui, Jetpack Compose, Three.js, Angular, Laravel). 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, desktop app, .html, .tsx, .vue, .svelte, .xaml. 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."
|
||||
},
|
||||
"sections": {
|
||||
"quickReference": true
|
||||
},
|
||||
"title": "UI/UX Pro Max - Design Intelligence",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"skillOrWorkflow": "Skill"
|
||||
}
|
||||
|
||||
@ -16,6 +16,6 @@
|
||||
"quickReference": false
|
||||
},
|
||||
"title": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"skillOrWorkflow": "Skill"
|
||||
}
|
||||
|
||||
@ -16,6 +16,6 @@
|
||||
"quickReference": false
|
||||
},
|
||||
"title": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"skillOrWorkflow": "Skill"
|
||||
}
|
||||
|
||||
@ -16,6 +16,6 @@
|
||||
"quickReference": false
|
||||
},
|
||||
"title": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"skillOrWorkflow": "Skill"
|
||||
}
|
||||
|
||||
4
cli/assets/templates/platforms/copilot.json
Executable file → Normal file
4
cli/assets/templates/platforms/copilot.json
Executable file → Normal file
@ -10,12 +10,12 @@
|
||||
"scriptPath": "prompts/ui-ux-pro-max/scripts/search.py",
|
||||
"frontmatter": {
|
||||
"name": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks."
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks."
|
||||
},
|
||||
"sections": {
|
||||
"quickReference": false
|
||||
},
|
||||
"title": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"skillOrWorkflow": "Workflow"
|
||||
}
|
||||
|
||||
4
cli/assets/templates/platforms/cursor.json
Executable file → Normal file
4
cli/assets/templates/platforms/cursor.json
Executable file → Normal file
@ -10,12 +10,12 @@
|
||||
"scriptPath": "skills/ui-ux-pro-max/scripts/search.py",
|
||||
"frontmatter": {
|
||||
"name": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks."
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks."
|
||||
},
|
||||
"sections": {
|
||||
"quickReference": false
|
||||
},
|
||||
"title": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"skillOrWorkflow": "Skill"
|
||||
}
|
||||
|
||||
@ -16,6 +16,6 @@
|
||||
"quickReference": false
|
||||
},
|
||||
"title": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"skillOrWorkflow": "Skill"
|
||||
}
|
||||
|
||||
@ -10,12 +10,12 @@
|
||||
"scriptPath": "skills/ui-ux-pro-max/scripts/search.py",
|
||||
"frontmatter": {
|
||||
"name": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks."
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks."
|
||||
},
|
||||
"sections": {
|
||||
"quickReference": false
|
||||
},
|
||||
"title": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"skillOrWorkflow": "Skill"
|
||||
}
|
||||
|
||||
4
cli/assets/templates/platforms/kiro.json
Executable file → Normal file
4
cli/assets/templates/platforms/kiro.json
Executable file → Normal file
@ -10,12 +10,12 @@
|
||||
"scriptPath": "steering/ui-ux-pro-max/scripts/search.py",
|
||||
"frontmatter": {
|
||||
"name": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks."
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks."
|
||||
},
|
||||
"sections": {
|
||||
"quickReference": false
|
||||
},
|
||||
"title": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"skillOrWorkflow": "Workflow"
|
||||
}
|
||||
|
||||
@ -16,6 +16,6 @@
|
||||
"quickReference": false
|
||||
},
|
||||
"title": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"skillOrWorkflow": "Skill"
|
||||
}
|
||||
|
||||
@ -16,6 +16,6 @@
|
||||
"quickReference": false
|
||||
},
|
||||
"title": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"skillOrWorkflow": "Skill"
|
||||
}
|
||||
|
||||
4
cli/assets/templates/platforms/roocode.json
Executable file → Normal file
4
cli/assets/templates/platforms/roocode.json
Executable file → Normal file
@ -10,12 +10,12 @@
|
||||
"scriptPath": "skills/ui-ux-pro-max/scripts/search.py",
|
||||
"frontmatter": {
|
||||
"name": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks."
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks."
|
||||
},
|
||||
"sections": {
|
||||
"quickReference": false
|
||||
},
|
||||
"title": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"skillOrWorkflow": "Workflow"
|
||||
}
|
||||
|
||||
@ -16,6 +16,6 @@
|
||||
"quickReference": false
|
||||
},
|
||||
"title": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"skillOrWorkflow": "Skill"
|
||||
}
|
||||
|
||||
@ -13,6 +13,6 @@
|
||||
"quickReference": false
|
||||
},
|
||||
"title": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"skillOrWorkflow": "Skill"
|
||||
}
|
||||
|
||||
4
cli/assets/templates/platforms/windsurf.json
Executable file → Normal file
4
cli/assets/templates/platforms/windsurf.json
Executable file → Normal file
@ -10,12 +10,12 @@
|
||||
"scriptPath": "skills/ui-ux-pro-max/scripts/search.py",
|
||||
"frontmatter": {
|
||||
"name": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks."
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks."
|
||||
},
|
||||
"sections": {
|
||||
"quickReference": false
|
||||
},
|
||||
"title": "ui-ux-pro-max",
|
||||
"description": "Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"description": "Comprehensive design guide for web, mobile, and desktop applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 22 technology stacks. Searchable database with priority-based recommendations.",
|
||||
"skillOrWorkflow": "Skill"
|
||||
}
|
||||
|
||||
17
src/ui-ux-pro-max/data/motion.csv
Normal file
17
src/ui-ux-pro-max/data/motion.csv
Normal file
@ -0,0 +1,17 @@
|
||||
No,Category,Intensity Tier,Keywords,Trigger,Duration,Easing,GSAP Snippet,Framework Notes,Do,Don't,Performance Notes
|
||||
1,Hover Micro-interaction,Subtle,"hover, button, opacity, lift, press feedback",hover,150-200ms,power1.out,"gsap.to(el, { y: -1, opacity: 0.9, duration: 0.15, ease: 'power1.out' });",Bind on mouseenter/mouseleave; in React wrap in a ref + useEffect (or onMouseEnter/onMouseLeave props directly calling gsap.to),Keep displacement under 2px so it reads as feedback not motion,Don't animate layout-affecting props (width/height/margin) on hover,Runs on transform/opacity only so it stays on the compositor thread
|
||||
2,Hover Micro-interaction,Standard,"hover, card, scale, tilt, cursor feedback",hover,200-300ms,power2.out,"gsap.to(el, { y: -4, scale: 1.02, boxShadow: '0 12px 24px rgba(0,0,0,0.12)', duration: 0.25, ease: 'power2.out' });","Use gsap.quickTo(el, 'y') for cards with many hover targets to avoid re-creating tweens every event",Pair with a matching mouseleave tween that reverses the same properties,Don't leave the hover state stuck if the pointer leaves fast; always attach the reverse tween,quickTo() avoids GC churn on lists with 20+ hoverable cards
|
||||
3,Hover Micro-interaction,Complex,"hover, magnetic, cursor follow, 3d tilt",hover + mousemove,300-500ms,"elastic.out(1,0.4)","const xTo = gsap.quickTo(el, 'x', { duration: 0.4, ease: 'elastic.out(1,0.4)' }); const yTo = gsap.quickTo(el, 'y', { duration: 0.4, ease: 'elastic.out(1,0.4)' }); el.addEventListener('mousemove', (e) => { const r = el.getBoundingClientRect(); xTo((e.clientX - r.left - r.width/2) * 0.3); yTo((e.clientY - r.top - r.height/2) * 0.3); });",Debounce is not needed since quickTo interpolates; remove listeners on component unmount in React/Vue to avoid leaks,Clamp the pull strength (e.g. * 0.3) so the element never fully leaves its hit box,Don't apply magnetic effect to more than 1-2 focal elements per screen; it becomes noisy,Use will-change: transform on the target element for smoother compositing
|
||||
4,Scroll Reveal,Subtle,"scroll, fade in, reveal, on view",scroll (viewport enter),300-400ms,power1.out,"gsap.from(el, { opacity: 0, y: 12, duration: 0.35, ease: 'power1.out', scrollTrigger: { trigger: el, start: 'top 90%', toggleActions: 'play none none reverse' } });",Requires the ScrollTrigger plugin registered once via gsap.registerPlugin(ScrollTrigger),"Keep the y offset small (8-16px) so it reads as a fade, not a slide",Don't reveal below-the-fold content needed for SEO/crawlers as invisible-by-default without a no-JS fallback,toggleActions 'play none none reverse' avoids re-triggering on every scroll direction change
|
||||
5,Scroll Reveal,Standard,"scroll, slide up, staggered section, reveal",scroll (viewport enter),400-600ms,power2.out,"gsap.from(el.children, { opacity: 0, y: 24, duration: 0.5, stagger: 0.08, ease: 'power2.out', scrollTrigger: { trigger: el, start: 'top 85%' } });","In React use useGSAP(() => {...}, { scope: containerRef }) from @gsap/react to auto-cleanup on unmount",Scope the ScrollTrigger to the section container so it doesn't re-scan the whole page,Don't stagger more than ~8 children; beyond that the last items feel laggy,Set scroller/markers: false in production; markers is dev-only
|
||||
6,Scroll Reveal,Complex,"scroll, pin, scrub, storytelling, scrollytelling",scroll (continuous scrub),tied to scroll position,none (scrub-driven),"gsap.timeline({ scrollTrigger: { trigger: section, start: 'top top', end: '+=150%', scrub: 1, pin: true } }).from('.headline', { opacity: 0, y: 40 }).to('.bg-layer', { yPercent: -20 }, '<');",Pinning needs the section to have deterministic height; recalc ScrollTrigger.refresh() after images/fonts load,Use scrub: true or a small number (0.5-1.5) instead of instant jumps so it feels tied to the scrollbar,Don't pin more than 1-2 sections per page; excessive pinning fights native scroll feel and hurts mobile UX,"Pinning forces layout reflow; test on mid-tier mobile devices, not just desktop"
|
||||
7,Stagger List,Subtle,"list, stagger, cards, grid entrance",load or scroll,250-350ms,power1.out,"gsap.from('.list-item', { opacity: 0, y: 8, duration: 0.3, stagger: 0.03 });",Select items with a stable class/data-attribute (not array index) so re-renders in React don't break targeting,Keep per-item stagger delay small (0.02-0.04s) for lists longer than 10 items,Don't stagger by more than 0.1s per item on long lists; total reveal time becomes sluggish,"For virtualized lists, only animate items currently mounted in the DOM"
|
||||
8,Stagger List,Standard,"grid, bento, cards, staggered scale",load or scroll,300-450ms,back.out(1.4),"gsap.from('.grid-item', { opacity: 0, scale: 0.92, y: 16, duration: 0.4, stagger: { each: 0.06, from: 'start', grid: 'auto' }, ease: 'back.out(1.4)' });",grid: 'auto' lets GSAP infer rows/columns from a CSS grid layout for a natural wave stagger,Combine with from: 'center' for a bento-grid layout to draw the eye inward first,Don't use back.out on dense data tables; the overshoot reads as sloppy on informational UI,Group DOM writes; avoid interleaving layout reads (getBoundingClientRect) between staggered tweens
|
||||
9,Stagger List,Complex,"stagger, wave, text reveal, split text",load or scroll,400-700ms,expo.out,"const split = new SplitText(headline, { type: 'chars' }); gsap.from(split.chars, { opacity: 0, y: 20, rotateX: -40, duration: 0.6, stagger: 0.015, ease: 'expo.out' });",SplitText is a GSAP Club/paid plugin; confirm license before shipping and provide a plain fade fallback if unavailable,Revert SplitText on unmount/cleanup (split.revert()) to restore original text nodes for accessibility tools,Don't split-animate long paragraphs; reserve for short headlines (under ~8 words),Splitting text creates one element per character; keep it to headline-length copy only for DOM size
|
||||
10,Page Transition,Subtle,"route change, fade, page transition",route change,200-300ms,power1.inOut,"gsap.to(main, { opacity: 0, duration: 0.2, onComplete: () => { navigate(); gsap.fromTo(main, { opacity: 0 }, { opacity: 1, duration: 0.2 }); } });","Pair with the router's transition hooks (Next.js App Router transitions, React Router's useNavigate, Vue Router's beforeEach/afterEach)",Preload the destination route's critical assets before the exit tween finishes,Don't block navigation on animation; cap exit duration at ~250ms so the app never feels unresponsive,Exit animation should always resolve faster than entrance (asymmetric timing) so back/forward feels snappy
|
||||
11,Page Transition,Standard,"route change, slide, overlay wipe",route change,400-600ms,power2.inOut,"const tl = gsap.timeline(); tl.to('.transition-overlay', { yPercent: 0, duration: 0.4, ease: 'power2.inOut' }).call(navigate).to('.transition-overlay', { yPercent: -100, duration: 0.4, ease: 'power2.inOut', delay: 0.1 });",Keep the overlay element mounted at the layout root (outside the page component) so it survives the route swap,Show a lightweight loading indicator if the destination route's data fetch outlasts the overlay,Don't tie the overlay's reveal directly to data-fetch completion without a max-wait timeout; a slow API stalls the whole transition,Prefer CSS transform (yPercent) over top/left to keep the overlay animation on the compositor thread
|
||||
12,Page Transition,Complex,"shared element, morph, hero transition",route change,500-800ms,expo.inOut,"const state = Flip.getState('.hero-image'); navigate(); Flip.from(state, { duration: 0.6, ease: 'expo.inOut', absolute: true, zIndex: 100 });",Requires the GSAP Flip plugin; the 'from' and 'to' route must render the same element with a shared data-flip-id,Verify the shared element exists in both DOM states before calling Flip.from to avoid a silent no-op,Don't use shared-element transitions across more than one element pair per navigation; compounding Flips are hard to time correctly,Flip recalculates layout (FLIP technique) so test on low-end devices for jank
|
||||
13,Parallax Scroll,Subtle,"parallax, background, depth, scroll speed",scroll (continuous),tied to scroll position,linear (scrub),"gsap.to('.bg-layer', { yPercent: 10, ease: 'none', scrollTrigger: { trigger: section, scrub: true } });","Apply parallax to background/decorative layers only, never to text or interactive controls",Keep the yPercent delta small (5-15) so foreground and background never desync distractingly,Don't parallax body copy; it hurts reading comfort and can trigger motion sickness,will-change: transform on the parallax layer only; remove it after scroll settles to free GPU memory
|
||||
14,Parallax Scroll,Standard,"multi-layer parallax, depth, hero background",scroll (continuous),tied to scroll position,linear (scrub),"gsap.utils.toArray('.parallax-layer').forEach((layer, i) => { gsap.to(layer, { yPercent: (i + 1) * -8, ease: 'none', scrollTrigger: { trigger: layer.parentElement, scrub: 0.5 } }); });",Layer count beyond 3-4 has diminishing visual return and multiplies scroll-listener cost,"Vary speed per layer (background slowest, foreground fastest) to sell the depth illusion",Don't let parallax layers overflow their container; clip with overflow: hidden on the wrapper,Batch all layers under one ScrollTrigger container where possible instead of one per layer
|
||||
15,Loading / Skeleton,Subtle,"loading, skeleton, shimmer, pulse",on mount / async wait,1200-1600ms loop,sine.inOut,"gsap.to('.skeleton', { backgroundPosition: '200% 0', duration: 1.4, ease: 'sine.inOut', repeat: -1 });",Kill the loop tween (tween.kill()) as soon as real content mounts to avoid orphaned repeating animations,Use a CSS gradient background-position sweep rather than opacity pulsing; reads as 'loading' more clearly,Don't run more than one shimmer loop per skeleton group; sync them under one timeline so the wave reads as a single unit,repeat: -1 tweens are cheap but must be explicitly killed on unmount or they leak in SPA route changes
|
||||
16,Loading / Skeleton,Standard,"progress, spinner, morphing loader",on mount / async wait,800-1200ms loop,power1.inOut,"gsap.timeline({ repeat: -1 }).to('.loader-dot', { y: -8, duration: 0.4, stagger: { each: 0.15, yoyo: true, repeat: 1 } });",Wrap the whole loop timeline in useGSAP with { revertOnUpdate: false } in React so it isn't rebuilt every render,Cap total loop duration under ~1.5s so long waits don't feel like the UI froze on a single beat,Don't use elaborate loaders for sub-300ms waits; they flash and feel worse than no indicator,Pause the timeline (tl.pause()) when the loading tab/view is not visible to save CPU on background tabs
|
||||
|
@ -55,6 +55,11 @@ CSV_CONFIG = {
|
||||
"search_cols": ["Category", "Icon Name", "Keywords", "Best For"],
|
||||
"output_cols": ["Category", "Icon Name", "Keywords", "Library", "Import Code", "Usage", "Best For", "Style"]
|
||||
},
|
||||
"gsap": {
|
||||
"file": "motion.csv",
|
||||
"search_cols": ["Category", "Intensity Tier", "Keywords", "Trigger"],
|
||||
"output_cols": ["Category", "Intensity Tier", "Trigger", "Duration", "Easing", "GSAP Snippet", "Framework Notes", "Do", "Don't", "Performance Notes"]
|
||||
},
|
||||
"react": {
|
||||
"file": "react-performance.csv",
|
||||
"search_cols": ["Category", "Issue", "Keywords", "Description"],
|
||||
@ -215,6 +220,7 @@ def detect_domain(query):
|
||||
"typography": ["font pairing", "typography pairing", "heading font", "body font"],
|
||||
"google-fonts": ["google font", "font family", "font weight", "font style", "variable font", "noto", "font for", "find font", "font subset", "font language", "monospace font", "serif font", "sans serif font", "display font", "handwriting font", "font", "typography", "serif", "sans"],
|
||||
"icons": ["icon", "icons", "lucide", "heroicons", "symbol", "glyph", "pictogram", "svg icon"],
|
||||
"gsap": ["gsap", "quickto", "scrolltrigger", "stagger", "magnetic cursor", "parallax", "page transition", "scroll reveal", "scroll-triggered", "scrollytelling", "flip plugin", "splittext", "shimmer", "skeleton loader"],
|
||||
"react": ["react", "next.js", "nextjs", "suspense", "memo", "usecallback", "useeffect", "rerender", "bundle", "waterfall", "barrel", "dynamic import", "rsc", "server component"],
|
||||
"web": ["aria", "focus", "outline", "semantic", "virtualize", "autocomplete", "form", "input type", "preconnect"]
|
||||
}
|
||||
|
||||
@ -40,6 +40,39 @@ SEARCH_CONFIG = {
|
||||
"typography": {"max_results": 2}
|
||||
}
|
||||
|
||||
# ============ DESIGN DIALS (1-10) ============
|
||||
# Inspired by taste-skill's DESIGN_VARIANCE / MOTION_INTENSITY / VISUAL_DENSITY
|
||||
# knobs: three optional 1-10 sliders that bias the existing query-based search
|
||||
# instead of replacing it. Each dial buckets into a low/mid/high tier.
|
||||
DIAL_TIERS = {
|
||||
"variance": [
|
||||
(1, 3, {"label": "Centered / Minimal", "style_keywords": ["Minimalism", "Exaggerated Minimalism", "centered", "symmetric", "grid-based"]}),
|
||||
(4, 7, {"label": "Balanced / Modern", "style_keywords": ["modern", "structured", "balanced"]}),
|
||||
(8, 10, {"label": "Bold / Asymmetric", "style_keywords": ["Brutalism", "Bento Grids", "asymmetric", "experimental"]}),
|
||||
],
|
||||
"motion": [
|
||||
(1, 3, {"label": "Subtle", "tier": "Subtle"}),
|
||||
(4, 7, {"label": "Standard", "tier": "Standard"}),
|
||||
(8, 10, {"label": "Complex", "tier": "Complex"}),
|
||||
],
|
||||
"density": [
|
||||
(1, 3, {"label": "Spacious", "spacing": {"xs": "4px", "sm": "8px", "md": "24px", "lg": "32px", "xl": "48px", "2xl": "64px", "3xl": "96px"}}),
|
||||
(4, 7, {"label": "Standard", "spacing": {"xs": "4px", "sm": "8px", "md": "16px", "lg": "24px", "xl": "32px", "2xl": "48px", "3xl": "64px"}}),
|
||||
(8, 10, {"label": "Dense / Dashboard", "spacing": {"xs": "2px", "sm": "4px", "md": "8px", "lg": "12px", "xl": "16px", "2xl": "24px", "3xl": "32px"}}),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _resolve_dial(dial_name: str, value) -> dict:
|
||||
"""Bucket a 1-10 dial value into its tier config. Returns None if value is None."""
|
||||
if value is None:
|
||||
return None
|
||||
value = max(1, min(10, int(value)))
|
||||
for lo, hi, info in DIAL_TIERS[dial_name]:
|
||||
if lo <= value <= hi:
|
||||
return {**info, "value": value}
|
||||
return None
|
||||
|
||||
|
||||
# ============ DESIGN SYSTEM GENERATOR ============
|
||||
class DesignSystemGenerator:
|
||||
@ -168,8 +201,18 @@ class DesignSystemGenerator:
|
||||
"""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."""
|
||||
def generate(self, query: str, project_name: str = None,
|
||||
variance: int = None, motion: int = None, density: int = None) -> dict:
|
||||
"""Generate complete design system recommendation.
|
||||
|
||||
variance/motion/density are optional 1-10 dials (see DIAL_TIERS) that bias
|
||||
style selection, pull in a matching motion.csv snippet, and override the
|
||||
spacing scale, without changing behavior when left unset.
|
||||
"""
|
||||
variance_info = _resolve_dial("variance", variance)
|
||||
motion_info = _resolve_dial("motion", motion)
|
||||
density_info = _resolve_dial("density", density)
|
||||
|
||||
# Step 1: First search product to get category
|
||||
product_result = search(query, "product", 1)
|
||||
product_results = product_result.get("results", [])
|
||||
@ -181,8 +224,14 @@ class DesignSystemGenerator:
|
||||
reasoning = self._apply_reasoning(category, {})
|
||||
style_priority = reasoning.get("style_priority", [])
|
||||
|
||||
# DESIGN_VARIANCE dial: bias style retrieval/selection toward
|
||||
# centered-minimal (low) or bold-asymmetric (high) keywords.
|
||||
effective_style_priority = style_priority
|
||||
if variance_info:
|
||||
effective_style_priority = variance_info["style_keywords"] + style_priority
|
||||
|
||||
# Step 3: Multi-domain search with style priority hints
|
||||
search_results = self._multi_domain_search(query, style_priority)
|
||||
search_results = self._multi_domain_search(query, effective_style_priority)
|
||||
search_results["product"] = product_result # Reuse product search
|
||||
|
||||
# Step 4: Select best matches from each domain using priority
|
||||
@ -191,11 +240,24 @@ class DesignSystemGenerator:
|
||||
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_style = self._select_best_match(style_results, effective_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 {}
|
||||
|
||||
# MOTION_INTENSITY dial: pull a matching GSAP skeleton from motion.csv
|
||||
# (domain key is "gsap", not "motion" - PR #296 already owns the "motion"
|
||||
# domain for Emil Kowalski's motion-design principles, motion-principles.csv).
|
||||
motion_snippet = {}
|
||||
if motion_info:
|
||||
motion_result = search(f"{query} {motion_info['tier']}", "gsap", 5)
|
||||
motion_matches = motion_result.get("results", [])
|
||||
tiered = [m for m in motion_matches if m.get("Intensity Tier") == motion_info["tier"]]
|
||||
if tiered:
|
||||
motion_snippet = tiered[0]
|
||||
elif motion_matches:
|
||||
motion_snippet = motion_matches[0]
|
||||
|
||||
# Step 5: Build final recommendation
|
||||
# Combine effects from both reasoning and style search
|
||||
style_effects = best_style.get("Effects & Animation", "")
|
||||
@ -250,7 +312,17 @@ class DesignSystemGenerator:
|
||||
"key_effects": combined_effects,
|
||||
"anti_patterns": reasoning.get("anti_patterns", ""),
|
||||
"decision_rules": reasoning.get("decision_rules", {}),
|
||||
"severity": reasoning.get("severity", "MEDIUM")
|
||||
"severity": reasoning.get("severity", "MEDIUM"),
|
||||
"dials": {
|
||||
"variance": variance_info["value"] if variance_info else None,
|
||||
"variance_label": variance_info["label"] if variance_info else None,
|
||||
"motion": motion_info["value"] if motion_info else None,
|
||||
"motion_label": motion_info["label"] if motion_info else None,
|
||||
"density": density_info["value"] if density_info else None,
|
||||
"density_label": density_info["label"] if density_info else None,
|
||||
},
|
||||
"motion_snippet": motion_snippet,
|
||||
"spacing_scale": density_info["spacing"] if density_info else None,
|
||||
}
|
||||
|
||||
|
||||
@ -296,6 +368,8 @@ def format_ascii_box(design_system: dict) -> str:
|
||||
typography = design_system.get("typography", {})
|
||||
effects = design_system.get("key_effects", "")
|
||||
anti_patterns = design_system.get("anti_patterns", "")
|
||||
dials = design_system.get("dials", {})
|
||||
motion_snippet = design_system.get("motion_snippet", {})
|
||||
|
||||
def wrap_text(text: str, prefix: str, width: int) -> list:
|
||||
"""Wrap long text into multiple lines."""
|
||||
@ -329,6 +403,16 @@ def format_ascii_box(design_system: dict) -> str:
|
||||
lines.append("╚" + "═" * w + "╝")
|
||||
lines.append("┌" + "─" * w + "┐")
|
||||
|
||||
# Design Dials section (only if at least one dial was set)
|
||||
if any(dials.get(k) is not None for k in ("variance", "motion", "density")):
|
||||
lines.append(section_header("DESIGN DIALS", BOX_WIDTH + 1))
|
||||
if dials.get("variance") is not None:
|
||||
lines.append(f"│ Variance: {dials['variance']}/10 — {dials['variance_label']}".ljust(BOX_WIDTH) + "│")
|
||||
if dials.get("motion") is not None:
|
||||
lines.append(f"│ Motion: {dials['motion']}/10 — {dials['motion_label']}".ljust(BOX_WIDTH) + "│")
|
||||
if dials.get("density") is not None:
|
||||
lines.append(f"│ Density: {dials['density']}/10 — {dials['density_label']}".ljust(BOX_WIDTH) + "│")
|
||||
|
||||
# Pattern section
|
||||
lines.append(section_header("PATTERN", BOX_WIDTH + 1))
|
||||
lines.append(f"│ Name: {pattern.get('name', '')}".ljust(BOX_WIDTH) + "│")
|
||||
@ -402,6 +486,17 @@ def format_ascii_box(design_system: dict) -> str:
|
||||
for line in wrap_text(effects, "│ ", BOX_WIDTH):
|
||||
lines.append(line.ljust(BOX_WIDTH) + "│")
|
||||
|
||||
# Motion section (GSAP skeleton, only if --motion dial was set)
|
||||
if motion_snippet:
|
||||
lines.append(section_header("MOTION", BOX_WIDTH + 1))
|
||||
lines.append(f"│ {motion_snippet.get('Category', '')} ({motion_snippet.get('Intensity Tier', '')})".ljust(BOX_WIDTH) + "│")
|
||||
lines.append(f"│ Trigger: {motion_snippet.get('Trigger', '')} | Duration: {motion_snippet.get('Duration', '')} | Easing: {motion_snippet.get('Easing', '')}".ljust(BOX_WIDTH) + "│")
|
||||
for line in wrap_text(f"GSAP: {motion_snippet.get('GSAP Snippet', '')}", "│ ", BOX_WIDTH):
|
||||
lines.append(line.ljust(BOX_WIDTH) + "│")
|
||||
if motion_snippet.get("Framework Notes"):
|
||||
for line in wrap_text(f"Framework: {motion_snippet.get('Framework Notes', '')}", "│ ", BOX_WIDTH):
|
||||
lines.append(line.ljust(BOX_WIDTH) + "│")
|
||||
|
||||
# Anti-patterns section
|
||||
if anti_patterns:
|
||||
lines.append(section_header("AVOID", BOX_WIDTH + 1))
|
||||
@ -436,11 +531,24 @@ def format_markdown(design_system: dict) -> str:
|
||||
typography = design_system.get("typography", {})
|
||||
effects = design_system.get("key_effects", "")
|
||||
anti_patterns = design_system.get("anti_patterns", "")
|
||||
dials = design_system.get("dials", {})
|
||||
motion_snippet = design_system.get("motion_snippet", {})
|
||||
|
||||
lines = []
|
||||
lines.append(f"## Design System: {project}")
|
||||
lines.append("")
|
||||
|
||||
# Design Dials section (only if at least one dial was set)
|
||||
if any(dials.get(k) is not None for k in ("variance", "motion", "density")):
|
||||
lines.append("### Design Dials")
|
||||
if dials.get("variance") is not None:
|
||||
lines.append(f"- **Variance:** {dials['variance']}/10 — {dials['variance_label']}")
|
||||
if dials.get("motion") is not None:
|
||||
lines.append(f"- **Motion:** {dials['motion']}/10 — {dials['motion_label']}")
|
||||
if dials.get("density") is not None:
|
||||
lines.append(f"- **Density:** {dials['density']}/10 — {dials['density_label']}")
|
||||
lines.append("")
|
||||
|
||||
# Pattern section
|
||||
lines.append("### Pattern")
|
||||
lines.append(f"- **Name:** {pattern.get('name', '')}")
|
||||
@ -515,6 +623,23 @@ def format_markdown(design_system: dict) -> str:
|
||||
lines.append(f"{effects}")
|
||||
lines.append("")
|
||||
|
||||
# Motion section (GSAP skeleton, only if --motion dial was set)
|
||||
if motion_snippet:
|
||||
lines.append("### Motion")
|
||||
lines.append(f"**{motion_snippet.get('Category', '')}** ({motion_snippet.get('Intensity Tier', '')}) — Trigger: {motion_snippet.get('Trigger', '')} | Duration: {motion_snippet.get('Duration', '')} | Easing: `{motion_snippet.get('Easing', '')}`")
|
||||
lines.append("```js")
|
||||
lines.append(motion_snippet.get("GSAP Snippet", ""))
|
||||
lines.append("```")
|
||||
if motion_snippet.get("Framework Notes"):
|
||||
lines.append(f"*Framework notes: {motion_snippet.get('Framework Notes', '')}*")
|
||||
motion_do = motion_snippet.get("Do", "")
|
||||
motion_dont = motion_snippet.get("Don't", "")
|
||||
if motion_do:
|
||||
lines.append(f"- ✅ {motion_do}")
|
||||
if motion_dont:
|
||||
lines.append(f"- ❌ {motion_dont}")
|
||||
lines.append("")
|
||||
|
||||
# Anti-patterns section
|
||||
if anti_patterns:
|
||||
lines.append("### Avoid (Anti-patterns)")
|
||||
@ -537,8 +662,9 @@ def format_markdown(design_system: dict) -> str:
|
||||
|
||||
|
||||
# ============ MAIN ENTRY POINT ============
|
||||
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:
|
||||
def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii",
|
||||
persist: bool = False, page: str = None, output_dir: str = None,
|
||||
variance: int = None, motion: int = None, density: int = None) -> str:
|
||||
"""
|
||||
Main entry point for design system generation.
|
||||
|
||||
@ -549,13 +675,16 @@ def generate_design_system(query: str, project_name: str = None, output_format:
|
||||
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)
|
||||
variance: Optional 1-10 DESIGN_VARIANCE dial (1=centered/minimal, 10=bold/asymmetric)
|
||||
motion: Optional 1-10 MOTION_INTENSITY dial, pulls a matching GSAP snippet from motion.csv
|
||||
density: Optional 1-10 VISUAL_DENSITY dial, overrides the spacing scale (1=spacious, 10=dense)
|
||||
|
||||
Returns:
|
||||
Formatted design system string
|
||||
"""
|
||||
generator = DesignSystemGenerator()
|
||||
design_system = generator.generate(query, project_name)
|
||||
|
||||
design_system = generator.generate(query, project_name, variance=variance, motion=motion, density=density)
|
||||
|
||||
# Persist to files if requested
|
||||
if persist:
|
||||
persist_design_system(design_system, page, output_dir, query)
|
||||
@ -627,11 +756,14 @@ def format_master_md(design_system: dict) -> str:
|
||||
typography = design_system.get("typography", {})
|
||||
effects = design_system.get("key_effects", "")
|
||||
anti_patterns = design_system.get("anti_patterns", "")
|
||||
|
||||
dials = design_system.get("dials", {})
|
||||
motion_snippet = design_system.get("motion_snippet", {})
|
||||
spacing_scale = design_system.get("spacing_scale")
|
||||
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
|
||||
lines = []
|
||||
|
||||
|
||||
# Logic header
|
||||
lines.append("# Design System Master File")
|
||||
lines.append("")
|
||||
@ -644,6 +776,15 @@ def format_master_md(design_system: dict) -> str:
|
||||
lines.append(f"**Project:** {project}")
|
||||
lines.append(f"**Generated:** {timestamp}")
|
||||
lines.append(f"**Category:** {design_system.get('category', 'General')}")
|
||||
if any(dials.get(k) is not None for k in ("variance", "motion", "density")):
|
||||
dial_parts = []
|
||||
if dials.get("variance") is not None:
|
||||
dial_parts.append(f"Variance {dials['variance']}/10 ({dials['variance_label']})")
|
||||
if dials.get("motion") is not None:
|
||||
dial_parts.append(f"Motion {dials['motion']}/10 ({dials['motion_label']})")
|
||||
if dials.get("density") is not None:
|
||||
dial_parts.append(f"Density {dials['density']}/10 ({dials['density_label']})")
|
||||
lines.append(f"**Design Dials:** {' | '.join(dial_parts)}")
|
||||
lines.append("")
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
@ -695,18 +836,24 @@ def format_master_md(design_system: dict) -> str:
|
||||
lines.append("```")
|
||||
lines.append("")
|
||||
|
||||
# Spacing Variables
|
||||
# Spacing Variables (overridden by the VISUAL_DENSITY dial when set)
|
||||
default_spacing = DIAL_TIERS["density"][1][2]["spacing"] # mid-tier = the historical defaults
|
||||
scale = spacing_scale or default_spacing
|
||||
spacing_usage = {
|
||||
"xs": "Tight gaps", "sm": "Icon gaps, inline spacing", "md": "Standard padding",
|
||||
"lg": "Section padding", "xl": "Large gaps", "2xl": "Section margins", "3xl": "Hero padding",
|
||||
}
|
||||
lines.append("### Spacing Variables")
|
||||
lines.append("")
|
||||
if spacing_scale:
|
||||
lines.append(f"*Density: {dials.get('density')}/10 — {dials.get('density_label')}*")
|
||||
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 |")
|
||||
for token in ("xs", "sm", "md", "lg", "xl", "2xl", "3xl"):
|
||||
px_value = scale[token]
|
||||
rem_value = f"{int(px_value.rstrip('px')) / 16:g}rem"
|
||||
lines.append(f"| `--space-{token}` | `{px_value}` / `{rem_value}` | {spacing_usage[token]} |")
|
||||
lines.append("")
|
||||
|
||||
# Shadow Depths
|
||||
@ -848,7 +995,32 @@ def format_master_md(design_system: dict) -> str:
|
||||
lines.append(f"- **CTA Placement:** {pattern.get('cta_placement', '')}")
|
||||
lines.append(f"- **Section Order:** {pattern.get('sections', '')}")
|
||||
lines.append("")
|
||||
|
||||
|
||||
# Motion section (GSAP skeleton, only if --motion dial was set)
|
||||
if motion_snippet:
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
lines.append("## Motion")
|
||||
lines.append("")
|
||||
lines.append(f"**{motion_snippet.get('Category', '')}** ({motion_snippet.get('Intensity Tier', '')}) — Trigger: {motion_snippet.get('Trigger', '')} | Duration: {motion_snippet.get('Duration', '')} | Easing: `{motion_snippet.get('Easing', '')}`")
|
||||
lines.append("")
|
||||
lines.append("```js")
|
||||
lines.append(motion_snippet.get("GSAP Snippet", ""))
|
||||
lines.append("```")
|
||||
lines.append("")
|
||||
if motion_snippet.get("Framework Notes"):
|
||||
lines.append(f"**Framework notes:** {motion_snippet.get('Framework Notes', '')}")
|
||||
lines.append("")
|
||||
motion_do = motion_snippet.get("Do", "")
|
||||
motion_dont = motion_snippet.get("Don't", "")
|
||||
if motion_do:
|
||||
lines.append(f"- ✅ {motion_do}")
|
||||
if motion_dont:
|
||||
lines.append(f"- ❌ {motion_dont}")
|
||||
if motion_snippet.get("Performance Notes"):
|
||||
lines.append(f"- ⚡ {motion_snippet.get('Performance Notes', '')}")
|
||||
lines.append("")
|
||||
|
||||
# Anti-Patterns section
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
|
||||
@ -5,10 +5,16 @@ 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]
|
||||
python search.py "<query>" --design-system [-p "Project Name"]
|
||||
python search.py "<query>" --design-system --persist [-p "Project Name"] [--page "dashboard"]
|
||||
python search.py "<query>" --design-system --variance 8 --motion 9 --density 7
|
||||
|
||||
Domains: style, prompt, color, chart, landing, product, ux, typography, google-fonts
|
||||
Domains: style, prompt, color, chart, landing, product, ux, typography, google-fonts, gsap
|
||||
Stacks: react, nextjs, vue, svelte, astro, swiftui, react-native, flutter, nuxtjs, nuxt-ui, html-tailwind, shadcn, jetpack-compose, threejs, angular, laravel, javafx, wpf, winui, avalonia, uno, uwp
|
||||
|
||||
Design dials (1-10, only with --design-system):
|
||||
--variance DESIGN_VARIANCE: 1=centered/minimal, 10=bold/asymmetric
|
||||
--motion MOTION_INTENSITY: 1=subtle, 10=complex; attaches a GSAP snippet from motion.csv
|
||||
--density VISUAL_DENSITY: 1=spacious, 10=dense/dashboard; overrides the spacing scale
|
||||
|
||||
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/
|
||||
@ -68,18 +74,25 @@ if __name__ == "__main__":
|
||||
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)")
|
||||
# Design dials (1-10), only applied with --design-system
|
||||
parser.add_argument("--variance", type=int, choices=range(1, 11), metavar="1-10", help="DESIGN_VARIANCE dial: 1=centered/minimal, 10=bold/asymmetric (only with --design-system)")
|
||||
parser.add_argument("--motion", type=int, choices=range(1, 11), metavar="1-10", help="MOTION_INTENSITY dial: 1=subtle, 10=complex; pulls a matching GSAP snippet from motion.csv (only with --design-system)")
|
||||
parser.add_argument("--density", type=int, choices=range(1, 11), metavar="1-10", help="VISUAL_DENSITY dial: 1=spacious, 10=dense/dashboard; overrides the spacing scale (only with --design-system)")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Design system takes priority
|
||||
if args.design_system:
|
||||
result = generate_design_system(
|
||||
args.query,
|
||||
args.project_name,
|
||||
args.query,
|
||||
args.project_name,
|
||||
args.format,
|
||||
persist=args.persist,
|
||||
page=args.page,
|
||||
output_dir=args.output_dir
|
||||
output_dir=args.output_dir,
|
||||
variance=args.variance,
|
||||
motion=args.motion,
|
||||
density=args.density
|
||||
)
|
||||
print(result)
|
||||
|
||||
|
||||
@ -110,6 +110,29 @@ If not, use the Master rules exclusively.
|
||||
Now, generate the code...
|
||||
```
|
||||
|
||||
### Step 2c: Design Dials (optional)
|
||||
|
||||
Three optional 1-10 sliders that tune `--design-system` output without changing your query. Add any combination of them to the same command:
|
||||
|
||||
```bash
|
||||
python3 skills/ui-ux-pro-max/scripts/search.py "<query>" --design-system --variance <1-10> --motion <1-10> --density <1-10>
|
||||
```
|
||||
|
||||
| Dial | Low (1-3) | Mid (4-7) | High (8-10) |
|
||||
|------|-----------|-----------|-------------|
|
||||
| `--variance` | Centered / minimal (biases toward Minimalism-style categories) | Balanced / modern | Bold / asymmetric (biases toward Brutalism, Bento Grids) |
|
||||
| `--motion` | Subtle micro-interactions | Standard scroll/stagger motion | Complex choreography (pin, Flip, SplitText) |
|
||||
| `--density` | Spacious (24-96px spacing scale) | Standard (16-64px, current default) | Dense/dashboard (8-32px spacing scale) |
|
||||
|
||||
- `--motion` attaches a ready-to-use GSAP snippet (with framework notes, Do/Don't, and performance notes) pulled from `--domain gsap`, matched to the resolved tier (Subtle/Standard/Complex).
|
||||
- `--density` overrides the `--space-*` CSS variable table in the ASCII/markdown/MASTER.md output — use it for dashboards (high) vs. marketing pages (low) without hand-editing tokens.
|
||||
- Leaving a dial unset keeps that part of the output exactly as it was before (no behavior change).
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
python3 skills/ui-ux-pro-max/scripts/search.py "internal analytics dashboard" --design-system --variance 8 --motion 7 --density 8 -p "Ops Console"
|
||||
```
|
||||
|
||||
### Step 3: Supplement with Detailed Searches (as needed)
|
||||
|
||||
After getting the design system, use domain searches to get additional details:
|
||||
@ -156,6 +179,7 @@ python3 skills/ui-ux-pro-max/scripts/search.py "<keyword>" --stack <stack>
|
||||
| `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 |
|
||||
| `gsap` | GSAP animation skeletons by intensity tier | scroll reveal, stagger, magnetic cursor, page transition |
|
||||
| `react` | React/Next.js performance | waterfall, bundle, suspense, memo, rerender, cache |
|
||||
| `web` | App interface guidelines (iOS/Android/React Native) | accessibilityLabel, touch targets, safe areas, Dynamic Type |
|
||||
| `prompt` | AI prompts, CSS keywords | (style name) |
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user