How to customize the WordPress Text Editor

How to customize the WordPress Text Editor

Many people think that the Visual Editor in WordPress can replace HTML but over the years, I worked with many clients and the same issues come up over and over again. Their posts are poorly formatted, with lots of inline code pasted from either rich text editors, emails or other websites which causes frustration for the both, the authors and their readers. You can find out more by reading 10 reasons why to disable TinyMCE editor. But what to do instead? Is there a way to persuade your clients to use HTML? I did an experiment. In this post you can learn how to customize the WordPress Text Editor for yourself and for your clients who don’t know anything about HTML and/or are not English speaking.

If you want to follow along with this tutorial, you will need:

  • a self-hosted WordPress site
  • ability to edit your custom WordPress theme or create a child theme
  • make sure you know how to upload new files on your hosting server
  • a text editor with syntax highlighting (I’m using a free text editor Atom)

Setting up PHP files

We are going to add quite a lot of custom code so I thought it would be better to create a separate PHP file rather than clutter functions.php.

In your WordPress custom theme locate inc folder. If it doesn’t exist, you can create one yourself. Then create a new document inside inc folder called custom-editor.php. Type <?php on the first line and Save the file.

Open functions.php and paste the following code at the bottom of the file:

 * Customize Text Editor
require get_parent_theme_file_path( '/inc/custom-editor.php' );

If you’re using a Child Theme, use the following code instead:

require_once( get_stylesheet_directory() . '/inc/custom-editor.php' );

This code tells WordPress to go to the theme directory to folder called inc and include custom-editor.php file.

How to disable TinyMCE editor globally for all users

I had a big dilemma, “To disable or not to disable TinyMCE editor globally for all users…” In the end I took a deep breath and did it. After all, people learn things if they have to and if there isn’t any other option but using HTML…

You can disable TinyMCE editor globally for all users by pasting the following code into custom-editor.php file just below <?php opening tag.

 * Disable TinyMCE editor globally for all users.
add_filter( 'user_can_richedit' , '__return_false', 50 );

Now all user will see the Text Editor by default. Some might even be surprised that it exists. 🙂

How to remove automatic paragraphs and line breaks from the editor

I don’t like automated paragraphs and line breaks creation in WordPress. This functionality won’t let you clearly structure your HTML code. HTML should ignore white space. By adding the following code into custom-editor.php, you will disable wpautop filter and therefore automatic paragraphs and line breaks creation globally.

Beware, this will remove line breaks from all your posts! If you already have lots of published articles using the Visual Editor, I’d recommend installing a plugin Toggle wpautop which gives you and option to disable wpautop filter only on newly created posts.

 *  Remove automatic paragraphs and line breaks from editor
remove_filter( 'the_content', 'wpautop' );
remove_filter( 'the_excerpt', 'wpautop' );

Now the text editor behaves better but it’s not overly easy to use and doesn’t do any syntax highlighting. There are various plugins you could use to do the trick. I’m using HTML Editor Syntax Highlighter and it works very well. However, at this moment of time there is one issue. If you like using NextGen gallery button, it won’t work. I worked around it by using a custom shortcode instead.

Installing HTML Editor Syntax Highlighter plugin

To install HTML Editor Syntax Highlighter plugin go to Plugins => Add new and Search for HTML Editor Syntax Highlighter.

Install HTML Editor Syntax Highlighter plugin

Install and Activate the plugin. Alternatively, you can download HTML Editor Syntax Highlighter from WordPress plugin repository.

Here are some of the great features of HTML Editor Syntax Highlighter:

  • syntax highlighting in all WordPress text editors including Theme & Plugin editors
  • save your posts and pages pressing Ctrl+S
  • customize colours, font size, show/hide line numbers, line wrap, indentation
  • highlight matching brackets or tags, auto close brackets or tags

Now we have a cool text editor with syntax highlighting and default Quicktags buttons.

WordPress text editor with HTML Editor Syntax Highlighter

