Building a Tailwind CSS Class Name Generator: From String Concatenation to Visual Builder
Building a Tailwind CSS Class Name Generator: From String Concatenation to Visual Builder#
What’s the most painful part of writing Tailwind CSS? It’s not memorizing class names—it’s debugging a long string like flex justify-center items-center p-4 bg-blue-500 and not knowing which class is taking effect or which one is being overridden. Today let’s build a visual Tailwind class name generator that turns class assembly into building blocks.
Core Design: State-Driven, Not String Concatenation#
Many people’s first instinct is string concatenation, but a better approach is using a Set for state management:
const [selectedClasses, setSelectedClasses] = useState<Set<string>>(new Set())
const toggleClass = useCallback((cls: string) => {
setSelectedClasses(prev => {
const next = new Set(prev)
if (next.has(cls)) {
next.delete(cls) // Remove if exists
} else {
next.add(cls) // Add if not exists
}
return next
})
}, [])
Why use a Set instead of an array?
- Automatic deduplication: Clicking the same class twice won’t add duplicates
- O(1) lookup: Checking if a class exists is faster
- Clear semantics: A Set represents a collection; an array represents a sequence
The final class string output:
const classString = Array.from(selectedClasses).join(' ')
Category Management: Organizing 700+ Class Names#
Tailwind CSS has over 700 class names—displaying them all at once would be overwhelming. Organizing by functionality is the only way:
type Category = 'layout' | 'spacing' | 'typography' | 'colors' | 'borders' | 'effects' | 'flexGrid'
const categoryTabs = [
{ key: 'layout', icon: <Layers />, label: 'Layout' },
{ key: 'spacing', icon: <Square />, label: 'Spacing' },
{ key: 'typography', icon: <Type />, label: 'Typography' },
{ key: 'colors', icon: <Palette />, label: 'Colors' },
{ key: 'borders', icon: <Square />, label: 'Borders' },
{ key: 'effects', icon: <Sparkles />, label: 'Effects' },
{ key: 'flexGrid', icon: <Grid3x3 />, label: 'Flex/Grid' },
]
Each category shows only relevant classes—users don’t need to see bg-red-500 next to text-xl.
The Spacing Direction Matrix#
Spacing classes are the most complex: padding has 7 directions (p/t/b/l/r/x/y), and so does margin. Each direction has 14 sizes (0-12 plus auto).
Displaying 7 × 14 × 2 = 196 buttons directly? A UX disaster.
The solution is direction + size separation:
const SPACING_DIRECTIONS = ['all', 't', 'b', 'l', 'r', 'x', 'y']
const SPACING_SIZES = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', 'auto']
// Render direction selector
{SPACING_DIRECTIONS.map(dir => (
<span key={dir}>
{dir === 'all' ? 'p-*' : `p${dir[0]}-*`}
</span>
))}
// Render size selector
{SPACING_SIZES.map(size => (
<button onClick={() => toggleClass(`p-${size}`)}>
{size}
</button>
))}
Combined with a padding/margin toggle, users can precisely target pt-4 or mx-auto.
Color Picker: From Strings to Visualization#
Tailwind’s color classes like bg-red-500 don’t convey the actual color visually. The solution is visualizing with color swatches:
const BASIC_COLORS = ['red', 'blue', 'green', 'yellow', 'purple', 'pink', 'cyan', 'gray']
const COLOR_SHADES = ['50', '100', '200', '300', '400', '500', '600', '700', '800', '900']
{BASIC_COLORS.map(color => (
<div key={color}>
<h5>{color}</h5>
<div className="flex gap-1.5">
{COLOR_SHADES.map(shade => {
const cls = colorMode === 'bg' ? `bg-${color}-${shade}` : `text-${color}-${shade}`
return (
<button
onClick={() => toggleClass(cls)}
className="w-8 h-8 rounded border"
style={{ backgroundColor: getColorFallback(color, shade) }}
title={cls}
/>
)
})}
</div>
</div>
))}
The getColorFallback function predefines all 10 shades for each color, ensuring correct display even if Tailwind hasn’t loaded.
Live Preview: What You See Is What You Get#
The preview component directly applies selected classes:
<div className={`bg-bg-secondary border rounded-lg p-4 ${classString}`}>
<span>Preview Element</span>
</div>
A subtle detail: the preview container itself has some classes (bg-bg-secondary, etc.), and user-selected classes are appended. If a user selects bg-red-500, it overrides the container’s default background—exactly how CSS cascade works visually.
Conflict Detection and Smart Hints#
Tailwind classes can conflict—for example, selecting both p-4 and p-8 means only the later-defined one takes effect. How to solve this?
Option 1: Mutex Groups#
const MUTEX_GROUPS = [
['p-0', 'p-1', 'p-2', 'p-3', 'p-4', ...], // padding same direction mutex
['text-xs', 'text-sm', 'text-base', ...], // font-size mutex
]
const toggleClass = (cls: string) => {
setSelectedClasses(prev => {
const next = new Set(prev)
// Check if belongs to a mutex group
for (const group of MUTEX_GROUPS) {
if (group.includes(cls)) {
// Remove other classes in same group
group.forEach(c => next.delete(c))
}
}
next.add(cls)
return next
})
}
Option 2: Warning Messages#
Don’t prevent selection, but warn about conflicts in the preview area:
const conflicts = findConflicts(selectedClasses)
{conflicts.length > 0 && (
<div className="text-yellow-500 text-sm">
⚠️ Conflicting classes detected: {conflicts.join(', ')}
</div>
)}
The current implementation uses option 2, because sometimes users intentionally override earlier classes with later ones.
One-Click Copy: Clipboard API#
const handleCopy = async () => {
if (!classString) return
await navigator.clipboard.writeText(classString)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
}
Simple and direct—show a ✓ animation after successful copy.
Conclusion#
A seemingly simple Tailwind class generator involves:
- Data structures: Set vs Array choice
- Information architecture: Organizing 700+ class names
- Interaction design: Direction+size separation, color visualization
- CSS cascade: Conflict detection and handling
- Engineering details: Clipboard API, state management
Final tool: Tailwind CSS Class Generator
Hope this helps you escape manual class writing and focus on layout itself.
Related tools: Flexbox Layout Generator | CSS Gradient Generator