Browser…​unplugged
Showcases of modern Browser technologies

August 4, 2016

A toolbox for SVG icon and style assets workflows

The use of stylesheets with standalone SVG files is not ubiquitious. Browsers understand them, some renderers (like librsvg ) do not. Workflow in editors mostly do not support their usage, or are at least not helpfull, even if they understand them.

I have published a node.js module  that aims to bridge that state of affairs by offering a workflow that

  • inserts stylesheets into SVG files, optionally compiling them from Sass
  • inlines stylesheets by distributing all styles into element style attributes

Secondly, it exports single objects from such a file to PNG or SVG (icon) files. This part of the workflow depends on the command line features of Inkscape  for

  • identifying the bounding box of objects: a major undertaking not easily done, so the module takes advantage of the existing solution
  • PNG export: to ensure the correct interpretation of stylesheets

Icon sets (or grafical assets, for example of a theme) aim to share a common design in terms of colors, sizes, strokes or more complex characteristics like imagery or metaphors.

The best way for a designer to keep track of those characteristics is to work in an integrated workspace, using DRY language to describe communalities only once, and reuse. In short: seperate the shape design in a SVG file from style properties in a stylesheet - those preferably in a higher language like Sass.

For applications, other formats may be needed. Icons may have to be shown in separate files, and in pixel and vector format. The conversion is a purely technical process and can be automated with this module.

XML manipulation with Cheerio

I found Cheerio  to be a fast and fluently written library to manipulate the SVG files. It parses XML/HTML files into a DOM-like tree, but lacks any functionality a headless browser would bring to emulate rendering. If it doesn't concern the XML text, it isn't there. Cheerio features an API that is almost identical to jQuery. Load a string with

var $ = cheerio.load(svgString, {
    xmlMode: true
});

and use the familiar functions. Here is an example:

I implemented inlining styles as a two-step process: first, the style rules from the stylesheet are attached to all elements the selectors state.

/*
 * rule = {
 +    selectors: string[] list of selectors, split on commas, 
 +    declarations: declaration[] list of objects with "property" and "value"
 + }
 */
rule.selectors.forEach((selector) => {
    // select all elements the selector applies to
    var $selected = $(selector);
    $selected.each((i, elem) => {
        // attach a data object with the selector as key (simplified)
        $(elem).data(selector, rule);
    });
});

Then, after all rules are distributed, the styles are written out into a style attribute taking the cascade rules into account:

// $el is a single element
// sort all applicable rules by their specificity
var sorted = sortSpecificity(Object.keys($el.data()));
sorted.forEach((selector) => {
    // get the rule object from the attached data
    var rule = $el.data(selector);
    rule.declarations.forEach((declaration) => {
        // compare each declaration with properties already present in the style attribute
        // highest specificity gets written first, later declarations fall through
        if (!$el.css(declaration.property)) {
            // write new properties to the style attribute
            $el.css(declaration.property, declaration.value);
        }
    }, this);
});