Even though having buttons to insert HTML tags is much easier than having to type them yourself, I have a few issues with the default functionality:

  • some buttons are misleading – b inserts <strong> tag and not <b> tag, i inserts <em> and not <i> tag, that goes against semantically correct markup (learn the difference between these HTML tags)
  • some buttons I never use (like ins or close tags)
  • some buttons insert additional new lines which I find annoying because I already have new lines where I want them to be
  • I could do with other buttons like headings or more text level HTML tags

First we are going to filter the default buttons and leave only the ones that do exactly what we want and then we add custom buttons to enhance the editor.

How to remove default QuickTags buttons from WordPress text editor

Copy and paste the following code into your custom-editor.php file. Replace the prefix my_ with your theme text domain in two places.

 * Filter default QuickTags.
function my_filter_default_quicktags( $qtInit ) {
    $qtInit['buttons'] = 'strong,em,link,block,del,img,ul,ol,li,code,more,close,fullscreen';
    return $qtInit;
add_filter('quicktags_settings', 'my_filter_default_quicktags');

You won’t see any change just yet. There is a complete list of all default buttons and the moment the function says to keep them all. Now remove all buttons you don’t want to keep. I’m going to remove all of them apart from the link and img button.

 * Filter default QuickTags.
function my_filter_default_quicktags( $qtInit ) {
    $qtInit['buttons'] = 'link,img';
    return $qtInit;
add_filter('quicktags_settings', 'my_filter_default_quicktags');

WordPress Text Editor with default Quicktags removed

It’s time add your own buttons to enhance the functionality of the default text editor.

How to add custom QuickTags to enhance the default WordPress text editor.

To add your own QuickTags buttons, copy and paste the following code into custom-editor.php and replace the prefix my_ with your theme text domain in two places.

 * Custom Quicktags
function my_custom_quicktags() {
    if ( wp_script_is( 'quicktags' ) ) { ?>
       <script type="text/javascript">

    <?php }
add_action('admin_print_footer_scripts', 'my_custom_quicktags');

How to create Quicktags for HTML elements with start tag and end tag

From now on all JavaScript code for custom Quicktags goes between <script type="text/javascript"> and </script> tags.

Adding a new Quicktag button for HTML elements with start and end tag is easy (see documentation for Quicktags API)

QTags.addButton( id, display, arg1, arg2, access_key, title, priority, instance );

Only the first three parameters are required.

  1. id – unique id for the button
  2. display – text on the button
  3. arg1 – start tag or a callback function
  4. arg2 – end tag
  5. access_key – keyboard shortcut
  6. title – shows when you hover over the button
  7. priority – order buttons on the toolbar
  8. instance – limit the button to a specific instance of Quicktags

For my non-English-speaking clients I put whole words in their language in the display parameter (intead of just a shortcut for a HTML tag) and hints into the title so that when they hover over the button, it provides them with a guidance.

Here is an example of a paragraph tag, headings and some text-level tags. Notice that for the first four I added a priority as the last parameter. This is because the leftover default tags have the priority set and I want some tags displayed before them. The rest of my custom Quicktags don’t have the priority set so they will display after the link and the img buttons in the order they are declared.

<script type="text/javascript">
QTags.addButton( 'p_tag', 'p', '<p>', '</p>', '', 'Paragraph', 1 );
QTags.addButton( 'h2_tag', 'h2', '<h2>', '</h2>', '', 'Heading 2', 2 );
QTags.addButton( 'h3_tag', 'h3', '<h3>', '</h3>', '', 'Heading 3', 3 );
QTags.addButton( 'h4_tag', 'h4', '<h4>', '</h4>', '', 'Heading 4', 4 );
QTags.addButton( 'strong_tag', 'strong', '<strong>', '</strong>', '', 'Important, serious, urgent' );
QTags.addButton( 'b_tag', 'b', '<b>', '</b>', '', 'Key words, product names, actionable words, the lede sentence' );
QTags.addButton( 'em_tag', 'em', '<em>', '</em>', '', 'Stress emphasis' );
QTags.addButton( 'i_tag', 'i', '<i>', '</i>', '', 'Alternate voice or mood, taxonomic designation, technical terms, idiomatic phrases, transliterations, thoughts' );
QTags.addButton( 'mark_tag', 'mark', '<mark>', '</mark>', '', 'Highlighter, for reference purposes' );
QTags.addButton( 'del_tag', 'del', '<del>', '</del>', '', 'Deleted text' );
QTags.addButton( 'ins_tag', 'ins', '<ins>', '</ins>', '', 'Inserted text' );
QTags.addButton( 'small_tag', 'small', '<small>', '</small>', '', 'Small text' );
QTags.addButton( 'sub_tag', 'sub', '<sub>', '</sub>', '', 'Subscript text' );
QTags.addButton( 'sup_tag', 'sup', '<sup>', '</sup>', '', 'Superscript text' );
QTags.addButton( 'span_tag', 'span', '<span class="">', '</span>', '', 'Span tag with class' );

Quicktags with a callback function

For some buttons you will want something more to happen than just inserting the start and end HMTL tag. In that case you can write a function that will be called when you click the button. First, we need a function that retrieves the selected text and then we can do something with it. To prevent processing extra white space, we will need another function. Copy and paste both functions between <script type="text/javascript"> and </script> tags.

// function to retrieve the selected text from the text editor
function getSelected() {
	var txtarea = document.getElementById("content");
	var start = txtarea.selectionStart;
	var finish = txtarea.selectionEnd;
	return txtarea.value.substring(start, finish);

// function to trim white space
function trimWhiteSpace(x) {
	return x.replace(/^\s+|\s+$/gm,'');

Here is an example of a Quicktag with prompt. When clicking the button for abbreviation, you will be prompted to add the title. If you type in the title, the <abbr title="My Abbreviation Expansion"></abbr> will be inserted, otherwise text will be wrapped in <abbr></abbr> without the expansion.

QTags.addButton( 'abbr_tag', 'abbr', abbreviation, '', '', 'Abbreviation, acronym' );

function abbreviation() {
	var selectedText = getSelected();
	var abbrTitle = prompt( 'Enter the title:', '' );

	if ( abbrTitle ) {
		QTags.insertContent('<abbr title="' + abbrTitle + '">' + selectedText + '</abbr>');
	} else {
		QTags.insertContent('<abbr>' + selectedText + '</abbr>');

Quicktags for computer code and preformatted text

Here are some more text level tags for computer code and preformatted text.

QTags.addButton( 'kbd_tag', 'kbd', '<kbd>', '</kbd>', '', 'Keyboard input' );
QTags.addButton( 'samp_tag', 'samp', '<samp>', '</samp>', '', 'Program output' );
QTags.addButton( 'var_tag', 'var', '<var>', '</var>', '', 'Variable in a mathematical expression or programming' );
QTags.addButton( 'code_tag', 'code', '<code>', '</code>', '', 'Computer code' );
QTags.addButton( 'pre_tag', 'pre', '<pre>\n', '\n</pre>', '', 'Preformatted text' );
QTags.addButton( 'prism', 'prism.js', prismSyntax, '', '', 'Prism.js' );

Programming syntax on this website is highighted with Prism.js. It requires the code to be wrapped in <pre><code class="language-xxx"></code></pre> tags with programming language specified. I also wanted to add an option of displaying the file title above the code by adding data-file="My file name" to <pre> tag. The title is then displayed in CSS pseudo-element. The following function will prompt you to enter the programming language and then the optional file name.

function prismSyntax() {
	var selectedText = getSelected();
	var language = prompt( 'Enter a language:', '' );
	var dataFile = prompt( 'Enter a File name:', '' );

	if ( language ) {
		if ( dataFile ) {
			QTags.insertContent('<pre data-file="' + dataFile + '">\n<code class="language-' + language + '">\n' + selectedText + '\n</code>\n</pre>');
		} else {
			QTags.insertContent('<pre>\n<code class="language-' + language + '">\n' + selectedText + '\n</code>\n</pre>');

Quicktags for citations

The following Quicktags for content that is quoted from another source.

QTags.addButton( 'q_tag', 'q', '<q>', '</q>', '', 'Inline quote' );
QTags.addButton( 'blockquote_tag', 'blockquote', '<blockquote>\n', '\n</blockquote>', '', 'Blockquote' );
QTags.addButton( 'cite_tag', 'cite', '<cite>', '</cite>', '', 'Work Title' );

Quicktags for lists

I wanted to be able to create list with sublists preferably with one click. The following functions will create a well formatted list out of items separated by a new line and sub lists from items separated by a new line and indented by a tab key. It works well three levels deep.

QTags.addButton( 'ul_list', 'ul', unorderedList, '', '', 'Unordered list from selected text' );
QTags.addButton( 'ol_list', 'ol', orderedList, '', '', 'Ordered list from selected text' );
QTags.addButton( 'li_tag', 'li', '<li>', '</li>', '', 'Add list item' );

function unorderedList(){
    var selectedText = getSelected();
    selectedText = trimWhiteSpace(selectedText);
    var output = indentedToHtmlList(selectedText, '\t', ':', 'ul' );
    QTags.insertContent( output );

function orderedList(){
    var selectedText = getSelected();
    selectedText = trimWhiteSpace(selectedText);
    var output = indentedToHtmlList(selectedText, '\t', ':', 'ol' );
    QTags.insertContent( output );

// function to create a html list
// based on
function indentedToHtmlList(text, indentChar, folderChar, listType) {
    indentChar = indentChar || '\t';
    folderChar = folderChar || ':';
    listType = listType || 'ul';

    var lastDepth = -1,
        lines = text.split(/\r?\n/),
        output = '',
        depthCounter = new RegExp('^(' + indentChar + '*)(.*)');

    for ( var i = 0; i < lines.length; i++ ) {
        var splitted = lines[i].match(depthCounter),
            indentStr = splitted[1],
            fileName = splitted[2],
            currentDepth = (indentStr === undefined) ? 0 : (indentStr.length / indentChar.length);

        if ( lastDepth === currentDepth ) {
            output += '</li>\n';

        } else if ( lastDepth > currentDepth ) {
            var depthJump = false;

            if ( lastDepth > currentDepth + 1 ) {
                depthJump = true;

            var _lastDepth = lastDepth;

            while ( lastDepth > currentDepth ) {
                if ( depthJump && _lastDepth != lastDepth && lastDepth > currentDepth ) {
                        output += '\t\t</' + listType + '>\n\t</li>\n';
                } else {
                    if ( lastDepth > 1 ) {
                        output += '</li>\n\t\t\t\t</' + listType + '>\n\t\t\t</li>\n';
                    } else if ( lastDepth == 1 ) {
                        output += '</li>\n\t\t</' + listType + '>\n\t\</li>\n';
                    } else {
                        output += '</li>\n</' + listType + '>\n</li>\n';


        } else if ( lastDepth < currentDepth ) {
            if ( currentDepth == 0 ) {
                output += '<' + listType + '>\n';
            } else if ( currentDepth == 1 ) {
                output += '\n\t\t<' + listType + '>\n';
            } else {
                output += '\n\t\t\t\t<' + listType + '>\n';

        if ( currentDepth == 0 ) {
            output += '\t<li>';
        } else if ( currentDepth == 1 ) {
            output += '\t\t\t<li>';
        } else {
            output += '\t\t\t\t\t<li>';

        output += fileName;

        lastDepth = currentDepth;

    while ( lastDepth >= 0 ) {
        if ( lastDepth > 1 ) {
            output += '</li>\n\t\t\t\t</' + listType + '>';
        } else if ( lastDepth == 1 ) {
            output += '\n\t\t\t</li>\n\t\t</' + listType + '>';
        } else {
            if ( depthJump == false ) {
                output += '\n\t</li>\n</' + listType + '>';
            } else {
                output += '</li>\n</' + listType + '>';


    return output;

A Quicktag for tables

Creating tables manually is a tedious task. The following handy function will create a table out of Markdown style formatting – rows each on a new line, columns divided by a vertical bar | as follows:

col 1 | col 2 | col 3
cell a1 | cell b1 | cell c1
cell a2 | cell b2 | cell c2
cell a3 | cell b3 | cell c3

This will output as:

	<th>col 1</th>
	<th>col 2</th>
	<th>col 3</th>
	<td>cell a1</td>
	<td>cell b1</td>
	<td>cell c1</td>
	<td>cell a2</td>
	<td>cell b2</td>
	<td>cell c2</td>
	<td>cell a3</td>
	<td>cell b3</td>
	<td>cell c3</td>

Also, you can add an optional table caption if you like.

QTags.addButton( 'table', 'table', createTable, '', '', 'Table from selected text' );

function createTable() {
    var selectedText = getSelected();
    selectedText = trimWhiteSpace(selectedText);
    var output = indentedToTable( selectedText );
    QTags.insertContent( output );

function indentedToTable( text ) {
    var rows = text.split(/\r?\n/);

    if ( rows ) {
        var output = '<table>\n';
        var caption = prompt( 'Caption:', '' );

        if ( caption ) {
            output += '<caption>' +  caption + '</caption>\n';

        for ( var i = 0; i < rows.length; i++ ) {
            if ( i == 0 ) {
                output += '<thead>\n';
            } else if ( i == 1 ) {
                output += '<tbody>\n';

            output += '<tr>';

            var cells = rows[i].split('|');
            for ( var j = 0; j < cells.length; j++ ) {
                if ( i == 0 ) {
                    output += '\n\t<th>' + trimWhiteSpace(cells[j]) + '</th>';
                } else {
                    output += '\n\t<td>' + trimWhiteSpace(cells[j]) + '</td>';

            output += '\n</tr>\n';

            if ( i == 0 ) {
                output += '</thead>\n';

        output += '</tbody>\n</table>';

    return output;

Quicktags without the closing tag

For Empty HTML Elements (don’t need a closing tag) simply ommit the arg2 parameter. Here are some examples:

QTags.addButton( 'hr_tag', 'hr', '<hr>', '', '', 'Horizontal rule - thematic break' );
QTags.addButton( 'br_tag', 'br', '<br>', '', '', 'Line Break' );
QTags.addButton( 'more', 'more', '<!--more-->', '', '', 'More link' );
QTags.addButton( 'nextpage', 'nextpage', '\n<!--nextpage-->\n', '', '', 'Page break' );

Quicktags for encoding and decoding HTML Entities (symbols)

If you want to use symbols or special characters in your post, they should be properly encoded other they might not display correctly in various browsers. Here is an example of a few common ones but you can add your own using W3C Character Reference. For each character there are three lines. Use \u and the last for characters from the second row for the display parameter and the code from the third row as arg1.

QTags.addButton( 'nbsp', '\u25E6', '&nbsp;', '', '', 'Non-breaking space' );
QTags.addButton( 'amp', '\u0026', '&#38;', '', '', 'Ampersand' );
QTags.addButton( 'dollar', '\u0024', '&#36;', '', '', 'Dollar' );
QTags.addButton( 'pound', '\u00A3', '&#163;', '', '', 'Pound' );
QTags.addButton( 'frac14;', '\u00BC', '&#188;', '', '', 'Fraction one-quarter' );
QTags.addButton( 'frac12;', '\u00BD', '&#189;', '', '', 'Fraction one-half' );
QTags.addButton( 'frac34', '\u00BE', '&#190;', '', '', 'Fraction three-quarters' );
QTags.addButton( 'rarrow', '\u21D2', '&rArr;', '', '', 'Right double arrow' );
QTags.addButton( 'sharp', '\u266F', '&#9839;', '', '', 'Sharp' );
QTags.addButton( 'flat', '\u266D', '&#9837;', '', '', 'Flat' );
QTags.addButton( 'natural', '\u266E', '&#9838;', '', '', 'Natural' );

If your post contains examples of HTML code (like in this post), all the special characters like <>&" need to be encoded. Doing that manually drives one crazy. Here are two handy functions to help you encode or decode HTML special characters in selected text with one click. The encode button will only encode specified characters (you can remove some or add your own by editing the function). The decode button will decode all the entities.

QTags.addButton( 'escapeHTML', 'encode', escapeHTML, '', '', 'Encode selected HTML characters' );
QTags.addButton( 'unescapeHTML', 'decode', unescapeHTML, '', '', 'Decoded encoded characters' );

// function to escape selected HTML characters
// based on
function escapeHTML() {
    var fn=function(tag) {
        var charsToReplace = {
            '&': '&#38;',
            '<': '&#60;',
            '>': '&#62;',
            '"': '&#34;',
            '\'': '&#39;',
            '[': '&#91;',
            ']': '&#93;'
        return charsToReplace[tag] || tag;
    var selectedText = getSelected();
    var output = selectedText.replace(/[&<>"\'\[\]]/g, fn);
    QTags.insertContent( output );

function unescapeHTML() {
    var selectedText = getSelected();
    var output = htmlDecode(selectedText);
    QTags.insertContent( output );

// function to decode encoded HTML characters
// based on
function htmlDecode(html){
    var e = document.createElement('div');
    e.innerHTML = html;
    return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;

Quicktags for WordPress shortcodes

You can create Quicktags for you favourite shortcodes as well! Here is an example of two. The first one is my custom build shortcode that inserts a signature with my logo. The second one is for embeding stuff in WordPress like YouTube videos. It will prompt you to enter the url address of what is to be embeded. There’s a lot to choose from. Find out more by visiting WordPress Embeds documentation.

QTags.addButton( 'signature', 'signature', '[signature]', '', '', 'Signature shortcode' );
QTags.addButton( 'embed', 'embed', embedStuff, '', '', '', 'Embed shortcode' );

function embedStuff() {
	var url = prompt( 'Enter the url:', '' );
	if ( url ) {
		QTags.insertContent('[embed]' + url + '[/embed]');

Adding a dropdown list of custom CSS classes

For additional styling options, there should be CSS classes defined like colours, font sizes, etc. in your WordPress theme. It’s handy to have a dropdown list in your WordPress Text editor toolbar. This function will do just that. You need to customize the content of the variable cssClasses. There are single quotes with what will be inserted, colon, single quotes with what will the dropdown option say and a comma (apart from the very last line). Replace these lines with your own css classes and their labels.

QTags.addButton( 'css_classes', 'CSS Classes', function(){} );

// function to create Select drop down
// based on
jQuery(window).load( function() {
    var cssClasses = {
        '': 'CSS Classes',
        'primary': 'Color red',
        'secondary': 'Color blue',
        'no-bullet': 'No bullets list'

    var selectClass = jQuery('<select />').attr('id','my-css-classes');
    for ( var val in cssClasses ) {
        jQuery('<option />', { value: val, text: cssClasses[val]} ).appendTo(selectClass);
    jQuery('#qt_content_css_classes')[0].outerHTML = selectClass[0].outerHTML;

    jQuery('#my-css-classes').on('change', function(){
        var sc = jQuery(this).val();

Wow, that’s a lot of buttons on the toolbar now!

WordPress Text Editor with custom Quicktags

Final touches

Now I’m going to show you how to color-code WordPress Quicktags. There are many buttons on the Toolbar and colors will make finding the right one much easier.

To add custom CSS to admin area, you will need to create a special css stylesheet unless you already happend to have one.

In your custom Theme folder, navigate to css folder (or create one if it doesn’t exist) and created a new file called editor.css and paste the following code in. You can target individual buttons by their id which is #qt_content_id_parameter.

.wp-core-ui .quicktags-toolbar input.button {
  color: #fff;
  background: #778899;
  border-color: #fff;
  box-shadow: none;

.quicktags-toolbar #qt_content_p_tag {
  background: #696969;

.quicktags-toolbar #qt_content_h2_tag,
.quicktags-toolbar #qt_content_h3_tag,
.quicktags-toolbar #qt_content_h4_tag {
  background: #BC8F8F;

.quicktags-toolbar #qt_content_link,
.quicktags-toolbar #qt_content_img {
  background: #FA8072;

.quicktags-toolbar #qt_content_strong_tag,
.quicktags-toolbar #qt_content_b_tag,
.quicktags-toolbar #qt_content_em_tag,
.quicktags-toolbar #qt_content_i_tag,
.quicktags-toolbar #qt_content_mark_tag,
.quicktags-toolbar #qt_content_del_tag,
.quicktags-toolbar #qt_content_ins_tag,
.quicktags-toolbar #qt_content_small_tag,
.quicktags-toolbar #qt_content_sub_tag,
.quicktags-toolbar #qt_content_sup_tag,
.quicktags-toolbar #qt_content_span_tag,
.quicktags-toolbar #qt_content_abbr_tag {
  background: #F4A460;

.quicktags-toolbar #qt_content_kbd_tag,
.quicktags-toolbar #qt_content_samp_tag,
.quicktags-toolbar #qt_content_var_tag,
.quicktags-toolbar #qt_content_code_tag,
.quicktags-toolbar #qt_content_pre_tag,
.quicktags-toolbar #qt_content_prism {
  background: #A0522D;

.quicktags-toolbar #qt_content_q_tag,
.quicktags-toolbar #qt_content_blockquote_tag,
.quicktags-toolbar #qt_content_cite_tag,
.quicktags-toolbar #qt_content_ul_list,
.quicktags-toolbar #qt_content_ol_list,
.quicktags-toolbar #qt_content_li_tag,
.quicktags-toolbar #qt_content_table {
  background: #20B2AA;

.quicktags-toolbar #qt_content_hr_tag,
.quicktags-toolbar #qt_content_br_tag,
.quicktags-toolbar #qt_content_more,
.quicktags-toolbar #qt_content_nextpage {
  background: #5F9EA0;

.quicktags-toolbar #qt_content_embed {
  background: #4682B4;

Now you need to enqueue style in function.php or custom-editor.php file. Replace the prefix my_ and my- with your theme text domain in three places.

function my_admin_scripts() {
    wp_enqueue_style( 'my-admin-style', get_template_directory_uri() . '/css/editor.css', true, '1.0.0' );
add_action( 'admin_enqueue_scripts', 'my_admin_scripts' );

function my_admin_scripts() {
    wp_enqueue_style( 'my-admin-style', get_stylesheet_directory_uri() . '/css/editor.css', true, '1.0.0' );
add_action( 'admin_enqueue_scripts', 'my_admin_scripts' );

WordPress Text Editor with custom color coded Quicktags


Hope that you found this tutorial helpful and your newly enhanced WordPress Text Editor will save you a lot of time while writing posts as well as encourage you to use semantically correct HTML markup. Fell free to drop a comment below or subscribe.


Subscribe to our newsletter!

We use a third party provider, Mailchimp, to deliver our newsletter. We gather statistics around email opening and clicks using industry standard technologies to help us monitor an improve our e-newsletter. For more information, please see our Privacy Notice.


  1. Hi jana thanks for your answer. It’s just that I couldn’t figure where to copy all the snippets of code you provide. That made my admin page turn blank. If you can’t see my code you can’t help obviously. With your source code as an example it’s gonna be much clearer. Thanks again for that.

    An other question :
    I use the well known ACF for custom fields extension in my theme. Unfortunatly it seems HTML Editor Syntax Highlighter doesn’t work with it. Would you have a magic solution in your sorcerer hat ?

    • I’m afraid I don’t have a fix for syntax highlighting in ACF. HTML Editor Syntax Highlighter targets the main editor area by ID which can only occur once on each page. I think the authors have targeting all editors on their to-do list sometimes in the future. See

  2. Hi Jana,

    Thanks for your tutorial. I could make almost all the quitag buttons I wanted.
    Unfortunatly I don’t get something in the tutorial.
    My admin crashes (blank page) when I copy and paste most the portions of code you provide.
    For example I tried to ccopy all the code you wrote on the chapter “Adding a dropdown list of custom CSS classes” and my wp admin turns blank when I reload with the changes.
    I believe there is something with the openning php tags that breaks the code. Anything after my_custom_quicktags doesn’t work. Any clue ?

    Thanks again for your work.

    • Hi François,
      I’m not quite sure what might make your admin page turn blank without seeing the code. Sometimes when you copy and paste a code from a website, it pastes a different type of single and double quotes than your editor uses. I usually manually retype them to make sure they are not breaking anything. Here is a custom-editor.php source code I’m currently using on my blog custom editor source code. You can unzip it and either copy and paste parts you need or include the whole file in your functions.php to test it. Hope that helps.

  3. “Hope that you found this tutorial helpful …”
    I assure you, it was really helpful !
    Thank you for this great and effective tutorial Ms Jana.

Leave a Comment