171

I want to store my background URLs in custom properties (CSS variables) and use them with the background property. However, I couldn't find a way to interpolate the string when using it as a parameter in url().

Here is my sample code:

:root {
    --url: "https://download.unsplash.com/photo-1420708392410-3c593b80d416";
}

body {
    background: url(var(--url));
}

I know that this can be easily done in Sass or LESS using the interpolation function but I'm curious if there is a way to do it without any pre-processor.

4
  • 22
    You were so close, --url: url(yoururl); background: var(--url); Commented Feb 19, 2017 at 16:57
  • 14
    @pol: That's not close at all. That's missing the point of this question by a mile. Still, it is the only solution to the problem with respect to url(). Commented Feb 19, 2017 at 17:53
  • @SharmaJ what kind of new answer are you looking for? Commented Apr 4, 2020 at 8:17
  • 4
    @TemaniAfif I can't be entirely sure the OP had the same point to the question as what brought me here, but I was hoping to do this: :root { --url: "https://example.com/images"; } body { background: url(var(--url)/bg1.jpg); } #wrapper { background: url(var(--url)/bg2.jpg); } Unfortunately, it seems I'm reduced to either javascript or editing a gazillion lines of code between development and production environments. Oh well. Commented Apr 28, 2020 at 18:45

5 Answers 5

224

You can perform interpolation with most CSS functions, including rgba() (see an example here). In fact, interpolation is one of the main features of custom properties.

But you cannot do this with url(), as url(var(--url)) is parsed not as a url( function token followed by var(--url) followed by a ), but a single url() token that is invalid because the var(--url) is being treated as a URL itself, and unquoted URLs in url() tokens cannot contain parentheses unless they are escaped. This means the substitution never actually occurs, because the parser never sees any var() expressions in the property value — indeed, your background declaration is completely invalid.

If you didn't understand any of that, that's fine. Just know that you cannot use var() interpolation with url() due to legacy reasons.

Even though the problem depicted in the question is related to the legacy url() token, you cannot do this by building URL tokens out of several var() expressions either, in case you were thinking of trying something like --uo: url(; --uc: ); or --uo: url("; --uc: ");, and background: var(--uo) var(--url) var(--uc);. This is because custom properties cannot contain unmatched string delimiters or parts of url() tokens (called bad URL tokens).

If you want to specify a URL in a custom property, you need to write out the entire url() expression, and substitute that entire expression:

:root {
  --url: url("https://download.unsplash.com/photo-1420708392410-3c593b80d416");
}

body {
  background: var(--url);
}

Or, use JavaScript instead of var() to perform the interpolation.

Sign up to request clarification or add additional context in comments.

2 Comments

Got your point! @boltclock. I didn't know how url() is parsed and tokenized. If anyone else wants read further, here is a useful link - typedef-url-token.
Thanks for the great answer. So sad that CSS designers did not address this use-case. :-(
12

You cannot interpolate css variables with url but what you can do is to implement the url function as part of your variable like this:

:root {
  --url: url("https://download.unsplash.com/photo-1420708392410-3c593b80d416");
}

body {
  background: var(--url);
}

in HTML could be:

<div class="css_class_setting_background" style="--url: url('https://download.unsplash.com/photo-1420708392410-3c593b80d416');"> </div>

This works on most of modern browsers.

Comments

4

On modern browsers, you can use image-set() instead of url() as a workaround, as unlike url(), image-set() does support interpolation. So something like this should work:

:root {
  --url: "https://download.unsplash.com/photo-1420708392410-3c593b80d416";
}

body {
  background: image-set(var(--url) 1x) center / cover no-repeat;

  min-height: 100vh;
  margin: 0;
}

This code should work fine on:

  • Chrome >= 113
  • Edge >= 113
  • Safari >= 14
  • Firefox >= 88
  • Opera >= 99

(currently global audience coverage is ~88%)

Comments

1

I had the same issue on a project with Cordova so I used:

header{
  --bg-header: url(../img/header_home.png) left center/cover no-repeat;
  background: var(--bg-header,
      url("../img/header_home.png") left center/cover no-repeat
  );
}

Apparently, if you use url("") with double quotes on the --var declaration, the value will not work.

1 Comment

I keep getting Css Syntax Effor when I try to set this: url(PATH_WITH_OR_WITHOUT_QUOTES) as var value. Any ideas?
-2

Here is the solution which works in vue3

<script setup>
const backImage = ref("url(/img/imImage.webp)")
</script>

<style>
div {
  background-image: v-bind(backImage );
}
</style>

Here is the official Vuejs documentation.

Below (playground link) is the solution with both Vuejs and CSS custom properties:

<script setup>
import { ref } from "vue"
  const divImage = ref("url('https://download.unsplash.com/photo-1696075619091-dedae25b1f65')")
</script>

<template>
  <div>Hello</div>
</template>

<style>
:root {
  --url: url("https://download.unsplash.com/photo-1420708392410-3c593b80d416");
}

body {
  background:  var(--url);
}

div {
  background:   v-bind(divImage);
}
</style>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.