Function

@svg

Creates an SVG image from XML or a CSS-like syntax. The result is a url() for background, mask or border-image, and an inline element when used with @content.

Syntax

  • @svg(<svg-body>)
  • @svg(<svg ...>...</svg>)
svg-body
CSS-like rules. Top-level declarations become attributes of the <svg> root; blocks become its children. Wrapping the body in svg { } is optional.
XML
Used as written when the body starts with <. xmlns is added if it is missing.

A simple shape

Write an element as a block, with its attributes inside. Here, circle creates a <circle>, and cx, cy: 5 sets both coordinates to 5. Declarations outside the block belong to the SVG itself.

viewBox: 10 is short for viewBox: 0 0 10 10: a coordinate area 10 units wide and 10 units high. Two numbers, such as viewBox: 10 20, set the width and height separately.

@grid: 1 / 60vmin;
background: @svg(
  viewBox: 10;
  circle {
    cx, cy: 5;
    r: 4;
    fill: #000;
  }
);

The same image as XML:

@grid: 1 / 60vmin;
background: @svg(
  <svg viewBox="0 0 10 10">
    <circle cx="5" cy="5" r="4" fill="#000"/>
  </svg>
);

Repeating elements

Add * after an element name to repeat it: circle*10 makes 10 circles. Use @n for the current step, starting at 1. Like @m, repetition also accepts a range (1-5) or a grid (16x16).

This grid uses @nx and @ny for each line’s coordinates, and @p to pick its direction. The padding .2 on the viewBox adds space around the edges so the strokes are not clipped.

@grid: 1 / 60vmin;
background: @svg(
  viewBox: 0 0 16 16 padding .2;
  stroke: #000;
  stroke-width: .4;
  stroke-linecap: round;
  line*16x16 {
    x1, y1, x2, y2: @p(
      @nx(-1) @ny(-1) @nx @ny,
      @nx @ny(-1) @nx(-1) @ny,
      @nx @ny(-1) @nx @ny
    );
  }
);

Functions work inside @svg too. Here, each circle’s position, size, and color depend on @n, forming a Fibonacci spiral.

@grid: 1 / 60vmin;
background: @svg(
  viewBox: -50 -50 100 100;
  circle*300 {
    fill: hsl($(120-90*@sin.n), 80%, 50%);
    r: @sqrt(@n/60);
    cx: $(@n*.618^4 * cos(*@n*.618));
    cy: $(@n*.618^4 * sin(*@n*.618));
  }
);

Gradients and definitions

A gradient can go directly in fill. It is placed in <defs> and given an id; the fill becomes url(#id). There is no need to write either yourself.

@grid: 1 / 60vmin;
background: @svg(
  viewBox: 0 0 10 10;
  rect {
    width, height: 10;
    fill: linearGradient {
      gradientTransform: rotate(45);
      stop { offset: 0; stop-color: #000 }
      stop { offset: 1; stop-color: #e6437d }
    }
  }
);

The same syntax works for patterns, filters, clip paths, masks, markers, and symbols. An explicit defs is optional: fill: defs linearGradient { … } works too. For href and xlink:href, the reference is #id rather than url(#id). For a shorter way to write gradients, see @linearGradient and @radialGradient.

Inline SVG

Use @content instead of background to put the SVG element inside the cell, rather than use it as a background image.

@grid: 1 / 60vmin auto;
@content: @svg(
  viewBox: -5 -5 10 10;
  circle*12 {
    r: @nN(*4);
    fill: none;
    stroke: #000;
    stroke-width: .15;
  }
);

Animation

draw: 3s infinite draws a shape’s stroke over three seconds, then repeats. Set a stroke to make it visible. This works on paths, lines, circles, ellipses, rectangles, polygons, and polylines.

@grid: 1 / 60vmin;
background: @svg(
  viewBox: 0 0 10 10 p 2;
  path {
    stroke: #000;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: .4;
    fill: none;
    draw: 3s infinite;
    d: M 0 0
       @M10.pn(0 @n @n 0, @n 0 0 @n)
       @M10.pn(@n @N @N @n, @N @n @n @N);
  }
);

To animate an attribute, write animate followed by its name. Separate the values with semicolons, then put the timing after /. These circles grow from a radius of 0.2 to 0.8 and shrink back over two seconds.

@grid: 1 / 60vmin;
background: @svg(
  viewBox: -5 -5 10 10;
  circle*8 {
    cx, cy: @Plot(r: 3);
    r: .6;
    fill: #000;
    animate r: .2; .8; .2 / 2s ease infinite;
  }
);

Timing accepts a duration, an optional delay, a repeat count or infinite, and an easing such as ease. For example, 2s 1s 3 waits one second, then runs three times for two seconds each. Add forwards or freeze to keep the final value. The same timing options work with draw.

A single animation value is the target value. Use animate transform for an SVG transform animation. With no attribute name, animate is an alias for draw. The viewBox padding also has a short form: p 2 means padding 2.

Other shortcuts

SyntaxMeaning
g { circle { … } }A circle inside a group. g circle { … } and g > circle { … } do the same.
circle, rect { … }Two elements with the same attributes.
x, y: 1, 2;Sets x="1" and y="2". A single value sets both attributes.
rect#a.b { … }A rectangle with id="a" and class="b".
content: hello;Text inside the element, for example in a text block.
style { circle { … } }A stylesheet inside the SVG. Its contents are ordinary CSS.
style fill: red;An inline style. Use style: { … } for several CSS declarations.
--r: 3;A variable for use in $() expressions inside the SVG.

Element and attribute names are case-insensitive in the CSS-like syntax: viewbox works like viewBox. To generate SVG markup as a string in JavaScript, use the svg() generator.

See also