Skip to content
Sasha Boginsky edited this page Jul 24, 2019 · 12 revisions

About

This wiki serves as a step by step guide for using our workflow, which is based on the Material Design documentation page for creating SVG Sprites.

Most of our icons are from the Material Design library, but since we store individual icons locally, they are not limited to it. Feel free to source icons from any library, while preferring Material Design for effortless consistency.

Adding an SVG Icon

Sourcing the Icon

The collection of Material Design icons can be found at material.io/tools/icons

  1. To use an icon, download it as SVG from their website. Presently, we keep our toolbar icons at 18px, so you should download the 24px version at 48dp.

  2. Drag the file into the assets/icons/svg directory.

  3. Rename the file if necessary: the name of the svg file (excluding extension) will become the id attribute used to reference it, so following these naming conventions will make using our workflow simpler later on:

    • lowercase
    • snake_case
    • keep it short: ex., Material Design's delete forever icon will download with the filename baseline-delete_forever-24px.svg. We renamed this to be delete_forever.svg.

Don't worry about adding or removing any attributes to the SVG code, as the next step will handle this for you.

Minifying and Compiling

We use the grunt-svgmin and grunt-svg-sprite plugins to minify the SVG files for the web and compile them into an SVG Sprite.

  1. Once you add your icon to the svg folder, run the following to have Grunt minify it (outputs a minified version to the assets/icons/svg-min folder) and re-compile the sprite sheet to include it (outputted as assets/icons/symbol/sprite.symbol.svg):
$ grunt icons
  • Grunt will also recompile an example HTML file assets/icons/symbol/sprite.symbol.html. If you open it in your browser, you will find a visual overview of the icons in it.

Rendering the Icon

sprite.symbol.html contains a nicely formatted code overview of the icons in the sprite sheet:

<!-- one <symbol> for each icon in the sprite sheet -->
<svg width="0" height="0" style="position:absolute">
   <symbol viewBox="0 0 18 18" id="border_clear"><path d="M5.25 3.75h1.5v-1.5h-1.5v1.5zm0 6h1.5v-1.5h-1.5v1.5zm0 6h1.5v-1.5h-1.5v1.5zm3-3h1.5v-1.5h-1.5v1.5zm0 3h1.5v-1.5h-1.5v1.5zm-6 0h1.5v-1.5h-1.5v1.5zm0-3h1.5v-1.5h-1.5v1.5zm0-3h1.5v-1.5h-1.5v1.5zm0-3h1.5v-1.5h-1.5v1.5zm0-3h1.5v-1.5h-1.5v1.5zm6 6h1.5v-1.5h-1.5v1.5zm6 3h1.5v-1.5h-1.5v1.5zm0-3h1.5v-1.5h-1.5v1.5zm0 6h1.5v-1.5h-1.5v1.5zm0-9h1.5v-1.5h-1.5v1.5zm-6 0h1.5v-1.5h-1.5v1.5zm6-4.5v1.5h1.5v-1.5h-1.5zm-6 1.5h1.5v-1.5h-1.5v1.5zm3 12h1.5v-1.5h-1.5v1.5zm0-6h1.5v-1.5h-1.5v1.5zm0-6h1.5v-1.5h-1.5v1.5z"/></symbol>
   <symbol viewBox="0 0 18 18" id="border_outer"><path d="M9.75 5.25h-1.5v1.5h1.5v-1.5zm0 3h-1.5v1.5h1.5v-1.5zm3 0h-1.5v1.5h1.5v-1.5zm-10.5-6v13.5h13.5V2.25H2.25zm12 12H3.75V3.75h10.5v10.5zm-4.5-3h-1.5v1.5h1.5v-1.5zm-3-3h-1.5v1.5h1.5v-1.5z"/></symbol>
   <symbol viewBox="0 0 18 18" id="crop_rotate"><path d="M5.603 16.117C3.15 14.947 1.394 12.57 1.125 9.75H0C.383 14.37 4.245 18 8.963 18c.172 0 .33-.015.495-.023L6.6 15.113l-.997 1.005zM9.037 0c-.172 0-.33.015-.495.03L11.4 2.888l.998-.998a7.876 7.876 0 0 1 4.477 6.36H18C17.617 3.63 13.755 0 9.037 0zM12 10.5h1.5V6A1.5 1.5 0 0 0 12 4.5H7.5V6H12v4.5zM6 12V3H4.5v1.5H3V6h1.5v6A1.5 1.5 0 0 0 6 13.5h6V15h1.5v-1.5H15V12H6z"/></symbol>
   <symbol viewBox="0 0 18 18" id="delete_forever"><path d="M4.5 14.25c0 .825.675 1.5 1.5 1.5h6c.825 0 1.5-.675 1.5-1.5v-9h-9v9zm1.845-5.34l1.058-1.058L9 9.443l1.59-1.59 1.058 1.058-1.59 1.59 1.59 1.59-1.058 1.058L9 11.558l-1.59 1.59-1.058-1.058 1.59-1.59-1.597-1.59zM11.625 3l-.75-.75h-3.75l-.75.75H3.75v1.5h10.5V3h-2.625z"/></symbol>
   <symbol ..... > <!-- and so on -->
