Skip to content

Instantly share code, notes, and snippets.

@sergiolopes
Last active February 11, 2024 23:57
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save sergiolopes/4739627 to your computer and use it in GitHub Desktop.
Save sergiolopes/4739627 to your computer and use it in GitHub Desktop.
A script to generate SVG sprites, including CSS and PNG fallbacks.

SVG sprite generator, by Sérgio Lopes

Requirements

Usage

  • Create a folder full of individual SVG files you want to sprite
    • SVG file name will be used as HTML className
    • Image size will be the default one used on CSS and PNG fallbacks
    • Folder name is important, will be used on CSS too
  • Run: build-svg-sprite.sh <sprite_folder> <output_path>

Using on HTML:

  • Import the sprite.css file
  • If your folder name was home.sprite and there you have an icon named search.svg, your HTML should be this: <span class="home home-search">Fallback text</span>
  • You also need some JS to detect SVG and retina support. Something like this on your head should do the trick:
// Feature detect, Modernizr inspired
document.documentElement.className = 

// javascript
'js '
	
// SVG
+ (!!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', "svg").createSVGRect ? 'svg ':'no-svg ')

// retina pixel ratio
+ 'ratio' + (!!(window.devicePixelRatio > 1.3) ? '2x ' : '1x ');

More

  • This is an experiment, with many possible improvements. Contributions are welcome.
  • You should optimize the generated PNG files too, using pngcrunch, pngout etc
#!/bin/bash
#
# High def sprite build.
# by Sérgio Lopes - http://sergiolopes.org/
#
# Params:
# $1 Sprite folder with SVGs
# $2 Output path
#
# See original gist: https://gist.github.com/sergiolopes/4739627
# Configuration: how many pixels between each image on the sprite?
SPACING=10
# Parsing command-line paramenters
SPRITE="$1"
SPRITE_NAME="$(basename "$SPRITE" | sed 's/\.[^\.]*$//g')"
OUTPATH="$(echo "$1" | sed 's/\.[^\.]*$//g')"
OUTDIR="$2/$OUTPATH"
# Helpers: simple functions
function round {
echo "($1+0.5)/1" | bc
}
function divisionByTen {
echo "scale=2; $1/100" | bc
}
# Prepare output directories
echo Outputing to $OUTDIR
rm -rf "$OUTDIR"
mkdir -p "$OUTDIR"
# Generate the SVG sprite.
# Uses svg_stack to create the sprite and scour to optimize the file.
# Creates two files: sprite.svg and sprite.svgz
python svg_stack.py --margin $SPACING --direction=v "$SPRITE"/*.svg > "$OUTDIR"/sprite.svg
python scour.py -i "$OUTDIR"/sprite.svg -o "$OUTDIR"/sprite-opt.svg
python scour.py -i "$OUTDIR"/sprite.svg -o "$OUTDIR"/sprite.svgz
mv "$OUTDIR"/sprite-opt.svg "$OUTDIR"/sprite.svg
# Get sprite final width and height, using ImageMagick
SPRITE_WIDTH=$(divisionByTen $(round $(identify -format '%w' "$OUTDIR"/sprite.svgz)))em
SPRITE_HEIGHT=$(divisionByTen $(round $(identify -format '%h' "$OUTDIR"/sprite.svgz)))em
# Generate base CSS for this sprite.
# Includes main className, sprite URL, size, and PNG fallbacks.
cat > "$OUTDIR"/sprite.css <<CSS
.${SPRITE_NAME} {
background-repeat: no-repeat;
-moz-background-size: ${SPRITE_WIDTH} ${SPRITE_HEIGHT}; /* Firefox 3.6 */
background-size: ${SPRITE_WIDTH} ${SPRITE_HEIGHT}; /* sprite_1x size */
display: inline-block;
position: relative;
font-size: 100px;
/* image replacement */
overflow: hidden;
text-indent: -999999px;
}
/* has SVG support */
.svg .${SPRITE_NAME} {
background-image: url(${OUTPATH}/sprite.svg);
}
/* no SVG but retina (and JS) */
.no-svg.ratio2x .${SPRITE_NAME} {
background-image: url(${OUTPATH}/sprite_2x.png);
}
/* (no-JS) or (no-SVG and no-retina) */
.no-js .${SPRITE_NAME},
.no-svg.ratio1x .${SPRITE_NAME} {
background-image: url(${OUTPATH}/sprite_1x.png);
}
/* individual icons */
CSS
sprite_size=0
# Iterate on all individual SVG files to generated each CSS class.
for svg_file in "$SPRITE"/*.svg; do
# Get this file width and height
svg_file_px_height=$(round $(identify -format '%h' "${svg_file}"))
svg_file_width=$(divisionByTen $(round $(identify -format '%w' "${svg_file}")))em
svg_file_height=$(divisionByTen $svg_file_px_height)em
# Get file name without .svg extension
svg_file_name="$(basename "$svg_file" | sed 's/\.[^\.]*$//g')"
# Output CSS class to this image.
# Configures background-position accordingly
cat >> "$OUTDIR"/sprite.css <<____CSS
.${SPRITE_NAME}-${svg_file_name} {
background-position: 0 -$(divisionByTen $sprite_size)em;
height: ${svg_file_height};
width: ${svg_file_width};
}
____CSS
sprite_size=$((sprite_size + svg_file_px_height + SPACING))
done
# Export PNG (@1x and @2x) versions using Inkscape
inkscape \
--export-png="$OUTDIR"/sprite_1x.png --export-area-page --export-dpi=90 \
--export-background-opacity=0 --without-gui "$OUTDIR"/sprite.svgz
inkscape \
--export-png="$OUTDIR"/sprite_2x.png --export-area-page --export-dpi=180 \
--export-background-opacity=0 --without-gui "$OUTDIR"/sprite.svgz
@benfrain
Copy link

Afraid I'm obviously doing something basic wrong...

I have saved the script above into my img folder (and called it build-svg-sprite.sh) - should it be saved somewhere else?

Inside the img folder is another folder containing my svgs (in a folder called svg).

Then on the terminal I move to the img folder (when I run ls I see the build-svg-sprite.sh file listed). However, when I run build-svg-sprite.sh svg /png I get a message command not found: build-svg-sprite.sh

Any ideas what I'm doing wrong?

@inacho
Copy link

inacho commented Jul 29, 2013

If you run the script without the parameters, it starts to delete all the files in your hard drive!

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