{"id":1109,"date":"2025-11-07T14:00:00","date_gmt":"2025-11-07T15:00:00","guid":{"rendered":"https:\/\/computercoursesonline.com\/?p=1109"},"modified":"2025-11-13T20:58:43","modified_gmt":"2025-11-13T20:58:43","slug":"smashing-animations-part-6-magnificent-svgs-with-and-css-custom-properties","status":"publish","type":"post","link":"https:\/\/computercoursesonline.com\/index.php\/2025\/11\/07\/smashing-animations-part-6-magnificent-svgs-with-and-css-custom-properties\/","title":{"rendered":"Smashing Animations Part 6: Magnificent SVGs\u00a0With “\u00a0And CSS Custom Properties"},"content":{"rendered":"

Smashing Animations Part 6: Magnificent SVGs\u00a0With `<use>`\u00a0And CSS Custom Properties<\/title><\/p>\n<article>\n<header>\n<h1>Smashing Animations Part 6: Magnificent SVGs\u00a0With `<use>`\u00a0And CSS Custom Properties<\/h1>\n<address>Andy Clarke<\/address>\n<p> 2025-11-07T15:00:00+00:00<br \/>\n 2025-11-13T20:32:27+00:00<br \/>\n <\/header>\n<p>I explained recently how I use <code><symbol><\/code>, <code><use><\/code>, and CSS Media Queries to develop what I call <a href=\"https:\/\/www.smashingmagazine.com\/2025\/10\/smashing-animations-part-5-building-adaptive-svgs\/\">adaptive SVGs<\/a>. Symbols let us define an element once and then <em>use<\/em> it again and again, making SVG animations easier to maintain, more efficient, and lightweight.<\/p>\n<p>Since I wrote that explanation, I\u2019ve designed and implemented new <a href=\"https:\/\/stuffandnonsense.co.uk\/blog\/say-hello-to-my-magnificent-7\">Magnificent 7<\/a> animated graphics across <a href=\"https:\/\/stuffandnonsense.co.uk\/\">my website<\/a>. They play on the web design pioneer theme, featuring seven magnificent Old West characters.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/stuffandnonsense.co.uk\/\"><\/p>\n<p> <img loading=\"lazy\" width=\"800\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Graphics featuring seven magnificent Old West characters\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/1-graphics-old-west-characters.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n View this animated SVG on <a href=\"https:\/\/stuffandnonsense.co.uk\/\">my website<\/a>. (<a href=\"https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/1-graphics-old-west-characters.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<p><code><symbol><\/code> and <code><use><\/code> let me define a character design and reuse it across multiple SVGs and pages. First, I created my characters and put each into a <code><symbol><\/code> inside a hidden library SVG:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-svg\"><!-- Symbols library -->\n<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"display:none;\">\n <symbol id=\"outlaw-1\">[...]<\/symbol>\n <symbol id=\"outlaw-2\">[...]<\/symbol>\n <symbol id=\"outlaw-3\">[...]<\/symbol>\n <!-- etc. -->\n<\/svg>\n<\/code><\/pre>\n<\/div>\n<p>Then, I referenced those symbols in two other SVGs, one for large and the other for small screens:<\/p>\n<pre><code class=\"language-svg\"><!-- Large screens -->\n<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" id=\"svg-large\">\n <use href=\"outlaw-1\" \/>\n <!-- ... -->\n<\/svg>\n\n<!-- Small screens -->\n<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" id=\"svg-small\">\n <use href=\"outlaw-1\" \/>\n <!-- ... -->\n<\/svg>\n<\/code><\/pre>\n<p>Elegant. But then came the infuriating. I could reuse the characters, but couldn\u2019t animate or style them. I added CSS rules targeting elements within the symbols referenced by a <code><use><\/code>, but nothing happened. Colours stayed the same, and things that should move stayed static. It felt like I\u2019d run into an invisible barrier, and I had.<\/p>\n<div data-audience=\"non-subscriber\" data-remove=\"true\" class=\"feature-panel-container\">\n<aside class=\"feature-panel\">\n<div class=\"feature-panel-left-col\">\n<div class=\"feature-panel-description\">\n<p>Meet <strong><a data-instant href=\"https:\/\/www.smashingconf.com\/online-workshops\/\">Smashing Workshops<\/a><\/strong> on <strong>front-end, design & UX<\/strong>, with practical takeaways, live sessions, <strong>video recordings<\/strong> and a friendly Q&A. With Brad Frost, St\u00e9ph Walter and <a href=\"https:\/\/smashingconf.com\/online-workshops\/workshops\">so many others<\/a>.<\/p>\n<p><a data-instant href=\"smashing-workshops\" class=\"btn btn--green btn--large\">Jump to the workshops \u21ac<\/a><\/div>\n<\/div>\n<div class=\"feature-panel-right-col\"><a data-instant href=\"smashing-workshops\" class=\"feature-panel-image-link\"><\/p>\n<div class=\"feature-panel-image\">\n<img loading=\"lazy\" class=\"feature-panel-image-img lazyload\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Feature Panel\" width=\"257\" height=\"355\" data-src=\"\/images\/smashing-cat\/cat-scubadiving-panel.svg\"><\/p>\n<\/div>\n<p><\/a>\n<\/div>\n<\/aside>\n<\/div>\n<h2 id=\"understanding-the-shadow-dom-barrier\">Understanding The Shadow DOM Barrier<\/h2>\n<p>When you reference the contents of a <code>symbol<\/code> with <code>use<\/code>, a browser creates a copy of it in the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Web_components\/Using_shadow_DOM\">Shadow DOM<\/a>. Each <code><use><\/code> instance becomes its own encapsulated copy of the referenced <code><symbol><\/code>, meaning that CSS from outside can\u2019t break through the barrier to style any elements directly. For example, in normal circumstances, this <code>tapping<\/code> value triggers a CSS animation:<\/p>\n<pre><code class=\"language-svg\"><g class=\"outlaw-1-foot tapping\">\n <!-- ... -->\n<\/g>\n<\/code><\/pre>\n<pre><code class=\"language-css\">.tapping {\n animation: tapping 1s ease-in-out infinite;\n}\n<\/code><\/pre>\n<p>But when the same animation is applied to a <code><use><\/code> instance of that same foot, nothing happens:<\/p>\n<pre><code class=\"language-svg\"><symbol id=\"outlaw-1\">\n <g class=\"outlaw-1-foot\"><!-- ... --><\/g>\n<\/symbol>\n\n<use href=\"#outlaw-1\" class=\"tapping\" \/>\n<\/code><\/pre>\n<pre><code class=\"language-css\">.tapping {\n animation: tapping 1s ease-in-out infinite;\n}\n<\/code><\/pre>\n<p>That\u2019s because the <code><g><\/code> inside the <code><symbol><\/code> element is in a protected shadow tree, and the CSS Cascade stops dead at the <code><use><\/code> boundary. This behaviour can be frustrating, but it\u2019s intentional as it ensures that reused symbol content stays consistent and predictable.<\/p>\n<p>While learning how to develop adaptive SVGs, I found all kinds of attempts to work around this behaviour, but most of them sacrificed the reusability that makes SVG so elegant. I didn\u2019t want to duplicate my characters just to make them blink at different times. I wanted a single <code><symbol><\/code> with instances that have their own timings and expressions.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/2-animated-elements-single-svg-symbol.png\"><\/p>\n<p> <img loading=\"lazy\" width=\"800\" height=\"450\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Several animated elements within a single SVG symbol\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/2-animated-elements-single-svg-symbol.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n Several animated elements within a single SVG symbol. (<a href=\"https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/2-animated-elements-single-svg-symbol.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<h2 id=\"css-custom-properties-to-the-rescue\">CSS Custom Properties To The Rescue<\/h2>\n<p>While working on my pioneer animations, I learned that <strong>regular CSS values can\u2019t cross the boundary into the Shadow DOM, but CSS Custom Properties can<\/strong>. And even though you can\u2019t directly style elements inside a <code><symbol><\/code>, you can pass custom property values to them. So, when you insert custom properties into an inline style, a browser looks at the cascade, and those styles become available to elements inside the <code><symbol><\/code> being referenced.<\/p>\n<p>I added <code>rotate<\/code> to an inline style applied to the <code><symbol><\/code> content:<\/p>\n<pre><code class=\"language-svg\"><symbol id=\"outlaw-1\">\n <g class=\"outlaw-1-foot\" style=\"\n transform-origin: bottom right; \n transform-box: fill-box; \n transform: rotate(var(--foot-rotate));\">\n <!-- ... -->\n <\/g>\n<\/symbol>\n<\/code><\/pre>\n<p>Then, defined the foot tapping animation and applied it to the element:<\/p>\n<pre><code class=\"language-css\">@keyframes tapping {\n 0%, 60%, 100% { --foot-rotate: 0deg; }\n 20% { --foot-rotate: -5deg; }\n 40% { --foot-rotate: 2deg; }\n}\n\nuse[data-outlaw=\"1\"] {\n --foot-rotate: 0deg;\n animation: tapping 1s ease-in-out infinite;\n}\n<\/code><\/pre>\n<h2 id=\"passing-multiple-values-to-a-symbol\">Passing Multiple Values To A Symbol<\/h2>\n<p>Once I\u2019ve set up a symbol to use CSS Custom Properties, I can pass as many values as I want to any <code><use><\/code> instance. For example, I might define variables for <code>fill<\/code>, <code>opacity<\/code>, or <code>transform<\/code>. What\u2019s elegant is that each <code><symbol><\/code> instance can then have its own set of values.<\/p>\n<pre><code class=\"language-svg\"><g class=\"eyelids\" style=\"\n fill: var(--eyelids-colour, #f7bea1);\n opacity: var(--eyelids-opacity, 1);\n transform: var(--eyelids-scale, 0);\"\n>\n <!-- etc. -->\n<\/g>\n<\/code><\/pre>\n<pre><code class=\"language-css\">use[data-outlaw=\"1\"] {\n --eyelids-colour: #f7bea1; \n --eyelids-opacity: 1;\n}\n\nuse[data-outlaw=\"2\"] {\n --eyelids-colour: #ba7e5e; \n --eyelids-opacity: 0;\n}\n<\/code><\/pre>\n<p>Support for passing CSS Custom Properties like this is solid, and every contemporary browser handles this behaviour correctly. Let me show you a few ways I\u2019ve been using this technique, starting with a multi-coloured icon system.<\/p>\n<div class=\"partners__lead-place\"><\/div>\n<h2 id=\"a-multi-coloured-icon-system\">A Multi-Coloured Icon System<\/h2>\n<p>When I need to maintain a set of icons, I can define an icon once inside a <code><symbol><\/code> and then use custom properties to apply colours and effects. Instead of needing to duplicate SVGs for every theme, each <code>use<\/code> can carry its own values.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/3-custom-properties-colours.png\"><\/p>\n<p> <img loading=\"lazy\" width=\"800\" height=\"167\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Custom properties for the fill colours in several Bluesky icons\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/3-custom-properties-colours.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n Custom properties for the fill colours in several Bluesky icons. (<a href=\"https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/3-custom-properties-colours.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<p>For example, I applied an <code>--icon-fill<\/code> custom property for the default <code>fill<\/code> colour of the <code><path><\/code> in this Bluesky icon :<\/p>\n<pre><code class=\"language-svg\"><symbol id=\"icon-bluesky\">\n <path fill=\"var(--icon-fill, currentColor)\" d=\"...\" \/>\n<\/symbol>\n<\/code><\/pre>\n<p>Then, whenever I need to vary how that icon looks — for example, in a <code><header><\/code> and <code><footer><\/code> — I can pass new <code>fill<\/code> colour values to each instance:<\/p>\n<pre><code class=\"language-html\"><header>\n <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n <use href=\"#icon-bluesky\" style=\"--icon-fill: #2d373b;\" \/>\n <\/svg>\n<\/header>\n\n<footer>\n <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n <use href=\"#icon-bluesky\" style=\"--icon-fill: #590d1a;\" \/>\n <\/svg>\n<\/footer>\n<\/code><\/pre>\n<p>These icons are the same shape but look different thanks to their inline styles.<\/p>\n<h2 id=\"data-visualisations-with-css-custom-properties\">Data Visualisations With CSS Custom Properties<\/h2>\n<p>We can use <code><symbol><\/code> and <code><use><\/code> in plenty more practical ways. They\u2019re also helpful for creating lightweight data visualisations, so imagine an infographic about three famous <a href=\"https:\/\/en.wikipedia.org\/wiki\/American_frontier\">Wild West<\/a> sheriffs: <a href=\"https:\/\/en.wikipedia.org\/wiki\/Wyatt_Earp\">Wyatt Earp<\/a>, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Pat_Garrett\">Pat Garrett<\/a>, and <a href=\"https:\/\/en.wikipedia.org\/wiki\/Bat_Masterson\">Bat Masterson<\/a>.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/4-data-visualisations-css-custom-properties.png\"><\/p>\n<p> <img loading=\"lazy\" width=\"800\" height=\"421\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Data visualisations with CSS Custom Properties\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/4-data-visualisations-css-custom-properties.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n Data visualisations with CSS Custom Properties. (<a href=\"https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/4-data-visualisations-css-custom-properties.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<p>Each sheriff\u2019s profile uses the same set of SVG three symbols: one for a bar representing the length of a sheriff\u2019s career, another to represent the number of arrests made, and one more for the number of kills. Passing custom property values to each <code><use><\/code> instance can vary the bar lengths, arrests scale, and kills colour without duplicating SVGs. I first created symbols for those items:<\/p>\n<pre><code class=\"language-svg\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"display:none;\">\n <symbol id=\"career-bar\">\n <rect\n height=\"10\"\n width=\"var(--career-length, 100)\" \n fill=\"var(--career-colour, #f7bea1)\"\n \/>\n <\/symbol>\n \n <symbol id=\"arrests-badge\">\n <path \n fill=\"var(--arrest-color, #d0985f)\" \n transform=\"scale(var(--arrest-scale, 1))\"\n \/>\n <\/symbol>\n \n <symbol id=\"kills-icon\">\n <path fill=\"var(--kill-colour, #769099)\" \/>\n <\/symbol>\n<\/svg>\n<\/code><\/pre>\n<p>Each symbol accepts one or more values:<\/p>\n<ul>\n<li><strong><code>--career-length<\/code><\/strong> adjusts the <code>width<\/code> of the career bar.<\/li>\n<li><strong><code>--career-colour<\/code><\/strong> changes the <code>fill<\/code> colour of that bar.<\/li>\n<li><strong><code>--arrest-scale<\/code><\/strong> controls the arrest badge size.<\/li>\n<li><strong><code>--kill-colour<\/code><\/strong> defines the <code>fill<\/code> colour of the kill icon.<\/li>\n<\/ul>\n<p>I can use these to develop a profile of each sheriff using <code><use><\/code> elements with different inline styles, starting with Wyatt Earp.<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-svg\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n <g id=\"wyatt-earp\">\n <use href=\"#career-bar\" style=\"--career-length: 400; --career-color: #769099;\"\/>\n <use href=\"#arrests-badge\" style=\"--arrest-scale: 2;\" \/>\n <!-- ... -->\n <use href=\"#arrests-badge\" style=\"--arrest-scale: 2;\" \/>\n <use href=\"#arrests-badge\" style=\"--arrest-scale: 1;\" \/>\n <use href=\"#kills-icon\" style=\"--kill-color: #769099;\" \/>\n <\/g>\n\n <g id=\"pat-garrett\">\n <use href=\"#career-bar\" style=\"--career-length: 300; --career-color: #f7bea1;\"\/>\n <use href=\"#arrests-badge\" style=\"--arrest-scale: 2;\" \/>\n <!-- ... -->\n <use href=\"#arrests-badge\" style=\"--arrest-scale: 2;\" \/>\n <use href=\"#arrests-badge\" style=\"--arrest-scale: 1;\" \/>\n <use href=\"#kills-icon\" style=\"--kill-color: #f7bea1;\" \/>\n <\/g>\n\n <g id=\"bat-masterson\">\n <use href=\"#career-bar\" style=\"--career-length: 200; --career-color: #c2d1d6;\"\/>\n <use href=\"#arrests-badge\" style=\"--arrest-scale: 2;\" \/>\n <!-- ... -->\n <use href=\"#arrests-badge\" style=\"--arrest-scale: 2;\" \/>\n <use href=\"#arrests-badge\" style=\"--arrest-scale: 1;\" \/>\n <use href=\"#kills-icon\" style=\"--kill-color: #c2d1d6;\" \/>\n <\/g>\n<\/svg>\n<\/code><\/pre>\n<\/div>\n<p>Each <code><use><\/code> shares the same symbol elements, but the inline variables change their colours and sizes. I can even animate those values to highlight their differences:<\/p>\n<pre><code class=\"language-css\">@keyframes pulse {\n 0%, 100% { --arrest-scale: 1; }\n 50% { --arrest-scale: 1.2; }\n}\n\nuse[href=\"#arrests-badge\"]:hover {\n animation: pulse 1s ease-in-out infinite;\n}\n<\/code><\/pre>\n<blockquote class=\"pull-quote\">\n<p>\n <a class=\"pull-quote__link\" aria-label=\"Share on Twitter\" href=\"https:\/\/twitter.com\/share?text=%0aCSS%20Custom%20Properties%20aren%e2%80%99t%20only%20helpful%20for%20styling;%20they%20can%20also%20channel%20data%20between%20HTML%20and%20SVG%e2%80%99s%20inner%20geometry,%20binding%20visual%20attributes%20like%20colour,%20length,%20and%20scale%20to%20semantics%20like%20arrest%20numbers,%20career%20length,%20and%20kills.%0a&url=https:\/\/smashingmagazine.com%2f2025%2f11%2fsmashing-animations-part-6-svgs-css-custom-properties%2f\"><\/p>\n<p>CSS Custom Properties aren\u2019t only helpful for styling; they can also channel data between HTML and SVG\u2019s inner geometry, binding visual attributes like colour, length, and scale to semantics like arrest numbers, career length, and kills.<\/p>\n<p> <\/a>\n <\/p>\n<div class=\"pull-quote__quotation\">\n<div class=\"pull-quote__bg\">\n <span class=\"pull-quote__symbol\">\u201c<\/span><\/div>\n<\/p><\/div>\n<\/blockquote>\n<h2 id=\"ambient-animations\">Ambient Animations<\/h2>\n<p>I started learning to animate elements within symbols while creating the animated graphics for my website\u2019s Magnificent 7. To reduce complexity and make my code lighter and more maintainable, I needed to define each character once and reuse it across SVGs:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-svg\"><!-- Symbols library -->\n<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"display:none;\">\n <symbol id=\"outlaw-1\">[\u2026]<\/symbol>\n <!-- ... -->\n<\/svg>\n\n<!-- Large screens -->\n<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" id=\"svg-large\">\n <use href=\"outlaw-1\" \/>\n <!-- ... -->\n<\/svg>\n\n<!-- Small screens -->\n<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" id=\"svg-small\">\n <use href=\"outlaw-1\" \/>\n <!-- ... -->\n<\/svg>\n<\/code><\/pre>\n<\/div>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/5-characters.png\"><\/p>\n<p> <img loading=\"lazy\" width=\"800\" height=\"450\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"My website\u2019s Magnificent 7 characters\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/5-characters.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n My website\u2019s Magnificent 7 characters. (<a href=\"https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/5-characters.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<p>But I didn\u2019t want those characters to stay static; I needed subtle movements that would bring them to life. I wanted their eyes to blink, their feet to tap, and their moustache whiskers to twitch. So, to animate these details, I pass animation data to elements inside those symbols using CSS Custom Properties, starting with the blinking.<\/p>\n<p>I implemented the blinking effect by placing an SVG group over the outlaws\u2019 eyes and then changing its <code>opacity<\/code>.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/6-blinking-effect.png\"><\/p>\n<p> <img loading=\"lazy\" width=\"800\" height=\"333\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Blinking effect by animating eyelids\u2019 opacity.\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/6-blinking-effect.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n Blinking effect by animating eyelids\u2019 opacity. (<a href=\"https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/6-blinking-effect.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<p>To make this possible, I added an inline style with a CSS Custom Property to the group:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-svg\"><symbol id=\"outlaw-1\" viewBox=\"0 0 712 2552\">\n <g class=\"eyelids\" style=\"opacity: var(--eyelids-opacity, 1);\">\n <!-- ... -->\n <\/g>\n<\/symbol>\n<\/code><\/pre>\n<\/div>\n<p>Then, I defined the blinking animation by changing <code>--eyelids-opacity<\/code>:<\/p>\n<pre><code class=\"language-css\">@keyframes blink {\n 0%, 92% { --eyelids-opacity: 0; }\n 93%, 94% { --eyelids-opacity: 1; }\n 95%, 97% { --eyelids-opacity: 0.1; }\n 98%, 100% { --eyelids-opacity: 0; }\n}\n<\/code><\/pre>\n<p>\u2026and applied it to every character:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-css\">use[data-outlaw] {\n --blink-duration: 4s;\n --eyelids-opacity: 1;\n animation: blink var(--blink-duration) infinite var(--blink-delay);\n}\n<\/code><\/pre>\n<\/div>\n<p>\u2026so that each character wouldn\u2019t blink at the same time, I set a different <code>--blink-delay<\/code> before they all start blinking, by passing another Custom Property:<\/p>\n<pre><code class=\"language-css\">use[data-outlaw=\"1\"] { --blink-delay: 1s; }\nuse[data-outlaw=\"2\"] { --blink-delay: 2s; }\n<!-- ... -->\nuse[data-outlaw=\"7\"] { --blink-delay: 3s; }\n<\/code><\/pre>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/7-foot-tapping-effect.png\"><\/p>\n<p> <img loading=\"lazy\" width=\"800\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Foot tapping effect by animating the foot\u2019s rotation\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/7-foot-tapping-effect.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n Foot tapping effect by animating the foot\u2019s rotation. (<a href=\"https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/7-foot-tapping-effect.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<p>Some of the characters tap their feet, so I added an inline style with a CSS Custom Property to those groups, too:<\/p>\n<pre><code class=\"language-svg\"><symbol id=\"outlaw-1\" viewBox=\"0 0 712 2552\">\n <g class=\"outlaw-1-foot\" style=\"\n transform-origin: bottom right; \n transform-box: fill-box; \n transform: rotate(var(--foot-rotate));\">\n <\/g>\n<\/symbol>\n<\/code><\/pre>\n<p>Defining the foot-tapping animation:<\/p>\n<pre><code class=\"language-css\">@keyframes tapping {\n 0%, 60%, 100% { --foot-rotate: 0deg; }\n 20% { --foot-rotate: -5deg; }\n 40% { --foot-rotate: 2deg; }\n}\n<\/code><\/pre>\n<p>And adding those extra Custom Properties to the characters\u2019 declaration:<\/p>\n<pre><code class=\"language-css\">use[data-outlaw] {\n --blink-duration: 4s;\n --eyelids-opacity: 1;\n --foot-rotate: 0deg;\n animation: \n blink var(--blink-duration) infinite var(--blink-delay),\n tapping 1s ease-in-out infinite;\n}\n<\/code><\/pre>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/8-jiggling-effect.png\"><\/p>\n<p> <img loading=\"lazy\" width=\"800\" height=\"333\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Jiggling effect by animating the moustaches\u2019 translation\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/8-jiggling-effect.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n Jiggling effect by animating the moustaches\u2019 translation. (<a href=\"https:\/\/files.smashing.media\/articles\/smashing-animations-part-6-svgs-css-custom-properties\/8-jiggling-effect.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<p>\u2026before finally making the character\u2019s whiskers jiggle via an inline style with a CSS Custom Property which describes how his moustache transforms:<\/p>\n<pre><code class=\"language-svg\"><symbol id=\"outlaw-1\" viewBox=\"0 0 712 2552\">\n <g class=\"outlaw-1-tashe\" style=\"\n transform: translateX(var(--jiggle-x, 0px));\"\n >\n <!-- ... -->\n <\/g>\n<\/symbol>\n<\/code><\/pre>\n<p>Defining the jiggle animation:<\/p>\n<pre><code class=\"language-css\">@keyframes jiggle {\n 0%, 100% { --jiggle-x: 0px; }\n 20% { --jiggle-x: -3px; }\n 40% { --jiggle-x: 2px; }\n 60% { --jiggle-x: -1px; }\n 80% { --jiggle-x: 4px; }\n}\n<\/code><\/pre>\n<p>And adding those properties to the characters\u2019 declaration:<\/p>\n<pre><code class=\"language-css\">use[data-outlaw] {\n --blink-duration: 4s;\n --eyelids-opacity: 1;\n --foot-rotate: 0deg;\n --jiggle-x: 0px;\n animation: \n blink var(--blink-duration) infinite var(--blink-delay),\n jiggle 1s ease-in-out infinite,\n tapping 1s ease-in-out infinite;\n}\n<\/code><\/pre>\n<p>With these moving parts, the characters come to life, but my markup remains remarkably lean. By combining several animations into a single declaration, I can choreograph their movements without adding more elements to my SVG. Every outlaw shares the same base <code><symbol><\/code>, and their individuality comes entirely from CSS Custom Properties.<\/p>\n<div class=\"partners__lead-place\"><\/div>\n<h2 id=\"pitfalls-and-solutions\">Pitfalls And Solutions<\/h2>\n<p>Even though this technique might seem bulletproof, there are a few traps it\u2019s best to avoid:<\/p>\n<ul>\n<li><strong>CSS Custom Properties only work if they\u2019re referenced with a <code>var()<\/code> inside a <code><symbol><\/code>.<\/strong> Forget that, and you\u2019ll wonder why nothing updates. Also, properties that aren\u2019t naturally inherited, like <code>fill<\/code> or <code>transform<\/code>, need to use <code>var()<\/code> in their value to benefit from the cascade.<\/li>\n<li><strong>It\u2019s always best to include a fallback value alongside a custom property<\/strong>, like <code>opacity: var(--eyelids-opacity, 1);<\/code> to ensure SVG elements render correctly even without custom property values applied.<\/li>\n<li><strong>Inline styles set via the <code>style<\/code> attribute take precedence<\/strong>, so if you mix inline and external CSS, remember that Custom Properties follow normal cascade rules.<\/li>\n<li><strong>You can always use DevTools to inspect custom property values.<\/strong> Select a <code><use><\/code> instance and check the Computed Styles panel to see which custom properties are active.<\/li>\n<\/ul>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>The <code><symbol><\/code> and <code><use><\/code> elements are among the most elegant but sometimes frustrating aspects of SVG. The Shadow DOM barrier makes animating them trickier, but <strong>CSS Custom Properties act as a bridge<\/strong>. They let you pass colour, motion, and personality across that invisible boundary, resulting in cleaner, lighter, and, best of all, fun animations.<\/p>\n<div class=\"signature\">\n <img src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Smashing Editorial\" width=\"35\" height=\"46\" loading=\"lazy\" class=\"lazyload\" data-src=\"https:\/\/www.smashingmagazine.com\/images\/logo\/logo--red.png\"><br \/>\n <span>(gg, yk)<\/span>\n<\/div>\n<\/article>\n","protected":false},"excerpt":{"rendered":"<p>Smashing Animations Part 6: Magnificent SVGs\u00a0With `<use>`\u00a0And CSS Custom Properties Smashing Animations Part 6: Magnificent SVGs\u00a0With `<use>`\u00a0And CSS Custom Properties Andy Clarke 2025-11-07T15:00:00+00:00 2025-11-13T20:32:27+00:00 I explained recently how I use <symbol>, <use>, and CSS Media Queries to develop what I call adaptive SVGs. Symbols let us define an element once and then use it again…<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[11],"tags":[],"_links":{"self":[{"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/posts\/1109"}],"collection":[{"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/comments?post=1109"}],"version-history":[{"count":1,"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/posts\/1109\/revisions"}],"predecessor-version":[{"id":1110,"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/posts\/1109\/revisions\/1110"}],"wp:attachment":[{"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/media?parent=1109"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/categories?post=1109"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/tags?post=1109"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}