</svg>
  1. To render a new icon, copy it's corresponding <symbol> element from sprite.symbol.html and add it to the chunk of <symbol>s in src/edit/tools/IconSet.js.

    • The <symbol>s in this set will later be injected via JavaScript during runtime.
L.IconSet = L.Class.extend({

  _svg: '<svg xmlns="http://www.w3.org/2000/svg">',

  render: function() { 
    this.addSymbols();
    return this._svg; 
  },

  addSymbols: function() {
    this._svg +=
      '<symbol viewBox="0 0 18 18" id="border_clear"><path d="M5.25 3.75h1.5v-1.5h-1.5v1.5zm0 6h1.5v-1.5h-1.5v1.5zm0 6h1.5v-1.5h-1.5v1.5zm3-3h1.5v-1.5h-1.5v1.5zm0 3h1.5v-1.5h-1.5v1.5zm-6 0h1.5v-1.5h-1.5v1.5zm0-3h1.5v-1.5h-1.5v1.5zm0-3h1.5v-1.5h-1.5v1.5zm0-3h1.5v-1.5h-1.5v1.5zm0-3h1.5v-1.5h-1.5v1.5zm6 6h1.5v-1.5h-1.5v1.5zm6 3h1.5v-1.5h-1.5v1.5zm0-3h1.5v-1.5h-1.5v1.5zm0 6h1.5v-1.5h-1.5v1.5zm0-9h1.5v-1.5h-1.5v1.5zm-6 0h1.5v-1.5h-1.5v1.5zm6-4.5v1.5h1.5v-1.5h-1.5zm-6 1.5h1.5v-1.5h-1.5v1.5zm3 12h1.5v-1.5h-1.5v1.5zm0-6h1.5v-1.5h-1.5v1.5zm0-6h1.5v-1.5h-1.5v1.5z"/></symbol>' +
      '<symbol viewBox="0 0 18 18" id="border_outer"><path d="M9.75 5.25h-1.5v1.5h1.5v-1.5zm0 3h-1.5v1.5h1.5v-1.5zm3 0h-1.5v1.5h1.5v-1.5zm-10.5-6v13.5h13.5V2.25H2.25zm12 12H3.75V3.75h10.5v10.5zm-4.5-3h-1.5v1.5h1.5v-1.5zm-3-3h-1.5v1.5h1.5v-1.5z"/></symbol>'
      '<symbol ..... >' // and so on
    ;
  }
});
  1. In the file where you are defining your new tool (src/edit/tools/Leaflet.DistortableImage.ControlBar.js or src/edit/tools/Leaflet.DistortableImage.PopupBar.js) pass an additional svg: true option to options.toolbarIcon and pass the id selector from the <symbol> element with a leading "#" to the html option. For example:
var Deletes = L.EditAction.extend({
    initialize: function(map, overlay, options) {
      var use = '#delete_forever';

      options = options || {};
      options.toolbarIcon = {
        svg: true,
        html: use,
        tooltip: 'Delete Images'
      };
...

<symbol> elements are rendered with a corresponding <use> element that includes a reference to it. Your icon won't actually render until you add its corresponding <use>, even if the <symbol> has already been injected.


Styling

In src/edit/tools/EditAction.js, you will find the shell of the <use> tags that will render our <symbols>:

_svgIconHelper: function(ref) {
  var subclass = ref.slice(1, ref.length);

  return (
    '<svg class="ldi-icon ' + subclass + '"role="img" focusable="false">' + 
    '<use xlink:href="' + ref + '"></use>' + 
    '</svg>'
  );
},

/* note on the `xlink:href` attribute: As of svg2 it is deprecated in favor of just `href`, 
   but you should use it for now as there is not full browser support for it */

Notice how all <symbol>s are nested under one <svg> tag, but each <use> will have an additional svg tag that is its own. We added the class="ldi-icon" attribute to it so that we can easily set some default properties in CSS for all our icons at once:

svg.ldi-icon {
  width: 18px;
  height: 18px;
  vertical-align: middle;
  fill: #0078A8;  /* the light blue-ish color that most of our toolbar icons are set in. */
}

If you wanted to say, update an individual images width / height or fill to override the one set via the broader "ldi-icon" declaration, you'll notice that we also add a subclass in the above _svgIconHelper method that is the same as the <use> xlink-href but without the "#".

Use this subclass to set attributes on specific individual icons by declaring it in the CSS file after the general .ldi-icon declaration.

For ex., we made the delete icon red in our CSS file with:

svg.delete_forever {
  fill: #c10d0d;
}

Note that you cannot override any attributes present in the original svg file using CSS. In case we didn't remove one during the minify step that you would like to override, remove it from the original svg file and recompile before updating it in css.

Clone this wiki locally