SVG data URIs & Sass functions

Tackling svg background images

4 min read

On my site all of my iconography is done by directly placing the svg into my html. (Well kinda you can read my post Icons on my site for more on this.) This is great for a lot of reasons but there are a couple places where it wasn’t possible/practical to do this. For those situations I use an svg data URI to set the desired svg as the background image. Here’s where I ran into problems. I use a fill of currentColor for all my svgs. My site supports the prefer-color-scheme media query and changes it’s colors accordingly. By using currentColor no matter what the preferred color scheme is the icons will automatically change color too. The problem is that svgs in background images don’t support currentColor as a fill. I could have solved this several ways but the way I chose to solve it was by writing a Sass function that accepted name, color and alpha parameters and would spit out the svg I wanted in the color and opacity I wanted it. This way I didn’t have to manually change fill values or keep multiple copies of the svg in the colors I needed. Let’s have a look at what that looks like.

First I needed the function to spit out the svg. Here’s what that looks like:

1
2
3
4
5
6
7
@function bg-img-svg($name, $fill : 'black', $alpha : 1.0) {
  @if ( $name == 'svg-name' ) {
    @return(url('data:image/svg+xml;utf8,<svg fill="#{real-rgba($fill,$alpha)}"><path/></svg>'));
  } @else {
    @return(none);
  }
}

So you can see we’re declaring the function bg-img-svg() and accepting the parameters name, fill, and alpha with defaults set on the fill and alpha just to try and ward off errors. Then we look to see if the name matches any of our svg’s names, place our color and alpha in our fill, and return that svg. You might have noticed that in the fill property on the svg that we call another function called real-rgba() This is a function to workaround Sass turning every color it can into HEX. For our svg to work it needs to be in rgba. That function looks like this:

1
2
3
@function real-rgba($color : black, $alpha : 1.0) {
  @return unquote("rgba(#{red($color)}, #{green($color)}, #{blue($color)}, #{$alpha})");
}

This allows me to set an svg of whatever color and opacity I need as a background image. Now let’s look at a few use cases for this. The first one is for my contact page background. For it I have an svg pattern that is repeated. for the dark theme i needed a dark grey colored pattern and for the light theme I needed a light grey pattern and both have different opacities. The scss to achieve this looks like this:

1
2
3
4
5
6
7
8
9
10
body {
  background: {
    color: var(--bg-alt);
    image: bg-img-svg('contact-pattern', $light-grey-500, 0.3);
    repeat: repeat;
  }
  @media( prefers-color-scheme: dark ) {
    background-image: bg-img-svg('contact-pattern', $dark-grey-700, 0.5);
  }
}

that spits out a background image that looks something like this:


Let’s look at another use case. Let’s look at checkboxes and radio buttons. I want to put a checkmark as the background image of checked radio buttons and checkboxes. Again here’s the scss:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
--checked: #{bg-img-svg('check', $light-grey-100)};

@media( prefers-color-scheme: dark ) {
  --checked: #{bg-img-svg('check', $pink-900)};
}

input[type="radio"]:checked,
input[type="checkbox"]:checked {
  background: {
    color: var(--accent-color);
    image: var(--checked)
    size: 1rem;
    position: center;
    repeat: no-repeat;
  }
}

And that produces something like this:

Pizza Pizza Toppings
Pizza Pizza Crust


Anyway that’s how I chose to solve this problem but maybe you know a better way. I’d love to hear about it if you do. I’m always looking for cleaner code.

Tags
Feel free to reach out to me.
    Mastodon Mastodon

Up Next

Webkit Gradients to Transparent

Finding and working around a render issue in webkit.

1 min read