Adding Styles to the Magento 2 WYSIWYG Editor

Magento's WYSIWYG editor isn't particularly great and hasn’t been changed for a long, long time. However, in order to ensure that non-technical content managers can carry out their work effectively, it is important that it is integrated correctly with the site's theme. Most themes have custom CSS functionality that can be used on the site.

There are generally two ways to facilitate the communication of their use to content managers. One is to focus on the HTML classes and perhaps set up a document that would have information on how to use them on the site. The other approach is to automate it by building custom modules.

This article focuses on the former: adding classes to the styles menu in the Magento WYSIWYG editor. It’s an easy solution and doesn't take more than an after plugin to add options to the styles menu. The goal of this post is to provide details on what is required to add the options.

For a succinct tutorial on how to add styles to the WYSIWYG, go the end of the article.

Add classes to WYSIWYG editor Styles menu:

You will need to create a module first for handling this. In the etc/ folder of your module, create a di.xml file. The di.xml is where you will specify the class in your module that will be a plugin to the core class. For this, you will use an after Plugin. This plugin will be for the Magento\Cms\Model\Wysiwyg\Config class which outputs configuration that the WYSIWYG editor uses to display. The di.xml should look like the following example (where the module namespace is SwiftOtter\Editor):

<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="\Magento\Cms\Model\Wysiwyg\Config">
        <plugin name="add_wysiwyg_data" type="\SwiftOtter\Editor\Plugin\WysiwygConfig" sortOrder="10" />
    </type>
</config>

Now create a class in the Plugin namespace of your module. While that namespace isn't required, it is a convention. That class needs to have a public function called afterGetConfig(). Because the function starts with after, Magento will know that this plugin is to be run after the original method. The GetConfig part is the name of the method inside of the base class: Magento\Cms\Model\Wysiwyg\Config::getConfig(). This function should receive two parameters: $subject and $config. The $config is the return value of the base method. At its simplest, this is all we need in order to be able to add some extra data to it. Because $config is a \Magento\Framework\DataObject, I suggest type hinting that as well.

Inside the method, we will add data to the config. The style options need to be provided through the theme_advanced_styles property. If you want to dig into the editor a little more, the best place to start is the setup.js file which can be found here: /lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js. The theme_advanced_styles value expects a specific format: "Hero=hero;Hero Image=hero__image;". That will display two options within the styles menu: Hero and Hero Image. When adding data to $config with the addData() method, you will use an array like this where the value is a key that the Javascript class expects: ['theme_advanced_styles' => 'valueString...'].

However, there is a small catch here due to a nuance with the Javascript class for the WYSIWYG editor. You need this array to be the value of another array that has a key of settings. This is because of the way that the options are loaded into the Javascript class. The values of the settings object extend the default configuration options. As a result, the parameter within addData() should look like this: ['settings' => ['theme_advanced_styles' => 'valueString...']].

To make it easier to add options and understand what is currently available, I chose to use an associative array as the basis for the additional configuration. This is not necessary, but I find it much easier to work with than the awkward protocol for the styles. I used a concise array_map() to compile the associative array into an indexed array with the key and value separated with =:

$styles = array_map(function($title, $class) {
    return "{$title}={$class}";
}, array_keys($styleArray), array_values($styleArray));

With those things put together, the method call that actually adds the data looks like this:

$config->addData(["settings" => ["theme_advanced_styles" => implode("; ", $styles)]]);

Don't forget to return the updated $config object. A return value is actually required for after plugins. After that, clear the cache, and you should have options display under the "Styles" menu in the WYSIWYG editor. As a result, content managers will be able to easily add custom styles to markup.

Areas for improvements

While there are many areas in the Magento 2 editor that could be improved, one is that this doesn't allow you to add containers or control which elements it should be applied to. For example, if a user selects all the elements in a list (<ul><li /><li /></ul>), it will add the selected class to each individual list item. I would prefer to have a degree of control over whether it is applied to the <ul /> or even a surrounding <div /> added with that class.

The other thing that would be good to do is move the associative array that provides the classes out into a separate file—like a model.

Short Tutorial:

Step #1: Inside a module, add a plugin for the \Magento\Cms\Model\Wysiwyg\Config class:

<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="\Magento\Cms\Model\Wysiwyg\Config">
        <plugin name="add_wysiwyg_data" type="\SwiftOtter\Editor\Plugin\WysiwygConfig" sortOrder="10" />
    </type>
</config>

Step #2: Create a plugin class that will add data to the configuration data inside the file app/code/SwiftOtter/Editor/Plugin/WysiwygConfig.php:

namespace SwiftOtter\Editor\Plugin;

class WysiwygConfig {
    public function afterGetConfig($subject, \Magento\Framework\DataObject $config)
    {
        $styleArray = [
            'Hero - Image' => 'hero__image',
            'Hero - Heading' => 'hero__heading',
            'Hero - Action Button' => 'hero__primary-action'
        ];

        $styles = array_map(function($title, $class) {
            return "{$title}={$class}; ";
        }, array_keys($styleArray), array_values($styleArray));

        $config->addData(["settings" => ["theme_advanced_styles" => implode("; ", $styles)]]);

        return $config;
    }
}

Step #3: Clear cache and refresh admin page.

Jesse Maxwell

COO / Senior Developer at SwiftOtter - @bassplayer_7