npm package: @open-xchange/vite-plugin-icon-sprite
Description: Vite plugin that builds icon sprites from SVG or PNG icon files
Latest version: 1.3.0

# @open-xchange/vite-plugin-icon-sprite

A Vite plugin that builds icon sprites from multiple SVG or PNG icon files.

## Usage

This plugin collects multiple SVG or PNG image files, assembles them into a single icon sprite file, and provides virtual modules that can be imported in source code.

In Vite server mode, the icon sprite files will be generated on demand. When bundling the project, all icon sprite files referenced in source code will be generated and bundled.

It is possible to instantiate this plugin repeatedly, e.g. in order to generate an SVG sprite and a PNG sprite in the same project.

### Common Options

Common configuration options for SVG sprites and PNG sprites.

#### Option `format`

- Type: `'svg' | 'png'`
- _required_

The file format specifier of the icon sprite, and the source images. Depending on the value of this option, the plugin expects various file-format specific options (see below).

#### Option `imagesPath`

- Type: `string`
- _required_

Path to root directory with all image resource files to be processed.

#### Option `mappingPath`

- Type: `string`
- _required_

Path to the JSON or YAML configuration file containing the mapping between icon identifiers and SVG source file names (see below).

### SVG Sprites

The plugin collects multiple SVG files, and generates a virtual import for an SVG sprite file with multiple `<symbol>` elements that can be imported from source code.

Example how to consume the SVG sprite in source code:

```ts
// path/to/source.ts

import svgSprite from 'virtual:svg/my-icons.svg'

// insert the SVG sprite into the DOM to be able to refer to the <symbol> elements
document.createElement('div').innerHTML = svgSprite

// create an SVG element referring to an icon in the sprite
function createIcon(id: string): SVGElement {
  const useEl = document.createElementNS('http://www.w3.org/2000/svg', 'use')
  useEl.setAttribute('href', '#my-icon')
  const iconEl = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
  iconEl.append(useEl)
  return iconEl
}
```

#### Option `spriteName`

- Type: `string`
- _required_

The module name of the SVG sprite to be generated (with '.svg' extension).

The SVG markup of the sprite can be imported from the virtual module path `'virtual:svg/[spriteName]'`.

#### Option `idPrefix`

- Type: `string`
- _optional_

A prefix to be added to all icon identifiers declared in the mapping file. By default, no prefix will be added.

#### SVG Sprite Example

```ts
// vite.config.ts

import { defineConfig } from 'vite' // or 'vitest/config'
import spritePlugin from '@open-xchange/vite-plugin-icon-sprite'

export default defineConfig(() => {
  plugins: [
    spritePlugin({
      format: 'svg',
      imagesPath: 'src/icons/images',
      mappingPath: 'src/icons/svg-mapping.yaml',
      spriteName: 'icons.svg',
      idPrefix: 'svg-',
    }),
  ],
})
```

- Collects all SVG files in the directory `src/icons/images`.
- Uses the icon mapping in `src/icons/svg-mapping.yaml`.
- Creates a virtual import `'virtual:svg/icons.svg'`.
- Prefixes all icon identifiers with `svg-`, e.g. the icon key `my-icon` in the mapping file will result in the icon identifier `'svg-my-icon'` in source code.

### PNG Sprites

The plugin collects multiple PNG files, and generates a virtual imports for one or more sprite files with different icon sizes and a CSS file that can be imported from source code.

#### Option `cssName`

- Type: `string`
- _required_

The module name for the CSS file (with '.css' extension). The generated CSS markup can be imported from `'virtual:png/[cssName]'`.

#### Option `cssIconSize`

- Type: `number`
- _required_

Base size of all icons, in CSS pixels.

#### Option `cssIconPadding`

- Type: `number`
- Default: `0`

Additional padding around all icons to be generated in the sprites, in CSS pixels.

#### Option `cssIconSelector`

- Type: `string`
- Default: `'i.png-icon'`

The CSS selector for a PNG icon element to be used in all generated CSS rules.

#### Option `rootLocaleAttr`

- Type: `string`
- Default: `'lang'`

Name of the root element's attribute containing the locale identifier. Needed to generate CSS selectors for localized icons.

#### Option `spriteColorType`

- Type: `'source' | 'monochrome' | 'alpha'`
- Default: `'source'`

Specifies how to generate the sprite PNG files.

| Value | Description |
| - | - |
| `'source'` | The source PNGs will be copied into the generated sprites unmodified. They will contain three color channels, and an alpha channel. |
| `'monochrome'` | The generated sprites will be converted to monochrome. They will contain a gray channel and an alpha channel. |
| `'alpha'` | Only the alpha channels of the source PNGs will be copied into the generated sprites. They will contain a single gray channel representing the original alpha channels. |

#### Option `spriteFillType`

- Type: `'background' | 'mask'`
- Default: `'background'`

Specifies how the sprites are supposed to be used in CSS rules.

| Value | Description |
| - | - |
| `'background'` | The sprites will be attached via 'background-image'. |
| `'mask'` | The sprites will be attached via 'mask-image'. |

All related CSS properties (e.g. `background-position` vs. `mask-position` etc.) will be generated accordingly.

#### Option `sprites`

- Type: `Record<string, { factor: number; src: string }>`
- _required_

List of all icon sprites with different icon sizes to be generated.

- The keys of the dictionary are the module names of the PNG sprites (with '.png' extension). The generated PNG sprite can be imported from `'virtual:png/[key]'`.

- The values of the dictionary contain configuration options for the PNG sprite:

  | Option | Type | Default | Description |
  | - | - | - | - |
  | `factor` | `number` | _required_ | Icon scaling factor (a multiplier for plugin option `cssIconSize`). All source PNG files must have the effective pixel size (`cssIconSize * factor`). |
  | `src` | `string` | _required_ | The pattern used to build the path of the source PNG files. MUST contain the placeholder `[path]` that will be replaced with the base paths contained in the icon mapping file. |

