TIL — Why Custom properties don't work with the url() CSS function
Tags: css
Published: 2022-09-23 18:48:20

---
title: TIL — Why Custom properties don't work with the url() CSS function
published: true
description: Custom properties doesn't work in CSS url() functions because without quotes the parser treats the entire declaration as a url-token.
tags: css
canonical_url: https://www.stefanjudis.com/today-i-learned/custom-properties-dont-work-with-the-url-css-function/
---

Custom properties and CSS parsing are always good for surprises like [`!important` behaving slightly differently](https://www.stefanjudis.com/today-i-learned/the-surprising-behavior-of-important-css-custom-properties/) or [properties being "invalid at computed value time"](https://www.stefanjudis.com/today-i-learned/custom-properties-affect-how-invalid-css-declarations-are-handled/). 

Today I discovered yet another surprise — custom properties don't work in combination with the `url()` function. 😲

```css
.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](https://github.com/csstree/csstree/issues/197#issuecomment-1183839492), 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')` or `url("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`.

```css
.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()`?

```css
.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. 

```css
.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:

- [Roman Dvornov's stellar explanation of this topic](https://github.com/csstree/csstree/issues/197#issuecomment-1183839492)
- [The spec defining `src()` and `url()`](https://www.w3.org/TR/css-values/#urls)
- [The spec change adding the `src()` alias](https://github.com/w3c/csswg-drafts/issues/541)