ThemeCustomizer
themecustomizer-1779379195944.tsx
'use client'
import { useState } from 'react'
const PRESETS = [
{ name: 'Gold', primary: '#C9A84C', bg: '#0A0A0A', accent: '#6366f1' },
{ name: 'Neon', primary: '#22d3ee', bg: '#030712', accent: '#a78bfa' },
{ name: 'Crimson', primary: '#ef4444', bg: '#0f0a0a', accent: '#f97316' },
{ name: 'Sage', primary: '#4ade80', bg: '#071a0f', accent: '#34d399' },
]
const RADII = ['0', '4px', '8px', '12px', '20px', '9999px']
const FONTS = [12, 13, 14, 15, 16]
export default function ThemeCustomizer() {
const [preset, setPreset] = useState(PRESETS[0])
const [primary, setPrimary] = useState(PRESETS[0].primary)
const [radius, setRadius] = useState('8px')
const [fontSize, setFontSize] = useState(14)
const [copied, setCopied] = useState(false)
const css = `:root {
--color-primary: ${primary};
--color-bg: ${preset.bg};
--color-accent: ${preset.accent};
--radius: ${radius};
--font-size-base: ${fontSize}px;
}`
function copy() {
navigator.clipboard.writeText(css)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
}
return (
<div style={{ padding: 32, background: '#0A0A0A', display: 'flex', gap: 24, flexWrap: 'wrap' }}>
{/* Controls */}
<div style={{ flex: 1, minWidth: 240, display: 'flex', flexDirection: 'column', gap: 20 }}>
<div style={{ color: '#F5F5F0', fontSize: 16, fontWeight: 700 }}>Theme Customizer</div>
{/* Presets */}
<div>
<div style={{ color: 'rgba(255,255,255,0.4)', fontSize: 12, marginBottom: 8 }}>Presets</div>
<div style={{ display: 'flex', gap: 6 }}>
{PRESETS.map(p => (
<button key={p.name} onClick={() => { setPreset(p); setPrimary(p.primary) }}
style={{ width: 28, height: 28, borderRadius: '50%', background: p.primary, border: preset.name === p.name ? '3px solid white' : '2px solid transparent', cursor: 'pointer', transition: 'border 0.2s' }} title={p.name} />
))}
</div>
</div>
{/* Primary color */}
<div>
<div style={{ color: 'rgba(255,255,255,0.4)', fontSize: 12, marginBottom: 8 }}>Primary color</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<input type="color" value={primary} onChange={e => setPrimary(e.target.value)} style={{ width: 36, height: 36, borderRadius: 8, border: 'none', cursor: 'pointer', background: 'none', padding: 0 }} />
<span style={{ color: '#F5F5F0', fontSize: 13, fontFamily: 'monospace' }}>{primary.toUpperCase()}</span>
</div>
</div>
{/* Border radius */}
<div>
<div style={{ color: 'rgba(255,255,255,0.4)', fontSize: 12, marginBottom: 8 }}>Border radius: {radius}</div>
<div style={{ display: 'flex', gap: 6 }}>
{RADII.map(r => (
<button key={r} onClick={() => setRadius(r)} style={{ background: radius === r ? primary + '20' : 'rgba(255,255,255,0.04)', border: '1px solid', borderColor: radius === r ? primary + '40' : 'rgba(255,255,255,0.08)', color: radius === r ? primary : 'rgba(255,255,255,0.4)', borderRadius: 6, padding: '4px 8px', cursor: 'pointer', fontSize: 11 }}>{r || '0'}</button>
))}
</div>
</div>
{/* Font size */}
<div>
<div style={{ color: 'rgba(255,255,255,0.4)', fontSize: 12, marginBottom: 8 }}>Font size: {fontSize}px</div>
<input type="range" min={12} max={16} value={fontSize} onChange={e => setFontSize(Number(e.target.value))} style={{ width: '100%', accentColor: primary }} />
</div>
</div>
{/* Preview + output */}
<div style={{ flex: 1, minWidth: 220, display: 'flex', flexDirection: 'column', gap: 16 }}>
{/* Preview */}
<div style={{ background: preset.bg, border: '1px solid rgba(255,255,255,0.08)', borderRadius: 14, padding: 20, display: 'flex', flexDirection: 'column', gap: 12 }}>
<button style={{ background: primary, color: '#0A0A0A', border: 'none', borderRadius: radius, padding: '10px 20px', fontWeight: 700, cursor: 'pointer', fontSize }}>Primary Button</button>
<button style={{ background: 'transparent', color: primary, border: `1px solid ${primary}`, borderRadius: radius, padding: '10px 20px', fontWeight: 600, cursor: 'pointer', fontSize }}>Outline Button</button>
<div style={{ background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.08)', borderRadius: radius, padding: '10px 14px', color: '#F5F5F0', fontSize }}>Input field</div>
<span style={{ background: primary + '20', color: primary, padding: '3px 10px', borderRadius: '9999px', fontSize: fontSize - 2, fontWeight: 600, width: 'fit-content' }}>Badge</span>
</div>
{/* CSS output */}
<div style={{ background: '#111', border: '1px solid rgba(255,255,255,0.08)', borderRadius: 10, padding: 14, fontFamily: 'monospace', fontSize: 12, color: 'rgba(255,255,255,0.6)', whiteSpace: 'pre', flex: 1 }}>{css}</div>
<button onClick={copy} style={{ background: copied ? 'rgba(34,197,94,0.12)' : primary, color: copied ? '#22c55e' : '#0A0A0A', border: copied ? '1px solid rgba(34,197,94,0.3)' : 'none', borderRadius: 8, padding: '10px', fontWeight: 700, cursor: 'pointer', fontSize: 13, transition: 'all 0.2s' }}>
{copied ? '✓ CSS Copied' : 'Copy CSS Variables'}
</button>
</div>
</div>
)
}Component info
CategoryUtility
Frameworkreact
TierFREE
Views0
Copies0
About
Live theme customizer with color pickers, font scale, radius preset, and CSS variables output
More from Utility
'use client'
import { useState, useCallback } from 'react'
interface UploadedFile {
id: number
name: string
size: number
type: string
progress: number
done: boolean
error?: string
}
const ALLOWED = ['image/png', 'image/jpeg', 'image/wFileUploadZone
Utility
'use client'
import { useState } from 'react'
interface ClipItem {
id: number
content: string
type: 'text' | 'code' | 'url' | 'email'
pinned: boolean
time: number
}
const INITIAL: ClipItem[] = [
{ id: 1, content: 'npx empire-ui-mcp --stClipboardManager
Utility