CSS Box Shadow Generator

Build a CSS box-shadow visually. Drag the offset, blur and spread sliders, pick a colour and opacity, stack multiple shadow layers, toggle inset, then copy the ready-to-use CSS. Try the Material elevation and neumorphism presets. Runs entirely in your browser.

rgba(0, 0, 0, 0.25)
Preview on:

How to use this box-shadow generator

  1. Set the offset — drag the horizontal and vertical sliders to move the shadow. A small downward offset (e.g. 0 × 4 px) reads as a natural overhead light source.
  2. Soften it — increase blur for a diffuse shadow, or set it to 0 for a crisp graphic edge. Use spread to make the shadow larger or tighter than the element.
  3. Choose the colour — click the swatch and lower the opacity so the shadow blends over any background instead of looking like solid black.
  4. Stack layers — click + Add layer to add a second (or third) shadow. Real elevation and neumorphism use two or more layers; the active layer is the one the sliders edit.
  5. Toggle inset — switch a layer to inset to carve the shadow inside the element for pressed inputs and soft-UI wells.
  6. Copy the CSS — click Copy and paste the box-shadow rule straight into your stylesheet.

box-shadow syntax reference

Part Example What it does
h-offset 4px Moves the shadow right (positive) or left (negative).
v-offset 8px Moves the shadow down (positive) or up (negative).
blur 16px Softens the edge. 0 = hard edge; larger = more diffuse. Cannot be negative.
spread -2px Expands (positive) or contracts (negative) the shadow before blurring.
color rgba(0,0,0,.2) Shadow colour — use rgba or an 8-digit hex for transparency.
inset inset 0 2px 6px … Optional keyword that draws the shadow inside the box.
multiple a, b, c Comma-separate several shadows; the first is painted on top.

Common box-shadow patterns

  • Cards & panels — a soft low-opacity shadow like 0 4px 12px rgba(0,0,0,.12) lifts a card off the page.
  • Material elevation — two stacked shadows (a tight one for the contact edge plus a wider ambient one) mimic Google's elevation levels.
  • Buttons & hover — a small resting shadow that grows on :hover gives a tactile lift; shrink it on :active to feel pressed.
  • Neumorphism / soft UI — a dark shadow on one side and a light shadow on the opposite side make an element look extruded from its background.
  • Inset fields & wells — an inset shadow makes inputs and progress tracks look recessed.
  • Glow & focus rings — a zero-offset coloured shadow with spread creates a glow or an accessible focus outline.

Every box-shadow value, decoded

box-shadow is defined in the CSS Backgrounds and Borders Module Level 3, the same specification that gives us border-radius and multiple backgrounds. Its formal grammar looks compact — [inset?] && [<length>{2,4}] && <color>? — but that notation hides a precise rule worth committing to memory: the length values are positional, but inset and the colour are not. Whenever lengths appear, the first two are always the horizontal and vertical offsets, a third value (if present) is the blur radius, and a fourth is the spread radius. The inset keyword and the colour can sit before or after that run of lengths, which is why inset 0 2px 6px #000 and #000 0 2px 6px inset are both valid and identical. By convention authors write inset first and the colour last, because it reads naturally and matches what most generators (this one included) output.

That positional rule is the single most common source of confusion. If you only supply two lengths you get a sharp, un-blurred offset shadow; the browser does not "guess" that your second value was meant to be a blur. A value of 0 0 12px is a centred, blurred halo with no offset, while 4px 4px is a crisp diagonal shadow with hard edges. When both offsets are 0 and there is no spread, the shadow hides directly behind the element and you see only the blurred fringe.

Browser support is no longer something to worry about. In the late 2000s box-shadow shipped behind vendor prefixes — -webkit-box-shadow for Safari and the early Chrome builds, -moz-box-shadow for Firefox before version 4 — while the syntax was still being finalised. Firefox 4 dropped its prefix in 2011 and Internet Explorer 9 shipped the plain, unprefixed property the same year, so for well over a decade every major browser has understood standard box-shadow. You can safely delete any -webkit- or -moz- shadow lines from production CSS.

Blur vs spread — what each really does

Blur and spread are easy to mix up because both make a shadow "bigger", but they work in completely different ways. Spread changes the size of the shadow shape before any blurring happens. A positive spread inflates the shadow outward in every direction (a spread of 4px makes the shadow 8 px wider and 8 px taller than the element); a negative spread deflates it, which is the classic trick for a shadow that peeks out only on one side — pair a downward offset with a negative spread so the top and sides tuck back under the element.

Blur controls how soft that edge is. The specification asks the browser to approximate a Gaussian blur whose standard deviation is half the blur-radius you write, and to keep every pixel within roughly 5% of the ideal value. A blur of 0 produces a perfectly hard edge; larger numbers spread the same amount of "ink" over a wider, fainter band, so big shadows naturally look lighter even at the same colour. Because the length you give is twice the standard deviation, the same number behaves differently in other CSS features: a value in filter: blur() or an SVG feGaussianBlur is the standard deviation itself, so blur(10px) is visibly softer than the fringe of a box-shadow with a 10px blur radius.

Historically browsers disagreed on the exact algorithm — older Chrome used a faster box-blur that left subtly squared-off corners, while Firefox used a truer Gaussian — but once the spec pinned the blur to "half the radius" they converged, and today the differences between engines are too small to notice in practice. The takeaway for designers: reach for blur to make a shadow feel diffuse and distant, and reach for spread to control its footprint independently of how soft it is.

Layering multiple shadows for realistic depth

A single shadow almost always looks flat and artificial, because real objects are never lit by one perfectly even light. The fix is to stack several shadows in one declaration, separated by commas. The z-ordering follows the same rule as text-shadow: the first shadow in the list is painted on top, the last is painted furthest back. So you usually order them tightest-and-darkest first, then progressively wider and fainter.

Google's Material Design formalised this idea with a two-light model. It imagines each surface as a sheet of paper lit by a sharp key light and a soft, omnidirectional ambient light. The key light casts a small, relatively dark shadow that defines the crisp contact edge directly beneath the element; the ambient light casts a larger, much lighter shadow that diffuses outward. An authentic elevation shadow is therefore two stacked shadows at once, which is exactly why the Material presets in this generator ship as a pair of layers rather than a single line.

A reliable recipe for natural depth is to use three or more layers that double in blur as they go: something like 0 1px 2px rgba(0,0,0,.12), 0 2px 4px rgba(0,0,0,.10), 0 4px 8px rgba(0,0,0,.08), 0 8px 16px rgba(0,0,0,.06). Each successive layer is wider and fainter, so the shadow ramps smoothly from a crisp contact line into a soft ambient haze — the same effect you would get from a properly modelled area light, approximated cheaply with four CSS values. Click + Add layer above to build your own stacks.

box-shadow vs filter: drop-shadow()

CSS offers a second way to cast a shadow — the drop-shadow() function used inside the filter property — and the two are not interchangeable. The decisive difference is what the shadow follows. box-shadow always traces the element's rectangular border box (respecting any border-radius), no matter what is painted inside it. filter: drop-shadow() instead traces the element's actual painted shape — its alpha channel — so it wraps around the visible pixels of a transparent PNG, an SVG icon, a clipped shape or text, ignoring the empty corners.

That single distinction drives the choice. A logo saved as a transparent PNG gets an ugly rectangular halo from box-shadow but a tight, shape-hugging shadow from drop-shadow(). The trade-off is that drop-shadow() is a deliberately smaller feature: it has no spread value and no inset, and it accepts only offset-x, offset-y, an optional blur and a colour. You can still layer it by chaining functions — filter: drop-shadow(...) drop-shadow(...) — and because it is a filter it also shadows every child of a group as one combined silhouette, which box-shadow cannot do.

Capability box-shadow filter: drop-shadow()
Follows Rectangular border box (+ border-radius) Actual painted alpha shape
Spread radius Yes No
Inset (inner) shadow Yes No
Multiple shadows Comma-separated in one value Chain several drop-shadow() functions
Best for Cards, buttons, panels, inputs Logos, icons, transparent PNGs, irregular shapes

Inset shadows, neumorphism and elevation

Adding the inset keyword flips a shadow inward: instead of being cast outside the element it is clipped to the inside of the padding box, sitting above the background but below the content. This is what makes something look recessed rather than raised — pressed buttons, text fields, toggle tracks, progress wells and the "carved out" look all rely on a small dark inset shadow near the top edge to simulate a surface the light cannot fully reach.

Neumorphism (a portmanteau of "new" and "skeuomorphism") takes this further by combining opposite shadows on the same element. An element extruded from its background gets a dark shadow on the side facing away from the light and a light, almost-white shadow on the side facing it — typically 8px 8px 16px in a darker tone plus -8px -8px 16px in white. Switch both layers to inset and the same element appears pushed into the surface instead. The effect only works when the element and its background share a colour, which is why neumorphic UIs live on soft grey canvases.

Material elevation uses shadows as a literal coordinate. Every surface has a height measured in dp (density-independent pixels), and that z-height is communicated only through its shadow: a resting card might sit at 1 dp with a barely-there shadow, while a floating action button or an open menu rises to 6–8 dp with a noticeably larger, softer cast. Raising elevation on hover or focus is how Material signals that something is interactive or has been picked up — the shadow is encoding spatial information, not just decoration.

Performance: animate shadows the smart way

A static box-shadow is cheap, but animating one is not. Changing a shadow's blur, spread or colour forces the browser to repaint the element on every single frame, and on a phone that can drop a smooth hover transition to a stutter. Only two properties can be animated without triggering layout or paint — opacity and transform — because the compositor can handle them on the GPU. So the performant pattern, popularised by Tobias Ahlin, is to not animate the shadow at all.

