Box Shadow Generator

Design a CSS box-shadow from presets or your own numbers, and judge it against a surface colour you choose.

Runs on your deviceDesign & ColorWorkingNo account needed

What this tool does

Box Shadow Generator writes a box-shadow declaration two ways. Leave the input empty and it builds one from the controls — horizontal and vertical offset, blur radius, spread, an inset toggle, a shadow colour and an opacity slider. Paste an existing value instead and it parses that, so you can read a shadow inherited from someone else's stylesheet, see it rendered, and copy it back in a consistent form. All of it happens in this browser tab; no CSS is uploaded.

Six presets cover the usual elevation steps: subtle, card, popover, modal, focus glow and inner shadow. Card, popover and modal are two-layer shadows, pairing a broader layer with a tighter one pulled in by negative spread, so the shadow keeps a defined core as well as a soft edge. The opacity slider then rescales a preset's built-in alpha values in proportion: at its default of 15% each preset renders at the alphas it was drawn with, and moving it deepens or lightens every layer at once without editing each colour by hand.

The output is normalised rather than echoed back. Unitless non-zero numbers gain px, an omitted spread is written as an explicit 0, and colours in any notation the parser reads — hex, CSS colour names, rgb(), hsl(), hwb(), lab(), lch(), oklab(), oklch() — come back as rgb() or rgba(). A layer written with no colour at all is left with no colour, because CSS resolves that to currentColor and not to black; the layer table names that default instead of inventing a value you never wrote.

Three limits shape how the tool is used. Pasting a value takes over completely — the preset and the offset, colour and opacity fields are skipped for that run, and only the preview surface still applies, so edit the text rather than expecting the sliders to move it. The controls also build one custom layer at a time: multi-layer output comes from a preset, or from CSS you write in the input. And the parser recognises px, rem, em and % lengths only, so a vw, ch or calc() length is treated as a colour token — the run stops with an error that complains about the colour rather than the unit that caused it.

How to use it

  1. Start from a preset, or from your own numbers. Pick subtle, card, popover, modal, focus glow or inner shadow, or leave it on Custom and set the horizontal and vertical offsets, blur, spread and the inset toggle yourself.
  2. Choose the colour and how much of it lands. The picker sets the shadow's hue — a very dark blue-grey usually sits better on a coloured page than pure black — and the opacity slider decides its strength. Blur cannot go below zero; spread can, and negative spread is what pulls a layer in tight.
  3. Judge it against the right surface. The preview surface colour sets the card the shadow falls on, and the backdrop behind that card switches to light or dark automatically depending on how light the surface you chose is.
  4. Copy the declaration, or paste one in to edit. The code block holds the finished rule and the table breaks each layer into inset, X, Y, blur, spread and colour. To work on an existing shadow, paste it into the input, one layer per line.

Processing & privacy

Runs on your device. Your file is processed directly in this browser.

Common use cases

Building an elevation scale

Card, popover and modal are three steps of one idea. Generating them from a single shadow colour keeps a component library coherent instead of accumulating eight near-identical values nobody can tell apart.

Reading a shadow you inherited

A minified stylesheet hands you one long comma-separated value. Pasting it splits the layers into a table, so you can see which layer is doing the work before changing anything.

Drawing a focus ring

The focus glow preset is a spread-only shadow with no blur and no offset, which is how a ring is usually drawn around an input without the layout shift a border would cause.

Sinking a well or a pressed control

The inner shadow preset turns on inset, so the shadow is painted inside the box — the usual treatment for text fields, toggles and anything meant to read as recessed.

Questions

Does anything I paste here get sent away?
No. The parsing, the preview and the generated declaration are all produced in this browser tab, so nothing you paste is transmitted or stored, and the tool still works with the network disconnected.
Why is my pasted value ignoring the sliders?
Because the input wins. When the box holds a shadow, the tool parses that and reports it back; the preset, offsets, colour and opacity fields are skipped for that run, and only the preview surface still applies. Clear the input to go back to generating from the controls.
Can I build more than one layer without pasting CSS?
Not from the controls — they produce a single custom layer. Multi-layer shadows come either from the card, popover or modal presets, or from writing the layers yourself in the input, one per line or comma-separated on a single line.
Does it produce a filter: drop-shadow() value?
No, it writes box-shadow only. A drop shadow through the filter property follows an element's alpha shape rather than its border box, which is what you want for PNG cut-outs and irregular SVG, and it is not a value this tool emits.
Why did my oklch() colour come back as rgba()?
Colours are parsed into sRGB and re-serialised as rgb() or rgba(). For a colour inside the sRGB gamut the rendered result is the same to within the rounding of the 0–255 channels; what you lose is the original notation, so keep the source value if the notation itself matters to you.
What exactly does the opacity slider do to a preset?
It multiplies every layer's alpha by the reading divided by 15, so 15% reproduces the preset as drawn, 30% doubles each layer's alpha and 0 leaves the shadow invisible. The resulting alpha is capped at fully opaque, but at 100% only focus glow reaches that cap; the two-layer presets peak at 0.8 alpha, so no layer of theirs is ever flattened into a solid block.
Why does an extra 0 appear in my value?
The output always writes all four lengths, so "0 2px 4px" becomes "0 2px 4px 0", and unitless non-zero numbers gain px. Nothing about the rendering changes; the form is simply consistent from one layer to the next.
What happens if a layer is missing a number?
A layer needs at least an X and a Y offset. With fewer than two lengths the tool names the layer that failed and quotes the text it could not read, rather than guessing at what you meant.

Related tools