SVG drop shadow and filters: shadow, blur and glow

Add a shadow to an SVG with the CSS drop-shadow() filter or with an SVG filter element such as feDropShadow. Blur and glow too, and why shadows get cut off.

Published

There are two ways to give an SVG a shadow. On a web page, the CSS rule filter: drop-shadow(...) on the <svg> or <img> element adds one that follows the outline of the drawing. Inside the file, a <filter> element holding <feDropShadow> does the same and travels with the SVG. box-shadow is the wrong tool: it shadows the rectangle around the image, not the shape.

The CSS way: drop-shadow()

.logo { filter: drop-shadow(2px 4px 3px rgba(0, 0, 0, 0.4)); }

The values are the shift to the right, the shift down, the blur and the colour. Applied to an inline <svg> or to an <img> showing an SVG file, the shadow follows the visible shapes, holes included, because it is worked out from the transparency of the picture.

filter: drop-shadow()box-shadow
Shape of the shadowThe outline of the drawingThe element’s rectangular box
Works on <svg> and <img>YesYes
Works on a shape inside an SVGIn inline SVG, with uneven browser supportNo
Can spread or sit insideNoYes, with spread and inset

For a shadow on one shape inside a drawing, an SVG filter is the dependable choice.

The SVG way: a filter element

This is a complete file:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
  <defs>
    <filter id="shadow" x="-50%" y="-50%" width="200%" height="200%">
      <feDropShadow dx="3" dy="3" stdDeviation="3" flood-color="black" flood-opacity="0.4"/>
    </filter>
  </defs>
  <circle cx="60" cy="60" r="40" fill="gold" filter="url(#shadow)"/>
</svg>

A gold disc with a soft grey shadow below and to the right of it.

AttributeMeaning
dx, dyHow far the shadow is shifted right and down
stdDeviationThe amount of blur. 0 gives a hard-edged shadow
flood-colorThe shadow’s colour
flood-opacityHow solid the shadow is, 0 to 1

The numbers are in the drawing’s own units, so the shadow grows and shrinks with the SVG. Put the filter attribute on a <g> to give a whole group one shared shadow.

A filter defined in the file works wherever a browser draws that file, including in an <img> and in a CSS background.

Why the shadow is cut off

A filter only paints inside its filter region. By default that region is the element’s bounding box plus a tenth of its size on every side. A long or soft shadow reaches further and is sliced off in a straight line.

The x, y, width and height attributes on the <filter> enlarge the region. The example above uses x="-50%" y="-50%" width="200%" height="200%": half the element’s size added on every side. Make it larger still for a big blur.

Two related cuts:

  • The edge of the SVG. A shadow that passes the viewBox is cut by the image itself. Leave a margin round the artwork. For an inline SVG, overflow: visible in CSS lets it paint outside its box.
  • Straight lines. A perfectly horizontal or vertical line has a bounding box with no height or width, so a region measured from that box is empty and the line vanishes when filtered. Add filterUnits="userSpaceOnUse" and give x, y, width and height as plain coordinates covering the line and its shadow.

The longer recipe, for older software

<feDropShadow> is one of the newer filter elements. Current browsers support it, but some other programs that read SVG do not. The same shadow can be built from older parts. This fragment replaces the <filter> in the file above:

<filter id="shadow" x="-50%" y="-50%" width="200%" height="200%">
  <feGaussianBlur in="SourceAlpha" stdDeviation="3"/>
  <feOffset dx="3" dy="3" result="blurred"/>
  <feComponentTransfer in="blurred" result="soft">
    <feFuncA type="linear" slope="0.4"/>
  </feComponentTransfer>
  <feMerge>
    <feMergeNode in="soft"/>
    <feMergeNode in="SourceGraphic"/>
  </feMerge>
</filter>

Read it as a production line. Take the shape’s silhouette in black (SourceAlpha) and blur it. Shift it. Thin it to 40 per cent. Then stack the original drawing (SourceGraphic) on top of the shadow. Each step takes the result of the one before unless in names another input, and result gives a step’s output a name.

Blur and glow

A blur on its own, again as a fragment for the <defs>:

<filter id="blur">
  <feGaussianBlur stdDeviation="2"/>
</filter>

On a web page, the CSS equivalent is filter: blur(2px).

A glow is a drop shadow that does not move, in a bright colour:

<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
  <feDropShadow dx="0" dy="0" stdDeviation="4" flood-color="gold"/>
</filter>

What else filters can do

A <filter> is a chain of steps, each an element whose name begins with fe.

ElementEffect
feGaussianBlurBlur
feOffsetShift
feFloodFill the region with one colour
feColorMatrixChange colours: greyscale, saturation, hue
feComponentTransferAdjust each colour channel: brightness, contrast, opacity
feMorphologyThicken or thin the shapes
feComposite, feBlend, feMergeCombine two or more results
feTurbulenceGenerate noise, for textures
feDisplacementMapDistort one image by another

An SVG filter can also be applied to an HTML element, with filter: url(#id) in CSS pointing at a filter in an inline SVG on the same page.

Filters outside the browser

A filter is worked out in pixels when the image is drawn. It is not a shape, and that limits where it survives.

  • Vector editors vary. Inkscape draws SVG filters and has its own Drop Shadow command in the Filters menu, under Shadows and Glows.
  • Cutting and laser software works from outlines, and a filter has none. Do not rely on a shadow or blur appearing there.
  • Pictures: to keep the shadow in a PNG, convert in a browser, which draws the filter. SVG to PNG does this.

For a shadow that a machine can cut, make it a real shape: a copy of the outline in a darker material, moved a little down and to one side, or an enlarged outline behind the design. Cricut Design Space has an Offset tool that makes such an outline as a new layer.

Common problems

What you seeCauseFix
The shadow ends in a straight edgeThe filter region is too smallEnlarge x, y, width, height on the <filter>
The shadow is cut at the image’s edgeIt passes the viewBoxAdd margin, or overflow: visible on an inline SVG
A filtered line disappearsIts bounding box is emptyfilterUnits="userSpaceOnUse" with coordinates
The shadow is a rectanglebox-shadow was usedfilter: drop-shadow()
The filter does nothing, or the shape vanishesThe id and url(#...) do not match. Browsers have handled this differentlyMatch them exactly
No shadow in another programIt does not support filters, or not feDropShadowUse the longer recipe, or export a PNG
The page scrolls or animates slowlyLarge blurs are costly to drawReduce the blur, or the area filtered

Questions

Can the shadow be inside the shape? Not with drop-shadow() or feDropShadow, which only draw outside and behind. An inner shadow needs a longer filter chain that inverts the shape, blurs it and clips the result to the shape.

Does a shadow work on SVG text? Yes. Apply the filter to the <text> element like any other.

Is this the same as a shadow box SVG? No. A shadow box is a picture built from stacked sheets of cut paper, where the shadows are real. The shadow box maker builds those.

How do other transparency effects work? See SVG transparency and opacity, and masks and clip paths for hiding parts of a shape.