The Problem
I was building a landing page for a side project last month and needed a card that looked like it was floating. You know the look — soft shadow underneath, slightly larger blur, maybe a subtle tint. Nothing crazy.
I opened my browser and searched for "CSS box shadow generator." I found plenty of them. Most were bloated with ads, some required signing up, and a few just looked like they were designed in 2012 and never touched again.
So I did what any reasonable developer would do. I built my own.
Why Not Just Use an Existing Tool?
Here's the thing — the existing tools weren't bad. They did the job. But they had a few problems:
- Too many features I didn't need. I just wanted a simple shadow for a card. Not an entire design system.
- No multi-layer support in a usable way. Some tools supported multiple shadows, but the UI made it painful. I'd rather hand-write the CSS than fight the interface.
- Slow and cluttered. Every tool had a sidebar with SEO text, a newsletter signup, and three different tracking scripts.
I wanted something that loaded instantly, worked offline, and did exactly one thing well. That's the philosophy behind most of my developer tools.
The Core Function
The heart of any shadow generator is the string builder. It's deceptively simple:
function buildValue(layer, isText) {
const parts = [];
if (layer.inset && !isText) parts.push('inset');
parts.push(`${layer.x}px ${layer.y}px ${layer.blur}px`);
if (!isText) parts.push(`${layer.spread}px`);
parts.push(layer.color);
return parts.join(' ');
}
That's it. That's the whole magic. The rest is just UI.
But there's a subtlety — text-shadow doesn't support spread or inset. I had to make sure the UI disabled those controls when switching modes. This is one of those "CSS is weird" moments that catches you off guard if you're not paying attention.
The AI Collaboration
I built this with an AI pair programmer. Here's how that actually went.
First prompt: "Create a box-shadow generator tool with a preview box and controls for x, y, blur, spread, and color. Support multiple layers."
The AI generated a working version in about 30 seconds. It had the basic structure — a preview div, some range inputs, and a textarea showing the output. Good start.
But there were problems. The first version didn't handle text-shadow at all. I had to specify that explicitly. Second version added it but didn't disable the spread control when in text mode. That produced invalid CSS that would just be silently ignored by the browser — which is confusing for users.
I had to iterate:
"Now disable the spread and inset controls when text-shadow mode is active.
Also add validation so users can't delete the last layer."
The AI handled these reasonably well, but I had to be specific about the edge cases. It's great at the 80% case, but you need to think through the remaining 20% yourself.
The Hardest Part Wasn't the Code
The actual shadow generation logic is trivial. What took me the most time was the UI state management.
Each layer has 6 properties: x, y, blur, spread, color, and inset flag. When you edit any of them, you need to:
- Update the layer object
- Re-render the preview
- Update the CSS output
- Update the copy button state
This is a classic state synchronization problem. I initially tried using a simple event-driven approach where each input had an onchange handler. That worked but got messy with multiple layers.
The cleaner approach was to centralize everything in a render() function that reads the current state and updates the DOM. This is a pattern I've learned to appreciate through building similar tools:
function render() {
const value = layers.map(l => buildValue(l, currentMode === 'text')).join(', ');
previewBox.style.boxShadow = value;
outputTextarea.value = `box-shadow: ${value};`;
}
Every input change just updates the state and calls render(). Simple, predictable, and easy to debug.
Performance Considerations
For this tool, performance wasn't a real concern — generating a CSS string is nothing. But I did make a few deliberate choices:
- No framework. Vanilla JS is fine for this. React or Vue would be overkill.
- No build step. The tool is a single HTML file. Load it, use it, close it.
- No external dependencies. Everything is native browser APIs.
This means it loads instantly and works offline. That's the whole point.
What I Learned
AI-Assisted Development Works Best With Specific Requirements
The AI was genuinely helpful for the boilerplate — the HTML structure, the CSS styling, the basic event handlers. But it struggled with the domain-specific details like text-shadow limitations and multi-layer state management.
The lesson: use AI for the scaffolding, but understand the domain yourself. You can't prompt your way out of not knowing CSS.
Edge Cases Are Where You Earn Your Keep
The "can't delete the last layer" rule is a good example. It's a tiny detail, but it prevents a confusing state where the user has no layers and the preview is empty. Similarly, clamping values to a range (-200 to 200) prevents people from generating shadows that are 5000px wide and breaking their browser tab.
Simple Tools Have a Place
Not everything needs to be a SaaS with a database and authentication. Sometimes a single HTML file that does one thing well is the right answer. I keep a collection of these small tools for my own use, and this shadow generator is one of them.
The Result
I ended up with a tool that loads instantly, supports multiple shadow layers, handles both box-shadow and text-shadow, and outputs clean CSS. It's not flashy, but it does exactly what I need.
If you're working on a project and need to generate a shadow quickly, you can check it out at Craftvo. It's the tool I wish existed when I started.
And if you're building your own tools, my advice is this: keep them simple, understand the domain, and let AI handle the boring parts. Your future self will thank you.
Tags
- CSS
- WebDevelopment
- JavaScript
- DeveloperTools
- AI
Top comments (0)