{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreigajtddbczb5tp4nwrnmrurwob47msiyvbqwhfr6twznf3syasuzi",
"uri": "at://did:plc:3okajqf5ifpzwzkuzluxd66m/app.bsky.feed.post/3mjhxqeek7md2"
},
"path": "/css-in-svg-symbols.html",
"publishedAt": "2026-04-13T22:00:00.000Z",
"site": "https://deterministic.space",
"tags": [
"ShadowRoot",
"developer.mozilla.org\n ShadowRoot on MDN web docs",
"Web Components",
"developer.mozilla.org\n Web Components on MDN web docs"
],
"textContent": "Styling SVG symbols that are defined using `<symbol id=\"x\">` and included using `<use href=\"#x\">`.\n\n## SVG symbol sprites\n\nIn a web frontend project I’m working on, we need to show a lot of icons in different configurations on a drawing. The symbols are SVGs exported from Figma and they have different layers that can be styled and enabled depending on the symbol’s status. E.g., we want to set the `bg` layer to green for showing the “success” state.\n\nThe structure we have looks like this: We have a `symbols.svg` which is basically just a list of definitions:\n\n\n <svg xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <symbol id=\"symbol-42\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\">\n <circle cx=\"8\" cy=\"8\" r=\"7\" class=\"bg\"/>\n <!-- ... -->\n </symbol>\n </defs>\n </svg>\n\n\nTo use the symbols, we inline this SVG into our HTML (hidden, so it doesn’t render on its own) and then reference individual symbols by ID:\n\n\n <svg class=\"symbol-wrap\">\n <use href=\"#symbol-42\" />\n </svg>\n\n\nYou can also keep the sprite as an external file and reference it with `<use href=\"symbols.svg#symbol-42\" />`, but inlining avoids CORS issues and is what most bundlers and frameworks do by default.\n\n## It’s a shadow root\n\nNow, naively I thought that styling the symbols would be as simple as writing a little CSS:\n\n\n .success .symbol-wrap .bg {\n fill: green;\n }\n\n\nThis does not work, however. When the symbols get injected, the DOM structure becomes\n\n\n <svg class=\"symbol-wrap\">\n <use href=\"#symbol-42\">\n #shadow-root\n <symbol id=\"symbol-42\">\n ...\n\n\nand our CSS selector can’t go past that `#shadow-root`. This is the dev tools indicator for a ShadowRoot \n\n developer.mozilla.org\n ShadowRoot on MDN web docs\n , which is (simplified) its own rendering context. This is the system used for implementing Web Components \n\n developer.mozilla.org\n Web Components on MDN web docs\n as well.\n\n## Styling inner symbols\n\nWhat can get through to the element in this `ShadowRoot` then? As it turns out, CSS variables! This means we can set `style=\"--bg-fill: green\"` on our `.symbol-wrap` and in our `symbols.svg` we can use it.\n\nThe one missing piece to make this convenient is to, in the `symbols.svg`, add a `<style>` that will pick up the variable and set the properties for our classes:\n\n\n <svg xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <style>\n .bg { fill: var(--bg-fill, currentColor); }\n </style>\n <symbol id=\"symbol-42\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\">\n <circle cx=\"8\" cy=\"8\" r=\"7\" class=\"bg\"/>\n <!-- ... -->\n </symbol>\n </defs>\n </svg>\n\n\nThe `currentColor` fallback means symbols render sensibly even when no variable is set. They just inherit the text color of their container.\n\nCSS custom properties are the one thing that crosses the `<use>` shadow boundary, so this pattern scales to as many layers and states as you need.",
"title": "Using CSS vars to style SVG symbols"
}