First implementation of the style guide.

This commit is contained in:
sean-eskerium
2025-09-22 02:52:45 -04:00
parent 4c910c1471
commit f188d3a27a
38 changed files with 9341 additions and 27 deletions

View File

@@ -0,0 +1,967 @@
# PRP: Interactive Style Guide Implementation
## Overview
Implement a comprehensive interactive style guide page for the Archon UI system that provides configurators for glass morphism components, standardized modal types, layout templates, and generates copy-ready code with AI-friendly documentation.
## Context for AI Implementation
This PRP provides complete implementation guidance for creating an interactive style guide at `/style-guide` route. The style guide will showcase existing Archon glass morphism components with interactive configurators, allowing developers to visually configure components and copy production-ready code.
**CRITICAL LAYOUT CONSTRAINT**: The Archon app has a fixed left navigation sidebar and potential right chat sidebar. The style guide must work within these constraints:
- Main app navigation stays on the left (always visible)
- Style guide has its own internal navigation (sections/components)
- Layout templates must include collapsible sidebars that work within the main content area
- The "Sidebar Layout" template should demonstrate a collapsible project sidebar (like ProjectsView)
**CRITICAL COMPONENT CONSISTENCY**: The style guide must use existing Radix UI primitives and extend them, not create new competing components. All components should import from `/features/ui/primitives/`.
## Existing Patterns to Follow
### Glass Morphism Styles (Reference)
- **Location**: `/archon-ui-main/src/features/ui/primitives/styles.ts`
- Glass backgrounds, borders, shadows with glow effects
- Priority colors system already defined
- Use existing `glassmorphism` and `compoundStyles` objects
### Button Component (Reference)
- **Location**: `/archon-ui-main/src/features/ui/primitives/button.tsx`
- Variants: default, destructive, outline, ghost, link, cyan, knowledge
- Sizes: xs, sm, default, lg, icon
- Loading state support built-in
### Modal Patterns (Reference)
- **Location**: `/archon-ui-main/src/features/ui/components/DeleteConfirmModal.tsx`
- Uses AlertDialog from primitives
- Size prop support (compact, default, large)
- Proper glass morphism styling
### Routing Pattern (Reference)
- **Location**: `/archon-ui-main/src/App.tsx`
- Add new route: `<Route path="/style-guide" element={<StyleGuidePage />} />`
- Follow existing page import pattern
## Implementation Tasks
### Task 0: Create Missing Radix Primitives (PRIORITY)
Before implementing the style guide, we need to create missing Radix primitives that the configurators will use:
#### Create RadioGroup Primitive
```typescript
// /src/features/ui/primitives/radio-group.tsx
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
import { Circle } from "lucide-react";
import React from "react";
import { cn, glassmorphism } from "./styles";
export const RadioGroup = RadioGroupPrimitive.Root;
export const RadioGroupItem = React.forwardRef<
React.ElementRef<typeof RadioGroupPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
>(({ className, ...props }, ref) => (
<RadioGroupPrimitive.Item
ref={ref}
className={cn(
"aspect-square h-4 w-4 rounded-full",
"backdrop-blur-md bg-gradient-to-b from-white/80 to-white/60",
"dark:from-white/10 dark:to-black/30",
glassmorphism.border.default,
glassmorphism.interactive.base,
"focus:outline-none focus:ring-2 focus:ring-cyan-500",
"disabled:cursor-not-allowed disabled:opacity-50",
"data-[state=checked]:border-cyan-500",
className
)}
{...props}
>
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
<Circle className="h-2.5 w-2.5 fill-cyan-500 text-cyan-500" />
</RadioGroupPrimitive.Indicator>
</RadioGroupPrimitive.Item>
));
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
```
#### Create Label Primitive
```typescript
// /src/features/ui/primitives/label.tsx
import * as LabelPrimitive from "@radix-ui/react-label";
import React from "react";
import { cn } from "./styles";
export const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(
"text-sm font-medium leading-none",
"text-gray-700 dark:text-gray-200",
"peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
className
)}
{...props}
/>
));
Label.displayName = LabelPrimitive.Root.displayName;
```
#### Create Card Primitive
```typescript
// /src/features/ui/primitives/card.tsx
import React from "react";
import { cn, glassmorphism } from "./styles";
interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
glowColor?: keyof typeof glassmorphism.shadow.glow | 'none';
edgePosition?: keyof typeof glassmorphism.edgeGlow | 'none';
size?: keyof typeof glassmorphism.sizes.card;
}
export const Card = React.forwardRef<HTMLDivElement, CardProps>(
({ className, glowColor = 'none', edgePosition = 'none', size = 'md', children, ...props }, ref) => {
return (
<div
ref={ref}
className={cn(
glassmorphism.background.card,
glassmorphism.border.default,
glassmorphism.shadow.md,
glowColor !== 'none' && glassmorphism.shadow.glow[glowColor],
edgePosition !== 'none' && glassmorphism.edgeGlow[edgePosition],
glassmorphism.sizes.card[size],
"rounded-lg transition-all duration-300",
className
)}
{...props}
>
{children}
</div>
);
}
);
Card.displayName = "Card";
```
#### Enhance styles.ts
```typescript
// Add to /src/features/ui/primitives/styles.ts
export const glassmorphism = {
// ... existing code ...
// Add edge glow positions for card configurator
edgeGlow: {
top: "border-t-2 border-t-cyan-400 relative before:content-[''] before:absolute before:top-0 before:left-0 before:right-0 before:h-[2px] before:bg-gradient-to-r before:from-transparent before:via-cyan-400 before:to-transparent",
left: "border-l-2 border-l-cyan-400 relative before:content-[''] before:absolute before:top-0 before:left-0 before:bottom-0 before:w-[2px] before:bg-gradient-to-b before:from-transparent before:via-cyan-400 before:to-transparent",
right: "border-r-2 border-r-cyan-400 relative before:content-[''] before:absolute before:top-0 before:right-0 before:bottom-0 before:w-[2px] before:bg-gradient-to-b before:from-transparent before:via-cyan-400 before:to-transparent",
bottom: "border-b-2 border-b-cyan-400 relative before:content-[''] before:absolute before:bottom-0 before:left-0 before:right-0 before:h-[2px] before:bg-gradient-to-r before:from-transparent before:via-cyan-400 before:to-transparent",
all: "border-2 border-cyan-400 relative before:content-[''] before:absolute before:inset-0 before:rounded-lg before:p-[2px] before:bg-gradient-to-r before:from-cyan-400 before:via-cyan-500 before:to-cyan-400 before:-z-10",
none: ""
},
// Add configurable sizes for cards
sizes: {
card: {
sm: "p-4 max-w-sm",
md: "p-6 max-w-md",
lg: "p-8 max-w-lg",
xl: "p-10 max-w-xl"
}
},
// Ensure all glow colors are available
shadow: {
// ... existing shadow code ...
glow: {
purple: "shadow-[0_0_10px_2px_rgba(168,85,247,0.4)] dark:shadow-[0_0_20px_5px_rgba(168,85,247,0.7)]",
blue: "shadow-[0_0_10px_2px_rgba(59,130,246,0.4)] dark:shadow-[0_0_20px_5px_rgba(59,130,246,0.7)]",
green: "shadow-[0_0_10px_2px_rgba(16,185,129,0.4)] dark:shadow-[0_0_20px_5px_rgba(16,185,129,0.7)]",
red: "shadow-[0_0_10px_2px_rgba(239,68,68,0.4)] dark:shadow-[0_0_20px_5px_rgba(239,68,68,0.7)]",
orange: "shadow-[0_0_10px_2px_rgba(251,146,60,0.4)] dark:shadow-[0_0_20px_5px_rgba(251,146,60,0.7)]",
cyan: "shadow-[0_0_10px_2px_rgba(34,211,238,0.4)] dark:shadow-[0_0_20px_5px_rgba(34,211,238,0.7)]",
pink: "shadow-[0_0_10px_2px_rgba(236,72,153,0.4)] dark:shadow-[0_0_20px_5px_rgba(236,72,153,0.7)]"
}
}
};
```
### Task 1: Create Page Structure and Routing
```typescript
// 1. Create /src/pages/StyleGuidePage.tsx
// 2. Add route in App.tsx
// 3. Add navigation link in MainLayout navigation
```
### Task 2: Create Base Components Structure
```
/src/components/style-guide/
├── shared/
│ ├── NavigationSidebar.tsx # 4 sections with expandable items
│ ├── LivePreview.tsx # Preview container with zoom controls
│ ├── CodeDisplay.tsx # Syntax highlighted code with copy
│ └── ConfigPanel.tsx # Configuration controls container
```
### Task 3: Implement Component Configurators
```
/src/components/style-guide/configurators/
├── GlassCardConfigurator.tsx # Glow colors, edge positions, sizes
├── ButtonConfigurator.tsx # All variants, sizes, states
├── ModalConfigurator.tsx # 6 standard types showcase
├── TableConfigurator.tsx # Table with glass effects
├── FormConfigurator.tsx # Input states and validation
└── ToggleConfigurator.tsx # PowerButton variations
```
### Task 4: Create Modal Standards
```typescript
// /src/components/style-guide/standards/modalStandards.ts
export const MODAL_TYPES = {
confirmation: { size: "sm", glowColor: "red", purpose: "Destructive actions" },
formCreate: { size: "md", glowColor: "green", purpose: "Creating resources" },
formEdit: { size: "md", glowColor: "blue", purpose: "Editing resources" },
display: { size: "lg", glowColor: "purple", purpose: "Detailed information" },
codeViewer: { size: "xl", glowColor: "cyan", purpose: "Code display" },
settings: { size: "lg", glowColor: "blue", purpose: "App settings" }
};
```
### Task 5: Implement Layout Templates
```
/src/components/style-guide/layout-templates/
├── DashboardTemplate.tsx # Grid of cards/widgets
├── SidebarTemplate.tsx # Collapsible sidebar + main (like ProjectsView)
├── KanbanTemplate.tsx # Column-based organization
├── TableViewTemplate.tsx # Data table with actions
└── CenteredFormTemplate.tsx # Single focus area
```
**SidebarTemplate Special Requirements:**
- Must show a collapsible sidebar within the main content area
- Sidebar toggles between top position (cards) and left position (list)
- Similar to ProjectsView with project cards that can be toggled to sidebar
- Include toggle button to switch between layouts
- Sidebar width: 280px when on left, full width when on top
### Task 6: Code Generation System
```typescript
// Each configurator must:
// 1. Generate real-time code based on configuration
// 2. Include all imports
// 3. Add AI context comments with decision trees
// 4. Provide copy-to-clipboard functionality
```
## Component Implementation Details
### ConfigPanel Component (Uses Card Primitive)
```typescript
// /src/components/style-guide/shared/ConfigPanel.tsx
import { Card } from '@/features/ui/primitives/card';
import { cn } from '@/features/ui/primitives/styles';
interface ConfigPanelProps {
title?: string;
children: React.ReactNode;
className?: string;
}
export const ConfigPanel = ({ title, children, className }: ConfigPanelProps) => (
<Card className={cn("space-y-4", className)}>
{title && <h3 className="font-semibold text-lg mb-2">{title}</h3>}
{children}
</Card>
);
```
### GlassCardConfigurator (Using All Primitives)
```typescript
// Configuration Options:
- glowColor: ["purple", "blue", "green", "orange", "red", "none"]
- edgePosition: ["top", "left", "right", "bottom", "all", "none"]
- size: ["sm", "md", "lg", "xl"]
// Generated Code must include:
/**
* 🤖 AI CONTEXT: GlassCard Component
* PURPOSE: Container with glass morphism effect
* WHEN TO USE: Primary content containers, cards, panels
* WHEN NOT TO USE: Backgrounds, layout wrappers
*
* DECISION TREE:
* - If primary content → glowColor="purple"
* - If success message → glowColor="green"
* - If error/danger → glowColor="red"
*/
```
### NavigationSidebar Structure
```typescript
const NAVIGATION = {
foundations: {
label: "Foundations",
items: ["Colors", "Typography", "Spacing", "Effects"]
},
components: {
label: "Components",
items: ["Cards", "Buttons", "Forms", "Tables", "Modals", "Toggles"]
},
patterns: {
label: "Patterns",
items: ["Layouts", "Feedback", "Navigation", "Data Display"]
},
examples: {
label: "Examples",
items: ["Compositions", "Pages", "Workflows"]
}
};
```
### LivePreview Component
```typescript
// Implementation: /src/components/style-guide/shared/LivePreview.tsx
import { useState } from 'react';
import { ZoomIn, ZoomOut, RotateCcw } from 'lucide-react';
import { Button } from '@/features/ui/primitives/button';
import { cn } from '@/features/ui/primitives/styles';
interface LivePreviewProps {
children: React.ReactNode;
className?: string;
minHeight?: string;
}
export const LivePreview = ({ children, className, minHeight = "400px" }: LivePreviewProps) => {
const [zoom, setZoom] = useState(100);
return (
<div className="relative rounded-lg overflow-hidden">
{/* Zoom Controls */}
<div className="absolute top-2 right-2 z-10 flex gap-1">
<Button
size="icon"
variant="outline"
onClick={() => setZoom(prev => Math.max(50, prev - 25))}
>
<ZoomOut className="w-4 h-4" />
</Button>
<Button
size="icon"
variant="outline"
onClick={() => setZoom(100)}
>
<RotateCcw className="w-4 h-4" />
</Button>
<Button
size="icon"
variant="outline"
onClick={() => setZoom(prev => Math.min(150, prev + 25))}
>
<ZoomIn className="w-4 h-4" />
</Button>
</div>
{/* Grid Background */}
<div
className={cn(
"bg-gray-50 dark:bg-gray-900/50",
"bg-[linear-gradient(to_right,#8882_1px,transparent_1px),linear-gradient(to_bottom,#8882_1px,transparent_1px)]",
"bg-[size:20px_20px]",
"p-8 flex items-center justify-center",
className
)}
style={{ minHeight }}
>
<div
style={{
transform: `scale(${zoom / 100})`,
transformOrigin: 'center'
}}
>
{children}
</div>
</div>
</div>
);
};
```
### CodeDisplay Component
```typescript
// Implementation: /src/components/style-guide/shared/CodeDisplay.tsx
import { useState } from 'react';
import { Check, Copy } from 'lucide-react';
import { Button } from '@/features/ui/primitives/button';
import { cn } from '@/features/ui/primitives/styles';
interface CodeDisplayProps {
code: string;
language?: 'typescript' | 'jsx' | 'css';
showLineNumbers?: boolean;
className?: string;
}
export const CodeDisplay = ({
code,
language = 'typescript',
showLineNumbers = false,
className
}: CodeDisplayProps) => {
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
await navigator.clipboard.writeText(code);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
const lines = code.split('\n');
// Basic syntax highlighting with regex
const highlightCode = (line: string) => {
// Escape HTML
line = line.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
// Keywords
line = line.replace(
/\b(import|export|from|const|let|var|function|return|if|else|interface|type|class|extends|implements)\b/g,
'<span class="text-purple-400">$1</span>'
);
// Strings
line = line.replace(
/(["'`])([^"'`]*)\1/g,
'<span class="text-green-400">$1$2$1</span>'
);
// Comments
line = line.replace(
/(\/\/.*$|\/\*.*\*\/)/g,
'<span class="text-gray-500">$1</span>'
);
// JSX tags
line = line.replace(
/&lt;([A-Z][A-Za-z0-9]*)(\s|&gt;|\/&gt;)/g,
'&lt;<span class="text-cyan-400">$1</span>$2'
);
// Props
line = line.replace(
/(\w+)=/g,
'<span class="text-orange-400">$1</span>='
);
return line;
};
return (
<div className={cn(
"relative rounded-lg overflow-hidden",
"bg-gray-900 border border-gray-800",
className
)}>
{/* Copy Button */}
<Button
size="icon"
variant="ghost"
onClick={handleCopy}
className="absolute top-2 right-2 z-10 text-gray-400 hover:text-white"
>
{copied ? (
<Check className="w-4 h-4 text-green-400" />
) : (
<Copy className="w-4 h-4" />
)}
</Button>
{/* Code Content */}
<pre className="p-4 overflow-x-auto">
<code className="text-sm font-mono">
{lines.map((line, index) => (
<div key={index} className="flex">
{showLineNumbers && (
<span className="text-gray-500 mr-4 select-none w-8 text-right">
{index + 1}
</span>
)}
<span
dangerouslySetInnerHTML={{
__html: highlightCode(line) || '&nbsp;'
}}
/>
</div>
))}
</code>
</pre>
</div>
);
};
```
### Code Generation System (Updated with Proper Imports)
```typescript
// Detailed implementation for code generation
// Example: /src/components/style-guide/configurators/GlassCardConfigurator.tsx
import { useState } from 'react';
import { Card } from '@/features/ui/primitives/card';
import { Button } from '@/features/ui/primitives/button';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/features/ui/primitives/select';
import { RadioGroup, RadioGroupItem } from '@/features/ui/primitives/radio-group';
import { Label } from '@/features/ui/primitives/label';
import { LivePreview } from '../shared/LivePreview';
import { CodeDisplay } from '../shared/CodeDisplay';
import { ConfigPanel } from '../shared/ConfigPanel';
import { glassmorphism } from '@/features/ui/primitives/styles';
interface GlassCardConfig {
glowColor: 'purple' | 'blue' | 'green' | 'orange' | 'red' | 'none';
edgePosition: 'top' | 'left' | 'right' | 'bottom' | 'all' | 'none';
size: 'sm' | 'md' | 'lg' | 'xl';
content: string;
}
export const GlassCardConfigurator = () => {
const [config, setConfig] = useState<GlassCardConfig>({
glowColor: 'purple',
edgePosition: 'top',
size: 'md',
content: 'Your content here'
});
const generateCode = (config: GlassCardConfig) => {
const imports = `import { Card } from '@/features/ui/primitives/card';`;
const aiContext = `/**
* 🤖 AI CONTEXT: GlassCard Component
*
* PURPOSE: Container with glass morphism effect for primary content
* WHEN TO USE: Feature cards, content sections, important information displays
* WHEN NOT TO USE: Backgrounds, full-page layouts, navigation elements
*
* DECISION TREE:
* - If primary/featured content → glowColor="purple"
* - If success state/creation → glowColor="green"
* - If error/danger/deletion → glowColor="red"
* - If informational/edit → glowColor="blue"
* - If warning/caution → glowColor="orange"
*
* SEMANTIC COLORS:
* - purple: Primary actions, featured content, main CTAs
* - green: Success states, creation, positive feedback
* - red: Destructive actions, errors, critical alerts
* - blue: Information, editing, secondary actions
* - orange: Warnings, important notices, caution states
*
* EDGE POSITION GUIDELINES:
* - top: Default, draws eye downward into content
* - left: For sidebar items or navigation cards
* - right: For action panels or secondary info
* - bottom: For footer-like content or summaries
* - all: For highly important or selected states
* - none: For subtle, non-emphasized containers
*/`;
const component = `export const MyCard = ({ children }) => {
return (
<Card
glowColor="${config.glowColor}"
edgePosition="${config.edgePosition}"
size="${config.size}"
>
{children}
</Card>
);
};`;
return `${imports}\n\n${aiContext}\n\n${component}`;
};
return (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Left side: Configuration */}
<div className="space-y-6">
<ConfigPanel title="Glass Card Configuration">
<div className="space-y-4">
{/* Glow Color */}
<div>
<Label>Glow Color</Label>
<Select
value={config.glowColor}
onValueChange={(value) => setConfig({...config, glowColor: value as any})}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="purple">Purple (Primary)</SelectItem>
<SelectItem value="blue">Blue (Info)</SelectItem>
<SelectItem value="green">Green (Success)</SelectItem>
<SelectItem value="orange">Orange (Warning)</SelectItem>
<SelectItem value="red">Red (Danger)</SelectItem>
<SelectItem value="none">None</SelectItem>
</SelectContent>
</Select>
</div>
{/* Edge Position */}
<div>
<Label>Edge Glow Position</Label>
<RadioGroup
value={config.edgePosition}
onValueChange={(value) => setConfig({...config, edgePosition: value as any})}
>
<div className="grid grid-cols-3 gap-2">
{['top', 'left', 'right', 'bottom', 'all', 'none'].map(position => (
<div key={position} className="flex items-center space-x-2">
<RadioGroupItem value={position} id={position} />
<Label htmlFor={position} className="capitalize">
{position}
</Label>
</div>
))}
</div>
</RadioGroup>
</div>
{/* Size */}
<div>
<Label>Size</Label>
<RadioGroup
value={config.size}
onValueChange={(value) => setConfig({...config, size: value as any})}
>
<div className="flex gap-4">
{['sm', 'md', 'lg', 'xl'].map(size => (
<div key={size} className="flex items-center space-x-2">
<RadioGroupItem value={size} id={`size-${size}`} />
<Label htmlFor={`size-${size}`} className="uppercase">
{size}
</Label>
</div>
))}
</div>
</RadioGroup>
</div>
</div>
</ConfigPanel>
{/* Live Preview */}
<LivePreview>
<Card
glowColor={config.glowColor}
edgePosition={config.edgePosition}
size={config.size}
>
<h3 className="text-lg font-semibold mb-2">Glass Card Example</h3>
<p className="text-gray-600 dark:text-gray-300">
{config.content}
</p>
</Card>
</LivePreview>
</div>
{/* Right side: Generated Code */}
<div>
<h3 className="text-lg font-semibold mb-4">Generated Code</h3>
<CodeDisplay
code={generateCode(config)}
language="typescript"
showLineNumbers
/>
</div>
</div>
);
};
```
### SidebarTemplate Implementation (Using Primitives)
```typescript
// /src/components/style-guide/layout-templates/SidebarTemplate.tsx
import { useState } from 'react';
import { ChevronLeft, ChevronRight, LayoutGrid, List } from 'lucide-react';
import { Button } from '@/features/ui/primitives/button';
import { Card } from '@/features/ui/primitives/card';
import { cn } from '@/features/ui/primitives/styles';
import { CodeDisplay } from '../shared/CodeDisplay';
export const SidebarTemplate = () => {
const [sidebarPosition, setSidebarPosition] = useState<'top' | 'left'>('top');
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const items = [
{ id: '1', title: 'Project Alpha', status: 'active' },
{ id: '2', title: 'Project Beta', status: 'pending' },
{ id: '3', title: 'Project Gamma', status: 'completed' },
];
const generateCode = () => {
return `import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { Button } from '@/features/ui/primitives/button';
import { Card } from '@/features/ui/primitives/card';
export const SidebarLayout = () => {
const [position, setPosition] = useState<'top' | 'left'>('top');
const [collapsed, setCollapsed] = useState(false);
return (
<div className="flex flex-col h-full">
{/* Toggle Button */}
<div className="flex justify-end p-2">
<Button
variant="outline"
size="sm"
onClick={() => setPosition(p => p === 'top' ? 'left' : 'top')}
>
{position === 'top' ? <List /> : <LayoutGrid />}
</Button>
</div>
<div className={cn(
"flex",
position === 'top' ? "flex-col" : "flex-row",
"flex-1 gap-4 p-4"
)}>
{/* Sidebar Content */}
<AnimatePresence mode="wait">
<motion.div
key={position}
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
className={cn(
position === 'left' && !collapsed && "w-[280px]",
position === 'left' && collapsed && "w-[60px]",
position === 'top' && "w-full",
"transition-all duration-300"
)}
>
{/* Sidebar items here */}
</motion.div>
</AnimatePresence>
{/* Main Content Area */}
<div className="flex-1">
{/* Main content here */}
</div>
</div>
</div>
);
};`;
};
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<h3 className="text-lg font-semibold">Sidebar Layout Template</h3>
<Button
variant="outline"
size="sm"
onClick={() => setSidebarPosition(p => p === 'top' ? 'left' : 'top')}
>
Toggle Position: {sidebarPosition === 'top' ? <LayoutGrid className="w-4 h-4 ml-2" /> : <List className="w-4 h-4 ml-2" />}
</Button>
</div>
{/* Live Demo */}
<div className="border rounded-lg p-4 min-h-[400px] bg-gray-50 dark:bg-gray-900/50">
<div className={cn(
"flex h-full",
sidebarPosition === 'top' ? "flex-col" : "flex-row",
"gap-4"
)}>
{/* Sidebar */}
<div className={cn(
sidebarPosition === 'left' && !sidebarCollapsed && "w-[280px]",
sidebarPosition === 'left' && sidebarCollapsed && "w-[60px]",
sidebarPosition === 'top' && "w-full",
"transition-all duration-300"
)}>
{sidebarPosition === 'top' ? (
<div className="grid grid-cols-3 gap-4">
{items.map(item => (
<Card key={item.id} glowColor="purple" className="hover:shadow-lg transition-shadow cursor-pointer">
<h4 className="font-medium">{item.title}</h4>
<p className="text-sm text-gray-500 dark:text-gray-400">{item.status}</p>
</Card>
))}
</div>
) : (
<div className="space-y-2">
{sidebarPosition === 'left' && (
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
onClick={() => setSidebarCollapsed(!sidebarCollapsed)}
>
{sidebarCollapsed ? <ChevronRight /> : <ChevronLeft />}
</Button>
)}
{items.map(item => (
<Card
key={item.id}
className={cn(
"hover:shadow-lg transition-shadow cursor-pointer",
sidebarCollapsed && "p-2"
)}
>
{!sidebarCollapsed ? (
<>
<h4 className="font-medium text-sm">{item.title}</h4>
<p className="text-xs text-gray-500 dark:text-gray-400">{item.status}</p>
</>
) : (
<div className="w-8 h-8 rounded bg-purple-500/20 flex items-center justify-center">
{item.title[0]}
</div>
)}
</Card>
))}
</div>
)}
</div>
{/* Main Content */}
<div className="flex-1 border-2 border-dashed border-gray-300 dark:border-gray-700 rounded-lg p-8">
<div className="text-center text-gray-500 dark:text-gray-400">
<p className="text-lg font-medium mb-2">Main Content Area</p>
<p className="text-sm">This is where your main content would go</p>
<p className="text-xs mt-4">Sidebar is currently: {sidebarPosition === 'top' ? 'Top' : `Left (${sidebarCollapsed ? 'Collapsed' : 'Expanded'})`}</p>
</div>
</div>
</div>
</div>
<CodeDisplay code={generateCode()} language="typescript" showLineNumbers />
</div>
);
};
```
## Confidence Score: 10/10
The PRP provides comprehensive implementation guidance with:
- **Radix primitive creation tasks** for missing components
- **Style enhancements** to support configurator needs
- **Consistent imports** from `/features/ui/primitives/`
- **Complete code examples** using actual primitives
- **Clear implementation order** with primitive creation first
The score is 10/10 because:
- **Component consistency**: All configurators use existing primitives
- **No duplicate components**: Reuses existing infrastructure
- **Enhanced flexibility**: styles.ts gets necessary additions
- **Complete primitive set**: Creates missing RadioGroup, Label, and Card
- **Production ready**: Uses same components developers will use
This PRP ensures the style guide showcases actual Archon components, not separate demo versions.
## Validation Gates
```bash
# Frontend checks
cd archon-ui-main
npm run lint # No linting errors
npx tsc --noEmit # No TypeScript errors
npm run test # All tests pass
# Verify primitives work
# Check that all new primitives render correctly
# Verify Card primitive replaces old Card.tsx
# Test RadioGroup and Label components
# Confirm styles.ts enhancements work
# Verify routes work
# Navigate to http://localhost:3737/style-guide
# Verify all configurators render
# Test code generation and copy functionality
# Check responsive behavior
```
## Dependencies
- Existing Archon UI primitives and components
- Tailwind CSS (already configured)
- Lucide React icons (already installed)
- React Router (already installed)
- Radix UI packages (already installed):
- @radix-ui/react-radio-group
- @radix-ui/react-label
- For syntax highlighting: Use simple regex-based solution (no external libs)
## File Creation Order
1. **Create missing primitives first** (RadioGroup, Label, Card)
2. **Enhance styles.ts** with edgeGlow and sizes
3. Create page and add route
4. Build shared components (NavigationSidebar, LivePreview, CodeDisplay, ConfigPanel)
5. Implement GlassCardConfigurator first (as template)
6. Add ButtonConfigurator
7. Create modal standards and ModalConfigurator
8. Add remaining configurators
9. Implement layout templates
10. Add AI context comments throughout
## Key Implementation Notes
### State Management
- Use React hooks (useState) for configuration state
- Each configurator manages its own state
- URL params for sharing configurations (optional enhancement)
### Import Consistency
- ALWAYS import from `/features/ui/primitives/` for UI components
- Never create duplicate components
- Use the Card primitive for all card needs
- Import glassmorphism styles from styles.ts
### Performance Considerations
- Lazy load configurators that aren't visible
- Debounce configuration changes (200ms)
- Use React.memo for expensive preview renders
- Limit blur effects to 3 layers maximum
## Success Criteria
- [ ] All primitives created and working
- [ ] Old Card.tsx replaced with new primitive
- [ ] All routes work and page loads without errors
- [ ] Navigation sidebar with 4 main sections
- [ ] GlassCard configurator with live preview using Card primitive
- [ ] Button configurator with all variants
- [ ] 6 modal types clearly documented
- [ ] 5 layout templates available
- [ ] Code generation with AI comments
- [ ] Copy-to-clipboard works
- [ ] Responsive design functions properly
- [ ] No TypeScript or linting errors
- [ ] All imports from primitives folder
## Common Pitfalls to Avoid
- Don't create new component variants - use existing ones
- Don't duplicate components - use primitives
- Don't add external libraries for syntax highlighting initially
- Don't forget AI context comments in generated code
- Don't nest glass effects more than 3 levels
- Don't hardcode values - use configuration objects
- Don't import from old `/components/ui/` folder
## Testing Approach
1. Manual testing of all configurators
2. Verify code generation accuracy
3. Test copy-to-clipboard functionality
4. Check responsive breakpoints
5. Validate AI comments are included
6. Test with actual AI assistant to verify comments help
7. Verify all imports use primitives
## References
- Button component: `/archon-ui-main/src/features/ui/primitives/button.tsx`
- Glass morphism styles: `/archon-ui-main/src/features/ui/primitives/styles.ts`
- Select primitive: `/archon-ui-main/src/features/ui/primitives/select.tsx`
- Modal example: `/archon-ui-main/src/features/ui/components/DeleteConfirmModal.tsx`
- Routing: `/archon-ui-main/src/App.tsx`
- Layout: `/archon-ui-main/src/components/layout/MainLayout.tsx`
- Projects View (sidebar example): `/archon-ui-main/src/features/projects/views/ProjectsView.tsx`
- Old Card to replace: `/archon-ui-main/src/components/ui/Card.tsx`

View File

@@ -12,8 +12,11 @@
"@radix-ui/react-alert-dialog": "^1.1.15",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toast": "^1.2.15",
"@radix-ui/react-tooltip": "^1.2.8",
@@ -2745,6 +2748,29 @@
}
}
},
"node_modules/@radix-ui/react-label": {
"version": "2.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz",
"integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.1.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-menu": {
"version": "2.1.16",
"resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz",
@@ -2925,6 +2951,38 @@
}
}
},
"node_modules/@radix-ui/react-radio-group": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz",
"integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-direction": "1.1.1",
"@radix-ui/react-presence": "1.1.5",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-roving-focus": "1.1.11",
"@radix-ui/react-use-controllable-state": "1.2.2",
"@radix-ui/react-use-previous": "1.1.1",
"@radix-ui/react-use-size": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-roving-focus": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz",
@@ -3040,6 +3098,35 @@
}
}
},
"node_modules/@radix-ui/react-switch": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz",
"integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-controllable-state": "1.2.2",
"@radix-ui/react-use-previous": "1.1.1",
"@radix-ui/react-use-size": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tabs": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz",

View File

@@ -32,8 +32,11 @@
"@radix-ui/react-alert-dialog": "^1.1.15",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toast": "^1.2.15",
"@radix-ui/react-tooltip": "^1.2.8",

View File

@@ -13,6 +13,7 @@ import { ToastProvider } from './features/ui/components/ToastProvider';
import { SettingsProvider, useSettings } from './contexts/SettingsContext';
import { TooltipProvider } from './features/ui/primitives/tooltip';
import { ProjectPage } from './pages/ProjectPage';
import StyleGuidePage from './pages/StyleGuidePage';
import { DisconnectScreenOverlay } from './components/DisconnectScreenOverlay';
import { ErrorBoundaryWithBugReport } from './components/bug-report/ErrorBoundaryWithBugReport';
import { MigrationBanner } from './components/ui/MigrationBanner';
@@ -29,6 +30,7 @@ const AppRoutes = () => {
<Route path="/onboarding" element={<OnboardingPage />} />
<Route path="/settings" element={<SettingsPage />} />
<Route path="/mcp" element={<MCPPage />} />
<Route path="/style-guide" element={<StyleGuidePage />} />
{projectsEnabled ? (
<>
<Route path="/projects" element={<ProjectPage />} />

View File

@@ -1,4 +1,4 @@
import { BookOpen, Settings } from "lucide-react";
import { BookOpen, Palette, Settings } from "lucide-react";
import type React from "react";
import { Link, useLocation } from "react-router-dom";
// TEMPORARY: Use old SettingsContext until settings are migrated
@@ -54,6 +54,12 @@ export function Navigation({ className }: NavigationProps) {
label: "MCP Server",
enabled: true,
},
{
path: "/style-guide",
icon: <Palette className="h-5 w-5" />,
label: "Style Guide",
enabled: true,
},
{
path: "/settings",
icon: <Settings className="h-5 w-5" />,

View File

@@ -0,0 +1,150 @@
import { useState } from 'react';
import { Palette, Component, Layout, Code } from 'lucide-react';
import { PillNavigation } from './shared/PillNavigation';
import { ThemeToggle } from '../ui/ThemeToggle';
import { GlassCardConfigurator } from './configurators/GlassCardConfigurator';
import { ButtonConfigurator } from './configurators/ButtonConfigurator';
import { ModalConfigurator } from './configurators/ModalConfigurator';
import { FormConfigurator } from './configurators/FormConfigurator';
import { TableConfigurator } from './configurators/TableConfigurator';
import { ToggleConfigurator } from './configurators/ToggleConfigurator';
import { ColorsFoundation } from './foundations/ColorsFoundation';
import { TypographyFoundation } from './foundations/TypographyFoundation';
import { SpacingFoundation } from './foundations/SpacingFoundation';
import { EffectsFoundation } from './foundations/EffectsFoundation';
import { LayoutsPattern } from './patterns/LayoutsPattern';
import { FeedbackPattern } from './patterns/FeedbackPattern';
import { NavigationPattern } from './patterns/NavigationPattern';
import { DataDisplayPattern } from './patterns/DataDisplayPattern';
import { CompositionsExample } from './examples/CompositionsExample';
import { PagesExample } from './examples/PagesExample';
import { WorkflowsExample } from './examples/WorkflowsExample';
const FOUNDATION_TABS = [
{ id: 'Colors', label: 'Colors', component: ColorsFoundation },
{ id: 'Typography', label: 'Typography', component: TypographyFoundation },
{ id: 'Spacing', label: 'Spacing', component: SpacingFoundation },
{ id: 'Effects', label: 'Effects', component: EffectsFoundation },
];
const COMPONENT_TABS = [
{ id: 'Cards', label: 'Cards', component: GlassCardConfigurator },
{ id: 'Buttons', label: 'Buttons', component: ButtonConfigurator },
{ id: 'Forms', label: 'Forms', component: FormConfigurator },
{ id: 'Tables', label: 'Tables', component: TableConfigurator },
{ id: 'Modals', label: 'Modals', component: ModalConfigurator },
{ id: 'Toggles', label: 'Toggles', component: ToggleConfigurator },
];
const PATTERN_TABS = [
{ id: 'Layouts', label: 'Layouts', component: LayoutsPattern },
{ id: 'Feedback', label: 'Feedback', component: FeedbackPattern },
{ id: 'Navigation', label: 'Navigation', component: NavigationPattern },
{ id: 'Data Display', label: 'Data Display', component: DataDisplayPattern },
];
const EXAMPLE_TABS = [
{ id: 'Compositions', label: 'Compositions', component: CompositionsExample },
{ id: 'Pages', label: 'Pages', component: PagesExample },
{ id: 'Workflows', label: 'Workflows', component: WorkflowsExample },
];
export const StyleGuideView = () => {
const [selectedSection, setSelectedSection] = useState('foundations');
const [selectedComponent, setSelectedComponent] = useState<string | null>('Colors');
const handleSectionChange = (section: string) => {
setSelectedSection(section);
// Reset to first item of new section
if (section === 'foundations') setSelectedComponent('Colors');
else if (section === 'components') setSelectedComponent('Cards');
else if (section === 'patterns') setSelectedComponent('Layouts');
else if (section === 'examples') setSelectedComponent('Compositions');
else setSelectedComponent(null);
};
const renderContent = () => {
if (selectedSection === 'foundations' && selectedComponent) {
const tab = FOUNDATION_TABS.find(t => t.id === selectedComponent);
if (tab) {
const FoundationComponent = tab.component;
return <FoundationComponent />;
}
}
if (selectedSection === 'components' && selectedComponent) {
const tab = COMPONENT_TABS.find(t => t.id === selectedComponent);
if (tab) {
const ComponentConfigurator = tab.component;
return <ComponentConfigurator />;
}
}
if (selectedSection === 'patterns' && selectedComponent) {
const tab = PATTERN_TABS.find(t => t.id === selectedComponent);
if (tab) {
const PatternComponent = tab.component;
return <PatternComponent />;
}
}
if (selectedSection === 'examples' && selectedComponent) {
const tab = EXAMPLE_TABS.find(t => t.id === selectedComponent);
if (tab) {
const ExampleComponent = tab.component;
return <ExampleComponent />;
}
}
// Default content for other sections or no selection
const sectionContent = {
foundations: { icon: <Palette className="w-16 h-16 mx-auto mb-4 opacity-50" />, text: "Select a foundation element from the dropdown above" },
components: { icon: <Component className="w-16 h-16 mx-auto mb-4 opacity-50" />, text: "Select a component from the dropdown above" },
patterns: { icon: <Layout className="w-16 h-16 mx-auto mb-4 opacity-50" />, text: "Select a pattern from the dropdown above" },
examples: { icon: <Code className="w-16 h-16 mx-auto mb-4 opacity-50" />, text: "Select an example from the dropdown above" }
};
const content = sectionContent[selectedSection as keyof typeof sectionContent];
return (
<div className="text-center py-16 text-gray-500 dark:text-gray-400">
{content.icon}
<p>{content.text}</p>
</div>
);
};
return (
<div className="space-y-12">
{/* Header */}
<div className="relative">
{/* Theme Toggle in top right */}
<div className="absolute top-0 right-0">
<ThemeToggle accentColor="blue" />
</div>
<div className="text-center space-y-4">
<h1 className="text-4xl font-bold text-gray-900 dark:text-white mb-4">Interactive Style Guide</h1>
<p className="text-gray-600 dark:text-gray-400 text-lg max-w-2xl mx-auto">
Configure and preview Archon's glassmorphism components with live code generation.
</p>
</div>
</div>
{/* Pill Navigation */}
<div className="flex justify-center">
<PillNavigation
selectedSection={selectedSection}
selectedItem={selectedComponent}
onSectionChange={handleSectionChange}
onItemChange={setSelectedComponent}
/>
</div>
{/* Content */}
<div>
{renderContent()}
</div>
</div>
);
};

View File

@@ -0,0 +1,214 @@
import { useState } from 'react';
import { Button } from '@/features/ui/primitives/button';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/features/ui/primitives/select';
import { Switch } from '@/features/ui/primitives/switch';
import { Input } from '@/features/ui/primitives/input';
import { LivePreview } from '../shared/LivePreview';
import { CodeDisplay } from '../shared/CodeDisplay';
import { ConfigPanel } from '../shared/ConfigPanel';
import { ConfigRow } from '../shared/ConfigRow';
import { Loader2, Download, Eye, Code } from 'lucide-react';
import type { ButtonVariant, ButtonSize } from '../types';
interface ButtonConfig {
variant: ButtonVariant;
size: ButtonSize;
loading: boolean;
disabled: boolean;
withIcon: boolean;
text: string;
}
export const ButtonConfigurator = () => {
const [config, setConfig] = useState<ButtonConfig>({
variant: 'default',
size: 'default',
loading: false,
disabled: false,
withIcon: false,
text: 'Click me'
});
const [activeTab, setActiveTab] = useState<'preview' | 'code'>('preview');
const generateCode = (config: ButtonConfig) => {
const imports = [`import { Button } from '@/features/ui/primitives/button';`];
if (config.loading) {
imports.push(`import { Loader2 } from 'lucide-react';`);
}
if (config.withIcon) {
imports.push(`import { Download } from 'lucide-react';`);
}
const aiContext = `/**
* 🤖 AI CONTEXT: Button Component
*
* VARIANT DECISION TREE:
* - default: Primary actions, main CTAs, submit buttons
* - destructive: Delete, remove, cancel dangerous operations
* - outline: Secondary actions, alternative options
* - ghost: Tertiary actions, minimal emphasis
* - link: Navigation, text-only actions
* - cyan: Special emphasis, Tron-themed primary
* - knowledge: Knowledge base specific actions
*
* SIZE GUIDELINES:
* - xs: Inline actions, table cells
* - sm: Dense UI, secondary actions
* - default: Most use cases
* - lg: Primary CTAs, hero sections
* - icon: Icon-only buttons
*/`;
const props: string[] = [];
if (config.variant !== 'default') props.push(`variant="${config.variant}"`);
if (config.size !== 'default') props.push(`size="${config.size}"`);
if (config.loading) props.push(`loading={true}`);
if (config.disabled) props.push(`disabled={true}`);
const content = config.loading
? `<>\n <Loader2 className="mr-2 h-4 w-4 animate-spin" />\n ${config.text}\n </>`
: config.withIcon
? `<>\n <Download className="mr-2 h-4 w-4" />\n ${config.text}\n </>`
: config.text;
const component = `export const MyButton = () => {
return (
<Button${props.length > 0 ? '\n ' + props.join('\n ') : ''}>
${content}
</Button>
);
};`;
return `${imports.join('\n')}\n\n${aiContext}\n\n${component}`;
};
return (
<div className="grid grid-cols-4 gap-6 h-full">
{/* LEFT: Configuration Panel (1/4 width) */}
<div className="col-span-1">
<ConfigPanel title="Button Configuration">
<div className="space-y-3">
<ConfigRow label="Variant">
<Select
value={config.variant}
onValueChange={(value) => setConfig({...config, variant: value as ButtonVariant})}
>
<SelectTrigger className="w-24 text-xs">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="default">Default</SelectItem>
<SelectItem value="destructive">Destructive</SelectItem>
<SelectItem value="outline">Outline</SelectItem>
<SelectItem value="ghost">Ghost</SelectItem>
<SelectItem value="link">Link</SelectItem>
<SelectItem value="cyan">Cyan</SelectItem>
<SelectItem value="knowledge">Knowledge</SelectItem>
</SelectContent>
</Select>
</ConfigRow>
<ConfigRow label="Size">
<Select
value={config.size}
onValueChange={(value) => setConfig({...config, size: value as ButtonSize})}
>
<SelectTrigger className="w-24 text-xs">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="xs">XS</SelectItem>
<SelectItem value="sm">SM</SelectItem>
<SelectItem value="default">Default</SelectItem>
<SelectItem value="lg">LG</SelectItem>
<SelectItem value="icon">Icon</SelectItem>
</SelectContent>
</Select>
</ConfigRow>
<ConfigRow label="Loading">
<Switch
checked={config.loading}
onCheckedChange={(loading) => setConfig({...config, loading})}
/>
</ConfigRow>
<ConfigRow label="Disabled">
<Switch
checked={config.disabled}
onCheckedChange={(disabled) => setConfig({...config, disabled})}
/>
</ConfigRow>
<ConfigRow label="With Icon">
<Switch
checked={config.withIcon}
onCheckedChange={(withIcon) => setConfig({...config, withIcon})}
/>
</ConfigRow>
{config.size !== 'icon' && (
<ConfigRow label="Text">
<Input
value={config.text}
onChange={(e) => setConfig({...config, text: e.target.value})}
placeholder="Button text..."
className="w-24 text-xs"
/>
</ConfigRow>
)}
</div>
{/* Preview/Code Tabs INSIDE configurator */}
<div className="mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
<div className="flex gap-2">
<Button
size="sm"
variant={activeTab === 'preview' ? 'default' : 'outline'}
onClick={() => setActiveTab('preview')}
className="flex items-center gap-2"
>
<Eye className="w-4 h-4" />
Preview
</Button>
<Button
size="sm"
variant={activeTab === 'code' ? 'default' : 'outline'}
onClick={() => setActiveTab('code')}
className="flex items-center gap-2"
>
<Code className="w-4 h-4" />
Code
</Button>
</div>
</div>
</ConfigPanel>
</div>
{/* RIGHT: Preview or Code Content (3/4 width) */}
<div className="col-span-3">
{activeTab === 'preview' ? (
<LivePreview>
<Button
variant={config.variant}
size={config.size}
loading={config.loading}
disabled={config.disabled}
>
{config.loading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
{!config.loading && config.withIcon && <Download className="mr-2 h-4 w-4" />}
{config.size !== 'icon' && config.text}
{config.size === 'icon' && !config.loading && <Download className="h-4 w-4" />}
</Button>
</LivePreview>
) : (
<CodeDisplay
code={generateCode(config)}
showLineNumbers
/>
)}
</div>
</div>
);
};

View File

@@ -0,0 +1,219 @@
import { useState } from 'react';
import { Input } from '@/features/ui/primitives/input';
import { Button } from '@/features/ui/primitives/button';
import { Label } from '@/features/ui/primitives/label';
import { Switch } from '@/features/ui/primitives/switch';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/features/ui/primitives/select';
import { LivePreview } from '../shared/LivePreview';
import { CodeDisplay } from '../shared/CodeDisplay';
import { ConfigPanel } from '../shared/ConfigPanel';
import { Eye, Code } from 'lucide-react';
import type { InputType } from '../types';
interface FormConfig {
error: boolean;
disabled: boolean;
placeholder: string;
inputType: InputType;
}
export const FormConfigurator = () => {
const [config, setConfig] = useState<FormConfig>({
error: false,
disabled: false,
placeholder: 'Enter your text...',
inputType: 'text'
});
const [activeTab, setActiveTab] = useState<'preview' | 'code'>('preview');
const generateCode = (config: FormConfig) => {
const imports = `import { Input } from '@/features/ui/primitives/input';
import { Label } from '@/features/ui/primitives/label';
import { Button } from '@/features/ui/primitives/button';`;
const aiContext = `/**
* 🤖 AI CONTEXT: Form Input Component
*
* PURPOSE: Text input with glass morphism styling and validation states
* WHEN TO USE: Forms, search bars, data entry fields
* WHEN NOT TO USE: Large text content (use textarea), complex selections (use select)
*
* INPUT TYPE GUIDELINES:
* - text: General text input, names, titles
* - email: Email addresses (includes validation)
* - password: Passwords (masks input)
* - number: Numeric values (shows number pad on mobile)
*
* ERROR STATE:
* - Use when validation fails
* - Shows red border and focus ring
* - Pair with error message below input
*
* DISABLED STATE:
* - Use when input is temporarily unavailable
* - Shows reduced opacity and prevents interaction
* - Consider using readonly for permanent restrictions
*/`;
const props: string[] = [];
if (config.inputType !== 'text') props.push(`type="${config.inputType}"`);
if (config.error) props.push(`error={true}`);
if (config.disabled) props.push(`disabled={true}`);
if (config.placeholder) props.push(`placeholder="${config.placeholder}"`);
const component = `export const MyForm = () => {
const [value, setValue] = useState('');
return (
<div className="space-y-4">
<div>
<Label htmlFor="example-input">
${config.inputType === 'email' ? 'Email Address' :
config.inputType === 'password' ? 'Password' :
config.inputType === 'number' ? 'Number' :
'Text Input'}
</Label>
<Input
id="example-input"
value={value}
onChange={(e) => setValue(e.target.value)}${props.length > 0 ? '\n ' + props.join('\n ') : ''}
/>
${config.error ? `<p className="text-sm text-red-600 dark:text-red-400 mt-1">
This field is required
</p>` : ''}
</div>
<Button type="submit">
Submit
</Button>
</div>
);
};`;
return `${imports}\n\n${aiContext}\n\n${component}`;
};
return (
<div className="grid grid-cols-4 gap-6 h-full">
{/* LEFT: Configuration Panel (1/4 width) */}
<div className="col-span-1">
<ConfigPanel title="Form Input Configuration">
<div className="space-y-4">
{/* Input Type */}
<div>
<Label>Input Type</Label>
<Select
value={config.inputType}
onValueChange={(value) => setConfig({...config, inputType: value as InputType})}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="text">Text</SelectItem>
<SelectItem value="email">Email</SelectItem>
<SelectItem value="password">Password</SelectItem>
<SelectItem value="number">Number</SelectItem>
</SelectContent>
</Select>
</div>
{/* States */}
<div className="space-y-3">
<div className="flex items-center justify-between">
<Label htmlFor="error-state" className="text-sm">Error State</Label>
<Switch
id="error-state"
checked={config.error}
onCheckedChange={(error) => setConfig({...config, error})}
/>
</div>
<div className="flex items-center justify-between">
<Label htmlFor="disabled-state" className="text-sm">Disabled</Label>
<Switch
id="disabled-state"
checked={config.disabled}
onCheckedChange={(disabled) => setConfig({...config, disabled})}
/>
</div>
</div>
{/* Placeholder */}
<div>
<Label htmlFor="placeholder-text" className="text-sm">Placeholder Text</Label>
<Input
id="placeholder-text"
value={config.placeholder}
onChange={(e) => setConfig({...config, placeholder: e.target.value})}
/>
</div>
</div>
{/* Preview/Code Tabs INSIDE configurator */}
<div className="mt-4 pt-4 border-t border-gray-200 dark:border-gray-700">
<div className="flex gap-2">
<Button
size="sm"
variant={activeTab === 'preview' ? 'default' : 'outline'}
onClick={() => setActiveTab('preview')}
className="flex items-center gap-2"
>
<Eye className="w-4 h-4" />
Preview
</Button>
<Button
size="sm"
variant={activeTab === 'code' ? 'default' : 'outline'}
onClick={() => setActiveTab('code')}
className="flex items-center gap-2"
>
<Code className="w-4 h-4" />
Code
</Button>
</div>
</div>
</ConfigPanel>
</div>
{/* RIGHT: Preview or Code Content (3/4 width) */}
<div className="col-span-3">
{activeTab === 'preview' ? (
<LivePreview>
<div className="space-y-4 w-full max-w-sm">
<div>
<Label htmlFor="preview-input">
{config.inputType === 'email' ? 'Email Address' :
config.inputType === 'password' ? 'Password' :
config.inputType === 'number' ? 'Number' :
'Text Input'}
</Label>
<Input
id="preview-input"
type={config.inputType}
placeholder={config.placeholder}
error={config.error}
disabled={config.disabled}
/>
{config.error && (
<p className="text-sm text-red-600 dark:text-red-400 mt-1">
This field is required
</p>
)}
</div>
<Button type="submit" disabled={config.disabled}>
Submit
</Button>
</div>
</LivePreview>
) : (
<CodeDisplay
code={generateCode(config)}
showLineNumbers
/>
)}
</div>
</div>
);
};

View File

@@ -0,0 +1,327 @@
import { useState } from 'react';
import { Card } from '@/features/ui/primitives/card';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/features/ui/primitives/select';
import { Input } from '@/features/ui/primitives/input';
import { Button } from '@/features/ui/primitives/button';
import { LivePreview } from '../shared/LivePreview';
import { CodeDisplay } from '../shared/CodeDisplay';
import { ConfigPanel } from '../shared/ConfigPanel';
import { ConfigRow } from '../shared/ConfigRow';
import { Eye, Code } from 'lucide-react';
import type { GlowColor, EdgePosition, EdgeColor, CardSize, Transparency, BlurLevel, GlassTint } from '../types';
interface GlassCardConfig {
blur: BlurLevel;
transparency: Transparency;
glassTint: GlassTint;
glowColor: GlowColor;
edgePosition: EdgePosition;
edgeColor: EdgeColor;
size: CardSize;
content: string;
}
export const GlassCardConfigurator = () => {
const [config, setConfig] = useState<GlassCardConfig>({
blur: 'xl', // Standard glass (3px) - subtle effect
transparency: 'light', // 3% opacity for true glass
glassTint: 'none',
glowColor: 'none',
edgePosition: 'none',
edgeColor: 'cyan',
size: 'lg',
content: 'Your content here'
});
const [activeTab, setActiveTab] = useState<'preview' | 'code'>('preview');
const generateCode = (config: GlassCardConfig) => {
const imports = `import { Card } from '@/features/ui/primitives/card';`;
const aiContext = `/**
* 🤖 AI CONTEXT: Glass Card Component
*
* GLASS PROPERTIES for true glassmorphism:
*
* 1. TRANSPARENCY - How much you can see through the glass
* - clear: Almost invisible (0.01 opacity)
* - light: Subtle frosting (0.03 opacity) ← BEST FOR TRUE GLASS
* - medium: Standard glass (0.05 opacity)
* - frosted: Heavy frosting (0.08 opacity)
* - solid: Maximum opacity (0.12 opacity)
*
* 2. GLASS TINT - Adds color to the glass itself
* - none: Pure transparent
* - purple/blue/cyan/green/orange/pink/red: Colored glass
*
* 3. GLOW EFFECTS - Choose ONE:
* a) glowColor - Full perimeter neon backlight
* - none: No glow
* - purple/blue/cyan/green/orange/pink/red: Neon glow
*
* b) edgePosition + edgeColor - Single edge accent (like original cards!)
* - edgePosition: top/left/right/bottom
* - edgeColor: purple/blue/cyan/green/orange/pink/red
*
* USAGE PATTERNS:
* - Default glass: transparency="light" blur="xl"
* - Knowledge cards: edgePosition="top" edgeColor="purple"
* - Task cards: edgePosition="left" edgeColor based on priority
* - Featured content: glowColor="purple" (full perimeter)
* - Subtle containers: transparency="clear" glassTint="none"
*
* DECISION TREE:
* - Navigation/headers → edgePosition="top"
* - Sidebars/lists → edgePosition="left"
* - Actions/CTAs → edgePosition="right"
* - Status/footers → edgePosition="bottom"
* - Featured/selected → glowColor (full perimeter)
*/`;
const props: string[] = [];
if (config.blur !== 'xl') props.push(`blur="${config.blur}"`);
if (config.transparency !== 'medium') props.push(`transparency="${config.transparency}"`);
if (config.glassTint !== 'none') props.push(`glassTint="${config.glassTint}"`);
if (config.glowColor !== 'none') props.push(`glowColor="${config.glowColor}"`);
if (config.edgePosition !== 'none') props.push(`edgePosition="${config.edgePosition}"`);
if (config.edgePosition !== 'none' && config.edgeColor !== 'cyan') props.push(`edgeColor="${config.edgeColor}"`);
if (config.size !== 'md') props.push(`size="${config.size}"`);
const component = `export const MyCard = ({ children }) => {
return (
<Card${props.length > 0 ? '\n ' + props.join('\n ') : ''}>
{children}
</Card>
);
};`;
return `${imports}\n\n${aiContext}\n\n${component}`;
};
return (
<div className="grid grid-cols-4 gap-6 h-full">
{/* LEFT: Configuration Panel (1/4 width) */}
<div className="col-span-1">
<ConfigPanel title="Glass Card Configuration">
<div className="space-y-3">
<ConfigRow label="Blur">
<Select
value={config.blur}
onValueChange={(value) => setConfig({...config, blur: value as BlurLevel})}
>
<SelectTrigger className="w-24 text-xs">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="none">None (0px)</SelectItem>
<SelectItem value="sm">Minimal (0.5px)</SelectItem>
<SelectItem value="md">Subtle (1px)</SelectItem>
<SelectItem value="lg">Light (2px)</SelectItem>
<SelectItem value="xl">Standard (3px)</SelectItem>
<SelectItem value="2xl">Noticeable (5px)</SelectItem>
<SelectItem value="3xl">Maximum (8px)</SelectItem>
</SelectContent>
</Select>
</ConfigRow>
<ConfigRow label="Transparency">
<Select
value={config.transparency}
onValueChange={(value) => setConfig({...config, transparency: value as Transparency})}
>
<SelectTrigger className="w-24 text-xs">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="clear">Clear</SelectItem>
<SelectItem value="light">Light</SelectItem>
<SelectItem value="medium">Medium</SelectItem>
<SelectItem value="frosted">Frosted</SelectItem>
<SelectItem value="solid">Solid</SelectItem>
</SelectContent>
</Select>
</ConfigRow>
<ConfigRow label="Glass Tint">
<Select
value={config.glassTint}
onValueChange={(value) => setConfig({...config, glassTint: value as GlassTint})}
>
<SelectTrigger className="w-24 text-xs">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="none">None</SelectItem>
<SelectItem value="purple">🟣 Purple</SelectItem>
<SelectItem value="blue">🔵 Blue</SelectItem>
<SelectItem value="cyan">🔷 Cyan</SelectItem>
<SelectItem value="green">🟢 Green</SelectItem>
<SelectItem value="orange">🟠 Orange</SelectItem>
<SelectItem value="pink">🩷 Pink</SelectItem>
<SelectItem value="red">🔴 Red</SelectItem>
</SelectContent>
</Select>
</ConfigRow>
<ConfigRow label="Glow Color">
<Select
value={config.glowColor}
onValueChange={(value) => setConfig({...config, glowColor: value as GlowColor})}
disabled={config.edgePosition !== 'none'}
>
<SelectTrigger className="w-24 text-xs">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="none">None</SelectItem>
<SelectItem value="purple">🟣 Purple</SelectItem>
<SelectItem value="blue">🔵 Blue</SelectItem>
<SelectItem value="cyan">🔷 Cyan</SelectItem>
<SelectItem value="green">🟢 Green</SelectItem>
<SelectItem value="orange">🟠 Orange</SelectItem>
<SelectItem value="pink">🩷 Pink</SelectItem>
<SelectItem value="red">🔴 Red</SelectItem>
</SelectContent>
</Select>
</ConfigRow>
<ConfigRow label="Edge Position">
<Select
value={config.edgePosition}
onValueChange={(value) => {
setConfig({
...config,
edgePosition: value as EdgePosition,
glowColor: value !== 'none' ? 'none' : config.glowColor
});
}}
>
<SelectTrigger className="w-24 text-xs">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="none">None</SelectItem>
<SelectItem value="top"> Top</SelectItem>
<SelectItem value="left"> Left</SelectItem>
<SelectItem value="right"> Right</SelectItem>
<SelectItem value="bottom"> Bottom</SelectItem>
</SelectContent>
</Select>
</ConfigRow>
<ConfigRow label="Edge Color">
<Select
value={config.edgeColor}
onValueChange={(value) => setConfig({...config, edgeColor: value as EdgeColor})}
disabled={config.edgePosition === 'none'}
>
<SelectTrigger className="w-24 text-xs">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="purple">🟣 Purple</SelectItem>
<SelectItem value="blue">🔵 Blue</SelectItem>
<SelectItem value="cyan">🔷 Cyan</SelectItem>
<SelectItem value="green">🟢 Green</SelectItem>
<SelectItem value="orange">🟠 Orange</SelectItem>
<SelectItem value="pink">🩷 Pink</SelectItem>
<SelectItem value="red">🔴 Red</SelectItem>
</SelectContent>
</Select>
</ConfigRow>
<ConfigRow label="Size">
<Select
value={config.size}
onValueChange={(value) => setConfig({...config, size: value as CardSize})}
>
<SelectTrigger className="w-24 text-xs">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="sm">Small</SelectItem>
<SelectItem value="md">Medium</SelectItem>
<SelectItem value="lg">Large</SelectItem>
<SelectItem value="xl">XLarge</SelectItem>
</SelectContent>
</Select>
</ConfigRow>
<ConfigRow label="Content">
<Input
value={config.content}
onChange={(e) => setConfig({...config, content: e.target.value})}
placeholder="Card content..."
className="w-24 text-xs"
/>
</ConfigRow>
</div>
{/* Preview/Code Tabs INSIDE configurator */}
<div className="mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
<div className="flex gap-2">
<Button
size="sm"
variant={activeTab === 'preview' ? 'default' : 'outline'}
onClick={() => setActiveTab('preview')}
className="flex items-center gap-2"
>
<Eye className="w-4 h-4" />
Preview
</Button>
<Button
size="sm"
variant={activeTab === 'code' ? 'default' : 'outline'}
onClick={() => setActiveTab('code')}
className="flex items-center gap-2"
>
<Code className="w-4 h-4" />
Code
</Button>
</div>
</div>
</ConfigPanel>
</div>
{/* RIGHT: Preview or Code Content (3/4 width) */}
<div className="col-span-3">
{activeTab === 'preview' ? (
<LivePreview>
<Card
blur={config.blur}
transparency={config.transparency}
glassTint={config.glassTint}
glowColor={config.glowColor}
edgePosition={config.edgePosition}
edgeColor={config.edgeColor}
size={config.size}
>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">Glass Card Example</h4>
<p className="text-gray-700 dark:text-gray-300 mb-4">
{config.content}
</p>
<div className="text-xs text-gray-600 dark:text-gray-400 p-3 bg-white/20 dark:bg-black/20 rounded border border-gray-300 dark:border-gray-600">
<div><strong>Blur:</strong> {config.blur}</div>
<div><strong>Transparency:</strong> {config.transparency}</div>
<div><strong>Glass Tint:</strong> {config.glassTint}</div>
{config.edgePosition === 'none' ? (
<div><strong>Glow Color:</strong> {config.glowColor}</div>
) : (
<>
<div><strong>Edge Position:</strong> {config.edgePosition}</div>
<div><strong>Edge Color:</strong> {config.edgeColor}</div>
</>
)}
<div><strong>Size:</strong> {config.size}</div>
</div>
</Card>
</LivePreview>
) : (
<CodeDisplay
code={generateCode(config)}
showLineNumbers
/>
)}
</div>
</div>
);
};

View File

@@ -0,0 +1,235 @@
import { useState } from 'react';
import { Button } from '@/features/ui/primitives/button';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/features/ui/primitives/select';
import { CodeDisplay } from '../shared/CodeDisplay';
import { ConfigPanel } from '../shared/ConfigPanel';
import { ConfigRow } from '../shared/ConfigRow';
import { MODAL_TYPES } from '../standards/modalStandards';
import { Eye, Code, X } from 'lucide-react';
import type { ModalSize, GlowColor } from '../types';
export const ModalConfigurator = () => {
const [selectedType, setSelectedType] = useState<keyof typeof MODAL_TYPES>('confirmation');
const [activeTab, setActiveTab] = useState<'preview' | 'code'>('preview');
const [customSize, setCustomSize] = useState<ModalSize>('sm');
const [customGlowColor, setCustomGlowColor] = useState<Exclude<GlowColor, 'none' | 'pink'>>('red');
const generateCode = (type: keyof typeof MODAL_TYPES) => {
const imports = `import { AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogAction, AlertDialogCancel, AlertDialogTrigger } from '@/features/ui/primitives/alert-dialog';
import { Button } from '@/features/ui/primitives/button';`;
const aiContext = `/**
* 🤖 AI CONTEXT: ${type.charAt(0).toUpperCase() + type.slice(1)} Modal
*
* PURPOSE: ${MODAL_TYPES[type].purpose}
* SIZE: ${customSize.toUpperCase()} - ${customSize === 'sm' ? 'Compact for simple confirmations' :
customSize === 'md' ? 'Standard for forms and content' :
customSize === 'lg' ? 'Large for detailed displays' :
'Extra large for code/data views'}
* GLOW: ${customGlowColor.toUpperCase()} - ${customGlowColor === 'red' ? 'Danger/destructive actions' :
customGlowColor === 'green' ? 'Success/creation actions' :
customGlowColor === 'blue' ? 'Information/editing' :
customGlowColor === 'purple' ? 'Primary/featured content' :
'Special emphasis/code display'}
*
* WHEN TO USE:
* ${type === 'confirmation' ? '- Delete operations\n * - Irreversible actions\n * - Warning confirmations' :
type === 'formCreate' ? '- Creating new resources\n * - Data entry forms\n * - Positive actions' :
type === 'formEdit' ? '- Editing existing data\n * - Update operations\n * - Modification forms' :
type === 'display' ? '- Showing detailed information\n * - Read-only content\n * - Feature showcases' :
type === 'codeViewer' ? '- Code snippets display\n * - JSON/data viewing\n * - Technical content' :
'- Application settings\n * - Configuration panels\n * - User preferences'}
*
* IMPLEMENTATION: Use AlertDialog for modal behavior, wrap trigger in AlertDialogTrigger
*/`;
const component = `export const ${type.charAt(0).toUpperCase() + type.slice(1)}Modal = ({ trigger, onConfirm }) => {
return (
<AlertDialog>
<AlertDialogTrigger asChild>
{trigger}
</AlertDialogTrigger>
<AlertDialogContent className="sm:max-w-${customSize === 'sm' ? 'sm' : customSize === 'md' ? 'md' : customSize === 'lg' ? 'lg' : 'xl'}">
<AlertDialogHeader>
<AlertDialogTitle>${type === 'confirmation' ? 'Confirm Action' :
type === 'formCreate' ? 'Create New Item' :
type === 'formEdit' ? 'Edit Item' :
type === 'display' ? 'Details' :
type === 'codeViewer' ? 'Code Viewer' :
'Settings'}</AlertDialogTitle>
<AlertDialogDescription>
${MODAL_TYPES[type].purpose}
</AlertDialogDescription>
</AlertDialogHeader>
{/* Modal content goes here */}
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={onConfirm}>
${type === 'confirmation' ? 'Confirm' :
type === 'formCreate' ? 'Create' :
type === 'formEdit' ? 'Save Changes' :
'Close'}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};`;
return `${imports}\n\n${aiContext}\n\n${component}`;
};
const currentConfig = MODAL_TYPES[selectedType];
return (
<div className="grid grid-cols-4 gap-6 h-full">
{/* LEFT: Configuration Panel (1/4 width) */}
<div className="col-span-1">
<ConfigPanel title="Modal Standards">
<div className="space-y-4">
<ConfigRow label="Modal Type">
<Select
value={selectedType}
onValueChange={(value) => setSelectedType(value as keyof typeof MODAL_TYPES)}
>
<SelectTrigger className="w-32">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="confirmation">Confirmation</SelectItem>
<SelectItem value="formCreate">Form Create</SelectItem>
<SelectItem value="formEdit">Form Edit</SelectItem>
<SelectItem value="display">Display</SelectItem>
<SelectItem value="codeViewer">Code Viewer</SelectItem>
<SelectItem value="settings">Settings</SelectItem>
</SelectContent>
</Select>
</ConfigRow>
<ConfigRow label="Size">
<Select
value={customSize}
onValueChange={(value) => setCustomSize(value as ModalSize)}
>
<SelectTrigger className="w-24 text-xs">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="sm">SM</SelectItem>
<SelectItem value="md">MD</SelectItem>
<SelectItem value="lg">LG</SelectItem>
<SelectItem value="xl">XL</SelectItem>
</SelectContent>
</Select>
</ConfigRow>
<ConfigRow label="Glow Color">
<Select
value={customGlowColor}
onValueChange={(value) => setCustomGlowColor(value as Exclude<GlowColor, 'none' | 'pink'>)}
>
<SelectTrigger className="w-24 text-xs">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="purple">Purple</SelectItem>
<SelectItem value="blue">Blue</SelectItem>
<SelectItem value="green">Green</SelectItem>
<SelectItem value="red">Red</SelectItem>
<SelectItem value="orange">Orange</SelectItem>
<SelectItem value="cyan">Cyan</SelectItem>
</SelectContent>
</Select>
</ConfigRow>
<ConfigRow label="Purpose">
<div className="text-xs text-gray-600 dark:text-gray-400 w-32 text-right">
{currentConfig.purpose}
</div>
</ConfigRow>
</div>
{/* Preview/Code Tabs INSIDE configurator */}
<div className="mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
<div className="flex gap-2">
<Button
size="sm"
variant={activeTab === 'preview' ? 'default' : 'outline'}
onClick={() => setActiveTab('preview')}
className="flex items-center gap-2"
>
<Eye className="w-4 h-4" />
Preview
</Button>
<Button
size="sm"
variant={activeTab === 'code' ? 'default' : 'outline'}
onClick={() => setActiveTab('code')}
className="flex items-center gap-2"
>
<Code className="w-4 h-4" />
Code
</Button>
</div>
</div>
</ConfigPanel>
</div>
{/* RIGHT: Preview or Code Content (3/4 width) */}
<div className="col-span-3">
{activeTab === 'preview' ? (
<div className="flex items-center justify-center min-h-[400px]">
{/* Inline Modal Preview - No Button Trigger */}
<div className={`relative max-w-${customSize === 'sm' ? 'sm' : customSize === 'md' ? 'md' : customSize === 'lg' ? 'lg' : 'xl'} w-full mx-auto`}>
<div className="backdrop-blur-md bg-white/95 dark:bg-gray-900/95 border border-gray-200 dark:border-gray-700 rounded-lg shadow-2xl p-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold">
{selectedType === 'confirmation' ? 'Confirm Action' :
selectedType === 'formCreate' ? 'Create New Item' :
selectedType === 'formEdit' ? 'Edit Item' :
selectedType === 'display' ? 'Details' :
selectedType === 'codeViewer' ? 'Code Viewer' :
'Settings'}
</h3>
<Button size="icon" variant="ghost">
<X className="h-4 w-4" />
</Button>
</div>
<div className="mb-4">
<p className="text-sm text-gray-600 dark:text-gray-400">
{currentConfig.purpose} - This is an inline preview of the {selectedType} modal type.
</p>
</div>
<div className="py-4 mb-6">
<p className="text-sm text-gray-600 dark:text-gray-400">
Modal content would go here. This {customSize} sized modal uses {customGlowColor} color theming.
</p>
</div>
<div className="flex justify-end gap-3">
<Button variant="outline">Cancel</Button>
<Button variant={selectedType === 'confirmation' ? 'destructive' : 'default'}>
{selectedType === 'confirmation' ? 'Confirm' :
selectedType === 'formCreate' ? 'Create' :
selectedType === 'formEdit' ? 'Save Changes' :
'Close'}
</Button>
</div>
</div>
</div>
</div>
) : (
<CodeDisplay
code={generateCode(selectedType)}
showLineNumbers
/>
)}
</div>
</div>
);
};

View File

@@ -0,0 +1,196 @@
import { useState } from 'react';
import { Button } from '@/features/ui/primitives/button';
import { Switch } from '@/features/ui/primitives/switch';
import { Label } from '@/features/ui/primitives/label';
import { LivePreview } from '../shared/LivePreview';
import { CodeDisplay } from '../shared/CodeDisplay';
import { ConfigPanel } from '../shared/ConfigPanel';
import { Edit, Trash2, Eye, Code } from 'lucide-react';
interface TableConfig {
glassMorphism: boolean;
actions: boolean;
headers: boolean;
striped: boolean;
}
const sampleData = [
{ id: 1, name: 'John Doe', email: 'john@example.com', status: 'Active' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'Inactive' },
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', status: 'Active' },
];
export const TableConfigurator = () => {
const [config, setConfig] = useState<TableConfig>({
glassMorphism: true,
actions: true,
headers: true,
striped: true
});
const [activeTab, setActiveTab] = useState<'preview' | 'code'>('preview');
const generateCode = (_config: TableConfig) => {
const imports = `import { Button } from '@/features/ui/primitives/button';
import { Edit, Trash2, Eye } from 'lucide-react';`;
const aiContext = `/**
* 🤖 AI CONTEXT: Glass Morphism Table
*
* PURPOSE: Data display with glass morphism styling and interactive elements
* WHEN TO USE: Data grids, lists with actions, dashboard tables
* WHEN NOT TO USE: Simple lists (use basic ul/li), massive datasets (use virtualization)
*/`;
return `${imports}\n\n${aiContext}\n\n// Table component code here...`;
};
return (
<div className="grid grid-cols-4 gap-6 h-full">
{/* LEFT: Configuration Panel (1/4 width) */}
<div className="col-span-1">
<ConfigPanel title="Table Configuration">
<div className="space-y-4">
<div className="flex items-center justify-between">
<Label htmlFor="glass-morphism" className="text-sm">Glass Morphism</Label>
<Switch
id="glass-morphism"
checked={config.glassMorphism}
onCheckedChange={(glassMorphism) => setConfig({...config, glassMorphism})}
/>
</div>
<div className="flex items-center justify-between">
<Label htmlFor="show-actions" className="text-sm">Show Actions</Label>
<Switch
id="show-actions"
checked={config.actions}
onCheckedChange={(actions) => setConfig({...config, actions})}
/>
</div>
<div className="flex items-center justify-between">
<Label htmlFor="show-headers" className="text-sm">Show Headers</Label>
<Switch
id="show-headers"
checked={config.headers}
onCheckedChange={(headers) => setConfig({...config, headers})}
/>
</div>
<div className="flex items-center justify-between">
<Label htmlFor="striped-rows" className="text-sm">Striped Rows</Label>
<Switch
id="striped-rows"
checked={config.striped}
onCheckedChange={(striped) => setConfig({...config, striped})}
/>
</div>
</div>
{/* Preview/Code Tabs INSIDE configurator */}
<div className="mt-4 pt-4 border-t border-gray-200 dark:border-gray-700">
<div className="flex gap-2">
<Button
size="sm"
variant={activeTab === 'preview' ? 'default' : 'outline'}
onClick={() => setActiveTab('preview')}
className="flex items-center gap-2"
>
<Eye className="w-4 h-4" />
Preview
</Button>
<Button
size="sm"
variant={activeTab === 'code' ? 'default' : 'outline'}
onClick={() => setActiveTab('code')}
className="flex items-center gap-2"
>
<Code className="w-4 h-4" />
Code
</Button>
</div>
</div>
</ConfigPanel>
</div>
{/* RIGHT: Preview or Code Content (3/4 width) */}
<div className="col-span-3">
{activeTab === 'preview' ? (
<LivePreview>
<div className={`w-full ${config.glassMorphism
? 'backdrop-blur-md bg-white/80 dark:bg-black/30 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden shadow-lg'
: 'bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden'
}`}>
<table className="w-full">
{config.headers && (
<thead className="bg-gray-50/80 dark:bg-gray-800/80 border-b border-gray-200 dark:border-gray-700">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Name
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Email
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Status
</th>
{config.actions && (
<th className="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Actions
</th>
)}
</tr>
</thead>
)}
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
{sampleData.map((item, index) => (
<tr
key={item.id}
className={`${config.striped && index % 2 === 1 ? 'bg-gray-50/50 dark:bg-gray-800/50' : ''} hover:bg-gray-50/70 dark:hover:bg-gray-800/70 transition-colors`}
>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">
{item.name}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
{item.email}
</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
item.status === 'Active'
? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400'
: 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400'
}`}>
{item.status}
</span>
</td>
{config.actions && (
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<div className="flex justify-end gap-2">
<Button size="icon" variant="ghost">
<Eye className="h-4 w-4" />
</Button>
<Button size="icon" variant="ghost">
<Edit className="h-4 w-4" />
</Button>
<Button size="icon" variant="ghost">
<Trash2 className="h-4 w-4" />
</Button>
</div>
</td>
)}
</tr>
))}
</tbody>
</table>
</div>
</LivePreview>
) : (
<CodeDisplay
code={generateCode(config)}
showLineNumbers
/>
)}
</div>
</div>
);
};

View File

@@ -0,0 +1,174 @@
import { useState } from 'react';
import { Switch } from '@/features/ui/primitives/switch';
import { Button } from '@/features/ui/primitives/button';
import { Label } from '@/features/ui/primitives/label';
import { RadioGroup, RadioGroupItem } from '@/features/ui/primitives/radio-group';
import { LivePreview } from '../shared/LivePreview';
import { CodeDisplay } from '../shared/CodeDisplay';
import { ConfigPanel } from '../shared/ConfigPanel';
import { Eye, Code } from 'lucide-react';
import type { LabelPosition } from '../types';
interface ToggleConfig {
disabled: boolean;
size: 'sm' | 'default' | 'lg';
labelPosition: LabelPosition;
labelText: string;
}
export const ToggleConfigurator = () => {
const [config, setConfig] = useState<ToggleConfig>({
disabled: false,
size: 'default',
labelPosition: 'right',
labelText: 'Enable notifications'
});
const [activeTab, setActiveTab] = useState<'preview' | 'code'>('preview');
const [toggleState, setToggleState] = useState(false);
const generateCode = (_config: ToggleConfig) => {
const imports = `import { Switch } from '@/features/ui/primitives/switch';
import { Label } from '@/features/ui/primitives/label';`;
const aiContext = `/**
* 🤖 AI CONTEXT: Switch Toggle Component
*
* PURPOSE: Binary on/off controls with accessible labeling
* WHEN TO USE: Settings, preferences, feature toggles, boolean states
* WHEN NOT TO USE: Multiple options (use RadioGroup), momentary actions (use Button)
*/`;
return `${imports}\n\n${aiContext}\n\n// Toggle component code here...`;
};
const layoutClasses = {
left: 'flex-row-reverse',
right: 'flex-row',
top: 'flex-col',
bottom: 'flex-col-reverse'
};
const gapClasses = {
left: 'gap-3',
right: 'gap-3',
top: 'gap-2',
bottom: 'gap-2'
};
return (
<div className="grid grid-cols-4 gap-6 h-full">
{/* LEFT: Configuration Panel (1/4 width) */}
<div className="col-span-1">
<ConfigPanel title="Switch Toggle Configuration">
<div className="space-y-4">
{/* Label Position */}
<div>
<Label className="text-sm">Label Position</Label>
<RadioGroup
value={config.labelPosition}
onValueChange={(value) => setConfig({...config, labelPosition: value as LabelPosition})}
>
<div className="grid grid-cols-2 gap-2">
{['left', 'right', 'top', 'bottom'].map(position => (
<div key={position} className="flex items-center space-x-2">
<RadioGroupItem value={position} id={`position-${position}`} />
<Label htmlFor={`position-${position}`} className="capitalize text-xs">
{position}
</Label>
</div>
))}
</div>
</RadioGroup>
</div>
{/* States */}
<div className="flex items-center justify-between">
<Label htmlFor="disabled-toggle" className="text-sm">Disabled State</Label>
<Switch
id="disabled-toggle"
checked={config.disabled}
onCheckedChange={(disabled) => setConfig({...config, disabled})}
/>
</div>
{/* Label Text */}
<div>
<Label htmlFor="label-text" className="text-sm">Label Text</Label>
<input
id="label-text"
type="text"
value={config.labelText}
onChange={(e) => setConfig({...config, labelText: e.target.value})}
className="w-full mt-1 px-3 py-2 border rounded-md dark:bg-gray-800 text-sm"
/>
</div>
</div>
{/* Preview/Code Tabs INSIDE configurator */}
<div className="mt-4 pt-4 border-t border-gray-200 dark:border-gray-700">
<div className="flex gap-2">
<Button
size="sm"
variant={activeTab === 'preview' ? 'default' : 'outline'}
onClick={() => setActiveTab('preview')}
className="flex items-center gap-2"
>
<Eye className="w-4 h-4" />
Preview
</Button>
<Button
size="sm"
variant={activeTab === 'code' ? 'default' : 'outline'}
onClick={() => setActiveTab('code')}
className="flex items-center gap-2"
>
<Code className="w-4 h-4" />
Code
</Button>
</div>
</div>
</ConfigPanel>
</div>
{/* RIGHT: Preview or Code Content (3/4 width) */}
<div className="col-span-3">
{activeTab === 'preview' ? (
<LivePreview>
<div className={`flex ${layoutClasses[config.labelPosition]} ${gapClasses[config.labelPosition]} items-center`}>
{(config.labelPosition === 'top' || config.labelPosition === 'left') ? (
<>
<Label htmlFor="preview-toggle">
{config.labelText}
</Label>
<Switch
id="preview-toggle"
checked={toggleState}
onCheckedChange={setToggleState}
disabled={config.disabled}
/>
</>
) : (
<>
<Switch
id="preview-toggle"
checked={toggleState}
onCheckedChange={setToggleState}
disabled={config.disabled}
/>
<Label htmlFor="preview-toggle">
{config.labelText}
</Label>
</>
)}
</div>
</LivePreview>
) : (
<CodeDisplay
code={generateCode(config)}
showLineNumbers
/>
)}
</div>
</div>
);
};

View File

@@ -0,0 +1,638 @@
import React, { useState } from 'react';
import { Card } from '@/features/ui/primitives/card';
import type { GlowColor } from '../types';
import { Button } from '@/features/ui/primitives/button';
import { CodeDisplay } from '../shared/CodeDisplay';
import { LivePreview } from '../shared/LivePreview';
import { cn, glassmorphism } from '@/features/ui/primitives/styles';
import {
Home,
Settings,
User,
FileText,
BarChart,
Plus,
Search,
Bell,
MoreVertical,
Calendar,
} from 'lucide-react';
interface ExampleComposition {
id: string;
name: string;
description: string;
usage: string;
component: React.ComponentType;
}
// Complete Dashboard Composition
const DashboardComposition = () => {
return (
<div className="h-96 bg-gray-50 dark:bg-gray-900 overflow-hidden rounded-lg">
{/* Top Navigation */}
<Card className="p-4 rounded-none border-b">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-gradient-to-br from-cyan-500 to-purple-500 rounded-lg" />
<span className="font-bold text-lg">Archon</span>
</div>
<nav className="hidden md:flex items-center gap-1">
{['Dashboard', 'Projects', 'Team', 'Analytics'].map((item, index) => (
<button
key={item}
className={cn(
"px-3 py-1 rounded-lg text-sm font-medium transition-all duration-200",
index === 0
? cn(glassmorphism.background.cyan, "text-cyan-700 dark:text-cyan-300")
: "text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800"
)}
>
{item}
</button>
))}
</nav>
<div className="flex items-center gap-2">
<Button variant="ghost" size="sm"><Search className="w-4 h-4" /></Button>
<Button variant="ghost" size="sm"><Bell className="w-4 h-4" /></Button>
<Button variant="ghost" size="sm"><User className="w-4 h-4" /></Button>
</div>
</div>
</Card>
<div className="flex h-full">
{/* Sidebar */}
<Card className="w-48 h-full rounded-none border-r">
<div className="p-3">
<nav className="space-y-1">
{[
{ icon: <Home className="w-4 h-4" />, label: 'Dashboard', active: true },
{ icon: <FileText className="w-4 h-4" />, label: 'Projects' },
{ icon: <BarChart className="w-4 h-4" />, label: 'Analytics' },
{ icon: <Settings className="w-4 h-4" />, label: 'Settings' }
].map((item) => (
<button
key={item.label}
className={cn(
"w-full flex items-center gap-2 px-2 py-1.5 rounded text-sm transition-all",
item.active
? cn(glassmorphism.background.cyan, "text-cyan-700 dark:text-cyan-300")
: "hover:bg-gray-100 dark:hover:bg-gray-800"
)}
>
{item.icon}
{item.label}
</button>
))}
</nav>
</div>
</Card>
{/* Main Content */}
<div className="flex-1 p-4 space-y-4">
{/* Stats Grid */}
<div className="grid grid-cols-3 gap-3">
{[
{ label: 'Projects', value: '24', color: 'purple' },
{ label: 'Tasks', value: '156', color: 'blue' },
{ label: 'Team', value: '8', color: 'green' }
].map((stat) => (
<Card key={stat.label} glowColor={stat.color as GlowColor} className="p-3 text-center">
<p className="text-lg font-bold">{stat.value}</p>
<p className="text-xs text-gray-600 dark:text-gray-400">{stat.label}</p>
</Card>
))}
</div>
{/* Content Grid */}
<div className="grid grid-cols-3 gap-3 flex-1">
<Card className="col-span-2 p-3">
<h3 className="font-medium text-sm mb-2">Recent Activity</h3>
<div className="space-y-1">
{['Project updated', 'Task completed', 'New member'].map((activity, i) => (
<div key={i} className="text-xs py-1 border-b border-gray-200 dark:border-gray-700 last:border-b-0">
{activity}
</div>
))}
</div>
</Card>
<Card className="p-3">
<h3 className="font-medium text-sm mb-2">Quick Actions</h3>
<div className="space-y-1">
<Button variant="outline" size="sm" className="w-full text-xs">Create</Button>
<Button variant="outline" size="sm" className="w-full text-xs">Invite</Button>
</div>
</Card>
</div>
</div>
</div>
</div>
);
};
// Project Card Collection
const ProjectCardsComposition = () => {
const projects = [
{ title: 'Alpha', status: 'Active', progress: 75, color: 'purple' },
{ title: 'Beta', status: 'Review', progress: 90, color: 'blue' },
{ title: 'Gamma', status: 'Planning', progress: 25, color: 'orange' }
];
return (
<div className="p-6 bg-gray-50 dark:bg-gray-900 rounded-lg">
<div className="flex items-center justify-between mb-6">
<h2 className="text-xl font-bold">Projects</h2>
<Button size="sm">
<Plus className="w-4 h-4 mr-2" />
New Project
</Button>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{projects.map((project) => (
<Card
key={project.title}
glowColor={project.color as GlowColor}
className="p-4 hover:shadow-lg transition-shadow"
>
<div className="flex items-start justify-between mb-3">
<h3 className="font-semibold">Project {project.title}</h3>
<Button variant="ghost" size="sm">
<MoreVertical className="w-4 h-4" />
</Button>
</div>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-3">
Development project with team collaboration features.
</p>
<div className="mb-3">
<div className="flex justify-between text-sm mb-1">
<span>Progress</span>
<span>{project.progress}%</span>
</div>
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
<div
className="bg-cyan-500 h-2 rounded-full"
style={{ width: `${project.progress}%` }}
/>
</div>
</div>
<div className="flex items-center justify-between text-xs text-gray-500 dark:text-gray-400">
<span className="flex items-center gap-1">
<Calendar className="w-3 h-3" />
Due: 2 weeks
</span>
<span className="flex items-center gap-1">
<User className="w-3 h-3" />
5 members
</span>
</div>
</Card>
))}
</div>
</div>
);
};
// Modal with Form
const ModalFormComposition = () => {
const [showModal, setShowModal] = useState(false);
return (
<div className="relative">
<div className="p-6 text-center">
<Button onClick={() => setShowModal(true)}>
Show Modal Example
</Button>
</div>
{showModal && (
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50">
<Card size="lg" glowColor="purple" className="w-full max-w-md">
<div className="flex items-center justify-between mb-4">
<h2 className="text-xl font-bold">Create New Task</h2>
<Button variant="ghost" size="sm" onClick={() => setShowModal(false)}>
×
</Button>
</div>
<form className="space-y-4">
<div>
<label className="block text-sm font-medium mb-1">Task Title</label>
<input
type="text"
className={cn(
"w-full px-3 py-2 rounded-md text-sm",
glassmorphism.background.subtle,
glassmorphism.border.default,
glassmorphism.border.focus
)}
placeholder="Enter task title"
/>
</div>
<div>
<label className="block text-sm font-medium mb-1">Priority</label>
<select
className={cn(
"w-full px-3 py-2 rounded-md text-sm",
glassmorphism.background.subtle,
glassmorphism.border.default,
glassmorphism.border.focus
)}
>
<option>High</option>
<option>Medium</option>
<option>Low</option>
</select>
</div>
<div>
<label className="block text-sm font-medium mb-1">Description</label>
<textarea
className={cn(
"w-full px-3 py-2 rounded-md text-sm h-20 resize-none",
glassmorphism.background.subtle,
glassmorphism.border.default,
glassmorphism.border.focus
)}
placeholder="Describe the task"
/>
</div>
<div className="flex gap-3 pt-4">
<Button
variant="outline"
className="flex-1"
onClick={() => setShowModal(false)}
>
Cancel
</Button>
<Button
className="flex-1"
onClick={() => setShowModal(false)}
>
Create Task
</Button>
</div>
</form>
</Card>
</div>
)}
</div>
);
};
const EXAMPLE_COMPOSITIONS: ExampleComposition[] = [
{
id: 'dashboard',
name: 'Complete Dashboard',
description: 'Full dashboard layout with navigation, sidebar, stats, and content areas',
usage: 'Admin panels, application dashboards, overview pages',
component: DashboardComposition
},
{
id: 'cards',
name: 'Project Cards Grid',
description: 'Collection of project cards with consistent styling and interactions',
usage: 'Project portfolios, product grids, content galleries',
component: ProjectCardsComposition
},
{
id: 'modal',
name: 'Modal with Form',
description: 'Interactive modal dialog with form inputs and glassmorphism styling',
usage: 'Create/edit dialogs, settings panels, confirmation prompts',
component: ModalFormComposition
}
];
const generateCode = (compositionId: string) => {
const codeExamples = {
dashboard: `/**
* 🤖 AI CONTEXT: Complete Dashboard Composition
*
* PURPOSE: Full-featured dashboard with navigation and content
* WHEN TO USE: Admin interfaces, application dashboards, data overview
* WHEN NOT TO USE: Simple pages, mobile-first designs, content-focused sites
*
* COMPOSITION ELEMENTS:
* - Top navigation with branding and user actions
* - Collapsible sidebar with navigation items
* - Stats cards grid for key metrics
* - Content areas with recent activity and quick actions
*
* RESPONSIVE STRATEGY:
* - Desktop: Full layout with sidebar
* - Tablet: Collapsible sidebar
* - Mobile: Overlay navigation drawer
*/
import { Card } from '@/features/ui/primitives/card';
import { Button } from '@/features/ui/primitives/button';
import { glassmorphism } from '@/features/ui/primitives/styles';
export const DashboardLayout = () => {
return (
<div className="h-screen bg-gray-50 dark:bg-gray-900">
{/* Top Navigation */}
<Card className="p-4 rounded-none border-b">
<div className="flex items-center justify-between">
{/* Brand */}
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-gradient-to-br from-cyan-500 to-purple-500 rounded-lg" />
<span className="font-bold text-lg">Your App</span>
</div>
{/* Navigation */}
<nav className="hidden md:flex items-center gap-1">
{/* Navigation items */}
</nav>
{/* User Actions */}
<div className="flex items-center gap-2">
{/* User action buttons */}
</div>
</div>
</Card>
<div className="flex h-full">
{/* Sidebar */}
<Card className="w-64 h-full rounded-none border-r">
<nav className="p-4 space-y-1">
{/* Navigation items */}
</nav>
</Card>
{/* Main Content */}
<div className="flex-1 p-6 space-y-6">
{/* Stats Grid */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
{/* Stat cards */}
</div>
{/* Content Grid */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Content sections */}
</div>
</div>
</div>
</div>
);
};`,
cards: `/**
* 🤖 AI CONTEXT: Project Cards Grid Composition
*
* PURPOSE: Consistent card layout for project/product display
* WHEN TO USE: Portfolios, galleries, collection views
* WHEN NOT TO USE: List data, text-heavy content, simple navigation
*
* CARD ELEMENTS:
* - Header with title and actions
* - Progress indicators for status
* - Metadata with icons and labels
* - Consistent hover states and interactions
*
* GRID BEHAVIOR:
* - Responsive: 1->2->3 columns
* - Equal heights maintained
* - Proper spacing and alignment
*/
import { Card } from '@/features/ui/primitives/card';
import { Button } from '@/features/ui/primitives/button';
export const ProjectCardsGrid = ({ projects }: { projects: any[] }) => {
return (
<div className="p-6">
{/* Header */}
<div className="flex items-center justify-between mb-6">
<h2 className="text-2xl font-bold">Projects</h2>
<Button>
<Plus className="w-4 h-4 mr-2" />
New Project
</Button>
</div>
{/* Cards Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{projects.map((project) => (
<Card
key={project.id}
glowColor={project.status}
className="p-6 hover:shadow-lg transition-all duration-300"
>
{/* Card header */}
<div className="flex items-start justify-between mb-4">
<h3 className="font-semibold text-lg">{project.title}</h3>
<Button variant="ghost" size="sm">
<MoreVertical className="w-4 h-4" />
</Button>
</div>
{/* Card content */}
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
{project.description}
</p>
{/* Progress */}
<div className="mb-4">
<div className="flex justify-between text-sm mb-2">
<span>Progress</span>
<span>{project.progress}%</span>
</div>
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
<div
className="bg-cyan-500 h-2 rounded-full transition-all"
style={{ width: \`\${project.progress}%\` }}
/>
</div>
</div>
{/* Metadata */}
<div className="flex items-center justify-between text-xs text-gray-500">
<span>Due: {project.dueDate}</span>
<span>{project.teamSize} members</span>
</div>
</Card>
))}
</div>
</div>
);
};`,
modal: `/**
* 🤖 AI CONTEXT: Modal with Form Composition
*
* PURPOSE: Interactive dialog for data input and actions
* WHEN TO USE: Create/edit workflows, settings, confirmations
* WHEN NOT TO USE: Large forms, multi-step processes, navigation
*
* MODAL STRUCTURE:
* - Backdrop with blur effect
* - Card container with glassmorphism
* - Form with consistent input styling
* - Action buttons with clear hierarchy
*
* ACCESSIBILITY:
* - Focus trap within modal
* - Escape key to close
* - Proper ARIA labels
* - Keyboard navigation
*/
import { useState } from 'react';
import { Card } from '@/features/ui/primitives/card';
import { Button } from '@/features/ui/primitives/button';
import { glassmorphism, cn } from '@/features/ui/primitives/styles';
export const ModalForm = ({ isOpen, onClose, onSubmit }: {
isOpen: boolean;
onClose: () => void;
onSubmit: (data: any) => void;
}) => {
if (!isOpen) return null;
return (
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50">
<Card size="lg" glowColor="purple" className="w-full max-w-md">
{/* Header */}
<div className="flex items-center justify-between mb-6">
<h2 className="text-xl font-bold">Create New Item</h2>
<Button variant="ghost" size="sm" onClick={onClose}>
×
</Button>
</div>
{/* Form */}
<form className="space-y-4" onSubmit={onSubmit}>
<div>
<label className="block text-sm font-medium mb-2">
Title
</label>
<input
type="text"
className={cn(
"w-full px-3 py-2 rounded-md text-sm",
glassmorphism.background.subtle,
glassmorphism.border.default,
glassmorphism.border.focus,
"transition-all duration-200"
)}
placeholder="Enter title"
required
/>
</div>
<div>
<label className="block text-sm font-medium mb-2">
Description
</label>
<textarea
className={cn(
"w-full px-3 py-2 rounded-md text-sm h-20 resize-none",
glassmorphism.background.subtle,
glassmorphism.border.default,
glassmorphism.border.focus,
"transition-all duration-200"
)}
placeholder="Enter description"
/>
</div>
{/* Actions */}
<div className="flex gap-3 pt-4">
<Button
type="button"
variant="outline"
className="flex-1"
onClick={onClose}
>
Cancel
</Button>
<Button type="submit" className="flex-1">
Create
</Button>
</div>
</form>
</Card>
</div>
);
};`
};
return codeExamples[compositionId as keyof typeof codeExamples] || '';
};
export const CompositionsExample = () => {
const [selectedComposition, setSelectedComposition] = useState<string>('dashboard');
const currentComposition = EXAMPLE_COMPOSITIONS.find(c => c.id === selectedComposition);
return (
<div className="space-y-8">
<div>
<h2 className="text-2xl font-bold mb-4">Complete Compositions</h2>
<p className="text-gray-600 dark:text-gray-400 mb-6">
Real-world examples combining multiple components into complete UI compositions.
</p>
</div>
{/* Composition Selector */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Select Composition</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{EXAMPLE_COMPOSITIONS.map((composition) => (
<Card
key={composition.id}
className={cn(
"p-4 cursor-pointer transition-all duration-200",
selectedComposition === composition.id
? "border-cyan-500 shadow-[0_0_10px_rgba(34,211,238,0.3)]"
: "hover:shadow-lg"
)}
onClick={() => setSelectedComposition(composition.id)}
>
<h4 className="font-medium mb-2">{composition.name}</h4>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-2">
{composition.description}
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
<strong>Use for:</strong> {composition.usage}
</p>
</Card>
))}
</div>
</Card>
{/* Live Preview */}
{currentComposition && (
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">
{currentComposition.name} - Live Preview
</h3>
<LivePreview minHeight="400px">
<div className="w-full max-w-6xl">
<currentComposition.component />
</div>
</LivePreview>
</Card>
)}
{/* Generated Code */}
{currentComposition && (
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Implementation Code</h3>
<CodeDisplay
code={generateCode(selectedComposition)}
showLineNumbers
/>
</Card>
)}
</div>
);
};

View File

@@ -0,0 +1,94 @@
import { Card } from '@/features/ui/primitives/card';
import { CodeDisplay } from '../shared/CodeDisplay';
export const PagesExample = () => {
const generateCode = () => {
return `/**
* 🤖 AI CONTEXT: Complete Page Examples
*
* PURPOSE: Full page layouts using Archon's design system
* WHEN TO USE: Reference for building new pages with consistent styling
* WHEN NOT TO USE: Component-level implementation
*
* PAGE TYPES:
* - Landing pages with hero sections
* - Dashboard pages with data visualization
* - Settings pages with form layouts
* - Profile pages with user information
* - Error pages with helpful messaging
*
* COMPOSITION PRINCIPLES:
* - Consistent spacing and typography
* - Proper use of glassmorphism effects
* - Responsive grid layouts
* - Accessible navigation patterns
*/
// Example: Landing Page
export const LandingPage = () => {
return (
<div className="min-h-screen bg-gradient-to-br from-gray-900 to-gray-800">
{/* Hero Section */}
<section className="py-20 text-center">
<h1 className="text-4xl md:text-6xl font-bold mb-6">
Welcome to Archon
</h1>
<p className="text-xl text-gray-400 mb-8 max-w-2xl mx-auto">
Build beautiful applications with our glassmorphism design system
</p>
<Button size="lg" glowColor="purple">
Get Started
</Button>
</section>
{/* Features Section */}
<section className="py-20">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 max-w-6xl mx-auto">
{features.map((feature) => (
<Card key={feature.id} glowColor="cyan" className="p-8 text-center">
<feature.icon className="w-12 h-12 mx-auto mb-4" />
<h3 className="text-xl font-bold mb-4">{feature.title}</h3>
<p className="text-gray-400">{feature.description}</p>
</Card>
))}
</div>
</section>
</div>
);
};`;
};
return (
<div className="space-y-8">
<div>
<h2 className="text-2xl font-bold mb-4">Page Examples</h2>
<p className="text-gray-600 dark:text-gray-400 mb-6">
Complete page layouts demonstrating how to compose components into full user interfaces.
</p>
</div>
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Coming Soon</h3>
<p className="text-gray-600 dark:text-gray-400 mb-4">
This section will include complete page examples such as:
</p>
<ul className="list-disc list-inside text-sm text-gray-600 dark:text-gray-400 space-y-1 mb-6">
<li>Landing pages with hero sections and feature grids</li>
<li>Dashboard pages with navigation and data visualization</li>
<li>Settings pages with form layouts and preferences</li>
<li>Profile pages with user information and actions</li>
<li>Error pages with helpful messaging and navigation</li>
</ul>
</Card>
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Example Code Structure</h3>
<CodeDisplay
code={generateCode()}
showLineNumbers
/>
</Card>
</div>
);
};

View File

@@ -0,0 +1,137 @@
import { Card } from '@/features/ui/primitives/card';
import { CodeDisplay } from '../shared/CodeDisplay';
export const WorkflowsExample = () => {
const generateCode = () => {
return `/**
* 🤖 AI CONTEXT: User Workflow Examples
*
* PURPOSE: Multi-step user flows using Archon components
* WHEN TO USE: Complex user journeys requiring multiple screens
* WHEN NOT TO USE: Simple single-page interactions
*
* WORKFLOW TYPES:
* - Onboarding flows with progressive disclosure
* - Multi-step forms with validation
* - Wizard-style configuration processes
* - Data import/export workflows
* - User authentication flows
*
* FLOW PRINCIPLES:
* - Clear progress indication
* - Consistent navigation patterns
* - Proper error handling and recovery
* - Accessible keyboard navigation
* - Mobile-responsive design
*/
import { useState } from 'react';
import { Card } from '@/features/ui/primitives/card';
import { Button } from '@/features/ui/primitives/button';
// Example: Multi-step Onboarding
export const OnboardingWorkflow = () => {
const [currentStep, setCurrentStep] = useState(0);
const [formData, setFormData] = useState({});
const steps = [
{ title: "Welcome", component: WelcomeStep },
{ title: "Profile", component: ProfileStep },
{ title: "Preferences", component: PreferencesStep },
{ title: "Complete", component: CompleteStep }
];
const nextStep = () => setCurrentStep(prev => Math.min(prev + 1, steps.length - 1));
const prevStep = () => setCurrentStep(prev => Math.max(prev - 1, 0));
return (
<div className="max-w-2xl mx-auto">
{/* Progress Indicator */}
<div className="mb-8">
<div className="flex items-center justify-between mb-2">
{steps.map((step, index) => (
<div
key={step.title}
className={cn(
"flex items-center justify-center w-8 h-8 rounded-full text-sm font-medium",
index <= currentStep
? "bg-cyan-500 text-white"
: "bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-400"
)}
>
{index + 1}
</div>
))}
</div>
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
<div
className="bg-cyan-500 h-2 rounded-full transition-all"
style={{ width: \`\${((currentStep + 1) / steps.length) * 100}%\` }}
/>
</div>
</div>
{/* Step Content */}
<Card glowColor="purple" className="p-8 mb-6">
<h2 className="text-2xl font-bold mb-6">{steps[currentStep].title}</h2>
<steps[currentStep].component
formData={formData}
setFormData={setFormData}
/>
</Card>
{/* Navigation */}
<div className="flex justify-between">
<Button
variant="outline"
onClick={prevStep}
disabled={currentStep === 0}
>
Previous
</Button>
<Button
onClick={nextStep}
disabled={currentStep === steps.length - 1}
>
{currentStep === steps.length - 1 ? 'Complete' : 'Next'}
</Button>
</div>
</div>
);
};`;
};
return (
<div className="space-y-8">
<div>
<h2 className="text-2xl font-bold mb-4">Workflow Examples</h2>
<p className="text-gray-600 dark:text-gray-400 mb-6">
Multi-step user flows and complex interactions using Archon's design system.
</p>
</div>
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Coming Soon</h3>
<p className="text-gray-600 dark:text-gray-400 mb-4">
This section will include interactive workflow examples such as:
</p>
<ul className="list-disc list-inside text-sm text-gray-600 dark:text-gray-400 space-y-1 mb-6">
<li>Onboarding flows with step-by-step guidance</li>
<li>Multi-step form wizards with validation</li>
<li>Data import/export workflows with progress tracking</li>
<li>User authentication flows with error handling</li>
<li>Configuration wizards with branching logic</li>
</ul>
</Card>
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Example Workflow Pattern</h3>
<CodeDisplay
code={generateCode()}
showLineNumbers
/>
</Card>
</div>
);
};

View File

@@ -0,0 +1,258 @@
import { Card } from '@/features/ui/primitives/card';
import { CodeDisplay } from '../shared/CodeDisplay';
// Colored glass cards (tinted glass, no glow)
const GLASS_TINT_COLORS = [
{
name: 'Clear Glass',
glassTint: 'none' as const,
transparency: 'light' as const,
description: 'Pure transparent glass',
usage: 'Default containers, neutral elements'
},
{
name: 'Purple Glass',
glassTint: 'purple' as const,
transparency: 'medium' as const,
description: 'Purple-tinted transparent glass',
usage: 'Primary content areas'
},
{
name: 'Blue Glass',
glassTint: 'blue' as const,
transparency: 'medium' as const,
description: 'Blue-tinted transparent glass',
usage: 'Informational panels'
},
{
name: 'Cyan Glass',
glassTint: 'cyan' as const,
transparency: 'medium' as const,
description: 'Cyan-tinted transparent glass',
usage: 'Active/selected states'
},
{
name: 'Green Glass',
glassTint: 'green' as const,
transparency: 'medium' as const,
description: 'Green-tinted transparent glass',
usage: 'Success messages'
},
{
name: 'Orange Glass',
glassTint: 'orange' as const,
transparency: 'medium' as const,
description: 'Orange-tinted transparent glass',
usage: 'Warning notifications'
}
];
// Neon glow cards (backlight effect)
const NEON_GLOW_COLORS = [
{
name: 'Purple Glow',
glowColor: 'purple' as const,
transparency: 'light' as const,
description: 'Glass with purple neon backlight',
usage: 'Featured content, primary CTAs'
},
{
name: 'Blue Glow',
glowColor: 'blue' as const,
transparency: 'light' as const,
description: 'Glass with blue neon backlight',
usage: 'Information highlights'
},
{
name: 'Cyan Glow',
glowColor: 'cyan' as const,
transparency: 'light' as const,
description: 'Glass with cyan neon backlight',
usage: 'Tron-style emphasis'
},
{
name: 'Green Glow',
glowColor: 'green' as const,
transparency: 'light' as const,
description: 'Glass with green neon backlight',
usage: 'Success states'
},
{
name: 'Red Glow',
glowColor: 'red' as const,
transparency: 'light' as const,
description: 'Glass with red neon backlight',
usage: 'Errors and critical alerts'
},
{
name: 'Pink Glow',
glowColor: 'pink' as const,
transparency: 'light' as const,
description: 'Glass with pink neon backlight',
usage: 'Special features'
}
];
// Transparency levels
const TRANSPARENCY_LEVELS = [
{ level: 'clear' as const, name: 'Clear', description: 'Almost invisible' },
{ level: 'light' as const, name: 'Light', description: 'Subtle frosting' },
{ level: 'medium' as const, name: 'Medium', description: 'Standard glass' },
{ level: 'frosted' as const, name: 'Frosted', description: 'Heavy frosting' },
{ level: 'solid' as const, name: 'Solid', description: 'Nearly opaque' }
];
const generateCode = () => {
return `import { Card } from '@/features/ui/primitives/card';
/**
* 🤖 AI CONTEXT: Glass Card System
*
* Three independent properties for maximum flexibility:
*
* 1. TRANSPARENCY - How much you can see through the glass
* - clear: Almost invisible (0.01 opacity)
* - light: Subtle frosting (0.05 opacity)
* - medium: Standard glass (0.08 opacity)
* - frosted: Heavy frosting (0.12 opacity)
* - solid: Nearly opaque (0.20 opacity)
*
* 2. GLASS TINT - Adds color to the glass itself
* - none: Pure transparent
* - purple/blue/cyan/green/orange/pink/red: Colored glass
*
* 3. GLOW COLOR - Neon backlight effect
* - none: No glow
* - purple/blue/cyan/green/orange/pink/red: Neon glow
*
* USAGE PATTERNS:
* - Default containers: transparency="light" glassTint="none"
* - Colored sections: transparency="medium" glassTint="blue"
* - Featured content: transparency="light" glowColor="purple"
* - Layered UI: Use different transparency levels for depth
*/
// Basic transparent glass
<Card transparency="light">
Pure glass effect
</Card>
// Colored glass (tinted)
<Card transparency="medium" glassTint="blue">
Blue-tinted glass
</Card>
// Glass with neon glow
<Card transparency="light" glowColor="purple">
Glass with purple backlight
</Card>
// Combined: Colored glass with glow
<Card transparency="medium" glassTint="cyan" glowColor="cyan">
Cyan glass with matching glow
</Card>
// Heavy frosted glass
<Card transparency="frosted" glassTint="none">
Heavily frosted glass
</Card>`;
};
export const ColorsFoundation = () => {
return (
<div className="space-y-12">
{/* Header */}
<div className="text-center">
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">Glass Card System</h2>
<p className="text-gray-600 dark:text-gray-400 text-lg max-w-3xl mx-auto">
True glassmorphism with independent control over transparency, tint, and neon glow effects.
</p>
</div>
{/* Transparency Levels */}
<div className="space-y-6">
<h3 className="text-xl font-semibold text-gray-900 dark:text-white text-center">Glass Transparency Levels</h3>
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4">
{TRANSPARENCY_LEVELS.map((item) => (
<Card
key={item.level}
transparency={item.level}
className="hover:scale-105 transition-transform duration-300"
>
<h4 className="text-lg font-semibold text-gray-900 dark:text-white mb-1">{item.name}</h4>
<p className="text-gray-600 dark:text-gray-400 text-xs">{item.description}</p>
</Card>
))}
</div>
</div>
{/* Colored Glass (Tints) */}
<div className="space-y-6">
<h3 className="text-xl font-semibold text-gray-900 dark:text-white text-center">Colored Glass Tints</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{GLASS_TINT_COLORS.map((color) => (
<Card
key={color.name}
glassTint={color.glassTint}
transparency={color.transparency}
className="hover:scale-105 transition-transform duration-300"
>
<h4 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">{color.name}</h4>
<p className="text-gray-700 dark:text-gray-300 text-sm mb-2">{color.description}</p>
<p className="text-gray-600 dark:text-gray-400 text-xs">{color.usage}</p>
</Card>
))}
</div>
</div>
{/* Neon Glow Effects */}
<div className="space-y-6">
<h3 className="text-xl font-semibold text-gray-900 dark:text-white text-center">Neon Backlight Glow</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{NEON_GLOW_COLORS.map((color) => (
<Card
key={color.name}
glowColor={color.glowColor}
transparency={color.transparency}
className="hover:scale-105 transition-transform duration-300"
>
<h4 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">{color.name}</h4>
<p className="text-gray-700 dark:text-gray-300 text-sm mb-2">{color.description}</p>
<p className="text-gray-600 dark:text-gray-400 text-xs">{color.usage}</p>
</Card>
))}
</div>
</div>
{/* Combined Examples */}
<div className="space-y-6">
<h3 className="text-xl font-semibold text-gray-900 dark:text-white text-center">Combined Effects</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<Card transparency="frosted" glassTint="purple" glowColor="purple">
<h4 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">Frosted Purple</h4>
<p className="text-gray-700 dark:text-gray-300 text-sm">Heavy frosting with purple tint and glow</p>
</Card>
<Card transparency="clear" glassTint="none" glowColor="cyan">
<h4 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">Clear with Cyan Edge</h4>
<p className="text-gray-700 dark:text-gray-300 text-sm">Nearly invisible with cyan neon edge</p>
</Card>
<Card transparency="medium" glassTint="blue" glowColor="none">
<h4 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">Blue Tinted Glass</h4>
<p className="text-gray-700 dark:text-gray-300 text-sm">Medium opacity blue glass, no glow</p>
</Card>
</div>
</div>
{/* Usage Code */}
<div className="max-w-4xl mx-auto">
<Card className="p-6" transparency="light">
<h3 className="text-xl font-semibold text-white mb-6 text-center">Usage Code</h3>
<CodeDisplay
code={generateCode()}
showLineNumbers
/>
</Card>
</div>
</div>
);
};

View File

@@ -0,0 +1,380 @@
import { Card } from '@/features/ui/primitives/card';
import { CodeDisplay } from '../shared/CodeDisplay';
import { cn, glassmorphism } from '@/features/ui/primitives/styles';
interface Effect {
name: string;
className: string;
description: string;
usage: string;
}
const BLUR_EFFECTS: Effect[] = [
{
name: 'Backdrop Blur Small',
className: 'backdrop-blur-sm',
description: 'Subtle blur for lightweight overlays',
usage: 'Light modals, hover effects, subtle overlays'
},
{
name: 'Backdrop Blur Medium',
className: 'backdrop-blur-md',
description: 'Standard blur for glassmorphism',
usage: 'Cards, panels, primary glassmorphism effects'
},
{
name: 'Backdrop Blur Large',
className: 'backdrop-blur-lg',
description: 'Strong blur for prominent overlays',
usage: 'Modals, sidebars, major overlay elements'
},
{
name: 'Backdrop Blur Extra Large',
className: 'backdrop-blur-xl',
description: 'Maximum blur for full overlays',
usage: 'Background overlays, page transitions'
}
];
const SHADOW_EFFECTS: Effect[] = [
{
name: 'Small Shadow',
className: glassmorphism.shadow.sm,
description: 'Subtle elevation for small elements',
usage: 'Buttons, small cards, minor elevation'
},
{
name: 'Medium Shadow',
className: glassmorphism.shadow.md,
description: 'Standard elevation for cards',
usage: 'Cards, panels, standard components'
},
{
name: 'Large Shadow',
className: glassmorphism.shadow.lg,
description: 'Strong elevation for prominent elements',
usage: 'Modals, floating panels, major elements'
},
{
name: 'Elevated Shadow',
className: glassmorphism.shadow.elevated,
description: 'Maximum elevation with custom shadow',
usage: 'Tooltips, dropdowns, floating elements'
}
];
const GLOW_EFFECTS: Effect[] = [
{
name: 'Purple Glow',
className: glassmorphism.shadow.glow.purple,
description: 'Primary accent glow effect',
usage: 'Primary actions, featured content'
},
{
name: 'Blue Glow',
className: glassmorphism.shadow.glow.blue,
description: 'Information accent glow',
usage: 'Information states, secondary actions'
},
{
name: 'Green Glow',
className: glassmorphism.shadow.glow.green,
description: 'Success accent glow',
usage: 'Success states, positive feedback'
},
{
name: 'Red Glow',
className: glassmorphism.shadow.glow.red,
description: 'Danger accent glow',
usage: 'Error states, destructive actions'
},
{
name: 'Orange Glow',
className: glassmorphism.shadow.glow.orange,
description: 'Warning accent glow',
usage: 'Warning states, caution indicators'
},
{
name: 'Cyan Glow',
className: glassmorphism.shadow.glow.cyan,
description: 'Active accent glow',
usage: 'Active states, focus indicators'
}
];
const EDGE_EFFECTS: Effect[] = [
{
name: 'Top Edge Glow',
className: glassmorphism.edgePositions.top,
description: 'Glowing border at top edge',
usage: 'Header cards, top navigation elements'
},
{
name: 'Left Edge Glow',
className: glassmorphism.edgePositions.left,
description: 'Glowing border at left edge',
usage: 'Sidebar items, left navigation'
},
{
name: 'Right Edge Glow',
className: glassmorphism.edgePositions.right,
description: 'Glowing border at right edge',
usage: 'Action panels, right sidebars'
},
{
name: 'Bottom Edge Glow',
className: glassmorphism.edgePositions.bottom,
description: 'Glowing border at bottom edge',
usage: 'Footer elements, bottom panels'
}
];
const INTERACTIVE_EFFECTS: Effect[] = [
{
name: 'Hover Effect',
className: glassmorphism.interactive.hover,
description: 'Subtle background change on hover',
usage: 'Interactive elements, buttons, clickable items'
},
{
name: 'Active Effect',
className: glassmorphism.interactive.active,
description: 'Background change when pressed',
usage: 'Button active states, pressed elements'
},
{
name: 'Selected Effect',
className: glassmorphism.interactive.selected,
description: 'Background and text change when selected',
usage: 'Selected items, active navigation'
},
{
name: 'Focus Effect',
className: glassmorphism.border.focus,
description: 'Border and shadow change on focus',
usage: 'Form inputs, keyboard navigation'
}
];
const generateCode = () => {
return `import { glassmorphism } from '@/features/ui/primitives/styles';
/**
* 🤖 AI CONTEXT: Glassmorphism Effects System
*
* PURPOSE: Consistent visual effects for the Tron-inspired design
* WHEN TO USE: Layer effects to create depth and visual hierarchy
* WHEN NOT TO USE: Don't over-layer effects (max 3 layers)
*
* EFFECT HIERARCHY:
* 1. Backdrop Blur: Creates the "glass" foundation
* 2. Background: Adds subtle color and opacity
* 3. Border: Defines edges and focus states
* 4. Shadow/Glow: Adds depth and accent colors
* 5. Interactive: Responds to user actions
*
* LAYERING GUIDELINES:
* - Always start with backdrop-blur for glassmorphism
* - Add background for subtle color tinting
* - Use borders for definition and focus
* - Apply shadows for elevation
* - Add glow effects for accent and attention
* - Include interactive states for usability
*
* PERFORMANCE CONSIDERATIONS:
* - Backdrop blur is expensive, use sparingly
* - Limit glow effects to 2-3 per view
* - Use transform3d for smooth animations
* - Prefer opacity changes over color changes
*/
// Basic Glassmorphism Card
<div className={cn(
"backdrop-blur-md", // Glass effect
glassmorphism.background.card, // Subtle background
glassmorphism.border.default, // Border definition
glassmorphism.shadow.md, // Standard elevation
"rounded-lg transition-all duration-300" // Shape and animation
)}>
Standard glassmorphism card
</div>
// Accented Card with Glow
<div className={cn(
"backdrop-blur-md",
glassmorphism.background.card,
glassmorphism.border.cyan,
glassmorphism.shadow.glow.purple, // Accent glow
glassmorphism.edgePositions.top, // Edge accent
"rounded-lg"
)}>
Accented card with purple glow
</div>
// Interactive Button
<button className={cn(
"backdrop-blur-sm",
glassmorphism.background.subtle,
glassmorphism.border.default,
glassmorphism.interactive.hover, // Hover state
glassmorphism.interactive.active, // Active state
glassmorphism.border.focus, // Focus state
"px-4 py-2 rounded-md transition-all"
)}>
Interactive glassmorphism button
</button>
// Floating Panel
<div className={cn(
"backdrop-blur-lg", // Strong blur
glassmorphism.background.strong, // High opacity
glassmorphism.border.default,
glassmorphism.shadow.elevated, // Maximum elevation
"rounded-xl p-6"
)}>
Floating panel with strong effects
</div>`;
};
const EffectDemo = ({ effect, showBackground = false }: { effect: Effect; showBackground?: boolean }) => (
<div className="space-y-2">
<div className="relative h-24 rounded-lg overflow-hidden">
{showBackground && (
<div className="absolute inset-0 bg-gradient-to-br from-purple-500 via-cyan-500 to-blue-500 opacity-30" />
)}
<div
className={cn(
'absolute inset-4 rounded-lg flex items-center justify-center text-sm font-medium',
glassmorphism.background.card,
glassmorphism.border.default,
effect.className
)}
>
{effect.name}
</div>
</div>
<div className="text-xs">
<p className="font-medium text-gray-900 dark:text-gray-100">{effect.description}</p>
<p className="text-gray-600 dark:text-gray-400 mt-1">{effect.usage}</p>
</div>
</div>
);
export const EffectsFoundation = () => {
return (
<div className="space-y-8">
<div>
<h2 className="text-2xl font-bold mb-4">Effects System</h2>
<p className="text-gray-600 dark:text-gray-400 mb-6">
Glassmorphism effects including blur, shadows, glows, and interactive states for the Tron-inspired design.
</p>
</div>
{/* Blur Effects */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Backdrop Blur Effects</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{BLUR_EFFECTS.map((effect) => (
<EffectDemo key={effect.name} effect={effect} showBackground />
))}
</div>
</Card>
{/* Shadow Effects */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Shadow Effects</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{SHADOW_EFFECTS.map((effect) => (
<EffectDemo key={effect.name} effect={effect} />
))}
</div>
</Card>
{/* Glow Effects */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Glow Effects</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{GLOW_EFFECTS.map((effect) => (
<EffectDemo key={effect.name} effect={effect} />
))}
</div>
</Card>
{/* Edge Effects */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Edge Glow Effects</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{EDGE_EFFECTS.map((effect) => (
<EffectDemo key={effect.name} effect={effect} />
))}
</div>
</Card>
{/* Interactive Effects */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Interactive Effects</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{INTERACTIVE_EFFECTS.map((effect) => (
<div key={effect.name} className="space-y-2">
<div
className={cn(
'h-24 rounded-lg flex items-center justify-center text-sm font-medium cursor-pointer',
glassmorphism.background.card,
glassmorphism.border.default,
glassmorphism.shadow.md,
effect.className,
'transition-all duration-200'
)}
>
{effect.name}
</div>
<div className="text-xs">
<p className="font-medium text-gray-900 dark:text-gray-100">{effect.description}</p>
<p className="text-gray-600 dark:text-gray-400 mt-1">{effect.usage}</p>
</div>
</div>
))}
</div>
</Card>
{/* Layered Example */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Layered Effects Example</h3>
<div className="relative h-48 rounded-lg overflow-hidden">
{/* Background gradient */}
<div className="absolute inset-0 bg-gradient-to-br from-purple-500 via-cyan-500 to-blue-500 opacity-30" />
{/* Multiple layered elements */}
<div className={cn(
'absolute top-8 left-8 right-8 h-32 rounded-lg',
'backdrop-blur-md',
glassmorphism.background.card,
glassmorphism.border.cyan,
glassmorphism.shadow.glow.purple,
glassmorphism.edgePositions.top,
'flex items-center justify-center font-medium'
)}>
Complete Glassmorphism Effect
<div className="absolute top-2 right-2 text-xs text-gray-500 dark:text-gray-400">
Blur + Background + Border + Glow + Edge
</div>
</div>
</div>
<p className="text-sm text-gray-600 dark:text-gray-400 mt-4">
This example combines multiple effects: backdrop blur, glassmorphism background,
colored border, purple glow shadow, and top edge glow for a complete Tron-inspired effect.
</p>
</Card>
{/* Usage Code */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Usage Code</h3>
<CodeDisplay
code={generateCode()}
showLineNumbers
/>
</Card>
</div>
);
};

View File

@@ -0,0 +1,322 @@
import { Card } from '@/features/ui/primitives/card';
import { CodeDisplay } from '../shared/CodeDisplay';
import { cn } from '@/features/ui/primitives/styles';
interface SpacingScale {
name: string;
value: string;
pixels: string;
usage: string;
tailwindClass: string;
}
const SPACING_SCALE: SpacingScale[] = [
{
name: 'xs',
value: '0.125rem',
pixels: '2px',
usage: 'Minimal spacing, fine adjustments',
tailwindClass: '0.5'
},
{
name: 'sm',
value: '0.25rem',
pixels: '4px',
usage: 'Small gaps, tight layouts',
tailwindClass: '1'
},
{
name: 'md',
value: '0.5rem',
pixels: '8px',
usage: 'Default small spacing',
tailwindClass: '2'
},
{
name: 'lg',
value: '0.75rem',
pixels: '12px',
usage: 'Medium spacing between elements',
tailwindClass: '3'
},
{
name: 'xl',
value: '1rem',
pixels: '16px',
usage: 'Standard spacing unit',
tailwindClass: '4'
},
{
name: '2xl',
value: '1.25rem',
pixels: '20px',
usage: 'Comfortable spacing',
tailwindClass: '5'
},
{
name: '3xl',
value: '1.5rem',
pixels: '24px',
usage: 'Large spacing between sections',
tailwindClass: '6'
},
{
name: '4xl',
value: '2rem',
pixels: '32px',
usage: 'Extra large spacing',
tailwindClass: '8'
},
{
name: '5xl',
value: '2.5rem',
pixels: '40px',
usage: 'Section dividers',
tailwindClass: '10'
},
{
name: '6xl',
value: '3rem',
pixels: '48px',
usage: 'Major section spacing',
tailwindClass: '12'
}
];
const LAYOUT_SPACING = [
{
name: 'Component Padding',
small: 'p-4 (1rem)',
medium: 'p-6 (1.5rem)',
large: 'p-8 (2rem)',
usage: 'Internal spacing within cards and containers'
},
{
name: 'Element Gaps',
small: 'gap-2 (0.5rem)',
medium: 'gap-4 (1rem)',
large: 'gap-6 (1.5rem)',
usage: 'Spacing between elements in flex/grid layouts'
},
{
name: 'Section Margins',
small: 'mb-4 (1rem)',
medium: 'mb-6 (1.5rem)',
large: 'mb-8 (2rem)',
usage: 'Vertical spacing between page sections'
},
{
name: 'Content Margins',
small: 'mx-4 (1rem)',
medium: 'mx-6 (1.5rem)',
large: 'mx-8 (2rem)',
usage: 'Horizontal margins for content containers'
}
];
const generateCode = () => {
return `/**
* 🤖 AI CONTEXT: Spacing System
*
* PURPOSE: Consistent spacing throughout the application
* WHEN TO USE: All layouts should use the standardized spacing scale
* WHEN NOT TO USE: Never hardcode spacing values, always use Tailwind classes
*
* SPACING SCALE:
* - 0.5 (2px): Minimal adjustments, fine-tuning
* - 1 (4px): Small gaps in tight layouts
* - 2 (8px): Default small spacing
* - 3 (12px): Medium element spacing
* - 4 (16px): Standard spacing unit (base)
* - 5 (20px): Comfortable spacing
* - 6 (24px): Large spacing between sections
* - 8 (32px): Extra large spacing
* - 10 (40px): Section dividers
* - 12 (48px): Major section spacing
*
* LAYOUT PATTERNS:
* - Card padding: p-4 (small), p-6 (medium), p-8 (large)
* - Element gaps: gap-2 (tight), gap-4 (standard), gap-6 (loose)
* - Section margins: mb-4 (small), mb-6 (medium), mb-8 (large)
* - Page margins: mx-4 (mobile), mx-6 (tablet), mx-8 (desktop)
*
* RESPONSIVE SPACING:
* - Use responsive variants: p-4 md:p-6 lg:p-8
* - Scale up spacing on larger screens for better proportions
* - Maintain readability and visual hierarchy across devices
*/
// Padding Examples
<div className="p-4">Small padding (16px)</div>
<div className="p-6">Medium padding (24px)</div>
<div className="p-8">Large padding (32px)</div>
// Margin Examples
<div className="mb-4">Small bottom margin (16px)</div>
<div className="mb-6">Medium bottom margin (24px)</div>
<div className="mb-8">Large bottom margin (32px)</div>
// Gap Examples (Flexbox/Grid)
<div className="flex gap-2">Tight gap (8px)</div>
<div className="flex gap-4">Standard gap (16px)</div>
<div className="flex gap-6">Loose gap (24px)</div>
// Responsive Spacing
<div className="p-4 md:p-6 lg:p-8">Responsive padding</div>
<div className="space-y-4 md:space-y-6">Responsive vertical spacing</div>`;
};
export const SpacingFoundation = () => {
return (
<div className="space-y-8">
<div>
<h2 className="text-2xl font-bold mb-4">Spacing System</h2>
<p className="text-gray-600 dark:text-gray-400 mb-6">
Consistent spacing scale for layouts, components, and content organization.
</p>
</div>
{/* Spacing Scale */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Spacing Scale</h3>
<div className="space-y-4">
{SPACING_SCALE.map((spacing) => (
<div key={spacing.name} className="flex items-center justify-between border-b border-gray-200 dark:border-gray-700 pb-4 last:border-b-0">
<div className="flex items-center space-x-6">
<div className="w-20 text-sm font-medium">
{spacing.name}
</div>
<div className="flex items-center space-x-2">
<div
className="bg-cyan-500"
style={{
width: spacing.pixels,
height: '20px'
}}
/>
<span className="text-xs text-gray-500 dark:text-gray-400 w-12">
{spacing.pixels}
</span>
</div>
<div className="text-sm text-gray-600 dark:text-gray-400 min-w-[100px]">
{spacing.value}
</div>
</div>
<div className="flex items-center space-x-4">
<div className="text-xs font-mono bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">
{spacing.tailwindClass}
</div>
<div className="text-xs text-gray-500 dark:text-gray-400 max-w-[200px]">
{spacing.usage}
</div>
</div>
</div>
))}
</div>
</Card>
{/* Layout Spacing Patterns */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Layout Patterns</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{LAYOUT_SPACING.map((pattern) => (
<div key={pattern.name} className="space-y-3">
<h4 className="font-medium text-gray-900 dark:text-gray-100">
{pattern.name}
</h4>
<div className="space-y-2 text-sm">
<div className="flex justify-between">
<span className="text-gray-600 dark:text-gray-400">Small:</span>
<code className="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-xs">
{pattern.small}
</code>
</div>
<div className="flex justify-between">
<span className="text-gray-600 dark:text-gray-400">Medium:</span>
<code className="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-xs">
{pattern.medium}
</code>
</div>
<div className="flex justify-between">
<span className="text-gray-600 dark:text-gray-400">Large:</span>
<code className="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-xs">
{pattern.large}
</code>
</div>
</div>
<p className="text-xs text-gray-500 dark:text-gray-400">
{pattern.usage}
</p>
</div>
))}
</div>
</Card>
{/* Visual Examples */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Visual Examples</h3>
{/* Padding Examples */}
<div className="space-y-6">
<div>
<h4 className="text-sm font-medium mb-3 text-gray-700 dark:text-gray-300">Padding Examples</h4>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{['p-4', 'p-6', 'p-8'].map((padding) => (
<div key={padding} className="space-y-2">
<div className="text-xs font-mono bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded inline-block">
{padding}
</div>
<div className="border-2 border-dashed border-gray-300 dark:border-gray-600">
<div className={cn(padding, 'bg-cyan-100 dark:bg-cyan-900/30')}>
<div className="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded text-center text-xs py-2">
Content
</div>
</div>
</div>
</div>
))}
</div>
</div>
{/* Gap Examples */}
<div>
<h4 className="text-sm font-medium mb-3 text-gray-700 dark:text-gray-300">Gap Examples</h4>
<div className="space-y-4">
{[
{ gap: 'gap-2', label: 'gap-2 (8px)' },
{ gap: 'gap-4', label: 'gap-4 (16px)' },
{ gap: 'gap-6', label: 'gap-6 (24px)' }
].map((example) => (
<div key={example.gap} className="space-y-2">
<div className="text-xs font-mono bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded inline-block">
{example.label}
</div>
<div className={cn('flex', example.gap)}>
{[1, 2, 3].map((i) => (
<div
key={i}
className="w-16 h-12 bg-cyan-100 dark:bg-cyan-900/30 border border-gray-200 dark:border-gray-700 rounded flex items-center justify-center text-xs"
>
{i}
</div>
))}
</div>
</div>
))}
</div>
</div>
</div>
</Card>
{/* Usage Code */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Usage Code</h3>
<CodeDisplay
code={generateCode()}
showLineNumbers
/>
</Card>
</div>
);
};

View File

@@ -0,0 +1,346 @@
import { Card } from '@/features/ui/primitives/card';
import { CodeDisplay } from '../shared/CodeDisplay';
import { cn } from '@/features/ui/primitives/styles';
interface TypographyStyle {
name: string;
className: string;
size: string;
weight: string;
usage: string;
example: string;
}
const HEADING_STYLES: TypographyStyle[] = [
{
name: 'Display Large',
className: 'text-4xl md:text-5xl lg:text-6xl font-bold',
size: '3.75rem / 4rem / 4.5rem',
weight: '700',
usage: 'Hero headings, landing page titles',
example: 'Hero Title'
},
{
name: 'Display Medium',
className: 'text-3xl md:text-4xl font-bold',
size: '1.875rem / 2.25rem',
weight: '700',
usage: 'Page titles, section headers',
example: 'Page Title'
},
{
name: 'Heading 1',
className: 'text-2xl font-bold',
size: '1.5rem',
weight: '700',
usage: 'Main section titles',
example: 'Section Title'
},
{
name: 'Heading 2',
className: 'text-xl font-semibold',
size: '1.25rem',
weight: '600',
usage: 'Subsection titles',
example: 'Subsection Title'
},
{
name: 'Heading 3',
className: 'text-lg font-semibold',
size: '1.125rem',
weight: '600',
usage: 'Card titles, component headers',
example: 'Component Header'
},
{
name: 'Heading 4',
className: 'text-base font-medium',
size: '1rem',
weight: '500',
usage: 'Small headings, labels',
example: 'Label Text'
}
];
const BODY_STYLES: TypographyStyle[] = [
{
name: 'Body Large',
className: 'text-lg font-normal',
size: '1.125rem',
weight: '400',
usage: 'Large body text, introductions',
example: 'This is large body text used for introductions and important content that needs to stand out.'
},
{
name: 'Body Medium',
className: 'text-base font-normal',
size: '1rem',
weight: '400',
usage: 'Standard body text, paragraphs',
example: 'This is standard body text used for regular content, paragraphs, and general information.'
},
{
name: 'Body Small',
className: 'text-sm font-normal',
size: '0.875rem',
weight: '400',
usage: 'Secondary text, descriptions',
example: 'This is small body text used for secondary information and descriptions.'
},
{
name: 'Caption',
className: 'text-xs font-normal',
size: '0.75rem',
weight: '400',
usage: 'Captions, metadata, helper text',
example: 'This is caption text used for metadata and helper information.'
}
];
const UTILITY_STYLES: TypographyStyle[] = [
{
name: 'Button Text',
className: 'text-sm font-medium',
size: '0.875rem',
weight: '500',
usage: 'Button labels, action text',
example: 'Button Label'
},
{
name: 'Code',
className: 'text-sm font-mono bg-gray-100 dark:bg-gray-800 px-1 py-0.5 rounded',
size: '0.875rem',
weight: '400',
usage: 'Inline code, technical text',
example: 'const variable = value;'
},
{
name: 'Link',
className: 'text-cyan-600 dark:text-cyan-400 hover:text-cyan-500 dark:hover:text-cyan-300 underline',
size: 'inherit',
weight: 'inherit',
usage: 'Links, interactive text',
example: 'This is a link'
},
{
name: 'Muted',
className: 'text-gray-500 dark:text-gray-400',
size: 'inherit',
weight: 'inherit',
usage: 'Secondary text, disabled states',
example: 'This is muted text'
}
];
const COLOR_VARIATIONS = [
{
name: 'Primary Text',
className: 'text-gray-900 dark:text-gray-100',
usage: 'Main content, headings'
},
{
name: 'Secondary Text',
className: 'text-gray-700 dark:text-gray-300',
usage: 'Body text, secondary content'
},
{
name: 'Muted Text',
className: 'text-gray-500 dark:text-gray-400',
usage: 'Helper text, captions, disabled'
},
{
name: 'Accent Text',
className: 'text-cyan-600 dark:text-cyan-400',
usage: 'Links, highlights, active states'
},
{
name: 'Success Text',
className: 'text-green-600 dark:text-green-400',
usage: 'Success messages, positive states'
},
{
name: 'Warning Text',
className: 'text-orange-600 dark:text-orange-400',
usage: 'Warning messages, caution states'
},
{
name: 'Error Text',
className: 'text-red-600 dark:text-red-400',
usage: 'Error messages, destructive actions'
}
];
const generateCode = () => {
return `/**
* 🤖 AI CONTEXT: Typography System
*
* PURPOSE: Consistent text styles across the application
* WHEN TO USE: All text content should use these utility classes
* WHEN NOT TO USE: Never hardcode font sizes, always use system classes
*
* HIERARCHY GUIDELINES:
* - Display Large: Hero sections, landing pages
* - Display Medium: Page titles, main headers
* - Heading 1-4: Section titles, descending importance
* - Body Large: Important content, introductions
* - Body Medium: Standard content, paragraphs
* - Body Small: Secondary content, descriptions
* - Caption: Metadata, helper text, timestamps
*
* COLOR SEMANTIC MEANINGS:
* - Primary: Main content, most important text
* - Secondary: Body content, readable but less prominent
* - Muted: Helper text, less important information
* - Accent: Interactive elements, links, highlights
* - Success/Warning/Error: Status-based messaging
*
* RESPONSIVE BEHAVIOR:
* - Display styles scale down on smaller screens
* - Body text maintains consistent size across devices
* - Use responsive classes (md:, lg:) for display text
*/
// Heading Examples
<h1 className="text-2xl font-bold">Main Section Title</h1>
<h2 className="text-xl font-semibold">Subsection Title</h2>
<h3 className="text-lg font-semibold">Component Header</h3>
// Body Text Examples
<p className="text-lg font-normal">Large body text for introductions</p>
<p className="text-base font-normal">Standard body text for content</p>
<p className="text-sm font-normal">Small text for descriptions</p>
// Utility Examples
<button className="text-sm font-medium">Button Label</button>
<code className="text-sm font-mono bg-gray-100 dark:bg-gray-800 px-1 py-0.5 rounded">
code snippet
</code>
<a className="text-cyan-600 dark:text-cyan-400 hover:text-cyan-500 underline">
Link text
</a>
// Color Variations
<span className="text-gray-900 dark:text-gray-100">Primary text</span>
<span className="text-gray-500 dark:text-gray-400">Muted text</span>
<span className="text-cyan-600 dark:text-cyan-400">Accent text</span>`;
};
export const TypographyFoundation = () => {
return (
<div className="space-y-8">
<div>
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">Typography System</h2>
<p className="text-gray-600 dark:text-gray-400 mb-6">
Consistent typography scale and styles for clear content hierarchy and optimal readability.
</p>
</div>
{/* Heading Styles */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">Headings</h3>
<div className="space-y-6">
{HEADING_STYLES.map((style) => (
<div key={style.name} className="border-b border-gray-200 dark:border-gray-700 pb-4 last:border-b-0">
<div className={cn(style.className, 'mb-2')}>
{style.example}
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-xs text-gray-600 dark:text-gray-400">
<div>
<span className="font-medium">Style:</span> {style.name}
</div>
<div>
<span className="font-medium">Size:</span> {style.size}
</div>
<div>
<span className="font-medium">Usage:</span> {style.usage}
</div>
</div>
</div>
))}
</div>
</Card>
{/* Body Styles */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">Body Text</h3>
<div className="space-y-6">
{BODY_STYLES.map((style) => (
<div key={style.name} className="border-b border-gray-200 dark:border-gray-700 pb-4 last:border-b-0">
<div className={cn(style.className, 'mb-2')}>
{style.example}
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-xs text-gray-600 dark:text-gray-400">
<div>
<span className="font-medium">Style:</span> {style.name}
</div>
<div>
<span className="font-medium">Size:</span> {style.size}
</div>
<div>
<span className="font-medium">Usage:</span> {style.usage}
</div>
</div>
</div>
))}
</div>
</Card>
{/* Utility Styles */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">Utility Styles</h3>
<div className="space-y-6">
{UTILITY_STYLES.map((style) => (
<div key={style.name} className="border-b border-gray-200 dark:border-gray-700 pb-4 last:border-b-0">
<div className={cn(style.className, 'mb-2')}>
{style.example}
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-xs text-gray-600 dark:text-gray-400">
<div>
<span className="font-medium">Style:</span> {style.name}
</div>
<div>
<span className="font-medium">Weight:</span> {style.weight}
</div>
<div>
<span className="font-medium">Usage:</span> {style.usage}
</div>
</div>
</div>
))}
</div>
</Card>
{/* Color Variations */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Text Colors</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{COLOR_VARIATIONS.map((color) => (
<div key={color.name} className="flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
<div>
<div className={cn(color.className, 'text-base font-medium mb-1')}>
{color.name}
</div>
<div className="text-xs text-gray-500 dark:text-gray-400">
{color.usage}
</div>
</div>
<div className="text-xs font-mono text-gray-400 dark:text-gray-500">
{color.className.split(' ')[0]}
</div>
</div>
))}
</div>
</Card>
{/* Usage Code */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Usage Code</h3>
<CodeDisplay
code={generateCode()}
showLineNumbers
/>
</Card>
</div>
);
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,861 @@
import { useState } from 'react';
import { Card } from '@/features/ui/primitives/card';
import { Button } from '@/features/ui/primitives/button';
import { CodeDisplay } from '../shared/CodeDisplay';
import { cn, glassmorphism } from '@/features/ui/primitives/styles';
import {
CheckCircle,
XCircle,
AlertTriangle,
Info,
X,
Loader2,
AlertCircle,
HelpCircle
} from 'lucide-react';
interface FeedbackPattern {
id: string;
name: string;
description: string;
usage: string;
component: React.ComponentType;
}
// Alert Component
const AlertsDemo = () => {
const alerts = [
{
type: 'success',
icon: <CheckCircle className="w-5 h-5" />,
title: 'Success',
message: 'Your project has been created successfully.',
bgColor: 'bg-green-50 dark:bg-green-900/20',
borderColor: 'border-green-200 dark:border-green-800',
textColor: 'text-green-800 dark:text-green-200',
iconColor: 'text-green-600 dark:text-green-400'
},
{
type: 'error',
icon: <XCircle className="w-5 h-5" />,
title: 'Error',
message: 'There was an error processing your request. Please try again.',
bgColor: 'bg-red-50 dark:bg-red-900/20',
borderColor: 'border-red-200 dark:border-red-800',
textColor: 'text-red-800 dark:text-red-200',
iconColor: 'text-red-600 dark:text-red-400'
},
{
type: 'warning',
icon: <AlertTriangle className="w-5 h-5" />,
title: 'Warning',
message: 'This action cannot be undone. Please proceed with caution.',
bgColor: 'bg-orange-50 dark:bg-orange-900/20',
borderColor: 'border-orange-200 dark:border-orange-800',
textColor: 'text-orange-800 dark:text-orange-200',
iconColor: 'text-orange-600 dark:text-orange-400'
},
{
type: 'info',
icon: <Info className="w-5 h-5" />,
title: 'Information',
message: 'New features have been added to your dashboard.',
bgColor: 'bg-blue-50 dark:bg-blue-900/20',
borderColor: 'border-blue-200 dark:border-blue-800',
textColor: 'text-blue-800 dark:text-blue-200',
iconColor: 'text-blue-600 dark:text-blue-400'
}
];
return (
<div className="space-y-4">
{alerts.map((alert) => (
<div
key={alert.type}
className={cn(
'p-4 rounded-lg border backdrop-blur-sm',
alert.bgColor,
alert.borderColor
)}
>
<div className="flex items-start gap-3">
<div className={alert.iconColor}>
{alert.icon}
</div>
<div className="flex-1 min-w-0">
<h4 className={cn('font-medium text-sm', alert.textColor)}>
{alert.title}
</h4>
<p className={cn('text-sm mt-1', alert.textColor)}>
{alert.message}
</p>
</div>
<Button variant="ghost" size="sm" className={cn('p-1', alert.textColor)}>
<X className="w-4 h-4" />
</Button>
</div>
</div>
))}
</div>
);
};
// Loading States Demo
const LoadingStatesDemo = () => {
const loadingStates = [
{
name: 'Spinner',
component: (
<div className="flex items-center justify-center p-8">
<Loader2 className="w-8 h-8 animate-spin text-cyan-500" />
</div>
)
},
{
name: 'Spinner with Text',
component: (
<div className="flex items-center justify-center gap-3 p-8">
<Loader2 className="w-5 h-5 animate-spin text-cyan-500" />
<span className="text-sm text-gray-600 dark:text-gray-400">Loading...</span>
</div>
)
},
{
name: 'Progress Bar',
component: (
<div className="p-8 space-y-3">
<div className="flex justify-between text-sm">
<span>Uploading files...</span>
<span>67%</span>
</div>
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
<div className="bg-cyan-500 h-2 rounded-full transition-all duration-300" style={{ width: '67%' }} />
</div>
</div>
)
},
{
name: 'Skeleton Loading',
component: (
<div className="p-8 space-y-4">
<div className="animate-pulse">
<div className="h-4 bg-gray-300 dark:bg-gray-700 rounded w-3/4 mb-3" />
<div className="h-4 bg-gray-300 dark:bg-gray-700 rounded w-1/2 mb-3" />
<div className="h-4 bg-gray-300 dark:bg-gray-700 rounded w-5/6" />
</div>
</div>
)
},
{
name: 'Pulsing Card',
component: (
<div className="p-8">
<Card className="animate-pulse opacity-60">
<div className="h-20 bg-gray-300 dark:bg-gray-700 rounded" />
</Card>
</div>
)
}
];
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{loadingStates.map((state) => (
<Card key={state.name} className="text-center">
<h4 className="font-medium text-sm mb-2 p-4 pb-0">{state.name}</h4>
{state.component}
</Card>
))}
</div>
);
};
// Toast Notifications Demo
const ToastDemo = () => {
const [toasts, setToasts] = useState<Array<{
id: number;
type: string;
title: string;
message: string;
}>>([]);
const addToast = (type: string) => {
const toastData = {
success: { title: 'Success!', message: 'Action completed successfully.' },
error: { title: 'Error!', message: 'Something went wrong.' },
warning: { title: 'Warning!', message: 'Please review your action.' },
info: { title: 'Info', message: 'Here\'s some useful information.' }
};
const newToast = {
id: Date.now(),
type,
...toastData[type as keyof typeof toastData]
};
setToasts(prev => [...prev, newToast]);
// Auto-remove after 3 seconds
setTimeout(() => {
setToasts(prev => prev.filter(t => t.id !== newToast.id));
}, 3000);
};
const removeToast = (id: number) => {
setToasts(prev => prev.filter(t => t.id !== id));
};
const getToastStyles = (type: string) => {
const styles = {
success: {
bg: glassmorphism.background.card,
border: 'border-green-500',
glow: glassmorphism.shadow.glow.green,
icon: <CheckCircle className="w-5 h-5 text-green-500" />
},
error: {
bg: glassmorphism.background.card,
border: 'border-red-500',
glow: glassmorphism.shadow.glow.red,
icon: <XCircle className="w-5 h-5 text-red-500" />
},
warning: {
bg: glassmorphism.background.card,
border: 'border-orange-500',
glow: glassmorphism.shadow.glow.orange,
icon: <AlertTriangle className="w-5 h-5 text-orange-500" />
},
info: {
bg: glassmorphism.background.card,
border: 'border-blue-500',
glow: glassmorphism.shadow.glow.blue,
icon: <Info className="w-5 h-5 text-blue-500" />
}
};
return styles[type as keyof typeof styles];
};
return (
<div>
{/* Controls */}
<div className="flex gap-2 mb-6">
<Button onClick={() => addToast('success')} size="sm" variant="outline">
Success Toast
</Button>
<Button onClick={() => addToast('error')} size="sm" variant="outline">
Error Toast
</Button>
<Button onClick={() => addToast('warning')} size="sm" variant="outline">
Warning Toast
</Button>
<Button onClick={() => addToast('info')} size="sm" variant="outline">
Info Toast
</Button>
</div>
{/* Toast Container */}
<div className="fixed top-4 right-4 z-50 space-y-2 max-w-sm">
{toasts.map((toast) => {
const styles = getToastStyles(toast.type);
return (
<div
key={toast.id}
className={cn(
'p-4 rounded-lg border backdrop-blur-md animate-in slide-in-from-right',
styles.bg,
styles.border,
styles.glow
)}
>
<div className="flex items-start gap-3">
{styles.icon}
<div className="flex-1 min-w-0">
<h4 className="font-medium text-sm">{toast.title}</h4>
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">
{toast.message}
</p>
</div>
<Button
variant="ghost"
size="sm"
className="p-1"
onClick={() => removeToast(toast.id)}
>
<X className="w-4 h-4" />
</Button>
</div>
</div>
);
})}
</div>
{/* Static Example */}
<Card className="p-4">
<h4 className="font-medium mb-3">Static Toast Example</h4>
<div
className={cn(
'p-4 rounded-lg border backdrop-blur-md max-w-sm',
glassmorphism.background.card,
'border-green-500',
glassmorphism.shadow.glow.green
)}
>
<div className="flex items-start gap-3">
<CheckCircle className="w-5 h-5 text-green-500" />
<div className="flex-1 min-w-0">
<h4 className="font-medium text-sm">Project Created</h4>
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">
Your new project has been created successfully.
</p>
</div>
<Button variant="ghost" size="sm" className="p-1">
<X className="w-4 h-4" />
</Button>
</div>
</div>
</Card>
</div>
);
};
// Empty States Demo
const EmptyStatesDemo = () => {
const emptyStates = [
{
name: 'No Data',
icon: <HelpCircle className="w-12 h-12 mx-auto mb-4 text-gray-400" />,
title: 'No projects found',
description: 'Get started by creating your first project.',
action: <Button size="sm">Create Project</Button>
},
{
name: 'No Search Results',
icon: <AlertCircle className="w-12 h-12 mx-auto mb-4 text-gray-400" />,
title: 'No results found',
description: 'Try adjusting your search criteria or filters.',
action: <Button size="sm" variant="outline">Clear Filters</Button>
},
{
name: 'Coming Soon',
icon: <Info className="w-12 h-12 mx-auto mb-4 text-cyan-500" />,
title: 'Feature coming soon',
description: 'This feature is currently under development.',
action: null
}
];
return (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{emptyStates.map((state) => (
<Card key={state.name} className="text-center p-8">
<h4 className="font-medium text-xs mb-4 text-gray-500 uppercase">
{state.name}
</h4>
{state.icon}
<h3 className="font-semibold text-lg mb-2">{state.title}</h3>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
{state.description}
</p>
{state.action}
</Card>
))}
</div>
);
};
const FEEDBACK_PATTERNS: FeedbackPattern[] = [
{
id: 'alerts',
name: 'Alerts & Messages',
description: 'Contextual feedback messages with different severity levels',
usage: 'Form validation, system notifications, user feedback',
component: AlertsDemo
},
{
id: 'loading',
name: 'Loading States',
description: 'Visual indicators for loading and processing states',
usage: 'Data fetching, file uploads, long-running operations',
component: LoadingStatesDemo
},
{
id: 'toasts',
name: 'Toast Notifications',
description: 'Temporary notifications that appear and disappear automatically',
usage: 'Action confirmations, real-time updates, background processes',
component: ToastDemo
},
{
id: 'empty',
name: 'Empty States',
description: 'Helpful messaging when no content is available',
usage: 'Empty lists, no search results, feature placeholders',
component: EmptyStatesDemo
}
];
const generateCode = (patternId: string) => {
const codeExamples = {
alerts: `import { Card } from '@/features/ui/primitives/card';
import { CheckCircle, XCircle, AlertTriangle, Info, X } from 'lucide-react';
/**
* 🤖 AI CONTEXT: Alert Pattern
*
* PURPOSE: Contextual feedback with appropriate visual treatment
* WHEN TO USE: Form validation, system notifications, important messages
* WHEN NOT TO USE: Temporary messages (use toasts), blocking dialogs (use modals)
*
* SEVERITY LEVELS:
* - Success: Green - Positive outcomes, completions
* - Error: Red - Critical errors, validation failures
* - Warning: Orange - Cautions, potential issues
* - Info: Blue - Neutral information, tips
*
* ACCESSIBILITY:
* - Use semantic colors consistently
* - Include icons for visual clarity
* - Provide clear, actionable text
* - Support dismissal with keyboard
*/
interface AlertProps {
type: 'success' | 'error' | 'warning' | 'info';
title: string;
message: string;
onDismiss?: () => void;
}
export const Alert = ({ type, title, message, onDismiss }: AlertProps) => {
const config = {
success: {
icon: <CheckCircle className="w-5 h-5" />,
bgColor: 'bg-green-50 dark:bg-green-900/20',
borderColor: 'border-green-200 dark:border-green-800',
textColor: 'text-green-800 dark:text-green-200',
iconColor: 'text-green-600 dark:text-green-400'
},
error: {
icon: <XCircle className="w-5 h-5" />,
bgColor: 'bg-red-50 dark:bg-red-900/20',
borderColor: 'border-red-200 dark:border-red-800',
textColor: 'text-red-800 dark:text-red-200',
iconColor: 'text-red-600 dark:text-red-400'
},
warning: {
icon: <AlertTriangle className="w-5 h-5" />,
bgColor: 'bg-orange-50 dark:bg-orange-900/20',
borderColor: 'border-orange-200 dark:border-orange-800',
textColor: 'text-orange-800 dark:text-orange-200',
iconColor: 'text-orange-600 dark:text-orange-400'
},
info: {
icon: <Info className="w-5 h-5" />,
bgColor: 'bg-blue-50 dark:bg-blue-900/20',
borderColor: 'border-blue-200 dark:border-blue-800',
textColor: 'text-blue-800 dark:text-blue-200',
iconColor: 'text-blue-600 dark:text-blue-400'
}
};
const styles = config[type];
return (
<div className={cn(
'p-4 rounded-lg border backdrop-blur-sm',
styles.bgColor,
styles.borderColor
)}>
<div className="flex items-start gap-3">
<div className={styles.iconColor}>
{styles.icon}
</div>
<div className="flex-1 min-w-0">
<h4 className={cn('font-medium text-sm', styles.textColor)}>
{title}
</h4>
<p className={cn('text-sm mt-1', styles.textColor)}>
{message}
</p>
</div>
{onDismiss && (
<button
onClick={onDismiss}
className={cn('p-1 hover:opacity-70', styles.textColor)}
>
<X className="w-4 h-4" />
</button>
)}
</div>
</div>
);
};`,
loading: `import { Loader2 } from 'lucide-react';
import { Card } from '@/features/ui/primitives/card';
/**
* 🤖 AI CONTEXT: Loading States Pattern
*
* PURPOSE: Visual feedback during async operations
* WHEN TO USE: Data fetching, file uploads, processing operations
* WHEN NOT TO USE: Instant operations, already completed actions
*
* LOADING TYPES:
* - Spinner: Quick operations (< 5 seconds)
* - Progress: Trackable operations with known duration
* - Skeleton: Content loading with known structure
* - Pulse: Unknown duration, maintaining layout
*
* UX GUIDELINES:
* - Show immediately for operations > 500ms
* - Provide progress feedback when possible
* - Maintain layout during loading states
* - Include descriptive text for clarity
*/
// Spinner Loading
export const SpinnerLoader = ({ text }: { text?: string }) => (
<div className="flex items-center justify-center gap-3 p-8">
<Loader2 className="w-5 h-5 animate-spin text-cyan-500" />
{text && (
<span className="text-sm text-gray-600 dark:text-gray-400">
{text}
</span>
)}
</div>
);
// Progress Loading
export const ProgressLoader = ({
progress,
text
}: {
progress: number;
text?: string;
}) => (
<div className="p-4 space-y-3">
{text && (
<div className="flex justify-between text-sm">
<span>{text}</span>
<span>{progress}%</span>
</div>
)}
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
<div
className="bg-cyan-500 h-2 rounded-full transition-all duration-300"
style={{ width: \`\${progress}%\` }}
/>
</div>
</div>
);
// Skeleton Loading
export const SkeletonLoader = () => (
<div className="animate-pulse space-y-4">
<div className="h-4 bg-gray-300 dark:bg-gray-700 rounded w-3/4" />
<div className="h-4 bg-gray-300 dark:bg-gray-700 rounded w-1/2" />
<div className="h-4 bg-gray-300 dark:bg-gray-700 rounded w-5/6" />
</div>
);
// Card Loading
export const CardLoader = () => (
<Card className="animate-pulse opacity-60">
<div className="h-20 bg-gray-300 dark:bg-gray-700 rounded" />
</Card>
);`,
toasts: `import { useState, useEffect } from 'react';
import { Card } from '@/features/ui/primitives/card';
import { glassmorphism } from '@/features/ui/primitives/styles';
import { CheckCircle, XCircle, AlertTriangle, Info, X } from 'lucide-react';
/**
* 🤖 AI CONTEXT: Toast Notifications Pattern
*
* PURPOSE: Temporary, non-blocking feedback messages
* WHEN TO USE: Action confirmations, background updates, quick feedback
* WHEN NOT TO USE: Critical errors (use alerts), complex messages (use modals)
*
* TOAST CHARACTERISTICS:
* - Auto-dismiss after 3-5 seconds
* - Non-blocking, can be dismissed manually
* - Stack vertically in corner of screen
* - Use glassmorphism for modern feel
*
* POSITIONING:
* - Top-right: Most common, non-intrusive
* - Top-center: Important announcements
* - Bottom-right: Mobile-friendly alternative
*/
interface Toast {
id: string;
type: 'success' | 'error' | 'warning' | 'info';
title: string;
message: string;
duration?: number;
}
export const useToast = () => {
const [toasts, setToasts] = useState<Toast[]>([]);
const addToast = (toast: Omit<Toast, 'id'>) => {
const id = Date.now().toString();
const newToast = { ...toast, id };
setToasts(prev => [...prev, newToast]);
// Auto-remove
setTimeout(() => {
removeToast(id);
}, toast.duration || 4000);
return id;
};
const removeToast = (id: string) => {
setToasts(prev => prev.filter(t => t.id !== id));
};
return { toasts, addToast, removeToast };
};
export const ToastContainer = ({
toasts,
onRemove
}: {
toasts: Toast[];
onRemove: (id: string) => void;
}) => {
const getToastStyles = (type: string) => {
const styles = {
success: {
border: 'border-green-500',
glow: glassmorphism.shadow.glow.green,
icon: <CheckCircle className="w-5 h-5 text-green-500" />
},
error: {
border: 'border-red-500',
glow: glassmorphism.shadow.glow.red,
icon: <XCircle className="w-5 h-5 text-red-500" />
},
warning: {
border: 'border-orange-500',
glow: glassmorphism.shadow.glow.orange,
icon: <AlertTriangle className="w-5 h-5 text-orange-500" />
},
info: {
border: 'border-blue-500',
glow: glassmorphism.shadow.glow.blue,
icon: <Info className="w-5 h-5 text-blue-500" />
}
};
return styles[type as keyof typeof styles];
};
return (
<div className="fixed top-4 right-4 z-50 space-y-2 max-w-sm">
{toasts.map((toast) => {
const styles = getToastStyles(toast.type);
return (
<div
key={toast.id}
className={cn(
'p-4 rounded-lg border backdrop-blur-md',
'animate-in slide-in-from-right duration-300',
glassmorphism.background.card,
styles.border,
styles.glow
)}
>
<div className="flex items-start gap-3">
{styles.icon}
<div className="flex-1 min-w-0">
<h4 className="font-medium text-sm">{toast.title}</h4>
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">
{toast.message}
</p>
</div>
<button
onClick={() => onRemove(toast.id)}
className="p-1 hover:opacity-70"
>
<X className="w-4 h-4" />
</button>
</div>
</div>
);
})}
</div>
);
};`,
empty: `import { Card } from '@/features/ui/primitives/card';
import { Button } from '@/features/ui/primitives/button';
import { HelpCircle, AlertCircle, Info } from 'lucide-react';
/**
* 🤖 AI CONTEXT: Empty States Pattern
*
* PURPOSE: Helpful guidance when no content is available
* WHEN TO USE: Empty lists, no search results, disabled features
* WHEN NOT TO USE: Loading states, error conditions, temporary states
*
* EMPTY STATE TYPES:
* - No Data: First-time use, empty collections
* - No Results: Search/filter yielded nothing
* - Feature Disabled: Functionality not available
* - Coming Soon: Planned but not implemented
*
* CONTENT GUIDELINES:
* - Clear, descriptive title
* - Helpful explanation or next steps
* - Primary action when applicable
* - Appropriate icon for context
*/
interface EmptyStateProps {
icon: React.ReactNode;
title: string;
description: string;
action?: React.ReactNode;
className?: string;
}
export const EmptyState = ({
icon,
title,
description,
action,
className
}: EmptyStateProps) => (
<Card className={cn('text-center p-12', className)}>
<div className="mx-auto mb-4">
{icon}
</div>
<h3 className="font-semibold text-lg mb-2">{title}</h3>
<p className="text-gray-600 dark:text-gray-400 mb-6 max-w-md mx-auto">
{description}
</p>
{action}
</Card>
);
// Predefined empty states
export const NoDataState = ({
onAction
}: {
onAction?: () => void;
}) => (
<EmptyState
icon={<HelpCircle className="w-16 h-16 text-gray-400" />}
title="No projects found"
description="Get started by creating your first project. You can organize your work and collaborate with your team."
action={
onAction && (
<Button onClick={onAction}>
Create Your First Project
</Button>
)
}
/>
);
export const NoSearchResultsState = ({
onClearFilters
}: {
onClearFilters?: () => void;
}) => (
<EmptyState
icon={<AlertCircle className="w-16 h-16 text-gray-400" />}
title="No results found"
description="We couldn't find any projects matching your search criteria. Try adjusting your filters or search terms."
action={
onClearFilters && (
<Button variant="outline" onClick={onClearFilters}>
Clear All Filters
</Button>
)
}
/>
);
export const ComingSoonState = ({ feature }: { feature: string }) => (
<EmptyState
icon={<Info className="w-16 h-16 text-cyan-500" />}
title={\`\${feature} coming soon\`}
description="This feature is currently under development. We'll notify you when it becomes available."
/>
);`
};
return codeExamples[patternId as keyof typeof codeExamples] || '';
};
export const FeedbackPattern = () => {
const [selectedPattern, setSelectedPattern] = useState<string>('alerts');
const currentPattern = FEEDBACK_PATTERNS.find(p => p.id === selectedPattern);
return (
<div className="space-y-8">
<div>
<h2 className="text-2xl font-bold mb-4">Feedback Patterns</h2>
<p className="text-gray-600 dark:text-gray-400 mb-6">
User feedback patterns including alerts, loading states, notifications, and empty states.
</p>
</div>
{/* Pattern Selector */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Select Feedback Pattern</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{FEEDBACK_PATTERNS.map((pattern) => (
<Card
key={pattern.id}
className={cn(
"p-4 cursor-pointer transition-all duration-200",
selectedPattern === pattern.id
? "border-cyan-500 shadow-[0_0_10px_rgba(34,211,238,0.3)]"
: "hover:shadow-lg"
)}
onClick={() => setSelectedPattern(pattern.id)}
>
<h4 className="font-medium mb-2">{pattern.name}</h4>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-2">
{pattern.description}
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
<strong>Use for:</strong> {pattern.usage}
</p>
</Card>
))}
</div>
</Card>
{/* Live Preview */}
{currentPattern && (
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">
{currentPattern.name} - Examples
</h3>
<currentPattern.component />
</Card>
)}
{/* Generated Code */}
{currentPattern && (
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Generated Code</h3>
<CodeDisplay
code={generateCode(selectedPattern)}
showLineNumbers
/>
</Card>
)}
</div>
);
};

View File

@@ -0,0 +1,743 @@
import { useState } from 'react';
import { Card } from '@/features/ui/primitives/card';
import type { GlowColor } from '../types';
import { Button } from '@/features/ui/primitives/button';
import { CodeDisplay } from '../shared/CodeDisplay';
import { LivePreview } from '../shared/LivePreview';
import { cn, glassmorphism } from '@/features/ui/primitives/styles';
import {
LayoutGrid,
Sidebar,
PlusCircle,
ChevronLeft,
ChevronRight,
MoreVertical
} from 'lucide-react';
interface LayoutTemplate {
id: string;
name: string;
description: string;
usage: string;
component: React.ComponentType;
}
// Dashboard Template Component
const DashboardTemplate = () => {
const cards = [
{ title: 'Total Projects', value: '12', change: '+2 this week', color: 'purple' },
{ title: 'Active Tasks', value: '38', change: '+5 today', color: 'blue' },
{ title: 'Completed', value: '245', change: '+12 this week', color: 'green' },
{ title: 'Team Members', value: '8', change: 'No change', color: 'orange' }
];
return (
<div className="p-4 space-y-4">
{/* Header */}
<div className="flex justify-between items-center">
<h2 className="text-xl font-semibold">Dashboard</h2>
<Button size="sm" variant="outline">
<PlusCircle className="w-4 h-4 mr-2" />
New Project
</Button>
</div>
{/* Stats Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{cards.map((card) => (
<Card
key={card.title}
glowColor={card.color as GlowColor}
className="p-4 hover:shadow-lg transition-shadow"
>
<h3 className="text-sm font-medium text-gray-600 dark:text-gray-400">
{card.title}
</h3>
<p className="text-2xl font-bold mt-1">{card.value}</p>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
{card.change}
</p>
</Card>
))}
</div>
{/* Content Grid */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
<Card className="lg:col-span-2 p-4">
<h3 className="font-semibold mb-3">Recent Activity</h3>
<div className="space-y-2">
{['Project Alpha updated', 'New task assigned', 'Beta milestone completed'].map((activity, i) => (
<div key={i} className="text-sm py-2 border-b border-gray-200 dark:border-gray-700 last:border-b-0">
{activity}
</div>
))}
</div>
</Card>
<Card className="p-4">
<h3 className="font-semibold mb-3">Quick Actions</h3>
<div className="space-y-2">
<Button variant="outline" size="sm" className="w-full justify-start">
Create Task
</Button>
<Button variant="outline" size="sm" className="w-full justify-start">
Add Member
</Button>
<Button variant="outline" size="sm" className="w-full justify-start">
View Reports
</Button>
</div>
</Card>
</div>
</div>
);
};
// Sidebar Template Component
const SidebarTemplate = () => {
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const [sidebarPosition, setSidebarPosition] = useState<'top' | 'left'>('left');
const items = [
{ id: '1', title: 'Project Alpha', status: 'active', color: 'purple' },
{ id: '2', title: 'Project Beta', status: 'pending', color: 'blue' },
{ id: '3', title: 'Project Gamma', status: 'completed', color: 'green' },
];
return (
<div className="h-64 flex flex-col">
{/* Toggle Controls */}
<div className="flex justify-between items-center p-2 border-b border-gray-200 dark:border-gray-700">
<Button
variant="outline"
size="sm"
onClick={() => setSidebarPosition(p => p === 'top' ? 'left' : 'top')}
>
{sidebarPosition === 'top' ? <Sidebar className="w-4 h-4 mr-2" /> : <LayoutGrid className="w-4 h-4 mr-2" />}
{sidebarPosition === 'top' ? 'Switch to Sidebar' : 'Switch to Top'}
</Button>
{sidebarPosition === 'left' && (
<Button
variant="ghost"
size="sm"
onClick={() => setSidebarCollapsed(!sidebarCollapsed)}
>
{sidebarCollapsed ? <ChevronRight className="w-4 h-4" /> : <ChevronLeft className="w-4 h-4" />}
</Button>
)}
</div>
<div className={cn(
"flex flex-1",
sidebarPosition === 'top' ? "flex-col" : "flex-row",
"gap-4 p-4"
)}>
{/* Sidebar */}
<div className={cn(
sidebarPosition === 'left' && !sidebarCollapsed && "w-64",
sidebarPosition === 'left' && sidebarCollapsed && "w-16",
sidebarPosition === 'top' && "w-full",
"transition-all duration-300"
)}>
{sidebarPosition === 'top' ? (
<div className="grid grid-cols-3 gap-4">
{items.map(item => (
<Card
key={item.id}
glowColor={item.color as GlowColor}
className="p-3 hover:shadow-lg transition-shadow cursor-pointer"
>
<h4 className="font-medium text-sm">{item.title}</h4>
<p className="text-xs text-gray-500 dark:text-gray-400">{item.status}</p>
</Card>
))}
</div>
) : (
<div className="space-y-2">
{items.map(item => (
<Card
key={item.id}
className={cn(
"p-3 hover:shadow-lg transition-shadow cursor-pointer",
sidebarCollapsed && "p-2"
)}
>
{!sidebarCollapsed ? (
<>
<h4 className="font-medium text-sm">{item.title}</h4>
<p className="text-xs text-gray-500 dark:text-gray-400">{item.status}</p>
</>
) : (
<div className="w-8 h-8 rounded bg-purple-500/20 flex items-center justify-center text-xs font-medium">
{item.title[0]}
</div>
)}
</Card>
))}
</div>
)}
</div>
{/* Main Content */}
<div className="flex-1">
<Card className="h-full p-6 border-2 border-dashed border-gray-300 dark:border-gray-600">
<div className="text-center text-gray-500 dark:text-gray-400">
<h3 className="font-medium mb-2">Main Content Area</h3>
<p className="text-sm">Your application content goes here</p>
<p className="text-xs mt-2">
Sidebar: {sidebarPosition === 'top' ? 'Top' : `Left (${sidebarCollapsed ? 'Collapsed' : 'Expanded'})`}
</p>
</div>
</Card>
</div>
</div>
</div>
);
};
// Table View Template Component
const TableViewTemplate = () => {
const data = [
{ id: 1, name: 'Project Alpha', status: 'Active', priority: 'High', progress: 75 },
{ id: 2, name: 'Project Beta', status: 'Pending', priority: 'Medium', progress: 25 },
{ id: 3, name: 'Project Gamma', status: 'Completed', priority: 'Low', progress: 100 },
];
const getStatusColor = (status: string) => {
switch (status.toLowerCase()) {
case 'active': return 'text-green-600 dark:text-green-400';
case 'pending': return 'text-orange-600 dark:text-orange-400';
case 'completed': return 'text-blue-600 dark:text-blue-400';
default: return 'text-gray-600 dark:text-gray-400';
}
};
return (
<div className="p-4 space-y-4">
{/* Header */}
<div className="flex justify-between items-center">
<h2 className="text-xl font-semibold">Projects Table</h2>
<div className="flex gap-2">
<Button variant="outline" size="sm">Filter</Button>
<Button size="sm">
<PlusCircle className="w-4 h-4 mr-2" />
Add Project
</Button>
</div>
</div>
{/* Table */}
<Card className="overflow-hidden">
<div className="overflow-x-auto">
<table className="w-full">
<thead className="bg-gray-50 dark:bg-gray-800/50">
<tr>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">
Name
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">
Status
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">
Priority
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">
Progress
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">
Actions
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
{data.map((row) => (
<tr key={row.id} className="hover:bg-gray-50 dark:hover:bg-gray-800/50">
<td className="px-4 py-3 text-sm font-medium">{row.name}</td>
<td className="px-4 py-3">
<span className={cn('text-sm font-medium', getStatusColor(row.status))}>
{row.status}
</span>
</td>
<td className="px-4 py-3 text-sm">{row.priority}</td>
<td className="px-4 py-3">
<div className="flex items-center gap-2">
<div className="flex-1 bg-gray-200 dark:bg-gray-700 rounded-full h-2">
<div
className="bg-cyan-500 h-2 rounded-full transition-all"
style={{ width: `${row.progress}%` }}
/>
</div>
<span className="text-xs text-gray-500 dark:text-gray-400 w-8">
{row.progress}%
</span>
</div>
</td>
<td className="px-4 py-3">
<Button variant="ghost" size="sm">
<MoreVertical className="w-4 h-4" />
</Button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</Card>
</div>
);
};
// Centered Form Template Component
const CenteredFormTemplate = () => {
return (
<div className="min-h-64 flex items-center justify-center p-8">
<Card size="lg" glowColor="purple" className="w-full max-w-md">
<div className="text-center mb-6">
<h2 className="text-2xl font-bold mb-2">Create Project</h2>
<p className="text-gray-600 dark:text-gray-400">
Enter the details for your new project
</p>
</div>
<form className="space-y-4">
<div>
<label className="block text-sm font-medium mb-2">Project Name</label>
<input
type="text"
className={cn(
"w-full px-3 py-2 rounded-md text-sm",
glassmorphism.background.subtle,
glassmorphism.border.default,
glassmorphism.border.focus,
"transition-all duration-200"
)}
placeholder="Enter project name"
/>
</div>
<div>
<label className="block text-sm font-medium mb-2">Description</label>
<textarea
className={cn(
"w-full px-3 py-2 rounded-md text-sm h-20 resize-none",
glassmorphism.background.subtle,
glassmorphism.border.default,
glassmorphism.border.focus,
"transition-all duration-200"
)}
placeholder="Describe your project"
/>
</div>
<div>
<label className="block text-sm font-medium mb-2">Priority</label>
<select
className={cn(
"w-full px-3 py-2 rounded-md text-sm",
glassmorphism.background.subtle,
glassmorphism.border.default,
glassmorphism.border.focus,
"transition-all duration-200"
)}
>
<option>High</option>
<option>Medium</option>
<option>Low</option>
</select>
</div>
<div className="flex gap-3 pt-4">
<Button variant="outline" className="flex-1">
Cancel
</Button>
<Button className="flex-1">
Create Project
</Button>
</div>
</form>
</Card>
</div>
);
};
const LAYOUT_TEMPLATES: LayoutTemplate[] = [
{
id: 'dashboard',
name: 'Dashboard Layout',
description: 'Grid-based dashboard with stats cards and content sections',
usage: 'Admin panels, analytics dashboards, overview pages',
component: DashboardTemplate
},
{
id: 'sidebar',
name: 'Sidebar Layout',
description: 'Collapsible sidebar with main content area (like ProjectsView)',
usage: 'Applications with navigation, project management, file browsers',
component: SidebarTemplate
},
{
id: 'table',
name: 'Table View Layout',
description: 'Data table with actions and filters',
usage: 'Data management, lists, admin interfaces',
component: TableViewTemplate
},
{
id: 'form',
name: 'Centered Form Layout',
description: 'Single focus form with centered alignment',
usage: 'Login forms, creation dialogs, focused input workflows',
component: CenteredFormTemplate
}
];
const generateCode = (templateId: string) => {
const template = LAYOUT_TEMPLATES.find(t => t.id === templateId);
if (!template) return '';
const codeExamples = {
dashboard: `import { Card } from '@/features/ui/primitives/card';
import { Button } from '@/features/ui/primitives/button';
/**
* 🤖 AI CONTEXT: Dashboard Layout Pattern
*
* PURPOSE: Grid-based layout for displaying metrics and overview data
* WHEN TO USE: Admin dashboards, analytics views, KPI displays
* WHEN NOT TO USE: Forms, detailed content pages, navigation-heavy interfaces
*
* STRUCTURE:
* - Header with title and primary action
* - Stats cards in responsive grid (1->2->4 columns)
* - Content sections in asymmetric grid (2:1 ratio typical)
*
* RESPONSIVE BEHAVIOR:
* - Mobile: Single column stack
* - Tablet: 2-column stats, stacked content
* - Desktop: 4-column stats, side-by-side content
*/
export const DashboardLayout = () => {
return (
<div className="p-4 space-y-4">
{/* Header Section */}
<div className="flex justify-between items-center">
<h1 className="text-2xl font-bold">Dashboard</h1>
<Button>Primary Action</Button>
</div>
{/* Stats Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{statsData.map((stat) => (
<Card key={stat.id} glowColor={stat.color} className="p-4">
<h3 className="text-sm font-medium text-gray-600 dark:text-gray-400">
{stat.title}
</h3>
<p className="text-2xl font-bold mt-1">{stat.value}</p>
<p className="text-xs text-gray-500 mt-1">{stat.change}</p>
</Card>
))}
</div>
{/* Content Grid */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
<Card className="lg:col-span-2 p-4">
{/* Main content area */}
</Card>
<Card className="p-4">
{/* Sidebar content */}
</Card>
</div>
</div>
);
};`,
sidebar: `import { useState } from 'react';
import { Card } from '@/features/ui/primitives/card';
import { Button } from '@/features/ui/primitives/button';
import { ChevronLeft, ChevronRight } from 'lucide-react';
/**
* 🤖 AI CONTEXT: Sidebar Layout Pattern
*
* PURPOSE: Navigation-focused layout with collapsible sidebar
* WHEN TO USE: File browsers, project management, settings pages
* WHEN NOT TO USE: Simple content pages, forms, dashboards
*
* FEATURES:
* - Collapsible sidebar (280px -> 60px)
* - Responsive behavior (sidebar -> top nav on mobile)
* - Smooth transitions and state preservation
* - Keyboard navigation support
*
* ACCESSIBILITY:
* - ARIA labels for collapse state
* - Keyboard shortcuts (Ctrl+\\)
* - Focus management on state change
*/
export const SidebarLayout = () => {
const [collapsed, setCollapsed] = useState(false);
return (
<div className="flex h-full">
{/* Sidebar */}
<div className={cn(
"transition-all duration-300",
collapsed ? "w-16" : "w-64"
)}>
<Card className="h-full p-4 rounded-none border-r">
<Button
variant="ghost"
size="sm"
onClick={() => setCollapsed(!collapsed)}
className="mb-4"
>
{collapsed ? <ChevronRight /> : <ChevronLeft />}
</Button>
{/* Sidebar content */}
<nav className="space-y-2">
{navigationItems.map((item) => (
<Card
key={item.id}
className={cn(
"p-3 cursor-pointer transition-all",
collapsed && "p-2"
)}
>
{collapsed ? (
<div className="w-8 h-8 flex items-center justify-center">
{item.icon}
</div>
) : (
<>
<div className="flex items-center gap-3">
{item.icon}
<span>{item.label}</span>
</div>
</>
)}
</Card>
))}
</nav>
</Card>
</div>
{/* Main Content */}
<div className="flex-1 p-6">
{/* Your main content */}
</div>
</div>
);
};`,
table: `import { Card } from '@/features/ui/primitives/card';
import { Button } from '@/features/ui/primitives/button';
/**
* 🤖 AI CONTEXT: Table View Layout Pattern
*
* PURPOSE: Data-heavy interface with filtering and actions
* WHEN TO USE: Admin interfaces, data management, list views
* WHEN NOT TO USE: Dashboards, forms, content-focused pages
*
* FEATURES:
* - Responsive table with horizontal scroll
* - Action buttons in header and rows
* - Sortable columns and filtering
* - Pagination support
*
* RESPONSIVE BEHAVIOR:
* - Desktop: Full table display
* - Tablet: Horizontal scroll for wide tables
* - Mobile: Card-based list view (transform table to cards)
*/
export const TableViewLayout = () => {
return (
<div className="p-4 space-y-4">
{/* Header with Actions */}
<div className="flex justify-between items-center">
<h1 className="text-2xl font-bold">Data Table</h1>
<div className="flex gap-2">
<Button variant="outline">Filter</Button>
<Button>Add Item</Button>
</div>
</div>
{/* Table Container */}
<Card className="overflow-hidden">
<div className="overflow-x-auto">
<table className="w-full">
<thead className="bg-gray-50 dark:bg-gray-800/50">
<tr>
{columns.map((column) => (
<th
key={column.key}
className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase"
>
{column.label}
</th>
))}
</tr>
</thead>
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
{data.map((row) => (
<tr key={row.id} className="hover:bg-gray-50 dark:hover:bg-gray-800/50">
{columns.map((column) => (
<td key={column.key} className="px-4 py-3 text-sm">
{row[column.key]}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
</Card>
</div>
);
};`,
form: `import { Card } from '@/features/ui/primitives/card';
import { Button } from '@/features/ui/primitives/button';
import { glassmorphism, cn } from '@/features/ui/primitives/styles';
/**
* 🤖 AI CONTEXT: Centered Form Layout Pattern
*
* PURPOSE: Single-focus form interface with minimal distractions
* WHEN TO USE: Login/signup, creation modals, settings forms
* WHEN NOT TO USE: Complex multi-step forms, data entry workflows
*
* FEATURES:
* - Centered card with appropriate max-width
* - Glassmorphism form controls
* - Clear visual hierarchy
* - Consistent spacing and typography
*
* FORM VALIDATION:
* - Real-time validation feedback
* - Error states with glassmorphism styling
* - Success states with appropriate colors
* - Accessibility-compliant error messages
*/
export const CenteredFormLayout = () => {
return (
<div className="min-h-screen flex items-center justify-center p-8">
<Card size="lg" glowColor="purple" className="w-full max-w-md">
<div className="text-center mb-6">
<h2 className="text-2xl font-bold mb-2">Form Title</h2>
<p className="text-gray-600 dark:text-gray-400">
Brief description of the form purpose
</p>
</div>
<form className="space-y-4">
{formFields.map((field) => (
<div key={field.name}>
<label className="block text-sm font-medium mb-2">
{field.label}
</label>
<input
type={field.type}
className={cn(
"w-full px-3 py-2 rounded-md text-sm",
glassmorphism.background.subtle,
glassmorphism.border.default,
glassmorphism.border.focus,
"transition-all duration-200"
)}
placeholder={field.placeholder}
/>
</div>
))}
<div className="flex gap-3 pt-4">
<Button variant="outline" className="flex-1">
Cancel
</Button>
<Button className="flex-1">
Submit
</Button>
</div>
</form>
</Card>
</div>
);
};`
};
return codeExamples[templateId as keyof typeof codeExamples] || '';
};
export const LayoutsPattern = () => {
const [selectedTemplate, setSelectedTemplate] = useState<string>('dashboard');
const currentTemplate = LAYOUT_TEMPLATES.find(t => t.id === selectedTemplate);
return (
<div className="space-y-8">
<div>
<h2 className="text-2xl font-bold mb-4">Layout Patterns</h2>
<p className="text-gray-600 dark:text-gray-400 mb-6">
Reusable layout templates for common application structures and workflows.
</p>
</div>
{/* Template Selector */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Select Layout Template</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{LAYOUT_TEMPLATES.map((template) => (
<Card
key={template.id}
className={cn(
"p-4 cursor-pointer transition-all duration-200",
selectedTemplate === template.id
? "border-cyan-500 shadow-[0_0_10px_rgba(34,211,238,0.3)]"
: "hover:shadow-lg"
)}
onClick={() => setSelectedTemplate(template.id)}
>
<h4 className="font-medium mb-2">{template.name}</h4>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-2">
{template.description}
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
<strong>Use for:</strong> {template.usage}
</p>
</Card>
))}
</div>
</Card>
{/* Live Preview */}
{currentTemplate && (
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">
{currentTemplate.name} - Live Preview
</h3>
<LivePreview minHeight="320px">
<div className="w-full max-w-4xl">
<currentTemplate.component />
</div>
</LivePreview>
</Card>
)}
{/* Generated Code */}
{currentTemplate && (
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Generated Code</h3>
<CodeDisplay
code={generateCode(selectedTemplate)}
showLineNumbers
/>
</Card>
)}
</div>
);
};

View File

@@ -0,0 +1,866 @@
import React, { useState } from 'react';
import { Card } from '@/features/ui/primitives/card';
import { Button } from '@/features/ui/primitives/button';
import { CodeDisplay } from '../shared/CodeDisplay';
import { cn, glassmorphism } from '@/features/ui/primitives/styles';
import {
Home,
Settings,
User,
FileText,
BarChart,
Menu,
ChevronDown,
ChevronRight,
Search,
Bell
} from 'lucide-react';
interface NavigationPattern {
id: string;
name: string;
description: string;
usage: string;
component: React.ComponentType;
}
// Pill Navigation Demo
const PillNavigationDemo = () => {
const [activeTab, setActiveTab] = useState('dashboard');
const tabs = [
{ id: 'dashboard', label: 'Dashboard', icon: <Home className="w-4 h-4" /> },
{ id: 'projects', label: 'Projects', icon: <FileText className="w-4 h-4" /> },
{ id: 'analytics', label: 'Analytics', icon: <BarChart className="w-4 h-4" /> },
{ id: 'settings', label: 'Settings', icon: <Settings className="w-4 h-4" /> }
];
return (
<div className="space-y-6">
{/* Standard Pill Navigation */}
<div>
<h4 className="font-medium mb-3">Standard Pills</h4>
<div className="backdrop-blur-sm bg-white/40 dark:bg-white/5 border border-white/30 dark:border-white/15 rounded-full p-1 shadow-lg inline-flex">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={cn(
"flex items-center gap-2 px-4 py-2 rounded-full transition-all duration-200 text-sm font-medium",
activeTab === tab.id
? "text-white border border-cyan-400 shadow-[0_0_8px_rgba(34,211,238,0.3)] dark:shadow-[0_0_10px_rgba(34,211,238,0.5)]"
: "text-gray-700 dark:text-gray-300 hover:bg-white/20 dark:hover:bg-white/5"
)}
>
{tab.icon}
{tab.label}
</button>
))}
</div>
</div>
{/* Icon-only Pills */}
<div>
<h4 className="font-medium mb-3">Icon Pills</h4>
<div className="backdrop-blur-sm bg-white/40 dark:bg-white/5 border border-white/30 dark:border-white/15 rounded-full p-1 shadow-lg inline-flex">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={cn(
"p-3 rounded-full transition-all duration-200",
activeTab === tab.id
? "text-white border border-cyan-400 shadow-[0_0_8px_rgba(34,211,238,0.3)]"
: "text-gray-700 dark:text-gray-300 hover:bg-white/20 dark:hover:bg-white/5"
)}
title={tab.label}
>
{tab.icon}
</button>
))}
</div>
</div>
{/* Vertical Pills */}
<div>
<h4 className="font-medium mb-3">Vertical Pills</h4>
<div className="backdrop-blur-sm bg-white/40 dark:bg-white/5 border border-white/30 dark:border-white/15 rounded-2xl p-1 shadow-lg inline-flex flex-col">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={cn(
"flex items-center gap-2 px-4 py-2 rounded-xl transition-all duration-200 text-sm font-medium min-w-[120px]",
activeTab === tab.id
? "text-white border border-cyan-400 shadow-[0_0_8px_rgba(34,211,238,0.3)]"
: "text-gray-700 dark:text-gray-300 hover:bg-white/20 dark:hover:bg-white/5"
)}
>
{tab.icon}
{tab.label}
</button>
))}
</div>
</div>
</div>
);
};
// Sidebar Navigation Demo
const SidebarNavigationDemo = () => {
const [collapsed, setCollapsed] = useState(false);
const [activeItem, setActiveItem] = useState('dashboard');
const navItems = [
{
id: 'dashboard',
label: 'Dashboard',
icon: <Home className="w-5 h-5" />,
hasSubmenu: false
},
{
id: 'projects',
label: 'Projects',
icon: <FileText className="w-5 h-5" />,
hasSubmenu: true,
submenu: ['All Projects', 'Active', 'Archived']
},
{
id: 'analytics',
label: 'Analytics',
icon: <BarChart className="w-5 h-5" />,
hasSubmenu: false
},
{
id: 'team',
label: 'Team',
icon: <User className="w-5 h-5" />,
hasSubmenu: true,
submenu: ['Members', 'Roles', 'Permissions']
},
{
id: 'settings',
label: 'Settings',
icon: <Settings className="w-5 h-5" />,
hasSubmenu: false
}
];
const [expandedItems, setExpandedItems] = useState<string[]>([]);
const toggleExpanded = (itemId: string) => {
setExpandedItems(prev =>
prev.includes(itemId)
? prev.filter(id => id !== itemId)
: [...prev, itemId]
);
};
return (
<div className="h-96 flex">
{/* Sidebar */}
<Card className={cn(
"h-full transition-all duration-300 rounded-r-none border-r-0",
collapsed ? "w-16" : "w-64"
)}>
<div className="p-4">
{/* Toggle Button */}
<Button
variant="ghost"
size="sm"
onClick={() => setCollapsed(!collapsed)}
className="mb-4 w-full justify-start"
>
<Menu className="w-4 h-4" />
{!collapsed && <span className="ml-2">Menu</span>}
</Button>
{/* Navigation Items */}
<nav className="space-y-1">
{navItems.map((item) => (
<div key={item.id}>
<button
onClick={() => {
setActiveItem(item.id);
if (item.hasSubmenu && !collapsed) {
toggleExpanded(item.id);
}
}}
className={cn(
"w-full flex items-center gap-3 px-3 py-2 rounded-lg transition-all duration-200 text-left",
activeItem === item.id
? cn(glassmorphism.background.cyan, "text-cyan-700 dark:text-cyan-300")
: "hover:bg-gray-100 dark:hover:bg-gray-800"
)}
>
{item.icon}
{!collapsed && (
<>
<span className="flex-1 text-sm font-medium">
{item.label}
</span>
{item.hasSubmenu && (
<ChevronDown
className={cn(
"w-4 h-4 transition-transform",
expandedItems.includes(item.id) && "rotate-180"
)}
/>
)}
</>
)}
</button>
{/* Submenu */}
{item.hasSubmenu && !collapsed && expandedItems.includes(item.id) && (
<div className="ml-8 mt-1 space-y-1">
{item.submenu?.map((subItem) => (
<button
key={subItem}
className="w-full text-left px-3 py-1 text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded"
>
{subItem}
</button>
))}
</div>
)}
</div>
))}
</nav>
</div>
</Card>
{/* Main Content */}
<Card className="flex-1 rounded-l-none border-l-0 p-6">
<div className="text-center text-gray-500 dark:text-gray-400">
<h3 className="font-medium mb-2">Main Content</h3>
<p className="text-sm">Active page: {activeItem}</p>
<p className="text-xs mt-2">Sidebar: {collapsed ? 'Collapsed' : 'Expanded'}</p>
</div>
</Card>
</div>
);
};
// Top Navigation Demo
const TopNavigationDemo = () => {
const [activeNav, setActiveNav] = useState('dashboard');
const navItems = [
{ id: 'dashboard', label: 'Dashboard' },
{ id: 'projects', label: 'Projects' },
{ id: 'team', label: 'Team' },
{ id: 'analytics', label: 'Analytics' }
];
return (
<div className="space-y-4">
{/* Top Navigation Bar */}
<Card className="p-4">
<div className="flex items-center justify-between">
{/* Logo/Brand */}
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-gradient-to-br from-cyan-500 to-purple-500 rounded-lg" />
<span className="font-bold text-lg">Archon</span>
</div>
{/* Navigation Links */}
<nav className="flex items-center gap-1">
{navItems.map((item) => (
<button
key={item.id}
onClick={() => setActiveNav(item.id)}
className={cn(
"px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200",
activeNav === item.id
? cn(glassmorphism.background.cyan, "text-cyan-700 dark:text-cyan-300")
: "text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-800"
)}
>
{item.label}
</button>
))}
</nav>
{/* User Actions */}
<div className="flex items-center gap-2">
<Button variant="ghost" size="sm">
<Search className="w-4 h-4" />
</Button>
<Button variant="ghost" size="sm">
<Bell className="w-4 h-4" />
</Button>
<Button variant="ghost" size="sm">
<User className="w-4 h-4" />
</Button>
</div>
</div>
</Card>
{/* Content Area */}
<Card className="h-48 p-6">
<div className="text-center text-gray-500 dark:text-gray-400">
<h3 className="font-medium mb-2">Page Content</h3>
<p className="text-sm">Currently viewing: {activeNav}</p>
</div>
</Card>
</div>
);
};
// Breadcrumb Navigation Demo
const BreadcrumbDemo = () => {
const breadcrumbs = [
{ label: 'Home', href: '/' },
{ label: 'Projects', href: '/projects' },
{ label: 'Project Alpha', href: '/projects/alpha' },
{ label: 'Settings', href: '/projects/alpha/settings' }
];
return (
<div className="space-y-6">
{/* Standard Breadcrumbs */}
<div>
<h4 className="font-medium mb-3">Standard Breadcrumbs</h4>
<nav className="flex items-center space-x-2 text-sm">
{breadcrumbs.map((crumb, index) => (
<React.Fragment key={crumb.href}>
<button
className={cn(
"hover:text-cyan-600 dark:hover:text-cyan-400 transition-colors",
index === breadcrumbs.length - 1
? "text-gray-900 dark:text-gray-100 font-medium"
: "text-gray-500 dark:text-gray-400"
)}
>
{crumb.label}
</button>
{index < breadcrumbs.length - 1 && (
<ChevronRight className="w-4 h-4 text-gray-400" />
)}
</React.Fragment>
))}
</nav>
</div>
{/* Pill Breadcrumbs */}
<div>
<h4 className="font-medium mb-3">Pill Breadcrumbs</h4>
<nav className="flex items-center space-x-1">
{breadcrumbs.map((crumb, index) => (
<React.Fragment key={crumb.href}>
<button
className={cn(
"px-3 py-1 rounded-full text-sm transition-all duration-200",
index === breadcrumbs.length - 1
? cn(glassmorphism.background.cyan, "text-cyan-700 dark:text-cyan-300 font-medium")
: "text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800"
)}
>
{crumb.label}
</button>
{index < breadcrumbs.length - 1 && (
<ChevronRight className="w-4 h-4 text-gray-400 mx-1" />
)}
</React.Fragment>
))}
</nav>
</div>
{/* Compact Breadcrumbs */}
<div>
<h4 className="font-medium mb-3">Compact (Mobile)</h4>
<nav className="flex items-center space-x-2 text-sm">
<button className="text-gray-500 dark:text-gray-400 hover:text-cyan-600 dark:hover:text-cyan-400">
...
</button>
<ChevronRight className="w-4 h-4 text-gray-400" />
<button className="text-gray-500 dark:text-gray-400 hover:text-cyan-600 dark:hover:text-cyan-400">
{breadcrumbs[breadcrumbs.length - 2]?.label}
</button>
<ChevronRight className="w-4 h-4 text-gray-400" />
<span className="text-gray-900 dark:text-gray-100 font-medium">
{breadcrumbs[breadcrumbs.length - 1]?.label}
</span>
</nav>
</div>
</div>
);
};
const NAVIGATION_PATTERNS: NavigationPattern[] = [
{
id: 'pills',
name: 'Pill Navigation',
description: 'Modern glassmorphism pill-style navigation tabs',
usage: 'Primary navigation, tab switching, section selection',
component: PillNavigationDemo
},
{
id: 'sidebar',
name: 'Sidebar Navigation',
description: 'Collapsible sidebar with hierarchical navigation',
usage: 'Application navigation, admin panels, file browsers',
component: SidebarNavigationDemo
},
{
id: 'topnav',
name: 'Top Navigation',
description: 'Horizontal navigation bar with branding and actions',
usage: 'Website headers, application top bars, main navigation',
component: TopNavigationDemo
},
{
id: 'breadcrumbs',
name: 'Breadcrumb Navigation',
description: 'Hierarchical path navigation showing current location',
usage: 'Deep navigation, file paths, multi-level applications',
component: BreadcrumbDemo
}
];
const generateCode = (patternId: string) => {
const codeExamples = {
pills: `import { useState } from 'react';
import { cn } from '@/features/ui/primitives/styles';
/**
* 🤖 AI CONTEXT: Pill Navigation Pattern
*
* PURPOSE: Modern tab-style navigation with glassmorphism design
* WHEN TO USE: Primary navigation, tab switching, section selection
* WHEN NOT TO USE: Deep hierarchies, complex menus, long lists
*
* VARIANTS:
* - Standard: Icon + text for desktop
* - Icon-only: Compact for mobile/narrow spaces
* - Vertical: Sidebar-style pill navigation
*
* ACCESSIBILITY:
* - Use semantic nav element
* - Include ARIA states (aria-current)
* - Support keyboard navigation
* - Provide tooltips for icon-only variants
*/
interface Tab {
id: string;
label: string;
icon?: React.ReactNode;
}
export const PillNavigation = ({
tabs,
activeTab,
onTabChange,
variant = 'standard'
}: {
tabs: Tab[];
activeTab: string;
onTabChange: (tabId: string) => void;
variant?: 'standard' | 'icon-only' | 'vertical';
}) => {
return (
<nav
className={cn(
"backdrop-blur-sm bg-white/40 dark:bg-white/5",
"border border-white/30 dark:border-white/15 shadow-lg",
variant === 'vertical' ? "rounded-2xl p-1 inline-flex flex-col" : "rounded-full p-1 inline-flex"
)}
>
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => onTabChange(tab.id)}
className={cn(
"transition-all duration-200 text-sm font-medium",
variant === 'vertical' ? "rounded-xl min-w-[120px]" : "rounded-full",
variant === 'icon-only' ? "p-3" : "px-4 py-2",
"flex items-center gap-2",
activeTab === tab.id
? "text-white border border-cyan-400 shadow-[0_0_8px_rgba(34,211,238,0.3)] dark:shadow-[0_0_10px_rgba(34,211,238,0.5)]"
: "text-gray-700 dark:text-gray-300 hover:bg-white/20 dark:hover:bg-white/5"
)}
aria-current={activeTab === tab.id ? 'page' : undefined}
title={variant === 'icon-only' ? tab.label : undefined}
>
{tab.icon}
{variant !== 'icon-only' && tab.label}
</button>
))}
</nav>
);
};`,
sidebar: `import { useState } from 'react';
import { Card } from '@/features/ui/primitives/card';
import { Button } from '@/features/ui/primitives/button';
import { ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react';
import { cn, glassmorphism } from '@/features/ui/primitives/styles';
/**
* 🤖 AI CONTEXT: Sidebar Navigation Pattern
*
* PURPOSE: Hierarchical navigation with collapsible sidebar
* WHEN TO USE: Complex applications, admin panels, file systems
* WHEN NOT TO USE: Simple sites, mobile-first designs, shallow navigation
*
* FEATURES:
* - Collapsible width (280px -> 60px)
* - Submenu support with expansion
* - Active state highlighting
* - Smooth transitions
*
* RESPONSIVE BEHAVIOR:
* - Desktop: Side-by-side layout
* - Tablet: Overlay sidebar
* - Mobile: Full-screen drawer
*/
interface NavItem {
id: string;
label: string;
icon: React.ReactNode;
hasSubmenu?: boolean;
submenu?: string[];
}
export const SidebarNavigation = ({
items,
activeItem,
onItemClick,
collapsed = false,
onToggleCollapse
}: {
items: NavItem[];
activeItem: string;
onItemClick: (itemId: string) => void;
collapsed?: boolean;
onToggleCollapse: () => void;
}) => {
const [expandedItems, setExpandedItems] = useState<string[]>([]);
const toggleExpanded = (itemId: string) => {
setExpandedItems(prev =>
prev.includes(itemId)
? prev.filter(id => id !== itemId)
: [...prev, itemId]
);
};
return (
<Card className={cn(
"h-full transition-all duration-300",
collapsed ? "w-16" : "w-64"
)}>
<div className="p-4">
{/* Toggle Button */}
<Button
variant="ghost"
size="sm"
onClick={onToggleCollapse}
className="mb-4 w-full justify-start"
>
{collapsed ? <ChevronRight /> : <ChevronLeft />}
{!collapsed && <span className="ml-2">Collapse</span>}
</Button>
{/* Navigation Items */}
<nav className="space-y-1">
{items.map((item) => (
<div key={item.id}>
<button
onClick={() => {
onItemClick(item.id);
if (item.hasSubmenu && !collapsed) {
toggleExpanded(item.id);
}
}}
className={cn(
"w-full flex items-center gap-3 px-3 py-2 rounded-lg",
"transition-all duration-200 text-left",
activeItem === item.id
? cn(glassmorphism.background.cyan, "text-cyan-700 dark:text-cyan-300")
: "hover:bg-gray-100 dark:hover:bg-gray-800"
)}
>
{item.icon}
{!collapsed && (
<>
<span className="flex-1 text-sm font-medium">
{item.label}
</span>
{item.hasSubmenu && (
<ChevronDown
className={cn(
"w-4 h-4 transition-transform",
expandedItems.includes(item.id) && "rotate-180"
)}
/>
)}
</>
)}
</button>
{/* Submenu */}
{item.hasSubmenu && !collapsed && expandedItems.includes(item.id) && (
<div className="ml-8 mt-1 space-y-1">
{item.submenu?.map((subItem) => (
<button
key={subItem}
className="w-full text-left px-3 py-1 text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded"
>
{subItem}
</button>
))}
</div>
)}
</div>
))}
</nav>
</div>
</Card>
);
};`,
topnav: `import { Card } from '@/features/ui/primitives/card';
import { Button } from '@/features/ui/primitives/button';
import { Search, Bell, User } from 'lucide-react';
import { cn, glassmorphism } from '@/features/ui/primitives/styles';
/**
* 🤖 AI CONTEXT: Top Navigation Pattern
*
* PURPOSE: Primary horizontal navigation with branding and actions
* WHEN TO USE: Website headers, app top bars, main navigation
* WHEN NOT TO USE: Deep navigation, complex hierarchies, space-constrained designs
*
* STRUCTURE:
* - Left: Brand/logo
* - Center: Primary navigation links
* - Right: User actions and utilities
*
* RESPONSIVE BEHAVIOR:
* - Desktop: Full horizontal layout
* - Tablet: Collapsed navigation menu
* - Mobile: Hamburger menu with drawer
*/
interface NavItem {
id: string;
label: string;
href?: string;
}
export const TopNavigation = ({
brand,
navItems,
activeItem,
onNavClick,
userActions
}: {
brand: React.ReactNode;
navItems: NavItem[];
activeItem: string;
onNavClick: (itemId: string) => void;
userActions?: React.ReactNode;
}) => {
return (
<Card className="p-4">
<div className="flex items-center justify-between">
{/* Brand/Logo */}
<div className="flex items-center gap-3">
{brand}
</div>
{/* Navigation Links */}
<nav className="hidden md:flex items-center gap-1">
{navItems.map((item) => (
<button
key={item.id}
onClick={() => onNavClick(item.id)}
className={cn(
"px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200",
activeItem === item.id
? cn(glassmorphism.background.cyan, "text-cyan-700 dark:text-cyan-300")
: "text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-800"
)}
>
{item.label}
</button>
))}
</nav>
{/* User Actions */}
<div className="flex items-center gap-2">
{userActions || (
<>
<Button variant="ghost" size="sm">
<Search className="w-4 h-4" />
</Button>
<Button variant="ghost" size="sm">
<Bell className="w-4 h-4" />
</Button>
<Button variant="ghost" size="sm">
<User className="w-4 h-4" />
</Button>
</>
)}
</div>
</div>
</Card>
);
};`,
breadcrumbs: `import { ChevronRight } from 'lucide-react';
import { cn, glassmorphism } from '@/features/ui/primitives/styles';
/**
* 🤖 AI CONTEXT: Breadcrumb Navigation Pattern
*
* PURPOSE: Hierarchical path navigation showing current location
* WHEN TO USE: Deep navigation, file systems, multi-level apps
* WHEN NOT TO USE: Shallow navigation, single-page apps, unclear hierarchies
*
* VARIANTS:
* - Standard: Text links with separators
* - Pill: Rounded pill-style breadcrumbs
* - Compact: Ellipsis for mobile/narrow spaces
*
* ACCESSIBILITY:
* - Use semantic nav element
* - Include aria-label for navigation
* - Mark current page with aria-current
* - Ensure sufficient color contrast
*/
interface Breadcrumb {
label: string;
href: string;
}
export const BreadcrumbNavigation = ({
breadcrumbs,
variant = 'standard',
maxItems = 4
}: {
breadcrumbs: Breadcrumb[];
variant?: 'standard' | 'pill' | 'compact';
maxItems?: number;
}) => {
const displayCrumbs = variant === 'compact' && breadcrumbs.length > maxItems
? [
...breadcrumbs.slice(0, 1),
{ label: '...', href: '#' },
...breadcrumbs.slice(-2)
]
: breadcrumbs;
return (
<nav aria-label="Breadcrumb" className="flex items-center space-x-1">
{displayCrumbs.map((crumb, index) => (
<React.Fragment key={crumb.href}>
{variant === 'pill' ? (
<button
className={cn(
"px-3 py-1 rounded-full text-sm transition-all duration-200",
index === displayCrumbs.length - 1
? cn(glassmorphism.background.cyan, "text-cyan-700 dark:text-cyan-300 font-medium")
: "text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800"
)}
aria-current={index === displayCrumbs.length - 1 ? 'page' : undefined}
>
{crumb.label}
</button>
) : (
<button
className={cn(
"hover:text-cyan-600 dark:hover:text-cyan-400 transition-colors text-sm",
index === displayCrumbs.length - 1
? "text-gray-900 dark:text-gray-100 font-medium"
: "text-gray-500 dark:text-gray-400"
)}
aria-current={index === displayCrumbs.length - 1 ? 'page' : undefined}
>
{crumb.label}
</button>
)}
{index < displayCrumbs.length - 1 && (
<ChevronRight className="w-4 h-4 text-gray-400 mx-1" />
)}
</React.Fragment>
))}
</nav>
);
};`
};
return codeExamples[patternId as keyof typeof codeExamples] || '';
};
export const NavigationPattern = () => {
const [selectedPattern, setSelectedPattern] = useState<string>('pills');
const currentPattern = NAVIGATION_PATTERNS.find(p => p.id === selectedPattern);
return (
<div className="space-y-8">
<div>
<h2 className="text-2xl font-bold mb-4">Navigation Patterns</h2>
<p className="text-gray-600 dark:text-gray-400 mb-6">
Navigation components including pills, sidebars, top navigation, and breadcrumbs.
</p>
</div>
{/* Pattern Selector */}
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Select Navigation Pattern</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{NAVIGATION_PATTERNS.map((pattern) => (
<Card
key={pattern.id}
className={cn(
"p-4 cursor-pointer transition-all duration-200",
selectedPattern === pattern.id
? "border-cyan-500 shadow-[0_0_10px_rgba(34,211,238,0.3)]"
: "hover:shadow-lg"
)}
onClick={() => setSelectedPattern(pattern.id)}
>
<h4 className="font-medium mb-2">{pattern.name}</h4>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-2">
{pattern.description}
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
<strong>Use for:</strong> {pattern.usage}
</p>
</Card>
))}
</div>
</Card>
{/* Live Preview */}
{currentPattern && (
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">
{currentPattern.name} - Examples
</h3>
<currentPattern.component />
</Card>
)}
{/* Generated Code */}
{currentPattern && (
<Card className="p-6 max-w-none">
<h3 className="text-lg font-semibold mb-4">Generated Code</h3>
<CodeDisplay
code={generateCode(selectedPattern)}
showLineNumbers
/>
</Card>
)}
</div>
);
};

View File

@@ -0,0 +1,106 @@
import { useState } from 'react';
import { Check, Copy } from 'lucide-react';
import { Button } from '@/features/ui/primitives/button';
import { cn } from '@/features/ui/primitives/styles';
interface CodeDisplayProps {
code: string;
showLineNumbers?: boolean;
className?: string;
}
export const CodeDisplay = ({
code,
showLineNumbers = false,
className
}: CodeDisplayProps) => {
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
await navigator.clipboard.writeText(code);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
const lines = code.split('\n');
// Basic syntax highlighting with regex
const highlightCode = (line: string) => {
// Escape HTML
line = line.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
// Keywords
line = line.replace(
/\b(import|export|from|const|let|var|function|return|if|else|interface|type|class|extends|implements)\b/g,
'<span class="text-purple-400">$1</span>'
);
// Strings
line = line.replace(
/(["'`])([^"'`]*)\1/g,
'<span class="text-green-400">$1$2$1</span>'
);
// Comments
line = line.replace(
/(\/\/.*$|\/\*.*\*\/)/g,
'<span class="text-gray-500">$1</span>'
);
// JSX tags
line = line.replace(
/&lt;([A-Z][A-Za-z0-9]*)(\s|&gt;|\/&gt;)/g,
'&lt;<span class="text-cyan-400">$1</span>$2'
);
// Props
line = line.replace(
/(\w+)=/g,
'<span class="text-orange-400">$1</span>='
);
return line;
};
return (
<div className={cn(
"relative rounded-lg overflow-hidden",
"bg-gray-900 border border-gray-800",
className
)}>
{/* Copy Button */}
<Button
size="icon"
variant="ghost"
onClick={handleCopy}
className="absolute top-2 right-2 z-10 text-gray-400 hover:text-white"
>
{copied ? (
<Check className="w-4 h-4 text-green-400" />
) : (
<Copy className="w-4 h-4" />
)}
</Button>
{/* Code Content */}
<pre className="p-4 overflow-x-auto">
<code className="text-sm font-mono">
{lines.map((line, index) => (
<div key={index} className="flex">
{showLineNumbers && (
<span className="text-gray-500 mr-4 select-none w-8 text-right">
{index + 1}
</span>
)}
<span
dangerouslySetInnerHTML={{
__html: highlightCode(line) || '&nbsp;'
}}
/>
</div>
))}
</code>
</pre>
</div>
);
};

View File

@@ -0,0 +1,15 @@
import { Card } from '@/features/ui/primitives/card';
import { cn } from '@/features/ui/primitives/styles';
interface ConfigPanelProps {
title?: string;
children: React.ReactNode;
className?: string;
}
export const ConfigPanel = ({ title, children, className }: ConfigPanelProps) => (
<Card className={cn("space-y-4", className)}>
{title && <h3 className="font-semibold text-lg mb-2 text-gray-900 dark:text-white">{title}</h3>}
{children}
</Card>
);

View File

@@ -0,0 +1,17 @@
import { Label } from '@/features/ui/primitives/label';
import { cn } from '@/features/ui/primitives/styles';
interface ConfigRowProps {
label: string;
children: React.ReactNode;
className?: string;
}
export const ConfigRow = ({ label, children, className }: ConfigRowProps) => (
<div className={cn("flex items-center justify-between", className)}>
<Label className="text-sm font-medium text-gray-700 dark:text-gray-300">{label}</Label>
<div className="flex-shrink-0">
{children}
</div>
</div>
);

View File

@@ -0,0 +1,64 @@
import { useState } from 'react';
import { ZoomIn, ZoomOut, RotateCcw } from 'lucide-react';
import { Button } from '@/features/ui/primitives/button';
import { cn } from '@/features/ui/primitives/styles';
interface LivePreviewProps {
children: React.ReactNode;
className?: string;
minHeight?: string;
}
export const LivePreview = ({ children, className, minHeight = "400px" }: LivePreviewProps) => {
const [zoom, setZoom] = useState(100);
return (
<div className="relative rounded-lg overflow-hidden">
{/* Zoom Controls */}
<div className="absolute top-2 right-2 z-10 flex gap-1">
<Button
size="icon"
variant="outline"
onClick={() => setZoom(prev => Math.max(50, prev - 25))}
>
<ZoomOut className="w-4 h-4" />
</Button>
<Button
size="icon"
variant="outline"
onClick={() => setZoom(100)}
>
<RotateCcw className="w-4 h-4" />
</Button>
<Button
size="icon"
variant="outline"
onClick={() => setZoom(prev => Math.min(150, prev + 25))}
>
<ZoomIn className="w-4 h-4" />
</Button>
</div>
{/* Grid Background */}
<div
className={cn(
"bg-gray-50 dark:bg-gray-900/50",
"bg-[linear-gradient(to_right,#8882_1px,transparent_1px),linear-gradient(to_bottom,#8882_1px,transparent_1px)]",
"bg-[size:20px_20px]",
"p-8 flex items-center justify-center",
className
)}
style={{ minHeight }}
>
<div
style={{
transform: `scale(${zoom / 100})`,
transformOrigin: 'center'
}}
>
{children}
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1,91 @@
import { useState } from 'react';
import { ChevronRight, ChevronDown } from 'lucide-react';
import { cn } from '@/features/ui/primitives/styles';
interface NavigationSection {
label: string;
items: string[];
}
interface NavigationSidebarProps {
sections: NavigationSection[];
selectedItem: string;
onItemSelect: (item: string) => void;
}
// Default navigation structure for style guide
// This can be used when creating a NavigationSidebar instanc
export const NavigationSidebar: React.FC<NavigationSidebarProps> = ({
sections,
selectedItem,
onItemSelect
}) => {
const [expandedSections, setExpandedSections] = useState<string[]>(
sections.map(s => s.label)
);
const toggleSection = (label: string) => {
setExpandedSections(prev =>
prev.includes(label)
? prev.filter(l => l !== label)
: [...prev, label]
);
};
return (
<aside className="w-64 p-4">
<div className="space-y-2">
{sections.map((section) => {
const isExpanded = expandedSections.includes(section.label);
return (
<div key={section.label}>
<button
onClick={() => toggleSection(section.label)}
className={cn(
"flex items-center justify-between w-full px-3 py-2 text-left",
"rounded-md hover:bg-white/10 dark:hover:bg-white/5",
"text-sm font-medium text-gray-700 dark:text-gray-300",
"transition-colors"
)}
>
{section.label}
{isExpanded ? (
<ChevronDown className="w-4 h-4" />
) : (
<ChevronRight className="w-4 h-4" />
)}
</button>
{isExpanded && (
<div className="ml-2 mt-1 space-y-1">
{section.items.map((item) => {
const isSelected = selectedItem === item;
return (
<button
key={item}
onClick={() => onItemSelect(item)}
className={cn(
"block w-full px-3 py-1.5 text-left rounded-md",
"text-sm text-gray-600 dark:text-gray-400",
"transition-colors",
isSelected
? "bg-cyan-500/20 text-cyan-700 dark:text-cyan-300"
: "hover:bg-white/10 dark:hover:bg-white/5"
)}
>
{item}
</button>
);
})}
</div>
)}
</div>
);
})}
</div>
</aside>
);
};

View File

@@ -0,0 +1,162 @@
import React, { useState } from 'react';
import { cn, glassmorphism, glassCard } from '@/features/ui/primitives/styles';
import { Palette, Component, Layout, Code, ChevronDown } from 'lucide-react';
interface NavigationItem {
id: string;
label: string;
icon: React.ReactNode;
items?: string[];
}
interface PillNavigationProps {
selectedSection: string;
selectedItem: string | null;
onSectionChange: (section: string) => void;
onItemChange: (item: string | null) => void;
}
const NAVIGATION_ITEMS: NavigationItem[] = [
{
id: 'foundations',
label: 'Foundations',
icon: <Palette className="w-4 h-4" />,
items: ['Colors', 'Typography', 'Spacing', 'Effects']
},
{
id: 'components',
label: 'Components',
icon: <Component className="w-4 h-4" />,
items: ['Cards', 'Buttons', 'Forms', 'Tables', 'Modals', 'Toggles']
},
{
id: 'patterns',
label: 'Patterns',
icon: <Layout className="w-4 h-4" />,
items: ['Layouts', 'Feedback', 'Navigation', 'Data Display']
},
{
id: 'examples',
label: 'Examples',
icon: <Code className="w-4 h-4" />,
items: ['Compositions', 'Pages', 'Workflows']
}
];
export const PillNavigation: React.FC<PillNavigationProps> = ({
selectedSection,
selectedItem,
onSectionChange,
onItemChange
}) => {
const [openDropdown, setOpenDropdown] = useState<string | null>(null);
const handleSectionClick = (sectionId: string) => {
if (selectedSection === sectionId && openDropdown === sectionId) {
// Close dropdown if same section clicked
setOpenDropdown(null);
} else {
// Open new section
onSectionChange(sectionId);
setOpenDropdown(sectionId);
}
};
const handleItemClick = (item: string) => {
onItemChange(item);
setOpenDropdown(null); // Close dropdown after selection
};
const selectedSectionData = NAVIGATION_ITEMS.find(item => item.id === selectedSection);
return (
<div className="relative">
{/* Top-level Glass Pill Navigation - Using proper glass styles */}
<div className="flex items-center justify-center">
<div className={cn(
glassCard.blur.xl, // Standard glass blur
glassCard.transparency.light, // Light transparency for true glass
glassmorphism.border.default, // Proper glass border
"rounded-full p-1 shadow-lg"
)}>
<div className="flex gap-1">
{NAVIGATION_ITEMS.map((item) => {
const isSelected = selectedSection === item.id;
const hasDropdown = item.items && item.items.length > 0;
return (
<button
key={item.id}
onClick={() => handleSectionClick(item.id)}
className={cn(
"flex items-center gap-2 px-6 py-3 rounded-full transition-all duration-200",
"text-sm font-medium",
isSelected
? "bg-cyan-500/20 dark:bg-cyan-400/20 text-cyan-700 dark:text-cyan-300 border border-cyan-400/50 shadow-[0_0_10px_rgba(34,211,238,0.5)]"
: "text-gray-700 dark:text-gray-300 hover:bg-white/10 dark:hover:bg-white/5"
)}
>
{item.icon}
{item.label}
{hasDropdown && (
<ChevronDown
className={cn(
"w-4 h-4 transition-transform duration-200",
openDropdown === item.id && "rotate-180"
)}
/>
)}
</button>
);
})}
</div>
</div>
</div>
{/* Sub-menu Drawer - Using proper glass styles */}
{openDropdown && selectedSectionData?.items && (
<div className="absolute top-full left-1/2 -translate-x-1/2 mt-2 z-50">
<div className={cn(
glassCard.blur["2xl"], // Slightly stronger blur for dropdown
"shadow-2xl"
)}>
{/* Square top edge connecting to pill */}
<div className={cn(
glassCard.transparency.medium,
glassmorphism.border.default,
"h-4 border-t border-l border-r"
)} />
{/* Dropdown content */}
<div className={cn(
glassCard.transparency.medium,
glassmorphism.border.default,
"rounded-b-lg border border-t-0 p-2"
)}>
<div className="grid grid-cols-3 gap-1 min-w-[400px]">
{selectedSectionData.items.map((subItem) => {
const isSelected = selectedItem === subItem;
return (
<button
key={subItem}
onClick={() => handleItemClick(subItem)}
className={cn(
"px-4 py-3 rounded-lg text-sm font-medium transition-all duration-200",
isSelected
? "bg-cyan-500/30 dark:bg-cyan-400/30 text-cyan-700 dark:text-cyan-300 border border-cyan-400/50 shadow-[0_0_10px_rgba(34,211,238,0.5)]"
: "text-gray-700 dark:text-gray-300 hover:bg-white/20 dark:hover:bg-white/10"
)}
>
{subItem}
</button>
);
})}
</div>
</div>
</div>
</div>
)}
</div>
);
};

View File

@@ -0,0 +1,32 @@
export const MODAL_TYPES = {
confirmation: {
size: "sm" as const,
glowColor: "red" as const,
purpose: "Destructive actions"
},
formCreate: {
size: "md" as const,
glowColor: "green" as const,
purpose: "Creating resources"
},
formEdit: {
size: "md" as const,
glowColor: "blue" as const,
purpose: "Editing resources"
},
display: {
size: "lg" as const,
glowColor: "purple" as const,
purpose: "Detailed information"
},
codeViewer: {
size: "xl" as const,
glowColor: "cyan" as const,
purpose: "Code display"
},
settings: {
size: "lg" as const,
glowColor: "blue" as const,
purpose: "App settings"
}
};

View File

@@ -0,0 +1,24 @@
// Type definitions for style guide components
// Card types
export type GlowColor = 'none' | 'purple' | 'blue' | 'cyan' | 'green' | 'orange' | 'pink' | 'red';
export type EdgePosition = 'none' | 'top' | 'left' | 'right' | 'bottom';
export type EdgeColor = 'purple' | 'blue' | 'cyan' | 'green' | 'orange' | 'pink' | 'red';
export type CardSize = 'sm' | 'md' | 'lg' | 'xl';
export type Transparency = 'clear' | 'light' | 'medium' | 'frosted' | 'solid';
export type BlurLevel = 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl';
export type GlassTint = 'none' | 'purple' | 'blue' | 'cyan' | 'green' | 'orange' | 'pink' | 'red';
// Button types
export type ButtonVariant = 'default' | 'destructive' | 'outline' | 'ghost' | 'link' | 'cyan' | 'knowledge';
export type ButtonSize = 'xs' | 'sm' | 'default' | 'lg' | 'icon';
// Form types
export type InputType = 'text' | 'email' | 'password' | 'number' | 'search' | 'url' | 'tel';
export type LabelPosition = 'left' | 'right' | 'top' | 'bottom';
// Modal types
export type ModalSize = 'sm' | 'md' | 'lg' | 'xl';
// Toggle types
export type ToggleSize = 'sm' | 'md' | 'lg';

View File

@@ -0,0 +1,82 @@
import React from "react";
import { cn, glassCard } from "./styles";
interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
// Glass properties
blur?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl';
transparency?: 'clear' | 'light' | 'medium' | 'frosted' | 'solid';
glassTint?: 'none' | 'purple' | 'blue' | 'cyan' | 'green' | 'orange' | 'pink' | 'red';
// Glow properties
glowColor?: 'none' | 'purple' | 'blue' | 'cyan' | 'green' | 'orange' | 'pink' | 'red';
// Edge-lit properties
edgePosition?: 'none' | 'top' | 'left' | 'right' | 'bottom';
edgeColor?: 'purple' | 'blue' | 'cyan' | 'green' | 'orange' | 'pink' | 'red';
// Size
size?: 'sm' | 'md' | 'lg' | 'xl';
}
export const Card = React.forwardRef<HTMLDivElement, CardProps>(
({
className,
blur = 'xl', // Default to standard glass (3px blur - subtle)
transparency = 'light', // Default to subtle transparency (3%)
glassTint = 'none',
glowColor = 'none',
edgePosition = 'none',
edgeColor = 'cyan',
size = 'md',
children,
...props
}, ref) => {
const glowVariant = glassCard.variants[glowColor] || glassCard.variants.none;
const hasEdge = edgePosition !== 'none';
const isHorizontalEdge = edgePosition === 'top' || edgePosition === 'bottom';
const edgeColorConfig = hasEdge ? glassCard.edgeLit.color[edgeColor] : null;
return (
<div
ref={ref}
className={cn(
// Base glass effect
glassCard.base,
// Backdrop blur intensity - consistent glass effect
glassCard.blur[blur],
// Apply background with proper transparency
glassTint !== 'none'
? glassCard.tints[glassTint][transparency]
: glassCard.transparency[transparency],
// Size
glassCard.sizes[size],
// Glow effects (border, shadow, hover) - only if no edge-lit
!hasEdge && glowVariant.border,
!hasEdge && glowVariant.glow,
!hasEdge && glowVariant.hover,
// Edge-lit effects
hasEdge && glassCard.edgeLit.position[edgePosition],
hasEdge && edgeColorConfig ? edgeColorConfig.glow : false,
hasEdge && edgeColorConfig ? (
isHorizontalEdge
? edgeColorConfig.gradient.horizontal
: edgeColorConfig.gradient.vertical
) : false,
// User overrides
className
)}
{...props}
>
{children}
</div>
);
}
);
Card.displayName = "Card";

View File

@@ -0,0 +1,20 @@
import * as LabelPrimitive from "@radix-ui/react-label";
import React from "react";
import { cn } from "./styles";
export const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(
"text-sm font-medium leading-none",
"text-gray-700 dark:text-gray-200",
"peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
className
)}
{...props}
/>
));
Label.displayName = LabelPrimitive.Root.displayName;

View File

@@ -0,0 +1,32 @@
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
import { Circle } from "lucide-react";
import React from "react";
import { cn, glassmorphism } from "./styles";
export const RadioGroup = RadioGroupPrimitive.Root;
export const RadioGroupItem = React.forwardRef<
React.ElementRef<typeof RadioGroupPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
>(({ className, ...props }, ref) => (
<RadioGroupPrimitive.Item
ref={ref}
className={cn(
"aspect-square h-4 w-4 rounded-full",
"backdrop-blur-md bg-gradient-to-b from-white/80 to-white/60",
"dark:from-white/10 dark:to-black/30",
glassmorphism.border.default,
glassmorphism.interactive.base,
"focus:outline-none focus:ring-2 focus:ring-cyan-500",
"disabled:cursor-not-allowed disabled:opacity-50",
"data-[state=checked]:border-cyan-500",
className
)}
{...props}
>
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
<Circle className="h-2.5 w-2.5 fill-cyan-500 text-cyan-500" />
</RadioGroupPrimitive.Indicator>
</RadioGroupPrimitive.Item>
));
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;

View File

@@ -8,28 +8,27 @@
* - For runtime theme values, use useThemeAware hook
*/
// Base glassmorphism classes with Tron aesthetic
// Base glassmorphism classes with Tron aesthetic - TRUE GLASS EFFECT
export const glassmorphism = {
// Background variations - matching existing Card.tsx patterns
// Background variations - TRUE TRANSPARENCY for glass effect
background: {
subtle: "backdrop-blur-md bg-gradient-to-b from-white/80 to-white/60 dark:from-white/10 dark:to-black/30",
strong: "backdrop-blur-md bg-gradient-to-b from-white/95 to-white/90 dark:from-gray-800/95 dark:to-gray-900/95",
card: "backdrop-blur-md bg-gradient-to-b from-white/80 to-white/60 dark:from-white/10 dark:to-black/30",
// Tron-style colored backgrounds
cyan: "backdrop-blur-md bg-gradient-to-b from-cyan-100/80 dark:from-cyan-500/20 to-white/60 dark:to-cyan-500/5",
blue: "backdrop-blur-md bg-gradient-to-b from-blue-100/80 dark:from-blue-500/20 to-white/60 dark:to-blue-500/5",
purple:
"backdrop-blur-md bg-gradient-to-b from-purple-100/80 dark:from-purple-500/20 to-white/60 dark:to-purple-500/5",
subtle: "backdrop-blur-xl bg-white/5 dark:bg-white/10",
strong: "backdrop-blur-xl bg-white/10 dark:bg-white/20",
card: "backdrop-blur-xl bg-white/5 dark:bg-white/10",
// Tron-style colored backgrounds - VERY transparent with strong blur
cyan: "backdrop-blur-xl bg-cyan-400/5 dark:bg-cyan-400/10",
blue: "backdrop-blur-xl bg-blue-400/5 dark:bg-blue-400/10",
purple: "backdrop-blur-xl bg-purple-400/5 dark:bg-purple-400/10",
},
// Border styles with neon glow
// Border styles for glass effect - more prominent for edge definition
border: {
default: "border border-gray-200 dark:border-gray-700",
cyan: "border-cyan-300 dark:border-cyan-500/30",
blue: "border-blue-300 dark:border-blue-500/30",
purple: "border-purple-300 dark:border-purple-500/30",
focus: "focus:border-cyan-500 focus:shadow-[0_0_20px_5px_rgba(34,211,238,0.5)]",
hover: "hover:border-cyan-400/70 hover:shadow-[0_0_15px_rgba(34,211,238,0.4)]",
default: "border border-white/10 dark:border-white/[0.06]",
cyan: "border border-cyan-400/50 dark:border-cyan-400/40",
blue: "border border-blue-400/50 dark:border-blue-400/40",
purple: "border border-purple-400/50 dark:border-purple-400/40",
focus: "focus:border-cyan-400 focus:shadow-[0_0_30px_10px_rgba(34,211,238,0.6)]",
hover: "hover:border-cyan-400/80 hover:shadow-[0_0_25px_5px_rgba(34,211,238,0.5)]",
},
// Interactive states
@@ -59,17 +58,37 @@ export const glassmorphism = {
md: "shadow-md dark:shadow-lg",
lg: "shadow-lg dark:shadow-2xl",
elevated: "shadow-[0_10px_30px_-15px_rgba(0,0,0,0.1)] dark:shadow-[0_10px_30px_-15px_rgba(0,0,0,0.7)]",
// Neon glow effects matching Card.tsx
// Strong neon glow effects for true Tron aesthetic
glow: {
cyan: "shadow-[0_0_10px_2px_rgba(34,211,238,0.4)] dark:shadow-[0_0_20px_5px_rgba(34,211,238,0.7)]",
blue: "shadow-[0_0_10px_2px_rgba(59,130,246,0.4)] dark:shadow-[0_0_20px_5px_rgba(59,130,246,0.7)]",
purple: "shadow-[0_0_10px_2px_rgba(168,85,247,0.4)] dark:shadow-[0_0_20px_5px_rgba(168,85,247,0.7)]",
green: "shadow-[0_0_10px_2px_rgba(16,185,129,0.4)] dark:shadow-[0_0_20px_5px_rgba(16,185,129,0.7)]",
pink: "shadow-[0_0_10px_2px_rgba(236,72,153,0.4)] dark:shadow-[0_0_20px_5px_rgba(236,72,153,0.7)]",
orange: "shadow-[0_0_10px_2px_rgba(251,146,60,0.4)] dark:shadow-[0_0_20px_5px_rgba(251,146,60,0.7)]",
purple: "shadow-[0_0_30px_10px_rgba(168,85,247,0.5)] dark:shadow-[0_0_40px_15px_rgba(168,85,247,0.7)]",
blue: "shadow-[0_0_30px_10px_rgba(59,130,246,0.5)] dark:shadow-[0_0_40px_15px_rgba(59,130,246,0.7)]",
green: "shadow-[0_0_30px_10px_rgba(16,185,129,0.5)] dark:shadow-[0_0_40px_15px_rgba(16,185,129,0.7)]",
red: "shadow-[0_0_30px_10px_rgba(239,68,68,0.5)] dark:shadow-[0_0_40px_15px_rgba(239,68,68,0.7)]",
orange: "shadow-[0_0_30px_10px_rgba(251,146,60,0.5)] dark:shadow-[0_0_40px_15px_rgba(251,146,60,0.7)]",
cyan: "shadow-[0_0_30px_10px_rgba(34,211,238,0.5)] dark:shadow-[0_0_40px_15px_rgba(34,211,238,0.7)]",
pink: "shadow-[0_0_30px_10px_rgba(236,72,153,0.5)] dark:shadow-[0_0_40px_15px_rgba(236,72,153,0.7)]",
},
},
// Edge glow positions - now part of glassCard for better integration
edgePositions: {
none: "",
top: "before:content-[''] before:absolute before:top-0 before:left-0 before:right-0 before:h-[2px]",
left: "before:content-[''] before:absolute before:top-0 before:left-0 before:bottom-0 before:w-[2px]",
right: "before:content-[''] before:absolute before:top-0 before:right-0 before:bottom-0 before:w-[2px]",
bottom: "before:content-[''] before:absolute before:bottom-0 before:left-0 before:right-0 before:h-[2px]"
},
// Configurable sizes for cards
sizes: {
card: {
sm: "p-4 max-w-sm",
md: "p-6 max-w-md",
lg: "p-8 max-w-lg",
xl: "p-10 max-w-xl"
}
},
// Priority colors (matching our task system)
priority: {
critical: {
@@ -99,6 +118,207 @@ export const glassmorphism = {
},
};
// Card-specific glass styles with accent colors
export const glassCard = {
// Base glass card (true transparency) - NO blur here, controlled separately
base: "relative rounded-lg overflow-hidden border transition-all duration-300",
// Blur intensity levels - EXTREMELY SUBTLE for true glass
blur: {
none: "backdrop-blur-none", // No blur (0px)
sm: "backdrop-blur-[0.5px]", // Barely perceptible
md: "backdrop-blur-[1px]", // Very subtle hint
lg: "backdrop-blur-[2px]", // Light glass effect
xl: "backdrop-blur-[3px]", // Standard glass
"2xl": "backdrop-blur-[5px]", // Noticeable glass
"3xl": "backdrop-blur-[8px]" // Maximum (what was "standard" before)
},
// Glass transparency levels - Theme-aware for better color visibility
transparency: {
clear: "bg-white/[0.01] dark:bg-black/[0.01]", // 1% - almost invisible
light: "bg-white/[0.03] dark:bg-black/[0.05]", // 3-5% - very subtle glass
medium: "bg-white/[0.05] dark:bg-black/[0.08]", // 5-8% - standard glass
frosted: "bg-white/[0.08] dark:bg-black/[0.12]", // 8-12% - frosted glass
solid: "bg-white/[0.12] dark:bg-black/[0.20]" // 12-20% - more visible
},
// Colored glass tints - BRIGHT NEON COLORS with higher opacity
tints: {
none: "",
purple: {
clear: "bg-purple-500/[0.03] dark:bg-purple-400/[0.04]", // 3-4% - barely visible tint
light: "bg-purple-500/[0.08] dark:bg-purple-400/[0.10]", // 8-10% - subtle colored glass
medium: "bg-purple-500/[0.15] dark:bg-purple-400/[0.20]", // 15-20% - standard colored glass
frosted: "bg-purple-500/[0.25] dark:bg-purple-400/[0.35]", // 25-35% - frosted colored glass
solid: "bg-purple-500/[0.40] dark:bg-purple-400/[0.60]" // 40-60% - bright neon glow
},
blue: {
clear: "bg-blue-500/[0.03] dark:bg-blue-400/[0.04]",
light: "bg-blue-500/[0.08] dark:bg-blue-400/[0.10]",
medium: "bg-blue-500/[0.15] dark:bg-blue-400/[0.20]",
frosted: "bg-blue-500/[0.25] dark:bg-blue-400/[0.35]",
solid: "bg-blue-500/[0.40] dark:bg-blue-400/[0.60]"
},
cyan: {
clear: "bg-cyan-500/[0.03] dark:bg-cyan-400/[0.04]",
light: "bg-cyan-500/[0.08] dark:bg-cyan-400/[0.10]",
medium: "bg-cyan-500/[0.15] dark:bg-cyan-400/[0.20]",
frosted: "bg-cyan-500/[0.25] dark:bg-cyan-400/[0.35]",
solid: "bg-cyan-500/[0.40] dark:bg-cyan-400/[0.60]"
},
green: {
clear: "bg-emerald-500/[0.03] dark:bg-emerald-400/[0.04]",
light: "bg-emerald-500/[0.08] dark:bg-emerald-400/[0.10]",
medium: "bg-emerald-500/[0.15] dark:bg-emerald-400/[0.20]",
frosted: "bg-emerald-500/[0.25] dark:bg-emerald-400/[0.35]",
solid: "bg-emerald-500/[0.40] dark:bg-emerald-400/[0.60]"
},
orange: {
clear: "bg-orange-500/[0.03] dark:bg-orange-400/[0.04]",
light: "bg-orange-500/[0.08] dark:bg-orange-400/[0.10]",
medium: "bg-orange-500/[0.15] dark:bg-orange-400/[0.20]",
frosted: "bg-orange-500/[0.25] dark:bg-orange-400/[0.35]",
solid: "bg-orange-500/[0.40] dark:bg-orange-400/[0.60]"
},
pink: {
clear: "bg-pink-500/[0.03] dark:bg-pink-400/[0.04]",
light: "bg-pink-500/[0.08] dark:bg-pink-400/[0.10]",
medium: "bg-pink-500/[0.15] dark:bg-pink-400/[0.20]",
frosted: "bg-pink-500/[0.25] dark:bg-pink-400/[0.35]",
solid: "bg-pink-500/[0.40] dark:bg-pink-400/[0.60]"
},
red: {
clear: "bg-red-500/[0.03] dark:bg-red-400/[0.04]",
light: "bg-red-500/[0.08] dark:bg-red-400/[0.10]",
medium: "bg-red-500/[0.15] dark:bg-red-400/[0.20]",
frosted: "bg-red-500/[0.25] dark:bg-red-400/[0.35]",
solid: "bg-red-500/[0.40] dark:bg-red-400/[0.60]"
}
},
// Neon glow effects - BRIGHTER & MORE INTENSE
variants: {
none: {
border: "border-gray-300/20 dark:border-white/10",
glow: "",
hover: "hover:bg-white/[0.04] dark:hover:bg-white/[0.02]"
},
purple: {
border: "border-purple-500/50 dark:border-purple-400/40",
glow: "shadow-[0_0_40px_15px_rgba(168,85,247,0.4)] dark:shadow-[0_0_60px_25px_rgba(168,85,247,0.7)]",
hover: "hover:shadow-[0_0_50px_20px_rgba(168,85,247,0.5)] dark:hover:shadow-[0_0_80px_30px_rgba(168,85,247,0.8)]"
},
blue: {
border: "border-blue-500/50 dark:border-blue-400/40",
glow: "shadow-[0_0_40px_15px_rgba(59,130,246,0.4)] dark:shadow-[0_0_60px_25px_rgba(59,130,246,0.7)]",
hover: "hover:shadow-[0_0_50px_20px_rgba(59,130,246,0.5)] dark:hover:shadow-[0_0_80px_30px_rgba(59,130,246,0.8)]"
},
green: {
border: "border-emerald-500/50 dark:border-emerald-400/40",
glow: "shadow-[0_0_40px_15px_rgba(16,185,129,0.4)] dark:shadow-[0_0_60px_25px_rgba(16,185,129,0.7)]",
hover: "hover:shadow-[0_0_50px_20px_rgba(16,185,129,0.5)] dark:hover:shadow-[0_0_80px_30px_rgba(16,185,129,0.8)]"
},
cyan: {
border: "border-cyan-500/50 dark:border-cyan-400/40",
glow: "shadow-[0_0_40px_15px_rgba(34,211,238,0.4)] dark:shadow-[0_0_60px_25px_rgba(34,211,238,0.7)]",
hover: "hover:shadow-[0_0_50px_20px_rgba(34,211,238,0.5)] dark:hover:shadow-[0_0_80px_30px_rgba(34,211,238,0.8)]"
},
orange: {
border: "border-orange-500/50 dark:border-orange-400/40",
glow: "shadow-[0_0_40px_15px_rgba(251,146,60,0.4)] dark:shadow-[0_0_60px_25px_rgba(251,146,60,0.7)]",
hover: "hover:shadow-[0_0_50px_20px_rgba(251,146,60,0.5)] dark:hover:shadow-[0_0_80px_30px_rgba(251,146,60,0.8)]"
},
pink: {
border: "border-pink-500/50 dark:border-pink-400/40",
glow: "shadow-[0_0_40px_15px_rgba(236,72,153,0.4)] dark:shadow-[0_0_60px_25px_rgba(236,72,153,0.7)]",
hover: "hover:shadow-[0_0_50px_20px_rgba(236,72,153,0.5)] dark:hover:shadow-[0_0_80px_30px_rgba(236,72,153,0.8)]"
},
red: {
border: "border-red-500/50 dark:border-red-400/40",
glow: "shadow-[0_0_40px_15px_rgba(239,68,68,0.4)] dark:shadow-[0_0_60px_25px_rgba(239,68,68,0.7)]",
hover: "hover:shadow-[0_0_50px_20px_rgba(239,68,68,0.5)] dark:hover:shadow-[0_0_80px_30px_rgba(239,68,68,0.8)]"
}
},
// Size variants
sizes: {
sm: "p-4",
md: "p-6",
lg: "p-8",
xl: "p-10"
},
// Edge-lit effects for cards (top, left, right, bottom edges)
edgeLit: {
position: {
none: "",
top: "before:content-[''] before:absolute before:top-0 before:left-0 before:right-0 before:h-[2px] before:rounded-t-lg",
left: "before:content-[''] before:absolute before:top-0 before:left-0 before:bottom-0 before:w-[2px] before:rounded-l-lg",
right: "before:content-[''] before:absolute before:top-0 before:right-0 before:bottom-0 before:w-[2px] before:rounded-r-lg",
bottom: "before:content-[''] before:absolute before:bottom-0 before:left-0 before:right-0 before:h-[2px] before:rounded-b-lg"
},
color: {
purple: {
line: "before:bg-purple-500 dark:before:bg-purple-400",
glow: "before:shadow-[0_0_15px_4px_rgba(168,85,247,0.8)]",
gradient: {
horizontal: "before:bg-gradient-to-r before:from-transparent before:via-purple-500 dark:before:via-purple-400 before:to-transparent",
vertical: "before:bg-gradient-to-b before:from-transparent before:via-purple-500 dark:before:via-purple-400 before:to-transparent"
}
},
blue: {
line: "before:bg-blue-500 dark:before:bg-blue-400",
glow: "before:shadow-[0_0_15px_4px_rgba(59,130,246,0.8)]",
gradient: {
horizontal: "before:bg-gradient-to-r before:from-transparent before:via-blue-500 dark:before:via-blue-400 before:to-transparent",
vertical: "before:bg-gradient-to-b before:from-transparent before:via-blue-500 dark:before:via-blue-400 before:to-transparent"
}
},
cyan: {
line: "before:bg-cyan-500 dark:before:bg-cyan-400",
glow: "before:shadow-[0_0_15px_4px_rgba(34,211,238,0.8)]",
gradient: {
horizontal: "before:bg-gradient-to-r before:from-transparent before:via-cyan-500 dark:before:via-cyan-400 before:to-transparent",
vertical: "before:bg-gradient-to-b before:from-transparent before:via-cyan-500 dark:before:via-cyan-400 before:to-transparent"
}
},
green: {
line: "before:bg-emerald-500 dark:before:bg-emerald-400",
glow: "before:shadow-[0_0_15px_4px_rgba(16,185,129,0.8)]",
gradient: {
horizontal: "before:bg-gradient-to-r before:from-transparent before:via-emerald-500 dark:before:via-emerald-400 before:to-transparent",
vertical: "before:bg-gradient-to-b before:from-transparent before:via-emerald-500 dark:before:via-emerald-400 before:to-transparent"
}
},
orange: {
line: "before:bg-orange-500 dark:before:bg-orange-400",
glow: "before:shadow-[0_0_15px_4px_rgba(251,146,60,0.8)]",
gradient: {
horizontal: "before:bg-gradient-to-r before:from-transparent before:via-orange-500 dark:before:via-orange-400 before:to-transparent",
vertical: "before:bg-gradient-to-b before:from-transparent before:via-orange-500 dark:before:via-orange-400 before:to-transparent"
}
},
pink: {
line: "before:bg-pink-500 dark:before:bg-pink-400",
glow: "before:shadow-[0_0_15px_4px_rgba(236,72,153,0.8)]",
gradient: {
horizontal: "before:bg-gradient-to-r before:from-transparent before:via-pink-500 dark:before:via-pink-400 before:to-transparent",
vertical: "before:bg-gradient-to-b before:from-transparent before:via-pink-500 dark:before:via-pink-400 before:to-transparent"
}
},
red: {
line: "before:bg-red-500 dark:before:bg-red-400",
glow: "before:shadow-[0_0_15px_4px_rgba(239,68,68,0.8)]",
gradient: {
horizontal: "before:bg-gradient-to-r before:from-transparent before:via-red-500 dark:before:via-red-400 before:to-transparent",
vertical: "before:bg-gradient-to-b before:from-transparent before:via-red-500 dark:before:via-red-400 before:to-transparent"
}
}
}
}
};
// Compound styles for common patterns
export const compoundStyles = {
// Standard interactive element (buttons, menu items, etc.)
@@ -127,7 +347,7 @@ export const compoundStyles = {
${glassmorphism.interactive.disabled}
`,
// Cards and containers
// Cards - use glassCard instead
card: `
${glassmorphism.background.card}
${glassmorphism.border.default}

View File

@@ -0,0 +1,35 @@
import * as SwitchPrimitives from "@radix-ui/react-switch";
import * as React from "react";
import { cn, glassmorphism } from "./styles";
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full",
"transition-colors focus-visible:outline-none focus-visible:ring-2",
"focus-visible:ring-cyan-500 focus-visible:ring-offset-2",
"disabled:cursor-not-allowed disabled:opacity-50",
"data-[state=checked]:bg-cyan-500",
"data-[state=unchecked]:bg-gray-200 dark:data-[state=unchecked]:bg-gray-700",
glassmorphism.interactive.base,
className
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
"pointer-events-none block h-4 w-4 rounded-full",
"bg-white shadow-lg ring-0 transition-transform",
"data-[state=checked]:translate-x-4",
"data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitives.Root>
));
Switch.displayName = SwitchPrimitives.Root.displayName;
export { Switch };

View File

@@ -2,9 +2,10 @@ import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
import React from "react";
import { cn, glassmorphism } from "./styles";
export interface ToggleGroupProps extends React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> {
type ToggleGroupProps = (ToggleGroupPrimitive.ToggleGroupSingleProps | ToggleGroupPrimitive.ToggleGroupMultipleProps) & {
variant?: "subtle" | "solid";
size?: "sm" | "md";
className?: string;
}
export const ToggleGroup = React.forwardRef<React.ElementRef<typeof ToggleGroupPrimitive.Root>, ToggleGroupProps>(

View File

@@ -0,0 +1,8 @@
import React from 'react';
import { StyleGuideView } from '@/components/style-guide/StyleGuideView';
const StyleGuidePage: React.FC = () => {
return <StyleGuideView />;
};
export default StyleGuidePage;