Drawing Double Borders on SVG Shapes Without an Editor

When SVG shapes need a double border at runtime, CSS and SVG both lack a direct stroke-style: double equivalent. The available workarounds vary widely in complexity, shape support, and visual quality. Here is a breakdown of the practical options and their trade-offs.

Quick Wins: Bounding-Box CSS

If the target shape is a square or rectangle, CSS properties applied to the wrapping SVG element offer the shortest path.

outline

Two CSS declarations suffice using outline. This keeps a transparent background visible through the shape and produces smooth borders.

  • Works only with rectangular shapes
  • Minimal code
  • Smooth border rendering
  • Background remains transparent

box-shadow

A single CSS declaration with box-shadow achieves the effect, but each shape must live in its own SVG element. The background color must be hard-coded into the shadow value, which prevents transparency.

  • Works only with rectangular shapes
  • Minimal code
  • Smooth border rendering
  • No transparent background support

Limited Gradient Trick

SVG radial gradients can fake a double border, but only for circles. Applying the gradient directly to the stroke works, though the color stops need frequent repetition, making CSS variables worthwhile.

  • Works only with circles
  • Simple code
  • Border smoothness is inconsistent
  • No transparent background support

Filters for Every Shape

CSS drop-shadow()

Applying filter: drop-shadow() works for any shape type, provided each sits in its own SVG. Colors stay flexible via CSS variables with a single declaration. The main drawback is visible pixelation around the borders.

  • Universal shape support
  • Simple code
  • Borders appear pixelated
  • No transparent background support

SVG Filters

A custom SVG filter referenced through the shape's filter attribute is more flexible but requires three compositing passes: one for the outer stroke, one for a background flood fill, and one to redraw the original shape on top. Output quality improves on the CSS drop-shadow approach, yet the border edges still look jagged.

  • Universal shape support
  • Complex filter setup
  • Borders appear pixelated
  • No transparent background support

Reusing Shape Definitions

Two duplication strategies work across all shapes and render clean borders, with different code and transparency characteristics.

Transforms

Define the shape once in <defs>, then render two instances. The base instance carries the fill and the outer stroke. The overlay instance has no fill, a contrasting stroke, and is centered using a scale transform. To scale from the shape's center, translate by half of the negative viewBox width and height.

  • Universal shape support
  • Shapes defined once but instantiated twice
  • Smooth border rendering
  • Background remains transparent

The <use> Pattern

Doug Schepers' approach from the www-svg mailing list relies on two <use> references to a single definition. The first instance uses a thicker stroke for the outer ring. The second instance halves that stroke width, removes the fill, and sets a stroke matching the page background, carving a gap that reveals the original fill underneath.

  • Universal shape support
  • Shapes defined once but instantiated twice
  • Smooth border rendering
  • Depends on a known background color, so no transparency

Side-by-Side Comparison

The reference implementation below gathers all discussed techniques in one view for direct comparison.

SolutionAll shapesSimple codeSmooth bordersTransparent background
outline🙁
box-shadow🙁🙁
SVG gradients🙁🙁🙁
filter: drop-shadow()🙁🙁
SVG filters🙁🙁🙁
Reusing shapes:
Tranforms
🙁
Reusing shapes:
<use>
🙁🙁