SVG presentation attributes resolve var(), whatever the comment in your repo says
•5 min read
A note in one repository said CSS custom properties do not work in SVG attributes, and a whole chart was hand-rolled around it. The measurement says otherwise: fill, stroke and stop-color all resolve var(), and they follow the dark-mode query with no JavaScript.
var() does resolve inside an SVG presentation attribute. Measured in Chromium 151 on 19 August 2026, a fill, a stroke and a gradient stop-color written as attribute values all picked up a CSS custom property defined on an ancestor, and all three followed a prefers-color-scheme media query with no JavaScript involved. The belief that they do not is common enough to have been written into a comment in this repository, where it justified hand-rolling a chart that hard-codes its colours.
Chromium 151The browser build the measurement was taken in, on 19 August 2026: var() resolved in fill, in stroke and in gradient stop-color
Why a presentation attribute can hold a var()
A presentation attribute is not an ordinary XML attribute with a colour string in it. Attributes such as fill and stroke have CSS property counterparts of the same name, and when both the attribute and the property are set on an element, the CSS property takes priority. That relationship is what makes a custom property reference legal in the attribute position: the value is handled as a CSS value, and a CSS value may be a var() reference. The presentation attributes with property counterparts cover the shapes people actually draw — circle, ellipse, path, polygon, polyline, rect — and the text elements text, textPath and tspan.
The practical consequence for a charting library is direct. Values handed to a chart component as stroke, fill or stopColor end up as those attributes in the rendered SVG, so a design token defined once in a stylesheet reaches the chart without any bridging code. Redefining the token inside a dark-mode media query changes the chart, because the element is re-resolving a custom property rather than reading a string it was given at render time.
What the wrong comment cost
In this repository a note claimed the opposite, and one chart was written as hand-rolled SVG with hexadecimal colours because of it. Two things follow from hard-coded hex in a chart, and neither is a small aesthetic matter. The chart cannot follow dark mode, so it stays bright inside a dark page. And it cannot answer a prefers-contrast preference, because there is no token left to redefine. A workaround for a limitation that does not exist bought nothing and removed both of those behaviours.
The link measured 297 by 44 pixels and reported a pass. Tapping its centre hit the paragraph behind it. An inline box that wraps is not one rectangle, and the pseudo-element meant to enlarge it was positioned against the same broken geometry.
Market4 turns one release note into a changelog page, a blog post, a mail-out and a week of social posts — and then tells you which of them brought anyone back.
No card to start. Cancel from the settings screen, not from an email.
the platform enforces. Browsers move, and a note that records a limitation without recording the date and the build it was measured in cannot be checked by the next reader.
Four ways to colour a chart, compared
SVG presentation attributes resolve var(), whatever the comment in your repo says
Approach
Behaviour in dark mode
What it costs
var() in the fill or stroke attribute
Follows the media query with no JavaScript
One token definition, reused everywhere
A CSS rule or style attribute setting fill
Follows, and it overrides the presentation attribute
One more place to look when a colour is wrong
Hard-coded hexadecimal in the markup
Stays on the light value
No dark mode, and no answer to prefers-contrast
Colour picked in JavaScript from a media query
Follows, once the script has run
Code that must re-run on every change of preference
How to measure it yourself in four steps
Define a custom property on a root element in the stylesheet, and redefine it inside a prefers-color-scheme dark block.
Draw a small SVG with a rect whose fill attribute is var() referring to that property, and a linear gradient whose stop-color attribute does the same.
Load the page and read the computed fill in the browser's element inspector. A resolved colour rather than the literal text is the answer.
Toggle the emulated colour scheme in the developer tools and watch the shape repaint without reloading. Record the browser and the build number next to the result, because that is the part a future reader needs.
Recording the build is what separates a measurement from an opinion. The note that was wrong here said what the platform could not do and gave no way to check it; the note that replaced it names the browser, the version and the date, so the next person to doubt it has a four-step reproduction rather than an argument.
Does var() work in SVG attributes?
Yes, in presentation attributes. fill, stroke and a gradient's stop-color accept a var() reference to a CSS custom property, and the value resolves like any other CSS value. Measured in Chromium 151 in August 2026, all three followed a custom property that was redefined inside a prefers-color-scheme media query, with no JavaScript. Ordinary SVG attributes that have no CSS counterpart, such as d or viewBox, do not take a var() reference.
Should I set SVG colours with attributes or with CSS?
Either works, and the CSS property wins when both are set on the same element. Attributes are convenient when a charting library writes them for you; a stylesheet rule is convenient when the colour belongs to a class of elements rather than to one. Mixing both on the same element is what creates confusing debugging sessions, because the attribute is visible in the markup while the property that overrides it is not.
Why does my SVG chart ignore dark mode?
Usually because its colours are literal hexadecimal values written into the markup, so there is nothing left for a media query to redefine. Replacing each literal with a var() reference to a token, and redefining that token inside the dark-mode block, is enough on its own. If the colours come from JavaScript instead, the chart will follow the preference only after the script re-runs, which is why it can flash the wrong palette on load.
Which SVG elements accept fill as a presentation attribute?
The shape elements circle, ellipse, path, polygon, polyline and rect, and the text elements text, textPath and tspan. On those, fill is a presentation attribute with a CSS property counterpart of the same name. On the animation elements the same word means something else entirely: fill there describes the final state of an animation, and has nothing to do with colour.
A screen-reader-only caption kept its padding after being hidden, and the invisible node measured 32 by 16 pixels instead of 1 by 1. The cause was not the hiding technique. It was two selectors of equal weight and the order they were imported in.
A table header declared sticky sat still while the rows scrolled past it, with no error anywhere. Two separate mistakes: a wrapper that could not overflow, and the sticky rule on the wrong element. Here is what each one does and how to tell them apart.