The CSS box-shadow property, defined in CSS Backgrounds and Borders Module Level 3, paints one or more shadows around the border edge of a box. Each shadow takes up to four length values (offset-x, offset-y, blur-radius, spread-radius), a color, and an optional inset keyword - and you can comma-separate as many of them as you like to build layered depth.
This generator builds the box-shadow string client-side using a small TypeScript helper (shadowCSS in the source above) that converts your hex colour and opacity slider into an rgba() literal and concatenates the offsets in the order CSS expects. The live preview is just a div with style={{ boxShadow: ... }} - the same string the browser's rendering engine consumes when you ship the rule, so what you see is exactly what production will paint.
The four length values do different things and getting them wrong is the most common source of ugly shadows. Offset-x and offset-y move the shadow relative to the box (positive y pushes it down, the most common direction because real-world light typically comes from above). Blur-radius is a Gaussian-style smoothing - 0 produces a hard rectangle, 12px produces a soft cloud. Spread expands or shrinks the shadow rectangle before blurring; negative spread is the trick that gives modern card shadows their tight, focused look.
Layering is where the realism comes from. Real ambient occlusion isn't a single dark blob - it's a near, sharp, low-opacity contact shadow stacked on a far, soft, diffuse glow. The Card and Layered presets here demonstrate the pattern: one shadow at y: 1, blur: 3 for the contact, another at y: 8-10, blur: 24-30 for the diffuse fall-off. Material Design's elevation system and Tailwind's shadow-md / shadow-lg utilities both follow this two-shadow recipe.
The inset keyword flips the shadow inwards so it appears inside the element rather than outside. Inset shadows are how you add convincing depth to input fields, pressed buttons, and cutaway panels. They don't mix with regular shadows on the same element by default - if you stack an inset and an outer shadow, the renderer paints both, but the inset shadow draws over the element's contents, so dark inset shadows can wash out text underneath.
Performance: box-shadow is GPU-accelerated in all modern browsers, but huge blur radii (200px+) on many elements can spike paint time on low-end Android. If you have shadows on hundreds of elements, prefer a single drop-shadow filter on a parent element, or pre-render the shadow into an SVG/PNG sprite. Animating box-shadow itself triggers a repaint every frame; animate transform: translateZ tricks or opacity instead for smooth 60fps elevation transitions.
Browser support is universal at this point - every browser since IE9 implements box-shadow without prefixes. The newer color-mix() and oklch() colour functions work inside box-shadow on Chrome 111+, Safari 16.4+, and Firefox 113+ if you want to express tints relative to a CSS custom property. For older browsers, this tool sticks to plain rgba() output, which works everywhere.