Files
Aperant/.design-system/src/App.tsx.backup
T

2218 lines
80 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState, useEffect } from 'react'
import {
User,
Bell,
Calendar,
Settings,
Check,
X,
MoreVertical,
MessageSquare,
ChevronLeft,
ChevronRight,
Slack,
Github,
Video,
Sun,
Moon,
Play,
RotateCcw,
Sparkles,
Zap,
Heart,
Star,
ArrowRight,
Plus,
Minus
} from 'lucide-react'
import { motion, AnimatePresence, useMotionValue, useTransform, useSpring } from 'framer-motion'
import { cn } from './lib/utils'
// ============================================
// THEME SYSTEM
// ============================================
type ColorTheme = 'default' | 'dusk' | 'lime' | 'ocean' | 'retro' | 'neo' | 'forest'
type Mode = 'light' | 'dark'
interface ThemeConfig {
colorTheme: ColorTheme
mode: Mode
}
const COLOR_THEMES: { id: ColorTheme; name: string; description: string; previewColors: { bg: string; accent: string; darkBg: string; darkAccent?: string } }[] = [
{
id: 'default',
name: 'Default',
description: 'Oscura-inspired with pale yellow accent',
previewColors: { bg: '#F2F2ED', accent: '#E6E7A3', darkBg: '#0B0B0F', darkAccent: '#E6E7A3' }
},
{
id: 'dusk',
name: 'Dusk',
description: 'Warmer variant with slightly lighter dark mode',
previewColors: { bg: '#F5F5F0', accent: '#E6E7A3', darkBg: '#131419', darkAccent: '#E6E7A3' }
},
{
id: 'lime',
name: 'Lime',
description: 'Fresh, energetic lime with purple accents',
previewColors: { bg: '#E8F5A3', accent: '#7C3AED', darkBg: '#0F0F1A' }
},
{
id: 'ocean',
name: 'Ocean',
description: 'Calm, professional blue tones',
previewColors: { bg: '#E0F2FE', accent: '#0284C7', darkBg: '#082F49' }
},
{
id: 'retro',
name: 'Retro',
description: 'Warm, nostalgic amber vibes',
previewColors: { bg: '#FEF3C7', accent: '#D97706', darkBg: '#1C1917' }
},
{
id: 'neo',
name: 'Neo',
description: 'Modern cyberpunk pink/magenta',
previewColors: { bg: '#FDF4FF', accent: '#D946EF', darkBg: '#0F0720' }
},
{
id: 'forest',
name: 'Forest',
description: 'Natural, earthy green tones',
previewColors: { bg: '#DCFCE7', accent: '#16A34A', darkBg: '#052E16' }
}
]
function useTheme() {
const [config, setConfig] = useState<ThemeConfig>(() => {
if (typeof window !== 'undefined') {
const stored = localStorage.getItem('design-system-theme-config')
if (stored) {
try {
const parsed = JSON.parse(stored)
// Validate that the stored theme still exists
const themeExists = COLOR_THEMES.some(t => t.id === parsed.colorTheme)
if (themeExists) {
return parsed
}
// Fall back to default if theme was removed
return {
colorTheme: 'default' as ColorTheme,
mode: parsed.mode || 'light'
}
} catch {}
}
return {
colorTheme: 'default' as ColorTheme,
mode: window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
}
return { colorTheme: 'default', mode: 'light' }
})
useEffect(() => {
const root = document.documentElement
// Set color theme
if (config.colorTheme === 'default') {
root.removeAttribute('data-theme')
} else {
root.setAttribute('data-theme', config.colorTheme)
}
// Set mode
if (config.mode === 'dark') {
root.classList.add('dark')
} else {
root.classList.remove('dark')
}
localStorage.setItem('design-system-theme-config', JSON.stringify(config))
}, [config])
const setColorTheme = (colorTheme: ColorTheme) => setConfig(c => ({ ...c, colorTheme }))
const setMode = (mode: Mode) => setConfig(c => ({ ...c, mode }))
const toggleMode = () => setConfig(c => ({ ...c, mode: c.mode === 'light' ? 'dark' : 'light' }))
return {
colorTheme: config.colorTheme,
mode: config.mode,
setColorTheme,
setMode,
toggleMode,
themes: COLOR_THEMES
}
}
// Theme Selector Component
function ThemeSelector({
colorTheme,
mode,
onColorThemeChange,
onModeToggle,
themes
}: {
colorTheme: ColorTheme
mode: Mode
onColorThemeChange: (theme: ColorTheme) => void
onModeToggle: () => void
themes: typeof COLOR_THEMES
}) {
const [isOpen, setIsOpen] = useState(false)
// Find theme with fallback to first theme (default)
const currentTheme = themes.find(t => t.id === colorTheme) || themes[0]
return (
<div className="flex items-center gap-3">
{/* Color Theme Dropdown */}
<div className="relative">
<button
onClick={() => setIsOpen(!isOpen)}
className="flex items-center gap-2 px-3 py-2 rounded-[var(--radius-lg)] bg-[var(--color-background-secondary)] hover:bg-[var(--color-border-default)] transition-colors"
>
<div
className="w-4 h-4 rounded-full border-2 border-white shadow-sm"
style={{ backgroundColor: mode === 'dark' ? currentTheme.previewColors.accent : currentTheme.previewColors.bg }}
/>
<span className="text-body-medium font-medium">{currentTheme.name}</span>
<ChevronLeft className={cn(
"w-4 h-4 text-[var(--color-text-tertiary)] transition-transform",
isOpen ? "rotate-90" : "-rotate-90"
)} />
</button>
{isOpen && (
<>
<div
className="fixed inset-0 z-40"
onClick={() => setIsOpen(false)}
/>
<div className="absolute top-full right-0 mt-2 w-64 p-2 bg-[var(--color-surface-card)] rounded-[var(--radius-lg)] shadow-[var(--shadow-lg)] border border-[var(--color-border-default)] z-50">
{themes.map((theme) => (
<button
key={theme.id}
onClick={() => {
onColorThemeChange(theme.id)
setIsOpen(false)
}}
className={cn(
"w-full flex items-center gap-3 px-3 py-2 rounded-[var(--radius-md)] transition-colors text-left",
colorTheme === theme.id
? "bg-[var(--color-accent-primary-light)]"
: "hover:bg-[var(--color-background-secondary)]"
)}
>
<div className="flex -space-x-1">
<div
className="w-5 h-5 rounded-full border-2 border-white shadow-sm"
style={{ backgroundColor: theme.previewColors.bg }}
/>
<div
className="w-5 h-5 rounded-full border-2 border-white shadow-sm"
style={{ backgroundColor: theme.previewColors.accent }}
/>
</div>
<div className="flex-1 min-w-0">
<p className="text-body-medium font-medium">{theme.name}</p>
<p className="text-body-small text-[var(--color-text-tertiary)] truncate">{theme.description}</p>
</div>
{colorTheme === theme.id && (
<Check className="w-4 h-4 text-[var(--color-accent-primary)]" />
)}
</button>
))}
</div>
</>
)}
</div>
{/* Light/Dark Toggle */}
<button
onClick={onModeToggle}
className="p-2 rounded-[var(--radius-lg)] bg-[var(--color-background-secondary)] hover:bg-[var(--color-border-default)] transition-colors"
aria-label={`Switch to ${mode === 'light' ? 'dark' : 'light'} mode`}
>
{mode === 'light' ? (
<Moon className="w-5 h-5 text-[var(--color-text-secondary)]" />
) : (
<Sun className="w-5 h-5 text-[var(--color-text-secondary)]" />
)}
</button>
</div>
)
}
// ============================================
// DESIGN SYSTEM COMPONENTS
// ============================================
// Button Component
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'ghost' | 'success' | 'danger'
size?: 'sm' | 'md' | 'lg'
pill?: boolean
}
function Button({
children,
variant = 'primary',
size = 'md',
pill = false,
className,
...props
}: ButtonProps) {
const baseStyles = 'inline-flex items-center justify-center font-medium transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2'
const variants = {
primary: 'bg-[var(--color-accent-primary)] text-[var(--color-text-inverse)] hover:bg-[var(--color-accent-primary-hover)] focus:ring-[var(--color-accent-primary)]',
secondary: 'bg-transparent border border-[var(--color-border-default)] text-[var(--color-text-primary)] hover:bg-[var(--color-background-secondary)]',
ghost: 'bg-transparent text-[var(--color-text-secondary)] hover:bg-[var(--color-background-secondary)]',
success: 'bg-[var(--color-semantic-success)] text-white hover:opacity-90',
danger: 'bg-[var(--color-semantic-error)] text-white hover:opacity-90'
}
const sizes = {
sm: 'h-8 px-3 text-xs',
md: 'h-10 px-4 text-sm',
lg: 'h-12 px-6 text-base'
}
const radius = pill ? 'rounded-full' : 'rounded-[var(--radius-md)]'
return (
<button
className={cn(baseStyles, variants[variant], sizes[size], radius, className)}
{...props}
>
{children}
</button>
)
}
// Badge Component
interface BadgeProps {
children: React.ReactNode
variant?: 'default' | 'primary' | 'success' | 'warning' | 'error' | 'outline'
}
function Badge({ children, variant = 'default' }: BadgeProps) {
const variants = {
default: 'bg-[var(--color-background-secondary)] text-[var(--color-text-secondary)]',
primary: 'bg-[var(--color-accent-primary-light)] text-[var(--color-accent-primary)]',
success: 'bg-[var(--color-semantic-success-light)] text-[var(--color-semantic-success)]',
warning: 'bg-[var(--color-semantic-warning-light)] text-[var(--color-semantic-warning)]',
error: 'bg-[var(--color-semantic-error-light)] text-[var(--color-semantic-error)]',
outline: 'bg-transparent border border-[var(--color-border-default)] text-[var(--color-text-secondary)]'
}
return (
<span className={cn(
'inline-flex items-center px-3 py-1 rounded-full text-label-small',
variants[variant]
)}>
{children}
</span>
)
}
// Avatar Component
interface AvatarProps {
src?: string
name?: string
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
}
function Avatar({ src, name = 'User', size = 'md', color }: AvatarProps & { color?: string }) {
const sizes = {
xs: 'w-6 h-6 text-[10px]',
sm: 'w-8 h-8 text-xs',
md: 'w-10 h-10 text-sm',
lg: 'w-14 h-14 text-base',
xl: 'w-20 h-20 text-xl',
'2xl': 'w-[120px] h-[120px] text-3xl'
}
const initials = name.split(' ').map(n => n[0]).join('').slice(0, 2).toUpperCase()
// Default to neutral gray, can be overridden with color prop
const bgStyle = color
? { backgroundColor: color }
: {}
return (
<div
className={cn(
'rounded-full flex items-center justify-center font-semibold border-2 border-[var(--color-surface-card)] overflow-hidden',
!color && 'bg-[var(--color-border-default)]',
sizes[size]
)}
style={bgStyle}
>
{src ? (
<img src={src} alt={name} className="w-full h-full object-cover" />
) : (
<span className={cn(color ? 'text-white' : 'text-[var(--color-text-primary)]')}>{initials}</span>
)}
</div>
)
}
// Avatar Group
function AvatarGroup({ avatars, max = 4 }: { avatars: { name: string; src?: string }[]; max?: number }) {
const visible = avatars.slice(0, max)
const remaining = avatars.length - max
return (
<div className="flex -space-x-2">
{visible.map((avatar, i) => (
<Avatar key={i} {...avatar} size="sm" />
))}
{remaining > 0 && (
<div className="w-8 h-8 rounded-full bg-[var(--color-background-secondary)] flex items-center justify-center text-xs font-medium text-[var(--color-text-secondary)] border-2 border-[var(--color-surface-card)]">
+{remaining}
</div>
)}
</div>
)
}
// Progress Circle Component
function ProgressCircle({
value,
size = 'md',
color = 'var(--color-accent-primary)'
}: {
value: number
size?: 'sm' | 'md' | 'lg'
color?: string
}) {
const sizes = {
sm: { width: 40, stroke: 4, fontSize: 'text-[10px]' },
md: { width: 56, stroke: 5, fontSize: 'text-xs' },
lg: { width: 80, stroke: 6, fontSize: 'text-base' }
}
const { width, stroke, fontSize } = sizes[size]
const radius = (width - stroke) / 2
const circumference = 2 * Math.PI * radius
const offset = circumference - (value / 100) * circumference
return (
<div className="relative inline-flex items-center justify-center">
<svg width={width} height={width} className="-rotate-90">
<circle
cx={width / 2}
cy={width / 2}
r={radius}
fill="none"
stroke="var(--color-border-default)"
strokeWidth={stroke}
/>
<circle
cx={width / 2}
cy={width / 2}
r={radius}
fill="none"
stroke={color}
strokeWidth={stroke}
strokeDasharray={circumference}
strokeDashoffset={offset}
strokeLinecap="round"
className="transition-all duration-500"
/>
</svg>
<span className={cn('absolute font-semibold', fontSize)}>
{value}%
</span>
</div>
)
}
// Card Component
function Card({
children,
className,
padding = true
}: {
children: React.ReactNode
className?: string
padding?: boolean
}) {
return (
<div className={cn(
'bg-[var(--color-surface-card)] rounded-[var(--radius-xl)] shadow-[var(--shadow-md)]',
padding && 'p-6',
className
)}>
{children}
</div>
)
}
// Input Component
function Input({
placeholder,
className,
...props
}: React.InputHTMLAttributes<HTMLInputElement>) {
return (
<input
className={cn(
'h-10 w-full px-4 rounded-[var(--radius-md)] border border-[var(--color-border-default)]',
'bg-[var(--color-surface-card)] text-[var(--color-text-primary)] text-sm',
'focus:outline-none focus:border-[var(--color-accent-primary)] focus:ring-2 focus:ring-[var(--color-accent-primary)]/20',
'placeholder:text-[var(--color-text-tertiary)]',
'transition-all duration-200',
'disabled:bg-[var(--color-background-secondary)] disabled:opacity-60',
className
)}
placeholder={placeholder}
{...props}
/>
)
}
// Toggle Component
function Toggle({ checked, onChange }: { checked: boolean; onChange: (checked: boolean) => void }) {
return (
<button
role="switch"
aria-checked={checked}
onClick={() => onChange(!checked)}
className={cn(
'relative inline-flex h-6 w-11 items-center rounded-full transition-colors duration-200',
checked ? 'bg-[var(--color-accent-primary)]' : 'bg-[var(--color-border-default)]'
)}
>
<span
className={cn(
'inline-block h-5 w-5 rounded-full bg-white shadow-sm transition-transform duration-200',
checked ? 'translate-x-[22px]' : 'translate-x-[2px]'
)}
/>
</button>
)
}
// ============================================
// DEMO COMPONENTS (Matching the screenshot)
// ============================================
// Profile Card
function ProfileCard() {
return (
<Card className="w-[280px]">
<div className="flex justify-end mb-4">
<button className="p-1 hover:bg-[var(--color-background-secondary)] rounded transition-colors">
<MoreVertical className="w-5 h-5 text-[var(--color-text-tertiary)]" />
</button>
</div>
<div className="flex flex-col items-center text-center">
<Avatar size="2xl" name="Christine Thompson" />
<h3 className="text-heading-large mt-4">Christine Thompson</h3>
<p className="text-body-medium text-[var(--color-text-secondary)]">Project manager</p>
<div className="flex flex-wrap gap-2 mt-4 justify-center">
<Badge variant="outline">UI/UX Design</Badge>
<Badge variant="outline">Project management</Badge>
<Badge variant="outline">Agile methodologies</Badge>
</div>
</div>
</Card>
)
}
// Notifications Card
function NotificationsCard() {
return (
<Card className="w-[320px]" padding={false}>
<div className="p-4 border-b border-[var(--color-border-default)]">
<div className="flex items-center justify-between">
<h3 className="text-heading-small">Notifications</h3>
<Badge variant="primary">6</Badge>
</div>
<p className="text-body-small text-[var(--color-text-tertiary)] mt-1">Unread</p>
</div>
<div className="divide-y divide-[var(--color-border-default)]">
<div className="p-4 flex gap-3">
<Avatar size="sm" name="Ashlynn George" />
<div className="flex-1 min-w-0">
<p className="text-body-small">
<span className="font-semibold">Ashlynn George</span>
<span className="text-[var(--color-text-tertiary)]"> · 1h</span>
</p>
<p className="text-body-small text-[var(--color-text-secondary)]">
has invited you to access "Magma project"
</p>
<div className="flex gap-2 mt-2">
<Button size="sm" variant="success" pill>
<Check className="w-3 h-3 mr-1" /> Accept
</Button>
<Button size="sm" variant="secondary" pill>
<X className="w-3 h-3 mr-1" /> Deny request
</Button>
</div>
</div>
<button className="p-1 hover:bg-[var(--color-background-secondary)] rounded self-start transition-colors">
<MoreVertical className="w-4 h-4 text-[var(--color-text-tertiary)]" />
</button>
</div>
<div className="p-4 flex gap-3">
<Avatar size="sm" name="Ashlynn George" />
<div className="flex-1">
<p className="text-body-small">
<span className="font-semibold">Ashlynn George</span>
<span className="text-[var(--color-text-tertiary)]"> · 1h</span>
</p>
<p className="text-body-small text-[var(--color-text-secondary)]">
changed status of task in "Magma project"
</p>
</div>
<button className="p-1 hover:bg-[var(--color-background-secondary)] rounded self-start transition-colors">
<MoreVertical className="w-4 h-4 text-[var(--color-text-tertiary)]" />
</button>
</div>
</div>
<div className="p-4 flex gap-2 border-t border-[var(--color-border-default)]">
<Button variant="secondary" className="flex-1" pill>Mark all as read</Button>
<Button variant="primary" className="flex-1" pill>View all</Button>
</div>
</Card>
)
}
// Calendar Card
function CalendarCard() {
const days = ['M', 'T', 'W', 'T', 'F', 'S', 'S']
const dates = [
[29, 30, 31, 1, 2, 3, 4],
[5, 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17, 18],
[19, 20, 21, 22, 23, 24, 25],
[26, 27, 28, 29, 30, 31, 1]
]
return (
<Card className="w-[300px]">
<div className="flex items-center justify-between mb-4">
<button className="p-1 hover:bg-[var(--color-background-secondary)] rounded transition-colors">
<ChevronLeft className="w-5 h-5 text-[var(--color-text-tertiary)]" />
</button>
<h3 className="text-heading-small">February, 2021</h3>
<button className="p-1 hover:bg-[var(--color-background-secondary)] rounded transition-colors">
<ChevronRight className="w-5 h-5 text-[var(--color-text-tertiary)]" />
</button>
</div>
<div className="grid grid-cols-7 gap-1 text-center">
{days.map((day, i) => (
<div key={i} className="text-label-small text-[var(--color-text-tertiary)] py-2">
{day}
</div>
))}
{dates.flat().map((date, i) => {
const isCurrentMonth = (i < 3 && date > 20) || (i > 30 && date < 10) ? false : true
const isSelected = date === 26 && isCurrentMonth
const isToday = date === 16 && isCurrentMonth
return (
<button
key={i}
className={cn(
'w-9 h-9 rounded-[var(--radius-md)] text-body-medium transition-colors',
!isCurrentMonth && 'text-[var(--color-text-tertiary)]',
isSelected && 'bg-[var(--color-accent-primary)] text-[var(--color-text-inverse)] rounded-full',
isToday && !isSelected && 'text-[var(--color-accent-primary)] font-semibold',
!isSelected && 'hover:bg-[var(--color-background-secondary)]'
)}
>
{date}
</button>
)
})}
</div>
</Card>
)
}
// Team Members Card
function TeamMembersCard() {
const members = [
{ name: 'Julie Andrews', role: 'Project manager' },
{ name: 'Kevin Conroy', role: 'Project manager' },
{ name: 'Jim Connor', role: 'Project manager' },
{ name: 'Tom Kinley', role: 'Project manager' }
]
return (
<Card className="w-[320px]" padding={false}>
<div className="divide-y divide-[var(--color-border-default)]">
{members.map((member, i) => (
<div key={i} className="p-4 flex items-center gap-3">
<Avatar name={member.name} />
<div className="flex-1">
<p className="text-heading-small">{member.name}</p>
<p className="text-body-small text-[var(--color-text-secondary)]">{member.role}</p>
</div>
<button className="p-1 hover:bg-[var(--color-background-secondary)] rounded transition-colors">
<MoreVertical className="w-4 h-4 text-[var(--color-text-tertiary)]" />
</button>
<button className="p-2 bg-[var(--color-semantic-error-light)] text-[var(--color-semantic-error)] rounded-[var(--radius-md)] hover:opacity-80 transition-opacity">
<MessageSquare className="w-4 h-4" />
</button>
</div>
))}
</div>
<div className="p-4 border-t border-[var(--color-border-default)] flex justify-center gap-3">
<img src="https://upload.wikimedia.org/wikipedia/commons/b/ba/Stripe_Logo%2C_revised_2016.svg" alt="Stripe" className="h-6" />
<div className="px-3 py-1 bg-[#1A1F71] text-white text-sm font-bold rounded">VISA</div>
<div className="px-2 py-1 bg-[#003087] text-white text-xs font-bold rounded">PayPal</div>
<div className="w-8 h-8 bg-gradient-to-r from-red-500 to-yellow-500 rounded-full" />
</div>
</Card>
)
}
// Project Status Card
function ProjectStatusCard() {
return (
<Card className="w-[380px]">
<div className="flex justify-between items-start mb-4">
<ProgressCircle value={43} size="md" />
<button className="p-1 hover:bg-[var(--color-background-secondary)] rounded transition-colors">
<MoreVertical className="w-5 h-5 text-[var(--color-text-tertiary)]" />
</button>
</div>
<h3 className="text-heading-large mb-2">Amber website redesign</h3>
<p className="text-body-medium text-[var(--color-text-secondary)] mb-4">
In today's fast-paced digital landscape, our mission is to transform our website into a more intuitive, engaging, and user-friendly platfor...
</p>
<AvatarGroup
avatars={[
{ name: 'User 1' },
{ name: 'User 2' },
{ name: 'User 3' },
{ name: 'User 4' },
{ name: 'User 5' }
]}
max={4}
/>
</Card>
)
}
// Milestone Card
function MilestoneCard() {
return (
<Card className="w-[380px]">
<div className="flex items-center justify-between mb-4">
<h3 className="text-heading-medium">Wireframes milestone</h3>
<Button variant="secondary" size="sm" pill>View details</Button>
</div>
<div className="flex items-center gap-6">
<div>
<p className="text-body-small text-[var(--color-text-secondary)]">Due date:</p>
<p className="text-heading-small">March 20th</p>
</div>
<ProgressCircle value={39} size="lg" />
<div>
<p className="text-body-small text-[var(--color-text-secondary)]">Asignees:</p>
<AvatarGroup
avatars={[
{ name: 'A' },
{ name: 'B' },
{ name: 'C' },
{ name: 'D' },
{ name: 'E' }
]}
max={4}
/>
</div>
</div>
</Card>
)
}
// Integrations Card
function IntegrationsCard() {
const [slack, setSlack] = useState(true)
const [meet, setMeet] = useState(true)
const [github, setGithub] = useState(false)
const integrations = [
{ icon: Slack, name: 'Slack', desc: 'Used as a main source of communication', enabled: slack, toggle: setSlack, color: '#E91E63' },
{ icon: Video, name: 'Google meet', desc: 'Used for all types of calls', enabled: meet, toggle: setMeet, color: '#00897B' },
{ icon: Github, name: 'Github', desc: 'Enables automated workflows, code synchronization', enabled: github, toggle: setGithub, color: '#333' }
]
return (
<Card className="w-[320px]">
<h3 className="text-heading-medium mb-4">Integrations</h3>
<div className="space-y-4">
{integrations.map((int, i) => (
<div key={i} className="flex items-center gap-3">
<div
className="w-10 h-10 rounded-[var(--radius-lg)] flex items-center justify-center"
style={{ backgroundColor: `${int.color}15` }}
>
<int.icon className="w-5 h-5" style={{ color: int.color }} />
</div>
<div className="flex-1 min-w-0">
<p className="text-heading-small">{int.name}</p>
<p className="text-body-small text-[var(--color-text-secondary)] truncate">{int.desc}</p>
</div>
<Toggle checked={int.enabled} onChange={int.toggle} />
</div>
))}
</div>
</Card>
)
}
// ============================================
// MAIN APP
// ============================================
export default function App() {
const [activeSection, setActiveSection] = useState('overview')
const { colorTheme, mode, setColorTheme, toggleMode, themes } = useTheme()
const sections = [
{ id: 'overview', label: 'Overview' },
{ id: 'colors', label: 'Colors' },
{ id: 'typography', label: 'Typography' },
{ id: 'components', label: 'Components' },
{ id: 'animations', label: 'Animations' },
{ id: 'themes', label: 'Themes' }
]
const currentThemeInfo = themes.find(t => t.id === colorTheme) || themes[0]
return (
<div className="min-h-screen p-8 transition-colors duration-300">
{/* Header */}
<div className="max-w-7xl mx-auto mb-8">
<Card className="!rounded-[var(--radius-2xl)]">
<div className="flex items-center justify-between">
<div>
<h1 className="text-display-medium">Auto-Build Design System</h1>
<p className="text-body-large text-[var(--color-text-secondary)] mt-1">
A modern, friendly design system for building beautiful interfaces
</p>
</div>
<div className="flex items-center gap-4">
{/* Theme Selector */}
<ThemeSelector
colorTheme={colorTheme}
mode={mode}
onColorThemeChange={setColorTheme}
onModeToggle={toggleMode}
themes={themes}
/>
{/* Section Navigation */}
<div className="flex gap-2">
{sections.map((section) => (
<Button
key={section.id}
variant={activeSection === section.id ? 'primary' : 'ghost'}
pill
onClick={() => setActiveSection(section.id)}
>
{section.label}
</Button>
))}
</div>
</div>
</div>
</Card>
</div>
{/* Content */}
<div className="max-w-7xl mx-auto">
{activeSection === 'overview' && (
<div className="space-y-8">
{/* Demo Cards Grid - Replicating the screenshot layout */}
<section>
<h2 className="text-heading-large mb-6">Component Showcase</h2>
<div className="flex flex-wrap gap-6">
<ProfileCard />
<CalendarCard />
<ProjectStatusCard />
</div>
<div className="flex flex-wrap gap-6 mt-6">
<NotificationsCard />
<TeamMembersCard />
<div className="space-y-6">
<MilestoneCard />
<IntegrationsCard />
</div>
</div>
</section>
</div>
)}
{activeSection === 'colors' && (
<div className="space-y-8">
<Card>
<div className="flex items-center justify-between mb-6">
<h2 className="text-heading-large">Color Palette</h2>
<p className="text-body-small text-[var(--color-text-tertiary)]">
Currently showing: <strong className="text-[var(--color-text-primary)]">{currentThemeInfo.name}</strong> theme
</p>
</div>
<div className="space-y-6">
<div>
<h3 className="text-heading-small mb-3">Background</h3>
<div className="flex gap-4">
<div className="text-center">
<div className="w-20 h-20 rounded-[var(--radius-lg)] bg-[var(--color-background-primary)] border border-[var(--color-border-default)]" />
<p className="text-label-small mt-2">Primary</p>
<p className="text-body-small text-[var(--color-text-tertiary)]">--bg-primary</p>
</div>
<div className="text-center">
<div className="w-20 h-20 rounded-[var(--radius-lg)] bg-[var(--color-background-secondary)] border border-[var(--color-border-default)]" />
<p className="text-label-small mt-2">Secondary</p>
<p className="text-body-small text-[var(--color-text-tertiary)]">--bg-secondary</p>
</div>
</div>
</div>
<div>
<h3 className="text-heading-small mb-3">Accent</h3>
<div className="flex gap-4">
<div className="text-center">
<div className="w-20 h-20 rounded-[var(--radius-lg)] bg-[var(--color-accent-primary)]" />
<p className="text-label-small mt-2">Primary</p>
<p className="text-body-small text-[var(--color-text-tertiary)]">--accent</p>
</div>
<div className="text-center">
<div className="w-20 h-20 rounded-[var(--radius-lg)] bg-[var(--color-accent-primary-hover)]" />
<p className="text-label-small mt-2">Hover</p>
<p className="text-body-small text-[var(--color-text-tertiary)]">--accent-hover</p>
</div>
<div className="text-center">
<div className="w-20 h-20 rounded-[var(--radius-lg)] bg-[var(--color-accent-primary-light)] border border-[var(--color-border-default)]" />
<p className="text-label-small mt-2">Light</p>
<p className="text-body-small text-[var(--color-text-tertiary)]">--accent-light</p>
</div>
</div>
</div>
<div>
<h3 className="text-heading-small mb-3">Semantic</h3>
<div className="flex gap-4">
<div className="text-center">
<div className="w-20 h-20 rounded-[var(--radius-lg)] bg-[var(--color-semantic-success)]" />
<p className="text-label-small mt-2">Success</p>
<p className="text-body-small text-[var(--color-text-tertiary)]">--success</p>
</div>
<div className="text-center">
<div className="w-20 h-20 rounded-[var(--radius-lg)] bg-[var(--color-semantic-warning)]" />
<p className="text-label-small mt-2">Warning</p>
<p className="text-body-small text-[var(--color-text-tertiary)]">--warning</p>
</div>
<div className="text-center">
<div className="w-20 h-20 rounded-[var(--radius-lg)] bg-[var(--color-semantic-error)]" />
<p className="text-label-small mt-2">Error</p>
<p className="text-body-small text-[var(--color-text-tertiary)]">--error</p>
</div>
<div className="text-center">
<div className="w-20 h-20 rounded-[var(--radius-lg)] bg-[var(--color-semantic-info)]" />
<p className="text-label-small mt-2">Info</p>
<p className="text-body-small text-[var(--color-text-tertiary)]">--info</p>
</div>
</div>
</div>
<div>
<h3 className="text-heading-small mb-3">Text</h3>
<div className="flex gap-4">
<div className="text-center">
<div className="w-20 h-20 rounded-[var(--radius-lg)] bg-[var(--color-text-primary)]" />
<p className="text-label-small mt-2">Primary</p>
<p className="text-body-small text-[var(--color-text-tertiary)]">--text-primary</p>
</div>
<div className="text-center">
<div className="w-20 h-20 rounded-[var(--radius-lg)] bg-[var(--color-text-secondary)]" />
<p className="text-label-small mt-2">Secondary</p>
<p className="text-body-small text-[var(--color-text-tertiary)]">--text-secondary</p>
</div>
<div className="text-center">
<div className="w-20 h-20 rounded-[var(--radius-lg)] bg-[var(--color-text-tertiary)]" />
<p className="text-label-small mt-2">Tertiary</p>
<p className="text-body-small text-[var(--color-text-tertiary)]">--text-tertiary</p>
</div>
</div>
</div>
</div>
{/* Theme-specific color values */}
<div className="mt-8 p-4 bg-[var(--color-background-secondary)] rounded-[var(--radius-lg)]">
<p className="text-body-small text-[var(--color-text-secondary)]">
<strong>Note:</strong> Colors vary by theme and mode. Switch themes using the dropdown above to see different palettes.
For specific hex values, see the <strong>Themes</strong> tab or check <code className="font-mono bg-[var(--color-background-neutral)] px-1 rounded">design.json</code>.
</p>
</div>
</Card>
</div>
)}
{activeSection === 'typography' && (
<div className="space-y-8">
<Card>
<h2 className="text-heading-large mb-6">Typography Scale</h2>
<div className="space-y-6">
<div className="border-b border-[var(--color-border-default)] pb-4">
<p className="text-label-small text-[var(--color-text-tertiary)] mb-2">Display Large • 36px / 700</p>
<p className="text-display-large">The quick brown fox jumps</p>
</div>
<div className="border-b border-[var(--color-border-default)] pb-4">
<p className="text-label-small text-[var(--color-text-tertiary)] mb-2">Display Medium • 30px / 700</p>
<p className="text-display-medium">The quick brown fox jumps over</p>
</div>
<div className="border-b border-[var(--color-border-default)] pb-4">
<p className="text-label-small text-[var(--color-text-tertiary)] mb-2">Heading Large • 24px / 600</p>
<p className="text-heading-large">The quick brown fox jumps over the lazy dog</p>
</div>
<div className="border-b border-[var(--color-border-default)] pb-4">
<p className="text-label-small text-[var(--color-text-tertiary)] mb-2">Heading Medium • 20px / 600</p>
<p className="text-heading-medium">The quick brown fox jumps over the lazy dog</p>
</div>
<div className="border-b border-[var(--color-border-default)] pb-4">
<p className="text-label-small text-[var(--color-text-tertiary)] mb-2">Heading Small • 16px / 600</p>
<p className="text-heading-small">The quick brown fox jumps over the lazy dog</p>
</div>
<div className="border-b border-[var(--color-border-default)] pb-4">
<p className="text-label-small text-[var(--color-text-tertiary)] mb-2">Body Large • 16px / 400</p>
<p className="text-body-large">The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs.</p>
</div>
<div className="border-b border-[var(--color-border-default)] pb-4">
<p className="text-label-small text-[var(--color-text-tertiary)] mb-2">Body Medium • 14px / 400</p>
<p className="text-body-medium">The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs.</p>
</div>
<div>
<p className="text-label-small text-[var(--color-text-tertiary)] mb-2">Body Small • 12px / 400</p>
<p className="text-body-small">The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs.</p>
</div>
</div>
</Card>
</div>
)}
{activeSection === 'components' && (
<div className="space-y-8">
{/* Buttons */}
<Card>
<h2 className="text-heading-large mb-6">Buttons</h2>
<div className="space-y-6">
<div>
<h3 className="text-heading-small mb-3">Variants</h3>
<div className="flex flex-wrap gap-4">
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="success">Success</Button>
<Button variant="danger">Danger</Button>
</div>
</div>
<div>
<h3 className="text-heading-small mb-3">Pill Buttons</h3>
<div className="flex flex-wrap gap-4">
<Button variant="primary" pill>Primary Pill</Button>
<Button variant="secondary" pill>Secondary Pill</Button>
<Button variant="ghost" pill>Ghost Pill</Button>
</div>
</div>
<div>
<h3 className="text-heading-small mb-3">Sizes</h3>
<div className="flex flex-wrap items-center gap-4">
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
</div>
</div>
</div>
</Card>
{/* Badges */}
<Card>
<h2 className="text-heading-large mb-6">Badges</h2>
<div className="flex flex-wrap gap-4">
<Badge variant="default">Default</Badge>
<Badge variant="primary">Primary</Badge>
<Badge variant="success">Success</Badge>
<Badge variant="warning">Warning</Badge>
<Badge variant="error">Error</Badge>
<Badge variant="outline">Outline</Badge>
</div>
</Card>
{/* Avatars */}
<Card>
<h2 className="text-heading-large mb-6">Avatars</h2>
<div className="space-y-6">
<div>
<h3 className="text-heading-small mb-3">Sizes</h3>
<div className="flex items-end gap-4">
<Avatar size="xs" name="XS" />
<Avatar size="sm" name="SM" />
<Avatar size="md" name="MD" />
<Avatar size="lg" name="LG" />
<Avatar size="xl" name="XL" />
<Avatar size="2xl" name="2XL" />
</div>
</div>
<div>
<h3 className="text-heading-small mb-3">Avatar Group</h3>
<AvatarGroup
avatars={[
{ name: 'Alice' },
{ name: 'Bob' },
{ name: 'Charlie' },
{ name: 'Diana' },
{ name: 'Eve' },
{ name: 'Frank' }
]}
max={4}
/>
</div>
</div>
</Card>
{/* Progress */}
<Card>
<h2 className="text-heading-large mb-6">Progress Circles</h2>
<div className="flex items-center gap-8">
<ProgressCircle value={25} size="sm" />
<ProgressCircle value={50} size="md" />
<ProgressCircle value={75} size="lg" />
<ProgressCircle value={100} size="lg" color="var(--color-semantic-success)" />
</div>
</Card>
{/* Inputs */}
<Card>
<h2 className="text-heading-large mb-6">Inputs</h2>
<div className="max-w-md space-y-4">
<Input placeholder="Default input" />
<Input placeholder="Disabled input" disabled />
</div>
</Card>
{/* Toggles */}
<Card>
<h2 className="text-heading-large mb-6">Toggles</h2>
<div className="flex gap-8">
<div className="flex items-center gap-3">
<Toggle checked={false} onChange={() => {}} />
<span className="text-body-medium">Off</span>
</div>
<div className="flex items-center gap-3">
<Toggle checked={true} onChange={() => {}} />
<span className="text-body-medium">On</span>
</div>
</div>
</Card>
{/* Cards */}
<Card>
<h2 className="text-heading-large mb-6">Cards</h2>
<div className="flex gap-4">
<Card className="w-64">
<h3 className="text-heading-small">Card Title</h3>
<p className="text-body-medium text-[var(--color-text-secondary)] mt-2">
This is a basic card with some content inside.
</p>
</Card>
<Card className="w-64 !rounded-[var(--radius-2xl)]">
<h3 className="text-heading-small">Large Radius</h3>
<p className="text-body-medium text-[var(--color-text-secondary)] mt-2">
This card uses the 2xl border radius.
</p>
</Card>
</div>
</Card>
</div>
)}
{activeSection === 'animations' && (
<AnimationsSection theme={mode} colorTheme={currentThemeInfo.name} />
)}
{activeSection === 'themes' && (
<ThemesSection
currentTheme={colorTheme}
currentMode={mode}
themes={themes}
onThemeChange={setColorTheme}
onModeChange={toggleMode}
/>
)}
</div>
</div>
)
}
// ============================================
// ANIMATIONS SECTION
// ============================================
// Animation Variants - Reusable motion configs
const animationVariants = {
// Fade animations
fadeIn: {
initial: { opacity: 0 },
animate: { opacity: 1 },
exit: { opacity: 0 }
},
// Scale animations
scaleIn: {
initial: { opacity: 0, scale: 0.9 },
animate: { opacity: 1, scale: 1 },
exit: { opacity: 0, scale: 0.9 }
},
// Slide animations
slideUp: {
initial: { opacity: 0, y: 20 },
animate: { opacity: 1, y: 0 },
exit: { opacity: 0, y: -20 }
},
slideDown: {
initial: { opacity: 0, y: -20 },
animate: { opacity: 1, y: 0 },
exit: { opacity: 0, y: 20 }
},
slideLeft: {
initial: { opacity: 0, x: 20 },
animate: { opacity: 1, x: 0 },
exit: { opacity: 0, x: -20 }
},
slideRight: {
initial: { opacity: 0, x: -20 },
animate: { opacity: 1, x: 0 },
exit: { opacity: 0, x: 20 }
},
// Spring pop
pop: {
initial: { opacity: 0, scale: 0.5 },
animate: {
opacity: 1,
scale: 1,
transition: { type: 'spring', stiffness: 500, damping: 25 }
},
exit: { opacity: 0, scale: 0.5 }
},
// Bounce
bounce: {
initial: { opacity: 0, y: -50 },
animate: {
opacity: 1,
y: 0,
transition: { type: 'spring', stiffness: 300, damping: 10 }
}
}
}
// Transition presets
const transitions = {
instant: { duration: 0.05 },
fast: { duration: 0.15 },
normal: { duration: 0.25 },
slow: { duration: 0.4 },
spring: { type: 'spring', stiffness: 400, damping: 25 },
springBouncy: { type: 'spring', stiffness: 300, damping: 10 },
springSmooth: { type: 'spring', stiffness: 200, damping: 20 },
easeOut: { duration: 0.25, ease: [0, 0, 0.2, 1] },
easeIn: { duration: 0.25, ease: [0.4, 0, 1, 1] },
easeInOut: { duration: 0.25, ease: [0.4, 0, 0.2, 1] }
}
// Demo component for showcasing an animation
function AnimationDemo({
title,
description,
children,
code
}: {
title: string
description: string
children: React.ReactNode
code?: string
}) {
const [key, setKey] = useState(0)
return (
<div className="bg-[var(--color-surface-card)] rounded-[var(--radius-xl)] shadow-[var(--shadow-md)] overflow-hidden border border-[var(--color-border-default)]">
<div className="p-6 border-b border-[var(--color-border-default)]">
<div className="flex items-center justify-between mb-2">
<h3 className="text-heading-small">{title}</h3>
<button
onClick={() => setKey(k => k + 1)}
className="p-2 rounded-[var(--radius-md)] bg-[var(--color-background-secondary)] hover:bg-[var(--color-border-default)] transition-colors"
title="Replay animation"
>
<RotateCcw className="w-4 h-4 text-[var(--color-text-secondary)]" />
</button>
</div>
<p className="text-body-small text-[var(--color-text-secondary)]">{description}</p>
</div>
<div className="p-8 bg-[var(--color-background-secondary)] min-h-[160px] flex items-center justify-center">
<div key={key}>
{children}
</div>
</div>
{code && (
<div className="p-4 bg-[var(--color-background-neutral)] border-t border-[var(--color-border-default)]">
<pre className="text-body-small font-mono text-[var(--color-text-secondary)] overflow-x-auto">
{code}
</pre>
</div>
)}
</div>
)
}
// Interactive hover card demo
function HoverCardDemo() {
return (
<motion.div
className="w-48 h-32 bg-[var(--color-surface-card)] rounded-[var(--radius-xl)] shadow-[var(--shadow-md)] flex items-center justify-center cursor-pointer border border-[var(--color-border-default)]"
whileHover={{
scale: 1.05,
boxShadow: 'var(--shadow-lg)',
y: -4
}}
whileTap={{ scale: 0.98 }}
transition={transitions.spring}
>
<span className="text-body-medium text-[var(--color-text-secondary)]">Hover me</span>
</motion.div>
)
}
// Button press demo
function ButtonPressDemo() {
return (
<motion.button
className="px-6 py-3 bg-[var(--color-accent-primary)] text-[var(--color-text-inverse)] rounded-[var(--radius-md)] font-medium"
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.95 }}
transition={transitions.spring}
>
Press me
</motion.button>
)
}
// Staggered list demo
function StaggeredListDemo() {
const items = ['First item', 'Second item', 'Third item', 'Fourth item']
const container = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.1
}
}
}
const item = {
hidden: { opacity: 0, x: -20 },
show: { opacity: 1, x: 0 }
}
return (
<motion.ul
className="space-y-2"
variants={container}
initial="hidden"
animate="show"
>
{items.map((text, i) => (
<motion.li
key={i}
variants={item}
className="px-4 py-2 bg-[var(--color-surface-card)] rounded-[var(--radius-md)] text-body-medium border border-[var(--color-border-default)]"
>
{text}
</motion.li>
))}
</motion.ul>
)
}
// Notification toast demo
function ToastDemo() {
const [show, setShow] = useState(true)
useEffect(() => {
if (!show) {
const timer = setTimeout(() => setShow(true), 500)
return () => clearTimeout(timer)
}
}, [show])
return (
<div className="relative h-20 w-72">
<AnimatePresence>
{show && (
<motion.div
initial={{ opacity: 0, y: 50, scale: 0.9 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: -20, scale: 0.9 }}
transition={transitions.spring}
className="absolute inset-x-0 bottom-0 px-4 py-3 bg-[var(--color-surface-card)] rounded-[var(--radius-lg)] shadow-[var(--shadow-lg)] flex items-center gap-3 border border-[var(--color-border-default)]"
>
<div className="w-8 h-8 rounded-full bg-[var(--color-semantic-success-light)] flex items-center justify-center">
<Check className="w-4 h-4 text-[var(--color-semantic-success)]" />
</div>
<div className="flex-1">
<p className="text-body-small font-medium">Success!</p>
<p className="text-body-small text-[var(--color-text-tertiary)]">Action completed</p>
</div>
<button
onClick={() => setShow(false)}
className="p-1 hover:bg-[var(--color-background-secondary)] rounded transition-colors"
>
<X className="w-4 h-4 text-[var(--color-text-tertiary)]" />
</button>
</motion.div>
)}
</AnimatePresence>
</div>
)
}
// Modal demo
function ModalDemo() {
const [isOpen, setIsOpen] = useState(false)
return (
<div className="relative">
<Button onClick={() => setIsOpen(true)}>Open Modal</Button>
<AnimatePresence>
{isOpen && (
<>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={transitions.fast}
className="fixed inset-0 bg-black/50 z-40"
onClick={() => setIsOpen(false)}
/>
<motion.div
initial={{ opacity: 0, scale: 0.9, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.9, y: 20 }}
transition={transitions.springSmooth}
className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-80 p-6 bg-[var(--color-surface-card)] rounded-[var(--radius-2xl)] shadow-[var(--shadow-xl)] z-50"
>
<h3 className="text-heading-medium mb-2">Modal Title</h3>
<p className="text-body-medium text-[var(--color-text-secondary)] mb-4">
This is a modal dialog with smooth enter/exit animations.
</p>
<div className="flex gap-2 justify-end">
<Button variant="secondary" onClick={() => setIsOpen(false)}>Cancel</Button>
<Button onClick={() => setIsOpen(false)}>Confirm</Button>
</div>
</motion.div>
</>
)}
</AnimatePresence>
</div>
)
}
// Counter animation demo
function CounterDemo() {
const [count, setCount] = useState(0)
return (
<div className="flex items-center gap-4">
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
onClick={() => setCount(c => c - 1)}
className="w-10 h-10 rounded-full bg-[var(--color-background-secondary)] flex items-center justify-center border border-[var(--color-border-default)]"
>
<Minus className="w-4 h-4" />
</motion.button>
<div className="w-20 text-center overflow-hidden">
<AnimatePresence mode="popLayout">
<motion.span
key={count}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={transitions.spring}
className="text-display-medium inline-block"
>
{count}
</motion.span>
</AnimatePresence>
</div>
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
onClick={() => setCount(c => c + 1)}
className="w-10 h-10 rounded-full bg-[var(--color-accent-primary)] text-[var(--color-text-inverse)] flex items-center justify-center"
>
<Plus className="w-4 h-4" />
</motion.button>
</div>
)
}
// Loading spinner demo
function LoadingDemo() {
return (
<div className="flex items-center gap-8">
{/* Spinning loader */}
<motion.div
className="w-8 h-8 border-3 border-[var(--color-border-default)] border-t-[var(--color-accent-primary)] rounded-full"
animate={{ rotate: 360 }}
transition={{ duration: 1, repeat: Infinity, ease: 'linear' }}
/>
{/* Pulsing dots */}
<div className="flex gap-1">
{[0, 1, 2].map((i) => (
<motion.div
key={i}
className="w-2 h-2 bg-[var(--color-accent-primary)] rounded-full"
animate={{ scale: [1, 1.5, 1], opacity: [1, 0.5, 1] }}
transition={{
duration: 0.8,
repeat: Infinity,
delay: i * 0.15,
ease: 'easeInOut'
}}
/>
))}
</div>
{/* Bouncing dots */}
<div className="flex gap-1">
{[0, 1, 2].map((i) => (
<motion.div
key={i}
className="w-2 h-2 bg-[var(--color-semantic-success)] rounded-full"
animate={{ y: [0, -8, 0] }}
transition={{
duration: 0.5,
repeat: Infinity,
delay: i * 0.1,
ease: 'easeInOut'
}}
/>
))}
</div>
</div>
)
}
// Drag demo
function DragDemo() {
return (
<div className="relative w-64 h-32 bg-[var(--color-background-neutral)] rounded-[var(--radius-lg)] border-2 border-dashed border-[var(--color-border-default)]">
<motion.div
drag
dragConstraints={{ left: 0, right: 176, top: 0, bottom: 64 }}
dragElastic={0.1}
whileDrag={{ scale: 1.1, cursor: 'grabbing' }}
className="absolute w-16 h-16 bg-[var(--color-accent-primary)] rounded-[var(--radius-lg)] flex items-center justify-center cursor-grab shadow-[var(--shadow-md)]"
>
<span className="text-white text-body-small">Drag</span>
</motion.div>
</div>
)
}
// Progress animation demo
function ProgressAnimationDemo() {
const [progress, setProgress] = useState(0)
useEffect(() => {
const timer = setTimeout(() => {
setProgress(75)
}, 300)
return () => clearTimeout(timer)
}, [])
return (
<div className="w-64 space-y-4">
<div className="h-2 bg-[var(--color-border-default)] rounded-full overflow-hidden">
<motion.div
className="h-full bg-[var(--color-accent-primary)] rounded-full"
initial={{ width: 0 }}
animate={{ width: `${progress}%` }}
transition={{ duration: 1, ease: 'easeOut' }}
/>
</div>
<div className="flex justify-between text-body-small text-[var(--color-text-secondary)]">
<span>Progress</span>
<motion.span
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.5 }}
>
{progress}%
</motion.span>
</div>
</div>
)
}
// Icon animation demos
function IconAnimationsDemo() {
const [liked, setLiked] = useState(false)
const [starred, setStarred] = useState(false)
return (
<div className="flex items-center gap-6">
{/* Heart like animation */}
<motion.button
whileTap={{ scale: 0.8 }}
onClick={() => setLiked(!liked)}
className="p-3 rounded-full bg-[var(--color-surface-card)] border border-[var(--color-border-default)]"
>
<motion.div
animate={liked ? { scale: [1, 1.3, 1] } : { scale: 1 }}
transition={{ duration: 0.3 }}
>
<Heart
className={cn(
'w-6 h-6 transition-colors',
liked ? 'fill-red-500 text-red-500' : 'text-[var(--color-text-tertiary)]'
)}
/>
</motion.div>
</motion.button>
{/* Star animation */}
<motion.button
whileTap={{ scale: 0.8 }}
onClick={() => setStarred(!starred)}
className="p-3 rounded-full bg-[var(--color-surface-card)] border border-[var(--color-border-default)]"
>
<motion.div
animate={starred ? { rotate: [0, 72, 144, 216, 288, 360], scale: [1, 1.2, 1] } : { rotate: 0, scale: 1 }}
transition={{ duration: 0.5 }}
>
<Star
className={cn(
'w-6 h-6 transition-colors',
starred ? 'fill-yellow-400 text-yellow-400' : 'text-[var(--color-text-tertiary)]'
)}
/>
</motion.div>
</motion.button>
{/* Continuous sparkle */}
<motion.div
animate={{
rotate: [0, 15, -15, 0],
scale: [1, 1.1, 1]
}}
transition={{
duration: 2,
repeat: Infinity,
ease: 'easeInOut'
}}
className="p-3 rounded-full bg-[var(--color-accent-primary-light)]"
>
<Sparkles className="w-6 h-6 text-[var(--color-accent-primary)]" />
</motion.div>
</div>
)
}
// Accordion demo
function AccordionDemo() {
const [isOpen, setIsOpen] = useState(false)
return (
<div className="w-72 bg-[var(--color-surface-card)] rounded-[var(--radius-lg)] overflow-hidden border border-[var(--color-border-default)]">
<motion.button
onClick={() => setIsOpen(!isOpen)}
className="w-full p-4 flex items-center justify-between text-left"
>
<span className="text-heading-small">Accordion Item</span>
<motion.div
animate={{ rotate: isOpen ? 180 : 0 }}
transition={transitions.spring}
>
<ChevronLeft className="w-5 h-5 -rotate-90 text-[var(--color-text-tertiary)]" />
</motion.div>
</motion.button>
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={transitions.springSmooth}
className="overflow-hidden"
>
<div className="p-4 pt-0 text-body-medium text-[var(--color-text-secondary)]">
This content smoothly animates in and out with height transitions.
</div>
</motion.div>
)}
</AnimatePresence>
</div>
)
}
// Main Animations Section Component
function AnimationsSection({ theme, colorTheme }: { theme: 'light' | 'dark'; colorTheme: string }) {
return (
<div className="space-y-8">
{/* Header */}
<Card>
<div className="flex items-center gap-3 mb-4">
<div className="p-2 rounded-[var(--radius-lg)] bg-[var(--color-accent-primary-light)]">
<Zap className="w-6 h-6 text-[var(--color-accent-primary)]" />
</div>
<div>
<h2 className="text-heading-large">Animation System</h2>
<p className="text-body-medium text-[var(--color-text-secondary)]">
Powered by Framer Motion • <strong>{colorTheme}</strong> theme in <strong>{theme}</strong> mode
</p>
</div>
</div>
<div className="grid grid-cols-3 gap-4 mt-6">
<div className="p-4 bg-[var(--color-background-secondary)] rounded-[var(--radius-lg)]">
<p className="text-label-small text-[var(--color-text-tertiary)] mb-1">Duration Presets</p>
<p className="text-body-medium">instant (50ms) → slow (400ms)</p>
</div>
<div className="p-4 bg-[var(--color-background-secondary)] rounded-[var(--radius-lg)]">
<p className="text-label-small text-[var(--color-text-tertiary)] mb-1">Easing Functions</p>
<p className="text-body-medium">spring, easeOut, easeInOut</p>
</div>
<div className="p-4 bg-[var(--color-background-secondary)] rounded-[var(--radius-lg)]">
<p className="text-label-small text-[var(--color-text-tertiary)] mb-1">Interaction Types</p>
<p className="text-body-medium">hover, tap, drag, gesture</p>
</div>
</div>
</Card>
{/* Basic Transitions */}
<div>
<h3 className="text-heading-medium mb-4">Basic Transitions</h3>
<div className="grid grid-cols-2 gap-6">
<AnimationDemo
title="Fade In"
description="Simple opacity transition for subtle entrances"
>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={transitions.normal}
className="px-6 py-3 bg-[var(--color-surface-card)] rounded-[var(--radius-lg)] shadow-[var(--shadow-md)] border border-[var(--color-border-default)]"
>
<span className="text-body-medium">Faded In</span>
</motion.div>
</AnimationDemo>
<AnimationDemo
title="Scale In"
description="Scale with opacity for modal-like entrances"
>
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={transitions.springSmooth}
className="px-6 py-3 bg-[var(--color-surface-card)] rounded-[var(--radius-lg)] shadow-[var(--shadow-md)] border border-[var(--color-border-default)]"
>
<span className="text-body-medium">Scaled In</span>
</motion.div>
</AnimationDemo>
<AnimationDemo
title="Slide Up"
description="Vertical slide for tooltips and dropdowns"
>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={transitions.spring}
className="px-6 py-3 bg-[var(--color-surface-card)] rounded-[var(--radius-lg)] shadow-[var(--shadow-md)] border border-[var(--color-border-default)]"
>
<span className="text-body-medium">Slid Up</span>
</motion.div>
</AnimationDemo>
<AnimationDemo
title="Spring Pop"
description="Bouncy spring animation for attention-grabbing elements"
>
<motion.div
initial={{ opacity: 0, scale: 0.5 }}
animate={{ opacity: 1, scale: 1 }}
transition={transitions.springBouncy}
className="px-6 py-3 bg-[var(--color-accent-primary)] text-[var(--color-text-inverse)] rounded-[var(--radius-lg)] shadow-[var(--shadow-md)]"
>
<span className="text-body-medium">Popped!</span>
</motion.div>
</AnimationDemo>
</div>
</div>
{/* Interactive Animations */}
<div>
<h3 className="text-heading-medium mb-4">Interactive Animations</h3>
<div className="grid grid-cols-2 gap-6">
<AnimationDemo
title="Hover Card"
description="Scale and elevation change on hover"
>
<HoverCardDemo />
</AnimationDemo>
<AnimationDemo
title="Button Press"
description="Tactile feedback with scale on tap"
>
<ButtonPressDemo />
</AnimationDemo>
<AnimationDemo
title="Draggable Element"
description="Constrained drag with elastic boundaries"
>
<DragDemo />
</AnimationDemo>
<AnimationDemo
title="Icon Interactions"
description="Like, favorite, and animated icons"
>
<IconAnimationsDemo />
</AnimationDemo>
</div>
</div>
{/* Component Animations */}
<div>
<h3 className="text-heading-medium mb-4">Component Animations</h3>
<div className="grid grid-cols-2 gap-6">
<AnimationDemo
title="Staggered List"
description="Sequential item animation for lists"
>
<StaggeredListDemo />
</AnimationDemo>
<AnimationDemo
title="Toast Notification"
description="Enter/exit animations for notifications"
>
<ToastDemo />
</AnimationDemo>
<AnimationDemo
title="Modal Dialog"
description="Overlay + scale animation for modals"
>
<ModalDemo />
</AnimationDemo>
<AnimationDemo
title="Accordion"
description="Height animation for expandable content"
>
<AccordionDemo />
</AnimationDemo>
</div>
</div>
{/* Utility Animations */}
<div>
<h3 className="text-heading-medium mb-4">Utility Animations</h3>
<div className="grid grid-cols-2 gap-6">
<AnimationDemo
title="Number Counter"
description="Animated number transitions"
>
<CounterDemo />
</AnimationDemo>
<AnimationDemo
title="Loading States"
description="Spinner, pulse, and bounce loaders"
>
<LoadingDemo />
</AnimationDemo>
<AnimationDemo
title="Progress Bar"
description="Animated progress indication"
>
<ProgressAnimationDemo />
</AnimationDemo>
</div>
</div>
{/* Animation Guidelines */}
<Card>
<h2 className="text-heading-large mb-6">Animation Guidelines</h2>
<div className="grid grid-cols-2 gap-6">
<div>
<h3 className="text-heading-small mb-3 text-[var(--color-semantic-success)]">✓ Do</h3>
<ul className="space-y-2 text-body-medium text-[var(--color-text-secondary)]">
<li>• Use animations to provide feedback</li>
<li>• Keep durations short (150-400ms)</li>
<li>• Use spring physics for natural feel</li>
<li>• Animate transforms and opacity (GPU)</li>
<li>• Respect reduced-motion preferences</li>
<li>• Use consistent timing across similar elements</li>
</ul>
</div>
<div>
<h3 className="text-heading-small mb-3 text-[var(--color-semantic-error)]">✗ Don't</h3>
<ul className="space-y-2 text-body-medium text-[var(--color-text-secondary)]">
<li>• Animate for decoration's sake</li>
<li>• Use slow animations that block users</li>
<li>• Animate layout properties (slow)</li>
<li>• Create jarring or unexpected motions</li>
<li>• Overuse bouncy springs</li>
<li>• Animate critical error states</li>
</ul>
</div>
</div>
<div className="mt-6 p-4 bg-[var(--color-accent-primary-light)] rounded-[var(--radius-lg)]">
<p className="text-body-medium text-[var(--color-accent-primary)]">
<strong>Accessibility Note:</strong> Always wrap animations in a check for <code className="font-mono bg-[var(--color-background-secondary)] px-1 rounded">prefers-reduced-motion</code> and provide static alternatives.
</p>
</div>
</Card>
</div>
)
}
// ============================================
// THEMES SECTION
// ============================================
function ThemePreviewCard({
theme,
isActive,
mode,
onClick
}: {
theme: typeof COLOR_THEMES[0]
isActive: boolean
mode: 'light' | 'dark'
onClick: () => void
}) {
// Preview colors based on mode
const bgColor = mode === 'light' ? theme.previewColors.bg : theme.previewColors.darkBg
const cardColor = mode === 'light' ? '#FFFFFF' : '#1A1A1A'
const accentColor = mode === 'dark' && theme.previewColors.darkAccent
? theme.previewColors.darkAccent
: theme.previewColors.accent
return (
<motion.button
whileHover={{ scale: 1.02, y: -4 }}
whileTap={{ scale: 0.98 }}
onClick={onClick}
className={cn(
"relative p-4 rounded-[var(--radius-2xl)] text-left transition-all overflow-hidden",
isActive
? "ring-2 ring-[var(--color-accent-primary)] ring-offset-2 ring-offset-[var(--color-background-primary)]"
: "hover:shadow-[var(--shadow-lg)]"
)}
style={{ backgroundColor: bgColor }}
>
{/* Mini UI Preview */}
<div className="space-y-3">
{/* Mini header */}
<div
className="h-8 rounded-[var(--radius-md)] flex items-center px-3 gap-2"
style={{ backgroundColor: cardColor }}
>
<div className="w-2 h-2 rounded-full" style={{ backgroundColor: accentColor }} />
<div className="w-16 h-2 rounded-full bg-gray-300" />
</div>
{/* Mini cards */}
<div className="grid grid-cols-2 gap-2">
<div
className="h-16 rounded-[var(--radius-md)] p-2"
style={{ backgroundColor: cardColor }}
>
<div className="w-8 h-8 rounded-full mb-1" style={{ backgroundColor: accentColor, opacity: 0.2 }} />
<div className="w-full h-1.5 rounded-full bg-gray-200" />
</div>
<div
className="h-16 rounded-[var(--radius-md)] p-2"
style={{ backgroundColor: cardColor }}
>
<div className="w-full h-2 rounded-full mb-2" style={{ backgroundColor: accentColor }} />
<div className="w-3/4 h-1.5 rounded-full bg-gray-200" />
<div className="w-1/2 h-1.5 rounded-full bg-gray-200 mt-1" />
</div>
</div>
{/* Mini button */}
<div
className="h-6 rounded-full flex items-center justify-center"
style={{ backgroundColor: accentColor }}
>
<div className="w-12 h-1.5 rounded-full bg-white/50" />
</div>
</div>
{/* Theme info */}
<div className="mt-4">
<div className="flex items-center gap-2">
<h3 className="text-heading-small" style={{ color: mode === 'light' ? '#1A1A2E' : '#F8FAFC' }}>
{theme.name}
</h3>
{isActive && (
<div className="px-2 py-0.5 rounded-full text-[10px] font-medium" style={{ backgroundColor: accentColor, color: '#FFF' }}>
Active
</div>
)}
</div>
<p className="text-body-small mt-1" style={{ color: mode === 'light' ? '#64748B' : '#A1A1B5' }}>
{theme.description}
</p>
</div>
{/* Color swatches */}
<div className="flex gap-1 mt-3">
<div
className="w-6 h-6 rounded-full border-2 border-white shadow-sm"
style={{ backgroundColor: theme.previewColors.bg }}
/>
<div
className="w-6 h-6 rounded-full border-2 border-white shadow-sm"
style={{ backgroundColor: theme.previewColors.accent }}
/>
</div>
</motion.button>
)
}
function ThemesSection({
currentTheme,
currentMode,
themes,
onThemeChange,
onModeChange
}: {
currentTheme: ColorTheme
currentMode: Mode
themes: typeof COLOR_THEMES
onThemeChange: (theme: ColorTheme) => void
onModeChange: () => void
}) {
return (
<div className="space-y-8">
{/* Header */}
<Card>
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="p-2 rounded-[var(--radius-lg)] bg-[var(--color-accent-primary-light)]">
<Sparkles className="w-6 h-6 text-[var(--color-accent-primary)]" />
</div>
<div>
<h2 className="text-heading-large">Theme Gallery</h2>
<p className="text-body-medium text-[var(--color-text-secondary)]">
{themes.length} color themes × 2 modes = {themes.length * 2} combinations
</p>
</div>
</div>
{/* Mode Toggle */}
<div className="flex items-center gap-3 p-1 bg-[var(--color-background-secondary)] rounded-full">
<button
onClick={() => currentMode === 'dark' && onModeChange()}
className={cn(
"px-4 py-2 rounded-full text-body-medium font-medium transition-all",
currentMode === 'light'
? "bg-[var(--color-surface-card)] shadow-sm"
: "text-[var(--color-text-secondary)]"
)}
>
<Sun className="w-4 h-4 inline mr-2" />
Light
</button>
<button
onClick={() => currentMode === 'light' && onModeChange()}
className={cn(
"px-4 py-2 rounded-full text-body-medium font-medium transition-all",
currentMode === 'dark'
? "bg-[var(--color-surface-card)] shadow-sm"
: "text-[var(--color-text-secondary)]"
)}
>
<Moon className="w-4 h-4 inline mr-2" />
Dark
</button>
</div>
</div>
</Card>
{/* Theme Grid */}
<div>
<h3 className="text-heading-medium mb-4">Color Themes</h3>
<div className="grid grid-cols-3 gap-6">
{themes.map((theme) => (
<ThemePreviewCard
key={theme.id}
theme={theme}
isActive={currentTheme === theme.id}
mode={currentMode}
onClick={() => onThemeChange(theme.id)}
/>
))}
</div>
</div>
{/* Current Theme Details */}
<Card>
<h3 className="text-heading-medium mb-4">Current Theme Colors</h3>
<div className="grid grid-cols-4 gap-4">
<div>
<p className="text-label-small text-[var(--color-text-tertiary)] mb-2">Background</p>
<div className="space-y-2">
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-[var(--radius-md)] bg-[var(--color-background-primary)] border border-[var(--color-border-default)]" />
<span className="text-body-small">Primary</span>
</div>
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-[var(--radius-md)] bg-[var(--color-background-secondary)] border border-[var(--color-border-default)]" />
<span className="text-body-small">Secondary</span>
</div>
</div>
</div>
<div>
<p className="text-label-small text-[var(--color-text-tertiary)] mb-2">Surface</p>
<div className="space-y-2">
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-[var(--radius-md)] bg-[var(--color-surface-card)] border border-[var(--color-border-default)]" />
<span className="text-body-small">Card</span>
</div>
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-[var(--radius-md)] bg-[var(--color-surface-elevated)] border border-[var(--color-border-default)]" />
<span className="text-body-small">Elevated</span>
</div>
</div>
</div>
<div>
<p className="text-label-small text-[var(--color-text-tertiary)] mb-2">Accent</p>
<div className="space-y-2">
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-[var(--radius-md)] bg-[var(--color-accent-primary)]" />
<span className="text-body-small">Primary</span>
</div>
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-[var(--radius-md)] bg-[var(--color-accent-primary-light)] border border-[var(--color-border-default)]" />
<span className="text-body-small">Light</span>
</div>
</div>
</div>
<div>
<p className="text-label-small text-[var(--color-text-tertiary)] mb-2">Semantic</p>
<div className="space-y-2">
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-[var(--radius-md)] bg-[var(--color-semantic-success)]" />
<span className="text-body-small">Success</span>
</div>
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-[var(--radius-md)] bg-[var(--color-semantic-error)]" />
<span className="text-body-small">Error</span>
</div>
</div>
</div>
</div>
</Card>
{/* Usage Instructions */}
<Card>
<h3 className="text-heading-medium mb-4">Using Themes</h3>
<div className="grid grid-cols-2 gap-6">
<div className="p-4 bg-[var(--color-background-secondary)] rounded-[var(--radius-lg)]">
<p className="text-label-small text-[var(--color-text-tertiary)] mb-2">HTML Setup</p>
<pre className="text-body-small font-mono text-[var(--color-text-primary)] overflow-x-auto">
{`<!-- Color theme -->
<html data-theme="ocean">
<!-- Mode -->
<html class="dark">
<!-- Combined -->
<html data-theme="neo" class="dark">`}
</pre>
</div>
<div className="p-4 bg-[var(--color-background-secondary)] rounded-[var(--radius-lg)]">
<p className="text-label-small text-[var(--color-text-tertiary)] mb-2">CSS Variables</p>
<pre className="text-body-small font-mono text-[var(--color-text-primary)] overflow-x-auto">
{`/* Use in your CSS */
background: var(--color-background-primary);
color: var(--color-text-primary);
border: 1px solid var(--color-border-default);`}
</pre>
</div>
</div>
<div className="mt-4 p-4 bg-[var(--color-accent-primary-light)] rounded-[var(--radius-lg)]">
<p className="text-body-medium text-[var(--color-accent-primary)]">
<strong>Tip:</strong> All themes automatically support light and dark modes. Just toggle the <code className="font-mono bg-[var(--color-background-secondary)] px-1 rounded">.dark</code> class!
</p>
</div>
</Card>
</div>
)
}