The gulp plugin to combine svg files into one using the stack method.
pnpm add -D gulp gulp-stacksvgAdd the following to your gulpfile.js:
import { stacksvg } from "gulp-stacksvg"
import { dest, src } from "gulp"
export function createStack () {
return src(`./src/shared/icons/**/*.svg`)
.pipe(stacksvg())
.pipe(dest(`./dist/shared/icons`))
}To combine all icons from ./src/shared/icons/ into the ./dist/shared/icons/stack.svg run:
pnpm gulp createStackEvery icon keeps its own svg element inside the stack, named after the path of the source file relative to the directory the icons were read from:
- the extension is dropped,
- the directory separators become underscores,
- the whitespace characters become hyphens.
So ./src/shared/icons/social/like me.svg becomes social_like-me, and the fragment is referenced as ./dist/shared/icons/stack.svg#social_like-me.
Two icons whose paths collapse to the same name — social/like.svg and social_like.svg, for instance — abort the build, since the stack has no way to tell such fragments apart.
Identifiers used inside an icon are renamed to <fragment name>_<number>, so that icons declaring the same gradient, mask or filter stay independent once stacked. Do not rely on these from the outside: the numbering follows the document order of the icon and shifts whenever the icon changes.
Unlike all other methods for assembling a sprite, the stack does not limit us in choosing how to insert a vector into a page. Take a look at the results of different ways to display fragments of different types of sprites.
We can use the stack in all four possible ways:
-
in markup:
- in
srcofimgtag — static, - in the
hrefof theusetag — with the possibility of repainting,
- in
-
in styles:
- in
url()propertiesbackground— static, - in
url()propertiesmask— with the possibility of repainting.
- in
Demo page to prove it.
This method was first mentioned in a Simurai article on April 2, 2012. But even it uses unnecessarily complex code transformations.
This can be done much easier. In general, the stack is arranged almost like a symbol sprite, but without changing the icon tag (it remains the svg tag, as in the original icon files) and with the addition of a tiny bit of style.
<svg xmlns="http://www.w3.org/2000/svg">
<style>:root svg:not(:target) { display: none }</style><svg id="sun" viewBox="0 0 24 24">
<!-- Inner code of sun icon -->
</svg><svg id="heart" viewBox="0 0 24 24">
<!-- Inner code of heart icon -->
</svg><svg id="thumbup" viewBox="0 0 24 24">
<!-- Inner code of thumbup icon -->
</svg></svg>The magic is in the stack inner style, which shows only the fragment requested by the link, hiding everything else:
:root svg:not(:target) { display: none }And now the icons from the external sprite are available in the styles
<button class="button button--icon_heart" type="button">
<span class="visually-hidden">Add to favorites</span>
</button>.button {
display: inline-flex;
align-items: center;
gap: 0.5em;
&:hover {
--fill: red;
}
&::before {
content: "";
width: 1em;
height: 1em;
/* icon shape */
mask: var(--icon) no-repeat center / contain;
/* icon color */
background: var(--fill, orangered);
}
&:where(.button--icon_heart) {
--icon: url("../icons/stack.svg#heart");
}
}For an icon inserted via mask, simply change the background. Moreover, unlike use, you can draw anything in the background under the mask, for example, a gradient.