CSS Box Shadow Generator

Build CSS box-shadows with live fields for offset, blur, spread, colour and inset. Stack layers, apply elevation presets, copy the finished declaration.

🌐 Español

Elevation presets
Shadow 1

🔒 Private by design: everything runs locally in your browser and never uploaded to any server.

One shadow is rarely how light behaves

A single box-shadow is easy to write by hand and easy to spot as fake. Real objects cast more than one shadow: a dark, tight contact shadow right under the edge where light is fully blocked, and a wider, much fainter one where the light source is only partly occluded. Every design system that looks convincingly three-dimensional builds depth out of at least two of those, and often three.

CSS supports this directly. The box-shadow property takes a comma-separated list, and each entry is drawn as its own shadow, so a stack of two or three declarations is a normal thing to write, not a hack. The hard part has never been the syntax; it is that four unlabelled lengths and an alpha per layer is a lot of numbers to hold in your head while guessing in a dev tools panel. This builder turns each of them into a field with a live preview attached, so you see the result first and read the CSS afterwards.

The four lengths, in the order CSS reads them

Every layer is written as offset-x, offset-y, blur, spread, colour, optionally prefixed by inset.

The colour swatch and Opacity % are separate fields on purpose. Almost every good shadow is a low-opacity black or a low-opacity dark tint of the surface colour, and separating the two makes it obvious when you have accidentally built a solid black slab.

Stacking, reordering and deleting layers

  1. Edit the fields on Shadow 1 until the preview looks close, then click + Add shadow layer to append another.
  2. Set the new layer to something wider and fainter than the first: more blur, a larger Y offset, lower opacity.
  3. Use the up and down arrow buttons in a layer’s header to move it. The first layer in the list is the one CSS draws on top, so order changes the result whenever layers overlap.
  4. Remove a layer with the cross button in its header. The last remaining layer cannot be removed, so there is always something to edit.
  5. Read the finished declaration in the CSS box and press Copy CSS.

Order matters more than people expect once semi-transparent layers overlap, and it matters most when one of the layers is inset. If a stack looks muddier than the sum of its parts, try moving the tightest layer to the top of the list before you start changing numbers.

Five elevation levels borrowed from Material-UI

The preset buttons under Elevation presets read Level 1 to Level 5, rising from a barely-there resting-card shadow to a heavy overlay shadow for dialogs; hovering one shows the longer description of what it suits. Each applies three layers at 20%, 14% and 12% opacity, the umbra, penumbra and ambient split that Material-UI’s own elevation implementation uses. That opacity split stays constant across levels while the shadows themselves get larger and softer, though not by a tidy rule: the two outer layers blur further at every step, while the tight umbra layer’s own offset and spread wander rather than climbing monotonically. Reading the values off a real scale is more instructive than assuming one.

They are modelled on Material-UI’s widely used values rather than an official Google specification. Material Design does not publish one canonical CSS table, and different frameworks render elevation differently, so treat these as a well-worn starting point rather than a citation.

Copy it before you close the tab

Nothing is stored. There is no account, no server call, no local storage and no state in the URL, so a refresh drops you back to the single default layer. Copy the declaration into your stylesheet as soon as it looks right. If the clipboard is unavailable, which happens in some embedded and non-secure contexts, the tool says so and the text in the CSS box can be selected and copied by hand.

From here, three neighbours pick up where this one stops. The CSS Gradient Generator uses the same live-preview-then-copy shape for linear, radial and conic backgrounds. The Color Converter turns a hex value into RGB or HSL with alpha, which is what you need when your design tokens are stored in a different notation from the one this tool emits. And once the stylesheet is finished, the CSS Minifier strips it down for production and shows the before and after size.

See it in action

Screenshot of the CSS Box Shadow Generator tool with a preview box carrying the shadow being built, the generated CSS beneath it, and sliders for offset, blur, spread and colour plus a row of preset buttons
CSS Box Shadow Generator mid-process: a preview box carrying the shadow being built, the generated CSS beneath it, and sliders for offset, blur, spread and colour plus a row of preset buttons.
Diagram: where the work happens on a SysFenix page that has no file input at all: the tool arrives as ordinary JavaScript inside the page, works the answer out on your own device and renders it in place, so the upload, queue and server-side record a typical online tool needs never happen
Where the work happens on a SysFenix page that has no file input at all: the tool arrives as ordinary JavaScript inside the page, works the answer out on your own device and renders it in place, so the upload, queue and server-side record a typical online tool needs never happen.

Frequently asked questions

What is loaded in the builder when the page first opens?

A single layer set to offset 0 and 4 pixels, 8 pixels of blur, no spread, black at 35% opacity. In the CSS box that reads box-shadow with the value 0px 4px 8px 0px rgba(0, 0, 0, 0.35). It is a deliberately unremarkable card shadow, chosen as a starting point you will edit rather than a look worth shipping as-is.

Why is my colour sometimes a hex code and sometimes an rgba() value?

Opacity decides it. A layer at 100% opacity is written as the plain hex colour you picked, because the alpha channel would add nothing. Anything below 100% is written as rgba(), with the percentage converted to a 0 to 1 alpha, so 35% becomes 0.35. That keeps the declaration as short as it can be while staying exact.

How far can each field go?

Both offsets run from -100 to 100 pixels, spread runs from -100 to 100, blur runs from 0 to 100, and opacity runs from 0 to 100 percent. Values are pulled back inside those bounds as you type them, so a pasted 5000 becomes 100 rather than an invalid preview. Blur is the one that cannot go negative, because a negative blur radius is not valid CSS.

Can I export the shadow as an image?

No. This page produces the CSS declaration and nothing else, which is the useful artefact for a shadow, since it needs to be rendered by the browser on your real element to look right. The gradient builder on this site does have a PNG export, because a gradient is genuinely an image you might want as a background file. A shadow is not.

Does the preview show exactly what my component will look like?

It shows exactly what a browser does with those numbers, because the preview is a real element with the generated declaration applied rather than a picture of one. What it cannot do is match your component's geometry, since the preview square is 140 pixels on a side with rounded corners, so a shadow tuned there will read differently on a wide, square-cornered panel. Check the final numbers in place.

What happens to my layers when I click an elevation preset?

The preset replaces all of them. Its own three layers become the whole stack, so anything you had built is gone and there is no undo. If you are part way through something you like, copy the CSS out first, then try a preset, and paste your version back into the file if you prefer it.

Related tools