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.

Perplexing Percentages

Nearly every length measurement in SVG can be expressed by a percentage. With a few exceptions, those percentages are relative to the SVG coordinate system size—the size defined by the viewBox of the nearest ancestor <svg> or <symbol> (or by its actual width and height, if it doesn’t have a viewBox).

Percentages in horizontal lengths are proportional to the coordinate system width. Percentages in vertical lengths are proportional to the coordinate system height.

But some lengths in SVG aren’t either strictly horizontal or vertical:

Tip

Font-related lengths (such as for font-size and baseline-shift) are also neither strictly horizontal or strictly vertical. But those properties have their own way of interpreting percentages.

Percentages in the radius and stroke properties are also proportional to the coordinate system size, but in a way that combines the width and height in to a single reference value.

These values can be thought of as “diagonal” lengths, because the percentages grow and shrink in proportion to the length of the diagonal of the SVG coordinate system.

But it’s not that simple. They aren’t percentages of the diagonal length. Instead, the value of 100% is adjusted to maintain the relationship from Pythagoras’ theorem.

Pythagoras’ theorem (as you might remember from geometry class) says that in a right-angled triangle, the square of the length of the diagonal always equals the sum of the squares of the horizontal and vertical lengths.

(diagonal)2=(width)2+(height)2(diagonal)=(width)2+(height)2 \begin{array}{l c r} (\text{diagonal})^2 &=& (\text{width})^2 + (\text{height})^2 \\ \\ (\text{diagonal}) &=& {\sqrt{(\text{width})^2 + (\text{height})^2}} \end{array}

For example, if a rectangle is 4-inches wide and 3-inches tall, the diagonal will be 5-inches long, as shown in Figure 25-1. You may have even memorized a few “Pythagorean triple” sets like this, where the numbers are all nice neat integers.

A right-angle triangle, drawn as thin lines with thick red or orange dashes as beads over top.  There are four beads across the top edge, three down the left, and five along the diagonal.  The formula 3-squared + 4-squared = 5-squared is written across the bottom.
Figure 25-1. A Pythagorean triple, drawn as even-length beads arranged in a right-angled triangle

In general, you can’t use Pythagoras’ theorem with SVG percentages, because 1% height is a different unit than 1% width. However, if a rectangle is 100% wide (measured by the width of the SVG) and 100% tall (measured by the height of the SVG), the diagonal (measured using the formula for circle radius) will equal √2 × 100%, or 141.4214…%.

The absolute distance of the diagonal (calculated using Pythagoras’ theorem) can therefore be used to figure out just how big a percentage circle will be. For example, in a 3×4-inch SVG, the diagonal is 5 inches, so that means 5 inches equals 141-ish-%. From there, you can calculate that 100% in the “diagonal percentages” would be (5in / 1.41), or approximately 3.53in.

If your SVG is square, however—say, 200px by 200px—then the numbers get a little nicer. The SVG’s full diagonal length would be √2 × 200px (approximately 282px), but 100% in diagonal mode would be (√2 × 200px)/(√2), or exactly 200px.

Tip

In the special case of a square SVG, 1% is 1% is 1%, no matter how you measure it.

The general formula is as follows:

distance=percentage value100%×(width)2+(height)22 \text{distance} = \frac{\text{percentage value}}{100\%} \times \frac{\sqrt{(\text{width})^2 + (\text{height})^2}}{\sqrt{2}}

That √2 factor means that the numbers aren’t usually nice neat integers anymore, but the same formula works in any SVG, of any dimensions.

If your SVG was 12cm wide and 5cm tall, the full diagonal would be √(122 + 52)cm, or 13cm. A 100% diagonal distance would be approximately (13cm / 1.41), or 9.19cm.

The main thing you need to know, is that a percentage length for circle radius is somewhere in-between the lengths that you would get for that percentage for width and that percentage for height.

There is one interesting side effect of the formula, that relates to the geometry of ellipses: when a circle and an ellipse both use the same percentage values for their radius, the radius will be the same on the diagonal of an ellipse. That relationship is demonstrated in Example 25-1.

