@abw/svg-icon-librarian
v1.0.9
Published
Utility for creating a library of SVG icons.
Downloads
33
Readme
SVG Icon Librarian
This module can be used to create a library of SVG icons. The library is a
Javascript file which can be imported into any framework or vanilla Javascript
code base. It contains the raw data extracted from source icons that you can
then use to display in svg
elements.
It was originally designed as a lightweight replacement for the @fortawesome/fontawesome-svg-core module. FontAwesome is awesome, and this isn't in any way suggesting that it isn't as awesome as it ever was. But I had some problems getting it to work with Static Site Generation (SSG) in a Next.js project and that led me down the path to writing this module.
The librarian can extract icons from the free FontAwesome icon sets (solid
,
regular
and brands
) so that you can build your own icon library that only
contains the icons that you need.
It also allows you to define your own custom SVG icons which are added into the library. You can add SVG files to a directory, run the svg-icon-librarian.js script, and it will take care of the rest.
It supports icons with multiple paths, style attributes and opacity, allowing you to include duotone icons and other icons with transparent elements or complex intersecting paths.
Getting Started
You can add the module to your own Javascript project to automate the process of building your own custom icon library.
Add the module as a devDependency
using your favourite package manager.
npm
npm add --save-dev @abw/svg-icon-librarian
pnpm
pnpm add --save-dev @abw/svg-icon-librarian
yarn
yarn add --dev @abw/svg-icon-librarian
You can then add a command to the scripts
section of your package.json
file, something like this:
"scripts": {
"icons": "svg-icon-librarian -f icons/config.yaml -c icons/custom -o lib/configicons.js -y"
}
To generate the icon libary (substitute npm
for yarn
or pnpm
if that's
what you're using)
npm icons
Try Out the Examples
A good way to understand how it works is to try out the examples. For that you should clone the github repository:
git clone https://github.com/abw/svg-icon-librarian-js.git
Then install the dependencies using your favourite package manager.
npm install # or yarn or pnpm
The examples directory contains a number of examples showing you how the library can be used.
Building an Icon Library
Run the bin/svg-icon-librarian.js script to build your own SVG icon library.
bin/svg-icon-librarian.js \
-f /path/to/your/icons-config-file.yaml \
-c /path/to/your/own/svg/icon/files/custom \
-o /path/to/output/lib/icons.js \
-y
The -f
(or --file
) option is used to specify a configuration file. You
can use an absolute path as shown here or a path relative to your current
directory. The -c
(or --custom
) option allows you to specify a directory
containing SVG icon files that you want to use as custom icons. These will be
available to use as an icon set with the same name as the directory (custom
in this example). The -o
(or --output
) option is used to specify where
you want the generated icon library file to be written to. Be warned that
this will overwrite any existing file.
The -y
(or --yes
) option telling the script to just get on and do it
without prompting you to confirm any of the values.
You can run the script with the -h
(or --help
) option to see help on all
the commands.
$ bin/svg-icon-librarian.js -h
Usage: svg-icon-librarian.js [options]
Generates a library of SVG icons.
Options:
-V, --version output the version number
-y, --yes Accept default answers
-v, --verbose Verbose output
-q, --quiet Quiet output
-f, --file <text> Configuration file
-c, --custom <text> Custom directory
-o, --output <text> Output file
-h, --help display help for command
Instead of using command line arguments you can just run the script and it will prompt you to enter the configuration options.
$ ../bin/svg-icon-librarian.js
✔ Where is the configuration file? … icons/config.yaml
✔ Where is the directory of custom SVG icons? … icons/custom
✔ Where should the output file be written? … lib/config/icons.js
✓ Wrote icon library to lib/config/icons.js
Configuration File Format
You'll need to define a configuration file to specify which icons you want to include in your library. A typical configuration file is shown below.
It can be a .yaml
or .json
file. We're using YAML here so we can embed
some comments for readability.
sets:
# icons to import from FontAwesome solid free set
solid:
- angle-left
- angle-right
- angle-down
- angle-up
# icons to import from FontAwesome regular free set
regular:
- circle
- circle-dot
# icons to import from FontAwesome brands free set
brands:
- github
# Import all custom icons
custom: '*'
# define your own icon names mapped to any icons from the solid, regular,
# brands or your custom icon sets
icons:
ok: solid:circle-check
frown: regular:face-frown
square: regular:square
ring: regular:circle
badger2: custom:badger-duo
The sets
section allows you to specify the names of the solid
,
regular
and brands
icons to be imported from the corresponding
FontAwesome sets. In this case they will be given the same icon name
in your icon library as the source (e.g. angle-left
, angle-right
, etc).
sets:
# icons to import from FontAwesome solid free set
solid:
- angle-left
- angle-right
- angle-down
- angle-up
# icons to import from FontAwesome regular free set
regular:
- circle
- circle-dot
# icons to import from FontAwesome brands free set
brands:
- github
If you have specified a custom icon directory then the icons contained in
that directory will be available as an icon set of the same name as that
directory. If the directory for your custom icons is appropriately called
custom
then the icon set will be called custom
.
The custom
set (or whatever your directory is named) comprises all the
.svg
files that are read from the that directory. The icons should be
specified without the .svg
extension, e.g. the badger-duo.svg
file has
the icon name badger-duo
. You can explicitly list out the custom
icons
that you want to import. Or if you want to import all the icons from the
custom
set, or indeed any other set, then specify *
for the set.
sets:
# ...etc...
# Import all custom icons
custom: '*'
So if your custom icon directory is named my-project
then the
icon set containing those icons will be called my-project
instead.
sets:
# ...etc...
# Import all icons from the badgers directory
my-project: '*'
The icons
section allows you to define your own names for icons. You can
create aliases for icons that you've already specified in one of the sets
or you can include new icons.
# define your own icon names mapped to any icons from the solid, regular,
# brands or your custom icon sets
icons:
ok: solid:circle-check
frown: regular:face-frown
square: regular:square
ring: regular:circle
badger2: custom:badger-duo
In this example, the generated library will contain an ok
item sourced
from the circle-check
icon in the solid
set, a frown
item source from
face-frown
in the regular
set and so on.
Writing Your Own Wrapper Code
The module exports a commandLine() function which provides the implementation for the bin/svg-icon-librarian.js script. You can write your own Javascript script to call this if you like.
import { commandLine } from '@abw/svg-icon-libarian';
async function main() {
await commandLine();
}
main();
However you probably don't want or need to deal with all the command line options as your configuration file, custom icons and output directories are probable fixed.
In that case you can use the build() function to cut to the chase.
import { build } from '@abw/svg-icon-libarian';
const configFile = 'icons/config.yaml';
const customDir = 'icons/custom';
const outputFile = 'lib/config/icons.js';
build({ configFile, customDir, outputFile });
You don't need to use a config file if you would rather just define the
icons you want in that file. In that case use the selectIcons
option
instead of configFile
. The data format should be the same as for the
configuration file.
import { build } from '@abw/svg-icon-librarian';
const customDir = 'icons/custom';
const outputFile = 'lib/config/icons.js';
const selectIcons = {
sets: {
solid: [
'angle-left', 'angle-right', 'angle-down', 'angle-up'
],
regular: [
'circle', 'circle-dot',
],
brands: [
'github'
],
custom: '*'
},
icons: {
ok: 'solid:circle-check',
frown: 'regular:face-frown',
square: 'regular:square',
ring: 'regular:circle',
badger2: 'custom:badger-duo',
}
};
build({ iconSets, configFile, customDir, outputFile });
You can also load up an existing icon set that you've already created and
include it as an icon set in your new library as the iconSets
option.
import { build } from '@abw/svg-icon-librarian';
import { iconSets } from './lib/another-icon-library.js'
const configFile = 'icons/config.yaml';
const customDir = 'icons/custom';
const outputFile = 'lib/config/icons.js';
build({ iconSets, configFile, customDir, outputFile });
Generated Icon Library
The generated library (example/lib/icons.js
in this example) contains a
named export iconSets
which contains the definitions of the icons in
different sets.
export const iconSets = {
"solid": {
"angle-left": {
"width": 256,
"height": 512,
"path": "M9.4 233.4c-12.5 12.5-12.5 32.8 ...etc..."
},
"angle-right": {
"width": 256,
"height": 512,
"path": "M246.6 233.4c12.5 12.5 12.5 32.8 ...etc..."
},
// ..etc...
}
}
It then defines the icons
named export (which is also the default export
)
which maps your chosen icon names to those sources.
export const icons = {
"angle-left": iconSets["solid"]["angle-left"],
"angle-right": iconSets["solid"]["angle-right"],
}
The reason for this indirection is that it allows you to create multiple aliases to the same icon definition. It also makes it possible to build new icons sets on top of existing ones.
All icons define the width
and height
. This is taken from the viewBox
in the original SVG file, or the width
and height
attributes if there
isn't a viewBox
attribute.
For simple icons (including all those from FontAwesome), the path
will be
defined as a string of SVG path data.
Using the Icon Library
You can use the existing FontAwesome react component to render icons, but you'll need to write a bit of wrapper code to convert the icon data format back into what it's expecting.
Something like this:
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import icons from '../path/to/your/icons.js'
const Icon = ({name, ...props}) => {
const icon = icons[name];
if (! icon) {
console.log(`Invalid icon name:${name}`);
return;
}
const faIcon = {
prefix: 'fas',
iconName: name,
icon: [
icon.width,
icon.height,
[ ],
'',
icon.path
]
}
return <FontAwesomeIcon icon={faIcon} {...props}/>
}
Creating Your Own Icon Component
You can generate an svg
element in a web page using the data defined in the
libary. You just need to create an svg
element with a path
element inside
it. For example, a very simple React component might look something like this:
import React from 'react'
import icons from '.../path/to/your/generated/icons.js'
const Icon = ({name, ...props}) => {
const icon = icons[name];
if (! icon) {
console.log(`Invalid icon name:${name}`);
return;
}
return (
<svg
className="svg-inline--fa"
aria-hidden="true" focusable="false"
role="img" xmlns="http://www.w3.org/2000/svg"
viewBox={`0 0 ${icon.width} ${icon.height}`}
>
<path d={icon.path} fill="currentColor"/>
</svg>
)
}
export default Icon
And you would use it like so:
import React from 'react'
import Icon from '.../path/to/the/above/Icon.jsx'
const YourComponent = () =>
<Icon name="badger"/>
export default YourComponent;
You'll need to include the FontAwesome CSS file in your web site, or add it into your main stylesheet. The CSS file for FontAwesome version 6.3.0 is included as styles/fontAwesome.css but you may want to check their repository to see if there's a more recent version: https://github.com/FortAwesome/Font-Awesome/blob/6.x/css/svg-with-js.css
For a more complete implementation which includes support for the transform
property see the Icon component
in badger-ui.
Multi Path and Styled Icons
The library supports icons that have multiple paths, style attributes (on
both the svg
element and any path
elements) and opacity
. This isn't the
case for FontAwesome icons (at least not those in the solid and regular sets)
but you might want to include your own custom icons that do.
If the svg
element has a style
attribute then it will be included in the
icon definition in the library.
export const iconSets = {
"custom": {
"icon-with-style": {
"width": 256,
"height": 512,
"style": "fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2",
"path": "M9.4 233.4c-12.5...etc..."
},
}
// ...etc...
}
If the path
has a style
or opacity
attribute then the path
will be an
object containing the d
(path data) and style
and/or opacity
properties.
The opacity
might also be defined as part of the style
attribute, e.g.
fill-opacity: 0.15
.
export const iconSets = {
"custom": {
"icon-with-stylish-path": {
"width": 256,
"height": 512,
"path": {
"d": "M9.4 233.4c-12.5...etc...",
"style": "fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2",
"opacity": "0.5",
}
},
}
// ...etc...
}
If the icon has multiple paths then the paths
property will be defined as
an array of paths. Each path can either be a string containing the path
data, or an object as described above.
export const iconSets = {
"custom": {
"icon-with-multiple-paths": {
"width": 256,
"height": 512,
"paths": [
// path with data and style
{
"d": "M9.4 233.4c-12.5...etc...",
"style": "fill-opacity:0.15;"
},
// path that is just data
"M101.675,0C102.276,0 102.873,0.004 103.479,0.011C149.73",
]
},
}
// ...etc...
}
If you're using icons like this then you'll need to code your display
components accordingly. It should handle either a path
or paths
, and
each path
can be a string or an object.
I suspect this will come back to bite me one day, but it seemed sensible to try and optimise the size of the library as much as possible at the cost of making the display component slightly more complicated.
Author
Andy Wardley https://github.com/abw