Inline SVG code within CSS using LESS
Here’s a funky trick for you. Using CSS pre-processors like LESS and SASS/SCSS are pretty sweet as they allow you extra functionality like nested selectors, variables, mixins and other sweet dynamic things when you're building CSS.
I have been working on a project recently (except it was using SCSS/Compass, not LESS), and I had a use-case where I just wanted to inline SVG code to use as a background image directly in the CSS file. It’s cool because nifty browsers support inline images (think base64-encoded PNGs, for example), except I just wanted plain ol’ SVG code to make some simple elements have the same shared colours as the project’s.
I had used inline SVG code before, but it was just as an experiment years ago. A quick Google online came up with CSS Tricks' article "Probably Don't Base64 SVG", which I used as a guide for inlining SVG code.
For that project I mentioned earlier, I ended up digging into Compass and Ruby and wrote an inline-svg-code method (check out the fork here). I also wrote another function inline-svg-to-png to output as a base64-encoded PNG image. Compass is kinda chuggy though, and most of the time I use LESS (when I can choose to use it) as I prefer it's simplier syntax which more closer resembles actual CSS.
Once I had some time in the weekend, I looked into porting the same functionality as the Compass inline-svg-code method to LESS. Thankfully LESS supports URL-encoding strings from the get-go! Really saved me a lot of work and makes me love LESS just that little bit more.
Since LESS supports URL-encoding strings out of the box, it was a cinch to write a mixin to handle the basic encoding of the SVG code:
// Inline SVG code images in LESS CSS // @author Matt Scheurich (http://lvl99.com) // Github: https://github.com/lvl99/less-inline-svg-code .inline-svg-code( @code ) { @-svg-code: escape(~'<?xml version="1.0" ?>@{code}'); @-inline-svg-code: ~'data:image/svg+xml,@{-svg-code}'; @-inline-svg-url: ~"url('@{-inline-svg-code}')"; }
Pretty simple and straight-forward, eh? You can copy and paste that snippet above, or if you want to view the Github repo, check it out at github.com/lvl99/less-inline-svg-code.
You can see the mixin sets 3 variables: @-svg-code, @-inline-svg-code and @-inline-svg-url. This is because LESS doesn’t support custom returning functions, but relies on mixins to set variables which can then be referenced by the class/mixin blocks.
Here’s a basic example on how to use the mixin:
.example-1 { display: block; width: 300px; height: 300px; margin: 0 auto; background-repeat: no-repeat; background-position: center center; background-size: contain; overflow: hidden; text-align: left; text-indent: -999em; // Encode the SVG image code into a string which can be used within this class declaration .inline-svg-code(~'<svg width="600" height="600" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 600 600" preserveAspectRatio="xMin yMin"><g><rect fill="white" stroke="#000" stroke-width="1.5" x="110.75" y="299.25" width="180" height="180" id="rectangle"/><circle fill="white" stroke="#000" stroke-width="1.5" cx="300" cy="300" r="107.5" id="circle"/><path fill="white" stroke="#000" stroke-width="1.5" d="m430.75,120.75l-123.5,176l242,0l-118.5,-176z" id="triangle"/></g></svg>'); // Apply the background image using the `@-inline-svg-url` variable background-image: @-inline-svg-url; }
.example-1 { display: block; width: 300px; height: 300px; margin: 0 auto; background-repeat: no-repeat; background-position: center center; background-size: contain; overflow: hidden; text-align: left; text-indent: -999em; background-image: url('data:image/svg+xml,%3C?xml%20version%3D%221.0%22%20?%3E%3Csvg%20width%3D%22600%22%20height%3D%22600%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20viewbox%3D%220%200%20600%20600%22%20preserveAspectRatio%3D%22xMin%20yMin%22%3E%3Cg%3E%3Crect%20fill%3D%22white%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20x%3D%22110.75%22%20y%3D%22299.25%22%20width%3D%22180%22%20height%3D%22180%22%20id%3D%22rectangle%22/%3E%3Ccircle%20fill%3D%22white%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20cx%3D%22300%22%20cy%3D%22300%22%20r%3D%22107.5%22%20id%3D%22circle%22/%3E%3Cpath%20fill%3D%22white%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20d%3D%22m430.75,120.75l-123.5,176l242,0l-118.5,-176z%22%20id%3D%22triangle%22/%3E%3C/g%3E%3C/svg%3E'); }
Check out this example on Codepen.io:
See the Pen Basic example of inlining SVG code using LESS by Matt Scheurich (@lvl99) on CodePen.
Dynamic example with variable interpolation
But how’s about that variable interpolation I talked about earlier to support dynamic colours and other things? Check this out:
// I've created a dynamic mixin to generate consistent SVG image code that can support color changes .example-inline-svg( @color-triangle: white, @color-square: white, @color-circle: white ) { @example-svg-code: ~'<svg width="600" height="600" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 600 600" preserveAspectRatio="xMin yMin"><g><rect fill="@{color-square}" stroke="#000" stroke-width="1.5" x="110.75" y="299.25" width="180" height="180" id="rectangle"/><circle fill="@{color-circle}" stroke="#000" stroke-width="1.5" cx="300" cy="300" r="107.5" id="circle"/><path fill="@{color-triangle}" stroke="#000" stroke-width="1.5" d="m430.75,120.75l-123.5,176l242,0l-118.5,-176z" id="triangle"/></g></svg>'; // Here I apply the inline SVG code mixin to retrieve the encoded URL to set as the background-image .inline-svg-code(@example-svg-code); background-image: @-inline-svg-url; } .example-2 { display: block; width: 100%; height: 300px; background-repeat: no-repeat; background-position: center center; background-size: contain; overflow: hidden; text-align: left; text-indent: -999em; // Here I set the initial state, which will be based on the default color values .example-inline-svg(); // Let's do some further customisation based on responsive widths... // -- RGB, bebe @media screen and (min-width: 500px) and (max-width: 799px) { .example-inline-svg(red, green, blue); } // -- How 'bout some red, white and blue (Viva la France!) @media screen and (min-width: 800px) and (max-width: 1199px) { .example-inline-svg(red, white, blue); } // -- Greyscale for fun @media screen and (min-width: 1200px) { .example-inline-svg(black, grey, white); } }
.example-2 { display: block; width: 100%; height: 300px; background-repeat: no-repeat; background-position: center center; background-size: contain; overflow: hidden; text-align: left; text-indent: -999em; background-image: url('data:image/svg+xml,%3C?xml%20version%3D%221.0%22%20?%3E%3Csvg%20width%3D%22600%22%20height%3D%22600%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20viewbox%3D%220%200%20600%20600%22%20preserveAspectRatio%3D%22xMin%20yMin%22%3E%3Cg%3E%3Crect%20fill%3D%22white%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20x%3D%22110.75%22%20y%3D%22299.25%22%20width%3D%22180%22%20height%3D%22180%22%20id%3D%22rectangle%22/%3E%3Ccircle%20fill%3D%22white%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20cx%3D%22300%22%20cy%3D%22300%22%20r%3D%22107.5%22%20id%3D%22circle%22/%3E%3Cpath%20fill%3D%22white%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20d%3D%22m430.75,120.75l-123.5,176l242,0l-118.5,-176z%22%20id%3D%22triangle%22/%3E%3C/g%3E%3C/svg%3E'); } @media screen and (min-width: 500px) and (max-width: 799px) { .example-2 { background-image: url('data:image/svg+xml,%3C?xml%20version%3D%221.0%22%20?%3E%3Csvg%20width%3D%22600%22%20height%3D%22600%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20viewbox%3D%220%200%20600%20600%22%20preserveAspectRatio%3D%22xMin%20yMin%22%3E%3Cg%3E%3Crect%20fill%3D%22green%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20x%3D%22110.75%22%20y%3D%22299.25%22%20width%3D%22180%22%20height%3D%22180%22%20id%3D%22rectangle%22/%3E%3Ccircle%20fill%3D%22blue%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20cx%3D%22300%22%20cy%3D%22300%22%20r%3D%22107.5%22%20id%3D%22circle%22/%3E%3Cpath%20fill%3D%22red%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20d%3D%22m430.75,120.75l-123.5,176l242,0l-118.5,-176z%22%20id%3D%22triangle%22/%3E%3C/g%3E%3C/svg%3E'); } } @media screen and (min-width: 800px) and (max-width: 1199px) { .example-2 { background-image: url('data:image/svg+xml,%3C?xml%20version%3D%221.0%22%20?%3E%3Csvg%20width%3D%22600%22%20height%3D%22600%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20viewbox%3D%220%200%20600%20600%22%20preserveAspectRatio%3D%22xMin%20yMin%22%3E%3Cg%3E%3Crect%20fill%3D%22white%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20x%3D%22110.75%22%20y%3D%22299.25%22%20width%3D%22180%22%20height%3D%22180%22%20id%3D%22rectangle%22/%3E%3Ccircle%20fill%3D%22blue%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20cx%3D%22300%22%20cy%3D%22300%22%20r%3D%22107.5%22%20id%3D%22circle%22/%3E%3Cpath%20fill%3D%22red%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20d%3D%22m430.75,120.75l-123.5,176l242,0l-118.5,-176z%22%20id%3D%22triangle%22/%3E%3C/g%3E%3C/svg%3E'); } } @media screen and (min-width: 1200px) { .example-2 { background-image: url('data:image/svg+xml,%3C?xml%20version%3D%221.0%22%20?%3E%3Csvg%20width%3D%22600%22%20height%3D%22600%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20viewbox%3D%220%200%20600%20600%22%20preserveAspectRatio%3D%22xMin%20yMin%22%3E%3Cg%3E%3Crect%20fill%3D%22grey%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20x%3D%22110.75%22%20y%3D%22299.25%22%20width%3D%22180%22%20height%3D%22180%22%20id%3D%22rectangle%22/%3E%3Ccircle%20fill%3D%22white%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20cx%3D%22300%22%20cy%3D%22300%22%20r%3D%22107.5%22%20id%3D%22circle%22/%3E%3Cpath%20fill%3D%22black%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20d%3D%22m430.75,120.75l-123.5,176l242,0l-118.5,-176z%22%20id%3D%22triangle%22/%3E%3C/g%3E%3C/svg%3E'); } }
Check out this example on Codepen.io:
See the Pen Dynamic example of inlining SVG code using LESS by Matt Scheurich (@lvl99) on CodePen.