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.

Interactive Text

Because SVG text is part of the DOM, in an interactive web document, it can be updated or restyled in response to user interaction.

In the simplest version, the CSS :hover class can be used to show and hide text labels, thereby reducing the amount of visual clutter as the user explores the image.

In Example 7-3, in the book, we added text labels to a product photograph of bicycle. Then, we showed how media queries could automatically increase the size of those labels when the graphic is scaled down.

With hover styles, we can hide and show those labels as the user explores the graphic, helping to focus attention on the individual parts.

We make the connection with invisible shapes that outline the features in the photograph that correspond to each label, turning the SVG into a simple image map. When the CSS registers a :hover on any of the shapes, the matching text element is revealed.

To keep things accessible, we’re only going to hide the labels if a hover-effect is detected somewhere on the SVG. Without mouse movement or active tap interaction, all the labels will display.

Tip

If the interaction was essential, we could make the elements keyboard-focusable and use the :focus pseudoclass, but too many tab stops can be an accessibility problem, too. So keyboard users will just see all the labels at once.

Example 7-X1 provides the complete code; Figure 7-X1 shows each label revealed in turn.

Four versions of the labelled bicycle, with only one label visible in each.
Figure 7-X1. The interactive labels, revealed one at a time
Example 7-X1. Making SVG text labels interactive on mouseover

SVG markup:

<svg xmlns="http://www.w3.org/2000/svg" xml:lang="en"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     width="1000" height="586"
     viewBox="0 0 1000 586" id="lynskey">
    <title>Lynskey 2017 Vialé Technical Details</title>
    <style>
        /* see below */
    </style>
    <image width="100%" height="100%" xlink:href="viale.jpg" />
    <g>
        <circle class="target" cx="770" cy="400" r="172"/>
        <circle class="target" cx="220" cy="380" r="172"/>       1
        <text x="360" y="520">DTSwiss TK540/X.9 Wheelset</text>
    </g>
    <g>
        <polygon class="target"
                 points="250,25 443,25 443,60 357,101 260,65" /> 2
        <text x="80" y="120">Selle Italia X1 Flow Saddle</text>
    </g>
    <g>
        <polygon class="target"
                 points="646,105 367,130 245,350 283,409
                         390,434 413,379 456,370 500,400
                         677,207 684,229 700,217 669,120" />
        <text x="420" y="200">Titanium frame</text>
    </g>
    <g>
        <circle class="target" cx="225" cy="400" r="55" />
        <polygon class="target"
                 points="210,400 230,480 260,480 280,390" />
        <text x="80" y="570">SRAM Apex rear dérailleur</text>    3
    </g>
</svg>
1

Each <text> label is now part of a group (<g> element) that also contains the shapes that outline the labelled regions of the photograph.

2

The mouse-target elements include a mix of <circle> and <polygon> elements. The target class will be used to make the elements invisible, so it doesn’t matter if the shapes are only rough approximations for the photographic regions.

3

Although they won’t be displayed, the order of the shapes still matters: the dérailleur (gear-shift) region overlaps one of the wheels, but it is later in the document and therefore “on top” as far as mouse events are concerned.

CSS styles:

svg text {
    font: 20px sans-serif;
    fill: darkBlue;
    transition: font-size 0.5s, opacity 0.3s;    1
}
@media (max-width: 600px), (max-height: 342px) {
    svg text { font-size: 28px; }                2
}
@media (max-width: 400px), (max-height: 235px) {
    svg text { font-size: 36px; }
}
.target {
    fill: none;
    pointer-events: visibleFill;                 3
}
svg:hover g:not(:hover) text {
    opacity: 0;                                  4
}
1

We previously added the transition property to smooth out font-size changes from the media queries. An additional transition has been added for the opacity property of the <text> elements, so that the labels fade in and out neatly.

2

The media queries for adjusting font-size are as described in “Responsive Text Scaling” in Chapter 7 of the book.

3

The shapes with the target class aren’t painted (fill is none instead of the default black) but they do respond to mouse-pointer events. The visibleFill value for the pointer-events property means that the fill region of the shape is active unless the shape is explicitly hidden with the visibility property, regardless of whether or not it is actually painted. (We discuss pointer-events in more detail in Chapter 18.)

4

When the SVG is hovered by a mouse pointer, the text elements are transparent (opacity: 0), unless they are inside a group that is also receiving the mouse hover event. The opacity property is used, instead of visibility or display, because it can be smoothly transitioned, and because it doesn’t affect pointer events on the text itself.

Interactive effects don’t work in an SVG used as an image. Mouse events on an <img> element are handled by the parent document, not passed to the SVG.

However, as we discussed in the book, there are already many other reasons why this example shouldn’t be used in an image: the text would be inaccessible, and the external photograph file would not load! So this approach is for <object> or (with adjusted media queries) inline SVG, only.