+ {UI_LIBRARIES.map((lib) => {
+ const selected = value === lib.id
+ const compatible = isCompatible(lib)
+ // hoveredId is tracked for potential future hover preview effects
+ const _hovered = hoveredId === lib.id
+ void _hovered // Suppress unused variable warning
+
+ return (
+
compatible && onChange(lib.id)}
+ onMouseEnter={() => setHoveredId(lib.id)}
+ onMouseLeave={() => setHoveredId(null)}
+ role="option"
+ aria-selected={selected}
+ aria-disabled={!compatible}
+ aria-label={`Select ${lib.name} component library`}
+ tabIndex={compatible ? 0 : -1}
+ onKeyDown={(e) => {
+ if (compatible && (e.key === 'Enter' || e.key === ' ')) {
+ e.preventDefault()
+ onChange(lib.id)
+ }
+ }}
+ >
+
+
+
+ {getLibraryIcon(lib.id)}
+ {lib.name}
+
+ {selected && (
+
+
+
+ )}
+
+
+ {lib.hasMcp && (
+
+
+ MCP Enabled
+
+ )}
+ {lib.isRecommended && framework === 'react' && (
+
+ Recommended
+
+ )}
+ {!compatible && (
+
+ Not compatible with {framework}
+
+ )}
+
+
+
+
+ {lib.description}
+
+ {lib.frameworks.length > 0 && lib.id !== 'none' && (
+
+ {lib.frameworks.map((fw) => (
+
+ {fw}
+
+ ))}
+
+ )}
+
+
+ )
+ })}
+
+ )
+}
+
+export { UI_LIBRARIES }
diff --git a/ui/src/components/VisualStyleSelector.tsx b/ui/src/components/VisualStyleSelector.tsx
new file mode 100644
index 00000000..7da2975d
--- /dev/null
+++ b/ui/src/components/VisualStyleSelector.tsx
@@ -0,0 +1,236 @@
+/**
+ * Visual Style Selector Component
+ *
+ * Allows users to select a visual style during project creation.
+ * Shows color swatches and style previews.
+ */
+
+import { useState } from 'react'
+import { Check, Paintbrush, Sparkles, Layers, Gamepad2, Settings2 } from 'lucide-react'
+import { cn } from '@/lib/utils'
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
+import { Badge } from '@/components/ui/badge'
+
+export interface VisualStyle {
+ id: string
+ name: string
+ description: string
+ colors: string[]
+ generatesTokens: boolean
+ previewImage?: string
+}
+
+const VISUAL_STYLES: VisualStyle[] = [
+ {
+ id: 'default',
+ name: 'Modern/Clean',
+ description: 'Minimal, professional design following library defaults',
+ colors: ['#3b82f6', '#64748b', '#ffffff'],
+ generatesTokens: false,
+ },
+ {
+ id: 'neobrutalism',
+ name: 'Neobrutalism',
+ description: 'Bold colors, hard shadows, no border-radius',
+ colors: ['#ff6b6b', '#4ecdc4', '#000000'],
+ generatesTokens: true,
+ },
+ {
+ id: 'glassmorphism',
+ name: 'Glassmorphism',
+ description: 'Frosted glass effects, blur, transparency',
+ colors: ['#8b5cf6', '#06b6d4', '#f472b6'],
+ generatesTokens: true,
+ },
+ {
+ id: 'retro',
+ name: 'Retro/Arcade',
+ description: 'Pixel-art inspired, vibrant neons, 8-bit aesthetic',
+ colors: ['#ff00ff', '#00ffff', '#ffff00'],
+ generatesTokens: true,
+ },
+ {
+ id: 'custom',
+ name: 'Custom',
+ description: 'Define your own design tokens',
+ colors: [],
+ generatesTokens: true,
+ },
+]
+
+interface VisualStyleSelectorProps {
+ value: string
+ onChange: (styleId: string) => void
+ className?: string
+}
+
+export function VisualStyleSelector({
+ value,
+ onChange,
+ className,
+}: VisualStyleSelectorProps) {
+ const [hoveredId, setHoveredId] = useState