#### PNG Sprite Example

```ts
// vite.config.ts

import { defineConfig } from 'vite' // or 'vitest/config'
import spritePlugin from '@open-xchange/vite-plugin-icon-sprite'

export default defineConfig(() => {
  plugins: [
    spritePlugin({
      format: 'png',
      imagesPath: 'src/icons/images',
      mappingPath: 'src/icons/png-mapping.yaml',
      cssName: 'icons.css',
      cssIconSize: 16,
      cssIconPadding: 1,
      cssIconSelector: 'i.my-icon',
      rootLocaleAttr: 'data-icon-locale',
      spriteColorType: 'alpha',
      spriteFillType: 'mask',
      sprites: {
        'icons1.png': { factor: 1, src: '[path]_16.png' },
        'icons2.png': { factor: 2, src: '[path]_32.png' },
      },
    }),
  ]
})
```

- Collects all PNG files in the directory `src/icons/images`. The images must exist in two sizes (16px and 32px), their file names must end with `_16.png` and `_32.png` respectively (according to the options `cssIconSize` and `sprites->factor`).
- Uses the icon mapping in `src/icons/png-mapping.yaml`.
- Creates the virtual imports `'virtual:svg/icons.css'`, `'virtual:svg/icons1.png'`, and `'virtual:svg/icons2.png'`.
- Adds one pixel padding around all icons in the PNG sprites.
- Generates CSS selectors for `<i>` elements with CSS class `my-icon`.
- Generates `:root[data-icon-locale]` CSS selectors for localized icons (i.e., the UI locale code must be stored in the root element's attribute 'data-icon-locale').
- Generates PNG sprites consisting of an 8-bit alpha channel only.
- Generates CSS rules using CSS mask (instead of background).

### Icon Mapping File

The plugin expects an icon mapping file (plugin option `mappingPath`) which is a JSON or YAML configuration file containing a mapping from arbitrary icon identifiers to the paths of the source images. The icon identifiers can be used later in source code to refer to a specific icon in the generated sprite.

- The name of the configuration file can be chosen freely.

- The configuration file must consist of a single object map.

- Each entry maps a unique icon identifier (used in source code) to a source image to be used for that icon.

- The source image file can be specified directly as string, or as dictionary for localized icons (more details below).

- Only the base name of the source image file must be specified relative to the configured root directory of the image files. It must not contain the image size suffix (_PNG only_), nor a file extension (`.svg` or `.png`). See examples below.

- Localized icons will be described by a dictionary mapping the ISO language identifiers (as comma-separated strings) to the image base names (as described above). The special locale code `'*'` is mandatory, and defines a default icon for unlisted locales. See examples below.

#### Icon Mapping Examples

In all examples, the configured image root directory (plugin option `imagesPath`) shall be `path/to/images`.

##### Example 1: Simple SVG Icons

- Assign the icon with the identifier `my-icon` to the SVG image `path/to/images/commons/icon1.svg`.
- Assign the icon with the identifier `other-icon` to the SVG image `path/to/images/commons/icon2.svg`.

```YAML
# svg-mapping.yaml

my-icon: commons/icon1
other-icon: commons/icon2
```

In source code, the icons can be used with the identifiers `'my-icon'` and `'other-icon'`.

##### Example 2: Simple PNG Icons

Assuming that the plugin will generate PNG sprites for icons with sizes of 16px and 32px.

- Assign the icon with the identifier `my-icon` to the PNG images `path/to/images/commons/icon1_16.png` (16x16 pixels) and `path/to/images/commons/icon1_32.png` (32x32 pixels).
- Respectively, assign an icon with the identifier `other-icon` to the PNG images `path/to/images/commons/icon2_*.png`.

The resulting mapping file looks exactly as the former mapping file for SVG icons:

```YAML
# png-mapping.yaml

my-icon: commons/icon1
other-icon: commons/icon2
```

##### Example 3: Localized Icons

- Assign the icon with the identifier `my-icon` to the SVG image `path/to/images/commons/icon1.svg` by default.
- Use the SVG image `path/to/images/commons/icon2.svg` in German and French UI instead.
- Use the SVG image `path/to/images/commons/icon3.svg` in Swedish UI instead.

```YAML
# svg-mapping.yaml

my-icon:
  '*': commons/icon1
  de,fr: commons/icon2
  sv: commons/icon3
```

The same applies to PNG icons as well.

#### Schema Validation

This package provides a JSON schema that can be used for validation in editors.

##### JSON Mapping File

Add the path to the schema file as property `'$schema'` to the mapping file:

```json
// mapping.json

{
  "$schema": "../../node_modules/@open-xchange/vite-plugin-icon-sprite/dist/mapping-schema.json",
  // ...
}
```

Adjust the number of parent path fragments according to the location of the mapping file in the project.

##### YAML Mapping File

Add the path to the schema file in a `yaml-language-server` directive to the mapping file:

```yaml
# mapping.yaml

# yaml-language-server: $schema=../../node_modules/@open-xchange/vite-plugin-icon-sprite/dist/mapping-schema.json

# ...
```

Adjust the number of parent path fragments according to the location of the mapping file in the project.

In VS Code, the plugin [redhat.vscode-yaml](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) needs to be installed to support this directive.

### Logging

By default, warning messages and error messages will be logged to the shell. The environment variable `PLUGIN_ICON_SPRITE_LOGLEVEL` can be used to change the log level of this plugin. Possible values are `info`, `warn`, `error`, and `silent`.