Recently one of my 1st year students proposed a UI design for her photography tutorial site, and asked for my advice as to how to pull it off. In my opinion, SVG was the best answer; while the student ultimately went another way, I thought that what I came up might still be a useful lesson.
Bitmap images and icon fonts are traditionally used for UI elements on web pages, but SVG has a number of advantages:
- Mouse and shape interaction are matched exactly in SVG, making the format perfect for creating accurate non-rectangular UI elements such as hexagons, geographical areas and (in this case) triangles.
- As a vector format, SVG is infinitely scalable, and is often smaller in file size than bitmaps or repurposed typography.
- Unlike icon fonts, SVG elements can be multicolored.
- SVG elements can be internally animated, using CSS transitions, animations, native SVG
<animate>
or JavaScript.
By internal animation I mean moving the individual components of the icon, rather than moving the entire element.
Each of the icons in this UI set – representing exposure, focus and brightness - is increasingly complex in its interaction. All of them were drawn in Adobe Illustrator, and exported collectively as an SVG document. The SVG code is placed directly on the page, allowing us to interact with the linked elements with CSS.
The Lightbulb
The lightbulb is the simplest of these elements, consisting of a <polygon>
(the outer triangle) and a <path>
(the lightbulb):
<a id="light" xlink:href="#" >
<polygon id="lightbulb-triangle" points="18.6,17.3 96.2,17.3 57.4,84.8"/>
<path id="lightbulb" d="M58.1, 43.6c0,0, 7.2-4.4, 4.5-6.7c0, 0-1-0.7-2.2, 1.8c0,0-1.2…" />
</a>
Applicable styles for these elements include SVG presentation attributes, most of which map directly to CSS:
#lightbulb-triangle {
fill: #efe7bc;
}
#lightbulb {
fill: transparent;
stroke: #fff;
}
This doesn’t mean that you can use fill
on arbitrary HTML elements: the properties are only applicable to SVG. However, we can use any CSS properties to change SVG, including :hover
and transitions:
a#light polygon, a#light path {
transition-duration: 1s;
}
a#light:hover #lightbulb {
stroke: #efe7bc;
}
a#light:hover #lightbulb-triangle {
fill: #000;
}
The Clock
The clock is more complex, consisting of a triangle, two circle shapes, and a triangular minute hand:
<a id="clock" xlink:href="#" >
<polygon id="clock-triangle" points="212.4, 88.4 191.2, 52.5 170, 16.5 212.4, 16.5 254.8, 16.5 233.6, 52.5" />
<path id="clock-circle" d="M228.9, 42.3c-0.4, 9-7.2, 15.8-16.2, 16.2c-9, 0.4-15…."/>
<path id="minute-hand" d="M213, 30.5c0-0.3-0.5-0.3-0.5, 0c-0.3, 3.3-0.7… "/>
</a>
The background fade animation for this element is the same, but rotating the minute hand is somewhat trickier: Firefox currently refuses to change the transform-origin
of SVG elements correctly. As a result, I’ve made the minute-hand animation work only in Webkit/Blink:
@-webkit-keyframes timer {
to { transform: rotate(1turn); }
}
a#clock path {
fill: #fff;
}
#clock-triangle {
fill: #bfe7dd;
transition: 1s;
}
a#clock:hover #clock-triangle {
fill: #000;
}
a#clock:hover path#minute-hand {
transform-origin: center 110%;
-webkit-animation: timer 2s forwards linear infinite;
}
The Iris
The exposure icon has the most moving parts:
<a id="iris" xlink:href="#">
<polygon id="iris-triangle" points="326,18.3 404.3,18.3 365.2,85.8"/>
<path id="leaf5" d="M364.8,26.4l-6.2,11.2l-3.8 6.2C357.5,27.8,362.2,26.7,364.8,26.4" />
<path id="leaf4" d="M352.2, 34.8l6.3, 11.2l-7.3, 0.1C349.6, 41.7,351.1, 37.2, 352.2, 34.8" />
<path id="leaf3" d="M352.8, 49.6l12.8, 0.2l-3.7, 6.4C357.5, 55.3,354.4, 51.8, 352.8,49.6" />
<path id="leaf2" d="M372.9, 45.4l3.8, 6.3c-2.9, 3.6-7.5, 4.6-10.2, 4.8L372.9, 45.4" />
<path id="leaf1" d="M380, 36.8c1.7, 4.3,0.1, 8.8-1, 11.2l-6.3-11.2L380, 36.8"/>
<path id="leaf6" d="M368.9, 26.8c4.6, 0.6,7.8, 4.1,9.4, 6.2l-12.8, 0.3L368.9, 26.8" />
</a>
Each of the iris leaves moves “inwards” by a small amount. Note that this motion is relative to the original size of the element, and will scale up and down as the SVG is resized:
a#iris polygon {
fill: #444341; transition: 1s;
}
a#iris:hover polygon {
fill: #000;
}
a#iris path {
fill: #fff;
transform: scale(0.8) 50 50;
transition-duration: .8s;
}
a#iris:hover #leaf1 {
transform: translateX(-3px);
}
a#iris:hover #leaf2 {
transform: translateX(-2px) translateY(-3px);
}
a#iris:hover #leaf3 {
transform: translateY(-3px) translateX(1px);
}
a#iris:hover #leaf4 {
transform: translateX(3px);
}
a#iris:hover #leaf5 {
transform: translateX(1px) translateY(3px);
}
a#iris:hover #leaf6 {
transform: translateX(-2px) translateY(3px);
}
Conclusion
SVG can make very effective linked interactions; IE8 would need an image fallback for each icon, which I’ll address in a future article.
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/bLpuG