Lion Logo Lion Fundamentals Guides Components Blog Toggle darkmode

Icon: Overview

The icon system provides a way of defining icon sets which are lazily loaded on demand when icons are rendered on the page. This way icon imports do not block the initial render of your application, and you don't need to worry about carefully coordinating the dynamic imports of your icons.

Icon format

For security reasons, icons are defined using lit-html templates to guarantee XSS safety:

import { html } from 'lit';

export default html` <svg focusable="false" ...>...</svg> `;

The icon can also be a function. In this case, it's possible to reuse the icons if a rendering mechanism different from lit-html is used to process a string.

export default tag => tag`
  <svg focusable="false" ...>...</svg>
`;

This ensures the same version of lit-html and the icon system used. This is the recommended approach.

Icon accessibility

On IE11 and some versions of Edge, SVG elements are focusable by default. Setting focusable="false" on the SVG prevents this.

Iconsets

Requesting many individual icons can be bad for performance. We should, therefore, group related icons together in icon sets.

Iconsets are managed by the IconManager, where you can register icon resolvers to resolve an icon id to the correct icon.

Creating an icon resolver

An icon resolver is a function that receives the icon set and the icon name and subsequently returns the icon to be rendered.

The most common use case is for this function to be async, and import the icon set on demand:

function resolveLionIcon(iconset, name) {
  switch (iconset) {
    case 'bugs':
      return import('./icons/iconset-bugs.js').then(module => module[name]);
    case 'space':
      return import('./icons/iconset-space.js').then(module => module[name]);
    case 'misc':
      return import('./icons/iconset-misc.js').then(module => module[name]);
    default:
      throw new Error(`Unknown iconset ${iconset}`);
  }
}

An icon resolver can also be synchronous, returning the icon directly:

const icons = {
  coolIcons: {
    'my-icon': html` <svg>... icon code ...</svg> `,
  },
};

function resolveLionIcon(iconset, name) {
  return coolIcons[iconSets][name];
}

Registering an icon resolver

Icon resolvers are registered in the IconManager on a namespace. There can be only one resolver per namespace, so make sure they are unique. A good idea is to use your package name as the namespace.

import { icons } from '@lion/ui/icon.js';

function resolveLionIcon(iconset, name) {
  switch (iconset) {
    case 'bugs':
      return import('./icons/iconset-bugs.js').then(module => module[name]);
    case 'space':
      return import('./icons/iconset-space.js').then(module => module[name]);
    case 'misc':
      return import('./icons/iconset-misc.js').then(module => module[name]);
    default:
      throw new Error(`Unknown iconset ${iconset}`);
  }
}

icons.addIconResolver('lion', resolveLionIcon);

Using icon resolvers

After register an icon resolver, icons can be resolved from the manager:

import { icons } from '@lion/ui/icon.js';

const spaceshipIcon = await icons.resolveIcon('lion', 'space', 'alienSpaceship');

Icons can also be resolved from a single string, using the pattern: namespace:iconset:name:

import { icons } from '@lion/ui/icon.js';

const spaceshipIcon = await icons.resolveIconForId('lion:space:alienSpaceship');

This syntax is used by the lion-icon component, where the id can be set on an attribute:

<lion-icon icon-id="lion:space:alienSpaceship"></lion-icon>
<lion-icon icon-id="lion:misc:arrowLeft"></lion-icon>