Skip to content

Instantly share code, notes, and snippets.

@darsain
Last active April 10, 2024 11:15
Show Gist options
  • Star 31 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save darsain/3a8e344f655621ce1d4f to your computer and use it in GitHub Desktop.
Save darsain/3a8e344f655621ce1d4f to your computer and use it in GitHub Desktop.
How to use SVG sprites in img[src] and css backgrounds

To make this work in CSS:

background: url('images.svg#chart');

or img:

<img src="images.svg#chart">

You need to structure images.svg in a way that enables SVG fragment identifiers. There are 3 ways I know of:

1. SVG stack with global viewBox

You'd use this when all images are the same size.

<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
	<defs>
		<style>
		.img { display: none }
		.img:target { display: inline }
		</style>
	</defs>

	<g id="chart" class="img">
		<rect x="6" width="4" height="16"/>
		<rect x="12" y="4" width="4" height="12"/>
		<rect x="0" y="8" width="4" height="8"/>
	</g>

	<!-- more "g" tags -->
</svg>
2. SVG stack with per image viewBox

You'd use this when every image has different size.

<svg xmlns="http://www.w3.org/2000/svg">
	<defs>
		<style>
		.img { display: none }
		.img:target { display: inline }
		</style>
	</defs>

	<svg viewBox="0 0 16 16">
		<g id="chart" class="img">
			<rect x="6" width="4" height="16"/>
			<rect x="12" y="4" width="4" height="12"/>
			<rect x="0" y="8" width="4" height="8"/>
		</g>
	</svg>

	<!-- more "svg > g" tags -->
</svg>
3. SVG sprite with ID'd views

This one is sub-optimal. When mismatching target element to image size ratio, you might see other sprites peeking from sides. You'd have to make big gaps between images, but that's ugly and not bulletproof.

It is also more annoying to write build scripts for. But here it is anyway:

<svg xmlns="http://www.w3.org/2000/svg">
	<view id="chart" viewBox="0 0 16 16"/>
	<view id="plus" viewBox="16 0 16 16"/>

	<g transform="translate(0 0)">
		<rect x="6" width="4" height="16"/>
		<rect x="12" y="4" width="4" height="12"/>
		<rect x="0" y="8" width="4" height="8"/>
	</g>

	<g transform="translate(16 0)">
		<mask id="m" x="0" y="0" width="1" height="1">
			<circle cx="8" cy="8" r="8" fill="white"/>
			<line x1="8" y1="3" x2="8" y2="13" stroke="black" stroke-width="2"/>
			<line x1="3" y1="8" x2="13" y2="8" stroke="black" stroke-width="2"/>
		</mask>
		<rect width="16" height="16" mask="url(#m)"/>
	</g>
</svg>

Currently, SVG fragment identifiers in CSS backgrounds work only in FF and IE, and will work in Chrome as soon as crbug.com/128055 is fixed.

@nickshanks
Copy link

There is also the option of just a list of defs > symbol#id elements.

@luizlopescom
Copy link

Great tip! Very useful. I'm using it, but instead of an .img class, I'm pointing the svg tag.
<style> .svg { display: none } .svg:target { display: inline } </style>

@leoj3n
Copy link

leoj3n commented Feb 27, 2024

@luizlopescom your markup shows class selector .svg which means you would need to still have class="svg".

If you were to do what you were suggesting, using the element selector like svg:target {}, that will work on Chrome but not Firefox.

Although it seems like it should work either way, I have found it does not on Firefox.

So, to support Firefox, I would stick with using a class selector. I like .hidden:

<svg xmlns="http://www.w3.org/2000/svg">
    <style>
        .hidden { display: none }
        .hidden:target { display: inline }
    </style>

    <svg class="hidden" id="bcard" viewBox="0 0 480 270" fill="#fff">
        <path d="M0 0h480v270H0z" fill="#192b73" />
        [...]
    </svg>

    <svg class="hidden" id="envelope" viewBox="0 0 533 305">
        <defs>
            <linearGradient id="a" gradientUnits="userSpaceOnUse" />
        </defs>
        [...]
    </svg>
</svg>

@luizlopescom
Copy link

@leoj3n, you are right that my suggestion don't work on Firefox. Great tip, I'll try yours.

@tobyperplex
Copy link

tobyperplex commented Apr 10, 2024

There is also the option of just a list of defs > symbol#id elements.

Could you show this? I tried:

<svg xmlns="http://www.w3.org/2000/svg">
    <defs>
        <symbol id="chevron-right" viewBox="0 0 320 512">
            <path fill="#e30075" d="M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L242.7 256 73.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z"/>
        </symbol>
    </defs>
</svg>

and then:
background-image: url('/icons/icons-rte.svg#chevron-right');
but that doesn't work

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment