Using SVG with CSS3 and HTML5 — Supplementary Material

Example code and online extras for the O'Reilly Media book by Amelia Bellamy-Royds, Kurt Cagle, and Dudley Storey.

Re-purposing Icons as Markers

In the book, we discuss how the viewBox attribute on the <marker> element can be used to change the position of the marker’s origin, and therefore of the point that gets aligned with the exact vertex (corner-point) of the marked shape.

A marker’s viewBox can also be used in the same way as any other SVG viewBox, to create a local scaled coordinate system. This is particularly useful when reusing icons that you already have, such as in a <symbol> definition.

However, pre-defined symbol icons aren’t likely to have been defined in such a way that the origin is at meaningful point for creating a marker. You can change the reference point of the <marker> element, separately from the viewBox origin, using refX and refY.

In sum, to convert a <symbol> to a marker:

Figure 14-X1 shows the result of adapting our card-suit heart icon to create a Valentine worthy of a besotted 11-year-old. Heart-shaped markers are positioned around a heart shape.

A copy of the heart icon in dark pink with a thick stroke in light pink.  Positioned mostly inside that stroke are additional hearts in red, at the top and sides of the lobes, and at the points where the two sides of the heart join together.
Figure 14-X1. A heart-marked valentine heart

For reference, this is the <symbol> version of the icon, from Chapter 10:

<symbol viewBox="0 0 20 20" id="heart"
     style="overflow: visible">
    <title>Heart</title>
    <path d="M10,6 Q10,0 15,0T20,6Q20,10 15,14
             T10,20Q10,18 5,14T0,6Q0,0 5,0T10,6Z" />
</symbol>

The viewBox of the marker will be set to the same 20×20 scale, with a top-left origin, that we used for the original icon. But the top-left corner isn’t a good reference point for the marker, since it is outside of the heart shape. Instead, we’ll use refX and refY to set the reference point to the center of the icon.

You’ll usually also want to define the marker’s dimensions, using markerWidth and markerHeight; by default, your viewBox will scale to fit a square three times as wide as the stroke on the shape you’re marking.

In Figure 14-X1, the markers are scaled to fit within a thick stroke on the shape. With the default strokeWidth value for markerUnits, that means that markerWidth and markerHeight are slightly less than 1.

Example 14-X1 gives the code.

Example 14-X1. Using icons as markers
<svg xmlns="http://www.w3.org/2000/svg" xml:lang="en"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     width="250px" height="250px" viewBox="-2 -2 24 24">
    <title>Heart-shaped Markers on a Heart Shape</title>
    <marker id="heart-marker" viewBox="0 0 20 20"
            markerHeight="0.9" markerWidth="0.9" orient="auto"
            refX="10" refY="10"
            fill="darkRed" stroke="none">
        <path id="heart"
              d="M10,6 Q10,0 15,0T20,6Q20,10 15,14
                 T10,20Q10,18 5,14T0,6Q0,0 5,0T10,6Z" />
    </marker>

    <use xlink:href="#heart" stroke-width="4"
         fill="indianRed" stroke="lightCoral"
         marker-mid="url(#heart-marker)" />

</svg>

When reusing shapes like this, be careful not to create infinite loops: if the <marker> contains any content that is styled to use the same marker, it won’t be drawn at all. This is why Example 14-X1 defined the shape within the <marker> and reused it in the main drawing, instead of the reverse.

Warning

Microsoft Edge and Internet Explorer’s loop error handling is overly strict. Neither browser will draw the markers in Example 14-X1 as it is written, or even if the heart path is defined separately (in a <defs>) and then reused in both the marker and the main figure.

In order to get the markers to be drawn in Microsoft browsers, the marker and the shape it is marking need to be completely independent. In other words, for Example 14-X1, you need to copy & past the <path> element in the <marker> separate from the one that is used (or reused) to draw the main shape. The result would be the same as Figure 14-X1 in other browsers; Figure 14-X2 shows the result in MS Edge.

Another version of the heart-marked heart, identical to the last except that the marker on the dimple is sideways instead of angled and the marker on the bottom point is upside-down.
Figure 14-X2. A heart-marked valentine heart, in MS Edge, after adjusting the code to satisfy Microsoft browsers

Figure 14-X1 (from Firefox) and Figure 14-X2 (from MS Edge) should serve as one final warning about the complexity of stroke and marker styling: some of the markers end up with different orientations.

The stroke line joins at the inside and outside points of the shape are both 180° U-turns. As a result, the strokes at both points are both cropped flat against the point of the shape. The markers centered over those points protrude outside the stroke. And the two browser renderings don’t agree on their orientations.

The fact there is a marker at all on the inner point—which is where the start and end of the <path> meet—is somewhat unexpected, because the markers were assigned with the marker-mid presentation attribute. The start and end points should therefore not be marked.

The marker that is drawn at that point is for the line join between the final curve and the zero-length line created by the Z close path command.

Tip

This unexpected marker is one of the main reasons that SVG 2 introduces a new Z syntax to define a curve that automatically closes the path. Unfortunately, that new syntax is not yet supported in browsers.

The different browsers orient this final marker differently. A zero-length line does not have a defined angle, and the browsers differ in how they determine the orientation in that case. SVG 2 clarifies that the browser should skip over zero-length lines until it reaches a line with a valid stroke angle, but the browsers have not yet all caught up.

The bottom point doesn’t have a zero-length line. But it does have two vertical tangent angles, for the incoming and outgoing curves. Half-way between those two vertical lines is going to be a horizontal line, but the browsers can’t decide whether it is facing up or facing down.