Savager

Getting Started

Install the package into your project

npm i savager

Run the create-symbols script against your SVG files.

create-symbols -i svg -o assets -t esm

Then import Savager and the symbols into your build script. The assets output will contain your new SVG markup!

import Savager from 'savager';
import symbols from './manifest.js';

const savager = new Savager(symbols);
const { assets, sheet } = savager.prepareAssets('balloon');
document.body.innerHTML = assets.balloon;

More information about the API can be found on github.

External Assets

When hosting assets externally, they should still be processed first by the create-symbols script. The script will create a manifest which the Savager instance uses to prepare assets.

import Savager from 'savager'; /* The constructor */
import symbols from './manifest.js'; /* manifest.js, created from the `create-symbols` script */

The assets created should be the ones hosted externally. Savager will then construct the urls based on the externalPath option provided. It's probably best to set this in the constructor options.

import Savager from 'savager';
import symbols from './manifest.js';

const SVG_CDN_PATH = 'https://assets.cdn.com/path/to/assets';
const savager = new Savager(symbols, { externalPath: SVG_CDN_PATH });

Internal Assets

If you aren't hosting assets externally, you'll need to include a reference sheet on the page. You can let the prepareAssets() method attempt to append the sheet to the document.body:

import Savager from 'savager';
import symbols from './manifest.js';

const savager = new Savager(symbols, { autoAppend: true });

This will only work in a browser context. When running the script on a server, you'll need to include the reference sheet in the response and write the returned reference sheet into the HTML:

import nunjucks from 'nunjucks';
import Savager from 'savager';
import symbols from './manifest.js';

const savager = new Savager(symbols);
const { assets, sheet } = savager.prepareAssets('balloon');
const html = nunjucks.render('index.njk', { assets, sheet });

The example above uses Nunjucks as a templating engine.

Accessibility

When working with SVGs, you'll often need to add additional attributes and children to the asset. The assetNames parameter can also take an object of the following shape to help populate the asset with accessibility features.

const assetNames = {
  name: 'balloon',
  title: 'My balloon', // Creates a <title> element in the svg
  desc: 'It is large and round.', // Creates a <desc> element in the svg
  attributes: {
    role: 'img', // Add additional attributes, ARIA can also go here
  }
}
const savager = new Savager(symbols);
const { assets } = savager.prepareAssets(assetNames);
document.body.innerHTML = assets.balloon; // Now includes accessibility features

This also allows for an array of these objects. When providing a desc, you must also provide a title.

Contingencies

Depending on the method you choose, there may be reasons why the SVG doesn't render.

Both of these can be resolved using the attemptInject option, which will take the referenced asset shape and make a copy to be injected into the node. When preparing the assets, you will also receive a inject function which should be executed on the page where the SVGs will appear.

import Savager from 'savager';
import symbols from './manifest.js';

const savager = new Savager(symbols, { attemptInject: true });
const { assets, inject } = savager.prepareAssets('balloon');

If you need the inject function in a different context from where you prepare assets, you can export it directly from the package. Be sure that when preparing assets, that attemptInject option was set to true. Otherwise, executing the function will do nothing.

import { injectionInit } from 'savager';

You can also make this into an IIFE by stringifying the function to be put on the page. This might be helpful for use in templating frameworks.

import { injectionInit } from 'savager';

const iife = `(${injectionInit.toString()})()`;

When the injection occurs, some of the nodes within the original asset may be removed. The script takes care in not removing the original host asset so you may keep a reference in the application lifecycle. Some attributes used in order to activate the script may be removed. Style attributes used to activate the script will remain but should not affect the presentation of your SVG.

Organization

One method of organizing is to create a savager.config.js which you may maintain as your source of truth for SVG assets.

// savager.config.js
import Savager from 'savager';
import symbols from './manifest.js';

export default new Savager(symbols);

Then you can prepare assets as needed.

import { prepareAssets } from './savager.config.js';

