Custom properties and CSS parsing are always good for surprises like !important
behaving slightly differently or properties being "invalid at computed value time".
Today I discovered yet another surprise — custom properties don't work in combination with the url()
function. 😲
.something {
/* this doesn't work */
--image: https://jo.com/image.jpg;
background: url(var(--image));
}
Roman Dvornov describes the details quite well in a GitHub issue, but let me give you a condensed explanation.
The two modes of url()
Your url()
-containing CSS will be parsed differently, depending on how you use url()
. There's an old and a newer way:
- Old:
url(https://jo.com/image.jpg)
- Newer:
url('https://jo.com/image.jpg')
orurl("https://jo.com/image.jpg")
The problem of the legacy url()
token
And these missing quotes of the old way might seem like a tiny detail, but they affect how your CSS is parsed.
Without quotes, the url()
syntax looks like a CSS function, but it isn't. CSS parsers will treat it as a single token, as so-called url-token
.
.something {
background: url(https://ja.com/image.jpg);
// \---------------------------/
// without quotes this ☝️ is
// a single CSS token
}
And this entire token from url(
to the closing )
enforces parentheses, whitespace characters, single quotes (') and double quotes (") to be escaped with a backslash.
url()
with a quoted string, on the other hand, is a normal and flexible CSS function notation, which is parsed part by part and works as expected.
But now, guess what happens when you want to use a custom property in combination with url()
?
.something {
/* this doesn't work */
--image: https://jo.com/image.jpg;
background: url(var(--image));
// ☝️ "No quotes? Cool, that's a url-token!"
// 😢 "Too bad though, `(` isn't allowed in here..."
// ❌ "I'll throw everything away!"
}
Because there are no quotes, this declaration is parsed as a url-token
. And unfortunately, the (
in var(--image)
isn't escaped, so the parser throws an error and invalidates the entire CSS declaration.
And this legacy url-token
parsing is why you can't use custom variables inside of url()
.
The solution to work around the legacy url()
What's the usual solution, when there's a problem with old web legacy? It's inventing new stuff because we can't break the web and just change things.
To enable custom properties in url()
there's a new alias in town. The src()
notation behaves the same way as url()
but without this weird legacy url-token
logic.
.something {
/* this works! (theoretically) */
--image: https://jo.com/image.jpg;
background: src(var(--image));
}
Problem solved! But yet, no browser supports src()
(I couldn't find browser support information but tested current Chrome, Safari and Firefox), so it's time for us to wait. 🤷♂️
CSS parsing and custom properties — always good for a surprise!
If you want to read more about this topic, here are some resources:
Top comments (0)