=== JuZ Rich Text Extender ===
Contributors: juzed
Tags: rich text, gutenberg, inline formats, toolbar
Requires at least: 6.1
Tested up to: 6.9
Requires PHP: 7.0
Stable tag: 1.0.0
License: GPL-2.0+
License URI: http://www.gnu.org/licenses/gpl-2.0.txt
Extends the Gutenberg block editor toolbar with custom inline format buttons, with smart nesting validation based on HTML spec rules.
== Description ==
JuZ Rich Text Extender allows developers to register custom inline format buttons in the Gutenberg (block editor) toolbar. Each button applies a specific HTML inline tag with an optional CSS class to the selected text.
The plugin includes a built-in nesting validation system: buttons are automatically disabled when their use would produce invalid HTML nesting (e.g. `` inside ``). When a format is already active, its button remains enabled so it can always be toggled off.
**Key features:**
* Register custom inline formats (tag + CSS class) via a WordPress filter
* `tagName` defaults to `span`, which is strongly recommended for compatibility
* Smart nesting validation against the full active tag stack (for non-span tags)
* Buttons are visually disabled when nesting is not allowed
* Exhaustive inline tag ruleset (`span`, `a`, `strong`, `em`, `mark`, `code`, `u`, `s`, `i`, `b`, `q`, `cite`, `abbr`, `small`, `sub`, `sup`)
* Inline tag rules are filterable for full customisation by themes or other plugins
**For developers:**
Formats and inline tag rules are both exposed via WordPress filters, making the plugin fully extensible without modifying core files.
== Installation ==
1. Upload the `juz-rich-text-extender` folder to the `/wp-content/plugins/` directory.
2. Activate the plugin through the **Plugins** menu in WordPress.
3. Use the `juz_rich_text_extender/formats` filter to register your custom inline formats.
== Frequently Asked Questions ==
= How do I register a custom format? =
Use the `juz_rich_text_extender/formats` filter to add your formats. Each format must be an associative array with the following keys:
* `name` - unique identifier (used as the format type slug)
* `title` - label shown in the toolbar tooltip
* `tagName` - HTML inline tag to apply. Optional, defaults to `span`. See the caution note below.
* `className` - CSS class applied to the tag
* `icon` - a Dashicon slug or SVG string
Example:
`
add_filter( 'juz_rich_text_extender/formats', function( $formats ) {
$formats[] = [
'name' => 'highlight',
'title' => __( 'Highlight', 'your-text-domain' ),
'className' => 'my-highlight',
'icon' => 'editor-textcolor',
];
return $formats;
} );
`
**CAUTION:** `tagName` is optional and defaults to `span`, which is strongly recommended. Using any other tag may produce invalid HTML nesting or conflict with Gutenberg's internal formats (e.g. `core/link` for `` tags). If you do specify a custom tag, the nesting validation system will still protect against invalid HTML where possible, but you may encounter editor limitations. See "How do I apply a custom class to a link?" for more details.
= How do I customise the inline tag nesting rules? =
Use the `juz_rich_text_extender/inline_tags` filter. Each tag entry has three keys: `canSelfNest` (bool), `canNestIn` (array of tag names), and `canContain` (array of tag names).
Example:
`
add_filter( 'juz_rich_text_extender/inline_tags', function( $tags ) {
// Allow as a custom inline tag
$tags['kbd'] = [
'canSelfNest' => false,
'canNestIn' => [ 'p', 'li', 'span', 'strong', 'em' ],
'canContain' => [],
];
return $tags;
} );
`
= Why is a button greyed out in the toolbar? =
The nesting validation system detected that applying this format at the current cursor position would produce invalid HTML. For example, clicking `` while already inside an `` tag is forbidden by the HTML specification, so the button is disabled. This validation only applies to formats using a non-span `tagName`.
= Can I override nesting rules for an existing tag? =
Yes. Use the `juz_rich_text_extender/inline_tags` filter and modify the entry for the tag you want to change before returning the array.
= How do I apply a custom class to a link? =
Gutenberg manages `` tags internally via its own `core/link` format, which makes it unreliable to apply a custom ``-based format on top of an existing link. The recommended approach is to leave `tagName` unset (defaults to `span`) and target it in CSS using the `:has()` selector:
`
add_filter( 'juz_rich_text_extender/formats', function( $formats ) {
$formats[] = [
'name' => 'button',
'title' => __( 'Button', 'your-text-domain' ),
'className' => 'btn',
'icon' => 'button',
];
return $formats;
} );
`
Then in your CSS:
`
a:has(> .btn) {
display: inline-block;
padding: 0.5em 1em;
background: #0073aa;
color: #fff;
border-radius: 4px;
text-decoration: none;
}
`
This approach produces valid HTML (`text`), survives the HTML ↔ visual editor round-trip without issues, and is supported by all modern browsers.
== Screenshots ==
1. Custom inline format buttons added to the Gutenberg toolbar.
2. A button is automatically disabled when its nesting would be invalid.
== Changelog ==
= 1.0.0 =
* Initial release.
* Custom inline format registration via filter.
* tagName defaults to span for maximum compatibility.
* Smart nesting validation on the full active tag stack for non-span tags.
* Exhaustive inline tag ruleset covering all standard HTML inline elements.
* Filterable inline tag rules for themes and plugins.
== Upgrade Notice ==
= 1.0.0 =
Initial release — no upgrade steps required.