const { assets, sheet } = prepareAssets('balloon');

You may also apply the reference sheet earlier and use the getAssets export from the package to render your assets. This function is independent from the symbols entered into a Savager instance; it just assumes these assets are included in the correct location given the options you provide.

// Assume the reference sheet is on the page
import { getAssets } from 'savager';

const { assets } = getAssets('balloon');

Options related to reference sheet management (consolidate, autoAppend) do not affect these assets.

Examples

Each one of the examples shows the code after initializing an instance of Savager as well as the output to this page with an SVG.

Reference sheets are automatically consolidated under the savager-primarysheet Element by default using consolidate option. This consolidates multiple reference sheets found on the page into a single sheet. For the purposes of example, this option has been set to false so sheets can be inspected within the page. Consolidation is normally an important step to ensure no id is duplicated.


Assets as innerHTML

Creating these assets will return strings. You can export the strings to a render function for templating or just write them as HTML. Remember to add the sheet to the page if the assets are not hosted.

const { assets, sheet } = savager.prepareAssets('bang-triangle');

/* Add the reference sheet to the page */
const sheetContainer = document.createElement('div');
sheetContainer.innerHTML = sheet;
document.body.appendChild(sheetContainer);

/* Set the innerHTML of an existing element */
const mySvg = document.querySelector('.mySvg.innerHtmlExample');
mySvg.innerHTML = assets['bang-triangle'];

Auto appending the sheet

Adding the reference sheet can be easier when running in a browser context.

/* Reference sheet is automatically appended to the document.body */
const options = { autoAppend: true };
const { assets } = savager.prepareAssets('bars-horizontal', options);

/* Set the innerHTML of an existing element */
const mySvg = document.querySelector('.mySvg.autoAppendExample');
mySvg.innerHTML = assets['bars-horizontal'];

Assets as document fragment

When using this method, it must be run in a browser context to create the DOM Element. Running this server-side will throw an error.

/* Create a document fragment for each asset, automatically append reference sheet */
const options = { toSvgElement: true, autoAppend: true };
const { assets } = savager.prepareAssets('caret-down', options);

/* Append the svg to an existing element */
const mySvg = document.querySelector('.mySvg.documentFragmentExample');
mySvg.appendChild(assets['caret-down']);

Adding class names to the assets

You can provide a single string or an array of strings.

/* Add the 'example-extraLarge' class to each svg, automatically append reference sheet */
const options = { classNames: 'example-extraLarge', autoAppend: true };
const { assets } = savager.prepareAssets('clock-reverse', options);

/* Set the innerHTML of an existing element */
const mySvg = document.querySelector('.mySvg.classNameExample');
mySvg.innerHTML = assets['clock-reverse'];

Referencing external assets

Provide the path to the assets as an option either in the constructor (re: External Assets) or in the prepareAssets() method. Using external assets does not need a reference sheet to be added in the page.

/* Include the path to the assets */
const options = { externalPath: 'assets' };
const { assets } = savager.prepareAssets('code-brackets', options);

/* Set the innerHTML of an existing element */
const mySvg = document.querySelector('.mySvg.externalPathExample');
mySvg.innerHTML = assets['code-brackets'];

Providing accessibility

If you need to add accessibility features to your SVG, you may pass in a configuration object instead of a string. The attributes key will apply any additional attributes (including class) to the SVG. Classes will be merged. You may also use the className key instead of class here.

/* More complex description of asset */
const a11yAssets = { 
  name: 'dots-vertical',
  title: 'Menu',
  desc: 'Navigational menu',
  attributes: {
    role: 'img',
    class: 'my-a11y-navigation'
  }
};
const { assets } = savager.prepareAssets(a11yAssets, { classNames: 'my-svg', autoAppend: true });

/* Set the innerHTML of an existing element */
const mySvg = document.querySelector('.mySvg.a11yExample');
mySvg.innerHTML = assets['dots-vertical'];