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:
Clark Cant 2026-07-02 23:26:22 +07:00 committed by GitHub
commit bf5c3cfb61
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
43 changed files with 1225 additions and 305 deletions

View File

@ -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 |

View 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 No Category Intensity Tier Keywords Trigger Duration Easing GSAP Snippet Framework Notes Do Don't Performance Notes
2 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
3 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
4 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
5 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
6 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
7 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
8 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
9 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
10 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
11 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
12 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
13 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
14 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
15 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
16 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
17 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

View File

@ -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"]
}

View File

@ -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("")

View File

@ -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))

View File

@ -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

View File

@ -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

1 No Product Type Primary On Primary Secondary On Secondary Accent On Accent Background Foreground Card Card Foreground Muted Muted Foreground Border Destructive On Destructive Ring Notes
159 158 Emergency SOS & Safety #DC2626 #FFFFFF #EF4444 #FFFFFF #2563EB #FFFFFF #FFF1F2 #0F172A #FFFFFF #0F172A #FCF1F1 #64748B #FAE4E4 #DC2626 #FFFFFF #DC2626 Alert red + safety blue
160 159 Wallpaper & Theme App #7C3AED #FFFFFF #EC4899 #FFFFFF #2563EB #FFFFFF #FAF5FF #0F172A #FFFFFF #0F172A #F7F3FD #64748B #EFE7FC #DC2626 #FFFFFF #7C3AED Aesthetic purple + trending pink
161 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
162 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
163 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
164 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
165 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
166 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
167 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
168 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
169 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
170 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
171 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
172 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
173 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
174 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
175 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
176 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
177 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
178 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
179 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
180 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
181 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
182 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
183 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
184 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
185 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
186 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
187 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
188 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
189 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
190 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
191 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
192 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
193 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
View 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
View File

Can't render this file because it is too large.

View 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 No Category Intensity Tier Keywords Trigger Duration Easing GSAP Snippet Framework Notes Do Don't Performance Notes
2 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
3 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
4 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
5 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
6 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
7 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
8 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
9 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
10 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
11 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
12 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
13 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
14 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
15 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
16 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
17 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

View File

@ -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.

View File

@ -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"]
}

View File

@ -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("")

View File

@ -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)

View File

@ -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`,

View File

@ -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"

View File

@ -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

View File

@ -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) {

View File

@ -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}],
}}
"""

View File

@ -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

View File

@ -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
View 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"
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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
View 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
View 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"
}

View File

@ -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"
}

View 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"
}

4
cli/assets/templates/platforms/kiro.json Executable file → Normal file
View 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"
}

View File

@ -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"
}

View File

@ -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
View 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"
}

View File

@ -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"
}

View File

@ -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
View 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"
}

View 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 No Category Intensity Tier Keywords Trigger Duration Easing GSAP Snippet Framework Notes Do Don't Performance Notes
2 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
3 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
4 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
5 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
6 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
7 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
8 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
9 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
10 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
11 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
12 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
13 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
14 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
15 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
16 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
17 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

View File

@ -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"]
}

View File

@ -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("")

View File

@ -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)

View File

@ -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) |