Instead, give the element a larger "target" shadow on a pseudo-element and fade that in. Apply your resting shadow to the element normally, attach a stronger shadow to its ::after pseudo-element with opacity: 0, position the pseudo-element to overlap exactly, and transition only its opacity on :hover. The shadow appears to grow and soften, but the browser is merely cross-fading two pre-painted layers — work it can composite at 60 fps. If you genuinely must animate a real box-shadow, add will-change sparingly (it reserves a compositing layer and overusing it wastes memory) and remove it once the animation ends.

Finally, treat shadows as an accessibility concern, not just an aesthetic one. A shadow should never be the only signal that an element is focused, selected or disabled, because low-vision users and anyone in bright sunlight may not perceive a faint cast — pair it with a colour change, border, icon or label. Honour prefers-reduced-motion by skipping shadow-driven hover animations for users who ask for less movement, and keep your shadow colours subtle enough that they do not reduce the contrast of nearby text.

A short history of shadows in UI design

Shadows have swung in and out of fashion as digital interfaces matured. Through the late 2000s and early 2010s, skeuomorphism ruled: buttons had glossy bevels, leather textures and heavy drop shadows meant to mimic physical objects so that users new to touchscreens knew what was tappable. Apple's early iOS was the poster child.

That reversed sharply in 2013, when Apple shipped iOS 7 and ushered in flat design — clean geometry, bold colour and almost no shadows or depth at all. Flat design was cleaner and faster to render, but stripping away every shadow also removed the visual cues that told people which elements were layered or clickable.

Google answered in 2014 with Material Design, sometimes called "flat design 2.0", which brought shadows back in a disciplined, physically-motivated way: the elevation-in-dp system described above, where a shadow's size encodes how high a surface floats. Then in late 2019 a viral Dribbble concept by designer Alexander Plyuto sparked neumorphism, the soft-UI revival of inset-and-outset shadows that peaked around 2020–2021 before settling into a niche (its low contrast made it hard to use accessibly). The throughline is that a well-judged shadow is never just decoration — it is the cheapest, most universal way to tell a flat screen which things are near and which are far.

Frequently asked questions

What do the box-shadow values mean?

The box-shadow shorthand is "box-shadow: h-offset v-offset blur spread color". The horizontal offset moves the shadow right (positive) or left (negative); the vertical offset moves it down (positive) or up (negative). Blur is how soft the edge is — 0 gives a hard edge, larger values fade it out. Spread grows (positive) or shrinks (negative) the shadow before the blur is applied. The colour is the shadow colour, usually a semi-transparent black such as rgba(0,0,0,0.2). The optional "inset" keyword draws the shadow inside the element instead of outside.

How do I add more than one shadow to an element?

CSS lets you apply several shadows to one element by separating each with a comma — for example "box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24)". The first shadow in the list is painted on top. Layering is how realistic Material Design elevation and the dual light/dark shadows of neumorphism are built. In this generator, click "+ Add layer" to stack shadows; each layer has its own offset, blur, spread, colour and inset.

What is an inset shadow used for?

An inset shadow is drawn inside the element’s border box instead of outside it, which makes the element look pressed in or carved out rather than raised up. It is commonly used for input fields, pressed buttons, wells, progress tracks and the "soft UI" / neumorphism style. Toggle the Inset switch on any layer to convert it, and combine an inset dark shadow with an inset light shadow for a concave neumorphic effect.

Why use rgba or a transparent colour instead of a solid one?

Real shadows are never pure black — they let some of the background show through. Using a semi-transparent colour such as rgba(0,0,0,0.2) lets the shadow blend naturally over any background, so the same component looks right on white, grey or coloured surfaces. This generator builds an rgba() value from your chosen colour and the opacity slider, so you control exactly how strong the shadow is. As a rule of thumb, large soft shadows look best at low opacity (10–20%) and small tight shadows can go higher.

Is box-shadow good for performance, and does it need a -webkit- prefix?

box-shadow has been supported unprefixed in every modern browser since 2011, so you no longer need the -webkit-box-shadow prefix for production. It is GPU-composited and far cheaper than a shadow image, but animating the blur or spread of a large shadow on many elements can cause repaint cost — for animations, prefer transitioning opacity on a pseudo-element that holds the shadow, or animate transform. For a single static shadow there is effectively no performance concern.

What is the difference between box-shadow and filter: drop-shadow()?

box-shadow follows the element’s rectangular border box (respecting border-radius), supports spread and inset, and accepts multiple comma-separated layers. filter: drop-shadow() instead follows the actual painted shape — including transparent PNGs, SVG paths and irregular content — but it has no spread value and no inset. Use box-shadow for cards, buttons and panels; use drop-shadow when you need a shadow that hugs a non-rectangular graphic.

Scopri altri strumenti

Vedi tutti i 70 strumenti →