Recently I’ve had a fascination with recursive SVG patterns: using multiple repetitions of a basic polygon inside itself to create complex designs. This requires precision alignment of scaled elements, which brings on another challenge: how to scale SVG elements around their centers.

The Fundamentals

Let’s start with the basic pattern unit of a hexagon, with all six points internally joined via a line to their opposite:

Figure 1: A base SVG shape pattern

As this structure will be used repeatedly in the final design, it is created as a group inside the <defs> section of the SVG, with a <use> reference written to place the first iteration of the design:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 
viewBox="0 0 750 750">
    <defs>
        <g id="hex">
            <polygon points="229.3,627.5 83.6,375.5 229.3,123.5 520.7,123.5 666.4,375.5 520.7,627.5" />
            <line x1="229.3" y1="123.5" x2="520.7" y2="627.5"/>
            <line x1="666" y1="375.5" x2="84" y2="375.5"/>
            <line x1="520.7" y1="123.5" x2="229.3" y2="627.5"/>
        </g>
    </defs> 
    <use xlink:href="#hex" />
</svg>

The SVG is styled with written inside the <defs>:

polygon, line { fill: none; stroke: #32679D; stroke-width: 7px; }

Next, we want to create a copy of the pattern, but at half its current size, centered on the same point. But the line of code that you might use - a scaled <use> element:

<use xlink:href="#hex" transform="scale(.5)" />

…doesn’t work in the way you might expect:

Figure 2: The result of a standard SVG scale copy

Unlike HTML elements in CSS transformations, SVG does not scale around its center by default; instead, it scales from the top left corner. To get around this problem, we can use a simple formula:

translate(-centerX * (factor - 1), -centerY * (factor - 1)) scale(factor)

We know that the this design is centered at the precise center of the SVG i.e.: 375 375. Therefore, to scale a copy of the design by half on this center point, the complete formula would be:

translate(-375 * (.5 - 1), -375 * (factor - .5)) scale(.5)

Which resolves to:

<use xlink:href="#hex" transform="translate(187.5, 187.5) scale(.5)" />

Creating this result:

Figure 3: A correctly transformed SVG scale

The remaining problem - at least for this design - is that the stroke is scaled along with the design itself. Thankfully, from an earlier article, we know how to address that too: using non-scaling-stroke in CSS, or as an SVG attribute:

polygon, line { 
    fill: none; stroke: #32679D; stroke-width: 7px;
    vector-effect: non-scaling-stroke; 
}

The result:

Figure 4: A transformed SVG scale with non-scaling stroke

Once you have that, it’s fairly straightforward to move and scale further copies of the base pattern to create complex designs.

Variants

One of the advantages of using a base pattern in SVG is that any changes to the base unit will be reflected in its copies. That is, if I rotate the original pattern in the <defs>:

<g id="hex" transform="rotate(30 375 375)">

All of the hexagons that derive from this base design will be rotated as a result, while retaining their individual translation and scale changes. You can see the effect of this in the example at the top of this article, and its associated CodePen.

Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/gwwJWd