DEV Community

Paul
Paul

Posted on • Originally published at boldoak.design

Custom icons in the WordPress block editor

It's been a while since I've done some WordPress work, so this was a nice change of pace. I read on the WP Tavern about a new plugin called JVM Gutenberg Rich Text Icons, which allows icons to be used in rich text fields (e.g. paragraph and headings, etc).

I'm a big fan of Nucleo icons, some of which I already use in my WordPress theme Garrick, but I've thought for a while now that it would be nice to have access to more icons as inline elements -- not just outside the editor or as block-level elements. This plugin fills that gap nicely, but it comes with Font Awesome 4 out of the box. Fortunately, it also provides some filters that allow for overriding this default functionality.

Creating a Nucleo icon font

I'm getting ahead of myself, a little bit. Before I can override Font Awesome, I need a replacement icon font and stylesheet. Because Nucleo is a paid icon library and contains over 30,000 icons, it doesn't have a ready-made icon font the way Font Awesome 4 does. As a result, I followed Nucleo's directions to create my own. As of this writing, the font I exported contains about 2,700 line icons. (The two main styles to choose from are solid and line, since full-color and duotone icons aren't possible with an icon font.)

Using the icon font

The resulting exported fonts and stylesheet can be used directly in my WordPress child theme. Since I have a webpack (Laravel Mix, actually) build process, I put the files in these locations:

/resources/fonts/
/resources/scss/nucleo.scss
Enter fullscreen mode Exit fullscreen mode

My build process copies the fonts and compiles the styles here, respectively:

/dist/fonts/
/dist/css/nucleo.css
Enter fullscreen mode Exit fullscreen mode

Since the stylesheet already references the font files, I just need to override the WordPress plugin's stylesheet with its provided filter:

add_filter( 'jvm_richtext_icons_css_file', function( $cssfile ) {
  $cssfile = get_stylesheet_directory_uri() . '/dist/css/nucleo.css';
  return $cssfile;
} );
Enter fullscreen mode Exit fullscreen mode

This stylesheet will get loaded in two places: the block editor and the rendered website.

Selectable icons in the block editor

The stylesheet, however, is only half the puzzle. There still needs to be a way for the user to know what icons are available to select in the block editor. To solve this, a separate file is needed, which lists the class names. The plugin's default file is in json format, which looks like this (albeit truncated here for readability):

[
  "fa fa-address-book",
  "fa fa-address-card",
  "fa fa-adjust"
]
Enter fullscreen mode Exit fullscreen mode

To be perfectly blunt, there is no easy or direct way to recreate this list for my Nucleo icons. The app does have a JSON export option, but it exports the SVG content of each icon, not just the names:

{
  "icons": [
    {
      "name": "piano",
      "content": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"24\" height=\"24\">...</svg>",
      "set_id": 1
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

The icon font export contains an unformatted unicode list of all the icons, which is actually pretty close to what I want (again, edited for brevity):

{ "nucleo-icon-digital-key": 59906, "nucleo-icon-line-chart": 59907 }
Enter fullscreen mode Exit fullscreen mode

Instead of an object with key-value paired properties, I just need a list of class named. To get this, I had to restructure the file a bit. I don't know of a way to completely automate this, but the process isn't so complicated that it can't be duplicated if I decide to add more icons.

First, I opened the file in Visual Studio Code and formatted the file (right-click > Format Document, or Shift+Alt+F):

{
  "nucleo-icon-digital-key": 59906,
  "nucleo-icon-line-chart": 59907
}
Enter fullscreen mode Exit fullscreen mode

I added a comma to the last item, then selected all the lines inside the curly brackets. From the Selection menu, I clicked Add Cursors to Line Ends (Shift+Alt+I) and deleted the comma, unicode value, and colon:

{
  "nucleo-icon-digital-key"
  "nucleo-icon-line-chart"
}
Enter fullscreen mode Exit fullscreen mode

While the multiple cursor mode was still active, I added a comma which added it to all the lines. Then I removed the comma from the last line and changed the curly brackets to square. I saved this file alongside my other pre-build files:

/resources/json/nucleo.json
Enter fullscreen mode Exit fullscreen mode

When I run my build process, it's placed in the directory I want to reference from the filter.

/dist/json/nucleo.json
Enter fullscreen mode Exit fullscreen mode

Just JSON file things

Now here's where things got hairy. According to the documentation, I should have been able to use the following filter to override the plugin's json file:


add_filter( 'jvm_richtext_icons_iconset_file', function ( $file ) {
    $file = get_stylesheet_directory() . '/dist/json/nucleo.json';
    return $file;
} );
Enter fullscreen mode Exit fullscreen mode

But this never worked. After some digging, I found this logic in the plugin:

$iconFile = apply_filters('jvm_richtext_icons_iconset_file', $iconFile);

$icons = [];
if (file_exists($iconFile)) {
  $iconData = file_get_contents($iconFile);
  $icons = json_decode($iconData);

  $icons = apply_filters('jvm_richtext_icons_iconset', $icons);            
}
Enter fullscreen mode Exit fullscreen mode

For whatever reason, the file_exists() function call was returning false, even though I confirmed that my new json file was being loaded successfully. I did notice, however, that there was another filter where I could override things. So I updated my filter logic to use the jvm_richtext_icons_iconset filter instead:

add_filter( 'jvm_richtext_icons_iconset', function( $icons ) {
  $file = get_stylesheet_directory_uri() . '/dist/json/nucleo.json';
  $icons = [];
  $iconData = file_get_contents( $file );
  $icons = json_decode( $iconData );
  return $icons;
} );
Enter fullscreen mode Exit fullscreen mode

I already knew my file existed, so instead of overriding the file, I overrode the collection being built using the file. And with that, I have successfully replaced Font Awesome with a custom Nucleo icon library for use within the WordPress block editor.


I do want to mention that in my WordPress theme, I have implemented icons by using individual SVGs instead of by icon font. By doing this, I avoid a couple problems which this plugin cannot in its current implementation address. Firstly, the entire icon library is loaded (via its fonts and stylesheet) with every page view. Secondly, there is a flash of the unsupported character in place of each icon until the fonts and stylesheet are loaded and parsed.

It is my hope that the plugin author will eventually address this and add support for loading SVGs in place of icon fonts. As it is, this plugin is the only one of its kind which allows for inline icons within the WordPress block editor.

Latest comments (0)