The code uses HTML and CSS to create a resizable container for an inline <svg>, allowing you to test the same percentages in different-sized graphics. Figure 25-2 shows the result when the graphic has been resized to various aspect ratios.

Three browser panels with scroll bars, each containing variations of the same figure: dark red rectangle with a white ellipse, and a circle on top that is solid black in center, dark blue surrounding, then transparent blue. The top panel is full width of the figure, the bottom two panels are the same height but one is 2/3 the width and the other 1/3 width. The rectangle and ellipse adjust to fill the panel, but the circles overflow it in the more stretched-out panels.
Figure 25-2. SVG circles and ellipses sized using percentages, at different aspect ratios
Example 25-1. Sizing circles and ellipses using percentages, in a resizeable SVG
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Keeping an Eye on Percentage Sizing</title>
    <style>
html, body {height: 100%; margin: 0;}
.wrapper {
    position: relative;
    border: thin solid;
    box-sizing: border-box;
    height: 100%;
}                                1
@supports (resize: both) {
  .wrapper {
    height: 10em;
    width: 20em;
    resize: both;
    overflow: hidden;
  }
}
svg {                            2
    display: block;
    position: absolute;
    height: 90%;
    width: 90%;
    top: 5%;
    left: 5%;

    background: darkRed;         3
}
    </style>
</head>
<body>
    <div class="wrapper">                                 4
        <svg>
            <g stroke="gray" stroke-width="3px" >         5
                <line x2="100%" y2="100%" />
                <line x1="100%" y2="100%" />
            </g>
            <ellipse cx="50%" cy="50%" ry="40%" rx="40%"
                     fill="white" />                      6
            <circle cx="50%" cy="50%" r="25%"
                    stroke="blue" stroke-width="30%"
                    stroke-opacity="0.6" />               7
        </svg>
    </div>
</body>
</html>
1

The HTML style block defines the properties that allow the user to resize the SVG. A wrapper element, with a defined width and height, and a non-default position property, is given the resize: both property to allow resizing in horizontal and vertical directions. The overflow property must be changed from the default (visible) to make this work. Since CSS resize is not supported in all browsers, an @supports rule is used to set these values; in other browsers, the graphic fills the entire screen, and you can resize the browser window.

2

The SVG is positioned absolutely within it’s wrapper, stretched to fit the current wrapper size, with a 5% offset from the edges.

3

The SVG box is given a solid, dark red background color which will provide contrast for the graphic.

4

The SVG code is contained in an inline <svg> element. Because this is HTML 5, the SVG namespace can be omitted from the markup.

5

Diagonal guidelines are drawn between each corner of the SVG.

6

The white ellipse is centered in the SVG region, with horizontal and vertical radius of 40%.

7

The circle is centered on the same point. Both the radius and the stroke-width are set using percentages; the stroke is partially transparent so that you can see the edges of the shapes underneath it.

View the live example.

The wide stroke is controlled by setting stroke-width to a percentage value. It will also scale in proportion to the diagonal of the SVG region.

The combined distance of the circle’s r value (25%) and the outside half of the stroke-width (half of 30%, or 15%) means that the outer edge of the circle’s stroke creates an effective radius of 40%. On the diagonals, this always exactly matches the edge of the white ellipse which has both horizontal and vertical radii of 40%. This can be confirmed by the guidelines drawn in Figure 25-2, or by opening the web page in a browser and resizing it yourself.

Warning

The CSS resize property used in the example is introduced in the CSS 3 Basic User Interface module. It has not been implemented in Internet Explorer or MS Edge as of EdgeHTML version 16, and is also not supported in many mobile browsers. If resizability is a fundamental part of your website, use JavaScript event handling.

As you can discover when testing Example 25-1 in your browser, the circle sized with percentage values can easily extend outside the bounds of the SVG if the SVG is wide and short or tall and narrow. With percentage sizing alone, there is no way to limit the circle to fit within the more constrained dimension.

Tip

To create a scalable shape that still always fits within the SVG, you need to create the scaling effect with viewBox, which we discuss in Chapter 8.

You rarely need to know the specifics of how diagonal percentages work in SVG. However, it’s good to know that they work in a complicated way, so you’re not expecting them to be simple!