# 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 `` element and/or its descendants. Typical use case is to apply `stroke` and `fill` via the `style` property. This module is not necessary for `...` DOM elements which can be styled with CSS. Unfortunately, CSS cannot be used to style image data derived from SVG, such as `` 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 `` 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 > **TL;DR** Skip to [API](#api) with examples. SVG data is displayed on a web page by the following DOM `Element`s: * `SVGSVGElement` — Represented in XML as `...` (with descendant elements) * `HTMLImageElement` — Represented in HTML as `` or `` * `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 `` 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.** ## Data encoding This module properly handles all flavors of SVG data: Entity | SVG Data Source | Example ------ | --------------- | ------- `HTMLImageElement` object | GET request | `` `HTMLImageElement` object | in-line, raw (unencoded) | `` `HTMLImageElement` object | in-line, base64-encoded | `` `CSSStyleRule` property | GET request | `background-image: url(yourfile.svg)` `CSSStyleRule` property | in-line, URI-encoded | `background-image: url(data:image/svg+xml,...)` `CSSStyleRule` property | in-line, base64-encoded | `background-image: url(data:image/svg+xml;base64,...)` > **Note:** Data in CSS `url()` constructs must always be encoded. (FYI, base64 encoding produces shorter results.) > **Note:** Some browsers (looking at you, IE11) require `HTMLImageElement#src` to be encoded as well, depending on the content. For this reason, formerly unencoded data is always URI-encoded on write-back to `src` after styling as the content is now indeterminate. ## `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: ```js var theme = { blackAndWhite: { backgroundColor: 'white', color: 'black' }, highwaySignage: { backgroundColor: '#006A4D', color: 'white' }, // Pantone 342 stopSignage: { backgroundColor: '#da291c', color: 'white' } // Pantone 485 }; ``` If you don't specify a custom prop setter for your SVG image, the fallback ([`svgThemer.setSvgProps`](#apisetsvgpropstheme)) 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: ```bash npm i svg-themer --save ``` ```js var svgThemer = require('svg-themer'); ``` Access UMD module (one of the following): ```html ``` ### `svgThemer.setSvgProps(theme)` Set properties on an `SVGSVGElement` element. Used by the other API methods to style an element extracted from an `` 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`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#currentColor_keyword) inside the svg. #### Styling `` DOM elements This method can also be called directly on "live" `SVGSVGElement`s in the DOM: ```html STOP STOP ``` ```js var svg = document.querySelectorAll('svg'); svgThemer.setSvgProps.call(svg[0], theme.blackAndWhite); svgThemer.setSvgProps.call(svg[1], theme.stopSignage); ``` Results (when `` elements use [this markup](https://raw.githubusercontent.com/joneit/svg-themer/master/STOP.svg)): ![black on white stop sign](STOP-black-on-white.svg) ![white on red stop sign](STOP-white-on-red.svg) #### Overriding the standard implementation The standard implementation may be overridden to accommodate custom theming schemes: ```js svgThemer.setSvgProps = function(theme) { ... }; ``` ### `svgThemer.setImgSvgProps(theme, setSvgProps = svgThemer.setSvgProps)` Themes SVG image data. Similar to calling `setSvgProps` on an `` element directly, except the context is expected to be an `HTMLImageElement` (shown here demonstrating both unencoded and encoded data): ```html ``` ```js var img = document.querySelectorAll('img'); svgThemer.setImgSvgProps.call(img[0], theme.blackAndWhite); svgThemer.setImgSvgProps.call(img[1], theme.stopSignage); ``` ![black on white stop sign](STOP-black-on-white.svg) ![white on red stop sign](STOP-white-on-red.svg) ### `svgThemer.setRuleSvgProps(theme, setSvgProps = svgThemer.setSvgProps, propName = undefined)` Themes an SVG style rule property. Similar to calling `setSvgProps` on an `` element directly, except the context is expected to be an `CSSStyleRule` (shown here using encoded data): ```html ``` ```js var rule = document.styleSheets[0].cssRules[0]; // first rule in first stylesheet svgThemer.setRuleSvgProps.call(rule, theme.stopSignage); ``` In this case, all referencing elements are themed simultaneously: ![white on red stop sign](STOP-white-on-red.svg) ![white on red stop sign](STOP-white-on-red.svg) 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` (`...`) * `HTMLImageElement (``) with underlying SVG markup in the `src` attribute * `CSSStyleRule` (a stylesheet rule) with SVG markup in a particular style's `url` function The method returns the object for chaining. ```js var svgEl = document.querySelector('svg'); var imgEl = document.querySelector('img'); var rule = document.styleSheets[0].cssRules[0]; // first rule in first stylesheet 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`](#apisetsvgpropstheme). It is mixed in as `setSvgProps`. Theme can then be applied the the above objects as follows: ```js svgEl.setTheme(theme); // calls setSvgProps on itself imgEl.setTheme(theme); // calls setImgSvgProps on itself styleRule.setTheme(theme); // calls setRuleSvgProps on itself ``` 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: ```js svgThemer.mixin(object).setTheme(theme); ``` `setTheme` also returns its context so you can make the following assignment: ```js var imgEl = svgThemer.mixin(document.querySelector('img')).setTheme(theme); ``` ## UAT Tested in the following browsers: * macOS 10.13.6 (17G65) * Chrome 70.0.3538.77 * Safari 12.0 (13606.2.11) * Opera 56.0.3051.102 * Firefox 63.0.1 * Windows 10 * Chrome 70.0.3538.77 * Opera 56.0.3051.70 * Firefox 63.0.0.6865 * Edge 42.17134.1.0 * Internet Explorer 11.345.17134.0 (Update 11.0.90) ## Version History See [releases](https://github.com/joneit/svg-themer/releases).