svg-themer
Apply a theme to SVG elements as well as image data and css rules constructed from SVG markup.
Abstract
The basic function of this module is to "theme" an SVG data by setting arbitrary properties on the root <svg>
element and/or its descendants. Typical use case is to apply stroke
and fill
via the style
property.
This module is not necessary for <svg>...</svg>
DOM elements which can be styled with CSS. Unfortunately, CSS cannot be used to style image data derived from SVG, such as <img/>
elements and CSS style rule properties with url()
values that reference SVG data.
With svg-themer
you can theme such objects as easily as actual <svg>
elements. You can also attach custom theming "scripts" (functions) so that all SVG data can be themed from a single theme
object definition regardless of their individual levels of complexity.
Background
SVG data is displayed on a web page by the following DOM Element
s:
SVGSVGElement
— Represented in XML as <svg>...</svg>
(with descendant elements)HTMLImageElement
— Represented in HTML as <img src="filename.svg"/>
or <img src="data:image/svg+xml...">
HTMLElement
— Any DOM element inheriting a CSS property value like url(filename.svg)
or url(data:image/svg+xml...)
An SVGSVGElement
, along with its descendant elements, picks up cascading style properties (including but not limited to stroke
and fill
) from CSS stylesheet rules. This works quite well, albeit at the memory cost of having to deep-clone the entire element everywhere it needs to appear. However, with the memory capacity of today's machines this cost is usually of no concern.
The situation is quite different, however, for <img/>
elements with underlying SVG data and for any element referencing SVG data via a CSS url()
function. In these cases applying styles has no effect on the image because it has already been rasterized.
With this module, you can apply a set of thematic styles directly to such SVG data.
IMPORTANT NOTE: For this to work, the SVG data must be "baked-in" to the object using data:image/svg+xml
; this version does not handle file URLs.
Data encoding: This module properly handles both unencoded data (data:image/svg+xml,
+ raw SVG markup) as well as base64-encoded data (data:image/svg+xml;base64,
+ encoded string). If you experience any issues trying to use unencoded SVG markup directly (such as nested quotes), you can always encode it.
theme
objects
Theming is performed in this module by calling property-setting functions that you supply. These "prop setters" take a theme
parameter and applies it to the SVG element.
Your theming scheme (the shape of your theme object) is up to you and your prop setter functions. The standard implementation uses a very simple scheme with two properties, color
and backgroundColor
. A theme registry using this scheme might look like this:
var theme = {
blackAndWhite: { backgroundColor: 'white', color: 'black' },
highwaySignage: { backgroundColor: '#006A4D', color: 'white' },
stopSignage: { backgroundColor: '#da291c', color: 'white' }
};
If you don't specify a custom prop setter for your SVG image, the fallback (svgThemer.setSvgProps
) will be called. The fallback assumes a particular simple scheme for theme
objects. It may however be overridden to accommodate custom theming schemes.
API
Access npm module:
npm i svg-themer --save
var svgThemer = require('svg-themer');
Access UMD module (one of the following):
<script src="https://unpkg.com/svg-themer@1.0/umd/svg-themer.js"></script>
<script src="https://unpkg.com/svg-themer@1.0/umd/svg-themer.min.js"></script>
svgThemer.setSvgProps(theme)
Set properties on an SVGSVGElement
element. Used by the other API methods to style an element extracted from an <img>
element's src
or a CSSStyleRule
object property with a url()
value. It then re-injects the styled version back into the src
attribute or the CSS url()
.
This method serves primarily as a fallback when a custom method is not supplied to the other API methods.
The standard implementation
The standard implementation accommodates a basic theming scheme, which it applies only to the root SVGSVGElement
:
Color | theme property | SVG style |
---|
Foreground | .color | stroke and color |
Background | .backgroundColor | fill |
Note: The reason the standard implementation also sets color
style is to facilitate using fill: currentColor
inside the svg.
Styling <svg>
DOM elements
This method can also be called directly on "live" SVGSVGElement
s in the DOM:
<html>
<body>
<svg viewBox="25 25 450 450" width="45" height="45" xmlns="http://www.w3.org/2000/svg">
<polygon points="475 350 350 475 150 475 25 350 25 150 150 25 350 25 475 150"></polygon>
<path d="M 450 339 L 339 450 L 161 450 L 50 339 L 50 161 L 161 50 L 339 50 L 450 161 Z" style="stroke-width:15px"></path>
<text transform="matrix(1, 0, 0, 1.689489, 3.74598, -160.023605)" style="fill: currentColor; font: 140.871px sans-serif; font-weight: bold; letter-spacing: -4.1px;" x="61" y="292">STOP</text>
</svg>
<svg viewBox="25 25 450 450" width="45" height="45" xmlns="http://www.w3.org/2000/svg">
<polygon points="475 350 350 475 150 475 25 350 25 150 150 25 350 25 475 150"></polygon>
<path d="M 450 339 L 339 450 L 161 450 L 50 339 L 50 161 L 161 50 L 339 50 L 450 161 Z" style="stroke-width:15px"></path>
<text transform="matrix(1, 0, 0, 1.689489, 3.74598, -160.023605)" style="fill: currentColor; font: 140.871px sans-serif; font-weight: bold; letter-spacing: -4.1px;" x="61" y="292">STOP</text>
</svg>
</body>
var svg = document.querySelectorAll('svg');
svgThemer.setSvgProps.call(svg[0], theme.blackAndWhite);
svgThemer.setSvgProps.call(svg[1], theme.stopSignage);
Results (when <svg>
elements use this markup):
Overriding the standard implementation
The standard implementation may be overridden to accommodate custom theming schemes:
svgThemer.setSvgProps = function(theme) { ... };
svgThemer.setImgSvgProps(theme, setSvgProps = svgThemer.setSvgProps)
Themes SVG image data. Similar to calling setSvgProps
on an <svg>
element directly, except the context is expected to be an HTMLImageElement
(shown here demonstrating both unencoded and encoded data):
<html>
<body>
<img src='data:image/svg+xml,<svg viewBox="25 25 450 450" width="45" height="45" xmlns="http://www.w3.org/2000/svg"><polygon points="475 350 350 475 150 475 25 350 25 150 150 25 350 25 475 150"></polygon><path d="M 450 339 L 339 450 L 161 450 L 50 339 L 50 161 L 161 50 L 339 50 L 450 161 Z" style="stroke-width:15px"></path><text transform="matrix(1, 0, 0, 1.689489, 3.74598, -160.023605)" style="fill: currentColor; font: 140.871px sans-serif; font-weight: bold; letter-spacing: -4.1px;" x="61" y="292">STOP</text></svg>'>
<img src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIyNSAyNSA0NTAgNDUwIiB3aWR0aD0iNDUiIGhlaWdodD0iNDUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBvbHlnb24gcG9pbnRzPSI0NzUgMzUwIDM1MCA0NzUgMTUwIDQ3NSAyNSAzNTAgMjUgMTUwIDE1MCAyNSAzNTAgMjUgNDc1IDE1MCI+PC9wb2x5Z29uPjxwYXRoIGQ9Ik0gNDUwIDMzOSBMIDMzOSA0NTAgTCAxNjEgNDUwIEwgNTAgMzM5IEwgNTAgMTYxIEwgMTYxIDUwIEwgMzM5IDUwIEwgNDUwIDE2MSBaIiBzdHlsZT0ic3Ryb2tlLXdpZHRoOjE1cHgiPjwvcGF0aD48dGV4dCB0cmFuc2Zvcm09Im1hdHJpeCgxLCAwLCAwLCAxLjY4OTQ4OSwgMy43NDU5OCwgLTE2MC4wMjM2MDUpIiBzdHlsZT0iZmlsbDogY3VycmVudENvbG9yOyBmb250OiAxNDAuODcxcHggc2Fucy1zZXJpZjsgZm9udC13ZWlnaHQ6IGJvbGQ7IGxldHRlci1zcGFjaW5nOiAtNC4xcHg7IiB4PSI2MSIgeT0iMjkyIj5TVE9QPC90ZXh0Pjwvc3ZnPg==">
</body>
var img = document.querySelectorAll('img');
svgThemer.setImgSvgProps.call(img[0], theme.blackAndWhite);
svgThemer.setImgSvgProps.call(img[1], theme.stopSignage);
svgThemer.setRuleSvgProps(theme, setSvgProps = svgThemer.setSvgProps, propName = undefined)
Themes an SVG style rule property. Similar to calling setSvgProps
on an <svg>
element directly, except the context is expected to be an CSSStyleRule
(shown here using encoded data):
<html>
<body>
<style>
.stop-sign {
background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIyNSAyNSA0NTAgNDUwIiB3aWR0aD0iNDUiIGhlaWdodD0iNDUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBvbHlnb24gcG9pbnRzPSI0NzUgMzUwIDM1MCA0NzUgMTUwIDQ3NSAyNSAzNTAgMjUgMTUwIDE1MCAyNSAzNTAgMjUgNDc1IDE1MCI+PC9wb2x5Z29uPjxwYXRoIGQ9Ik0gNDUwIDMzOSBMIDMzOSA0NTAgTCAxNjEgNDUwIEwgNTAgMzM5IEwgNTAgMTYxIEwgMTYxIDUwIEwgMzM5IDUwIEwgNDUwIDE2MSBaIiBzdHlsZT0ic3Ryb2tlLXdpZHRoOjE1cHgiPjwvcGF0aD48dGV4dCB0cmFuc2Zvcm09Im1hdHJpeCgxLCAwLCAwLCAxLjY4OTQ4OSwgMy43NDU5OCwgLTE2MC4wMjM2MDUpIiBzdHlsZT0iZmlsbDogY3VycmVudENvbG9yOyBmb250OiAxNDAuODcxcHggc2Fucy1zZXJpZjsgZm9udC13ZWlnaHQ6IGJvbGQ7IGxldHRlci1zcGFjaW5nOiAtNC4xcHg7IiB4PSI2MSIgeT0iMjkyIj5TVE9QPC90ZXh0Pjwvc3ZnPg==)
}
</style>
<span class='stop-sign'></span>
<span class='stop-sign'></span>
</body>
var rule = document.querySelector('style').sheet.rules[0];
svgThemer.setRuleSvgProps.call(rule, theme.stopSignage);
In this case, all referencing elements are themed simultaneously:
If propName
is not given, the following CSS style properties are searched in order for the first one with a url()
value beginning with data:image/svg+xml
:
background-image
list-style-image
border-image
content
svgThemer.mixin(svgOrImgOrRule, setSvgProps = svgThemer.setSvgProps)
This method installs the above calls on an object directly, providing an alternative means of theming the object.
Mixes in one of the above methods as setTheme
into the object referenced by svgOrImgOrRule
when that object is one of:
SVGSVGElement
(<svg>...</svg>
)HTMLImageElement (
) with underlying SVG markup in the
src` attributeCSSStyleRule
(a stylesheet rule) with SVG markup in a particular style's url
function
The method returns the object for chaining.
var svgEl = document.querySelector('svg');
var imgEl = document.querySelector('img');
var rule = document.querySelector('style').sheet.rules[0];
svgThemer.mixin(svgEl, propSetter);
svgThemer.mixin(imgEl, propSetter);
svgThemer.mixin(styleRule, propSetter);
The 2nd parameter is optional. If specified, it is used during the theming call (below), overriding the fallback svgThemer.setSvgProps
. It is mixed in as setSvgProps
.
Theme can then be applied the the above objects as follows:
svgEl.setTheme(theme);
imgEl.setTheme(theme);
styleRule.setTheme(theme);
Recall that setImgSvgProps
and setRuleSvgProps
take an optional 2nd parameter to override the prop setter; and setRuleSvgProps
takes and optional 3rd paramter to name the property holding the url()
to operate on. As noted in the comments in the example, the setTheme
methods are references to these methods and therefore can also accept these optional parameters.
mixin
is chainable (returns its context) for setting a default theme:
svgThemer.mixin(object).setTheme(theme);
setTheme
also returns its context so you can make the following assignment:
var imgEl = svgThemer.mixin(document.querySelector('img')).setTheme(theme);