{"id":290102,"date":"2026-03-19T21:39:30","date_gmt":"2026-03-19T21:39:30","guid":{"rendered":"https:\/\/fr.wordpress.org\/plugins\/juz-rich-text-extender\/"},"modified":"2026-10-20T16:42:02","modified_gmt":"2026-10-20T16:42:02","slug":"juz-rich-text-extender","status":"publish","type":"plugin","link":"https:\/\/br.wordpress.org\/plugins\/juz-rich-text-extender\/","author":23066318,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.1.1","stable_tag":"1.1.1","tested":"7.1.3","requires":"6.6","requires_php":"7.4","requires_plugins":null,"header_name":"JuZ Rich Text Extender","header_author":"Ju'Z[ed]","header_description":"Extends the Gutenberg block editor toolbar with custom inline format buttons.","assets_banners_color":"15191f","last_updated":"2026-10-20 16:42:02","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/juzed.dev\/projets\/juz-rich-text-extender","header_author_uri":"https:\/\/juzed.dev\/","rating":0,"author_block_rating":0,"active_installs":10,"downloads":585,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.0":{"tag":"1.0.0","author":"juzed","date":"2026-03-19 21:39:01","revision":3486837},"1.1.0":{"tag":"1.1.0","author":"juzed","date":"2026-10-20 12:56:06","revision":3756757},"1.1.1":{"tag":"1.1.1","author":"juzed","date":"2026-10-20 16:42:02","revision":3757121}},"upgrade_notice":{"1.1.1":"<p>Demo links only, no code change.<\/p>","1.1.0":"<p>Compatibility update for WordPress 7.1.<\/p>","1.0.0":"<p>Initial release \u2014 no upgrade steps required.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3756757,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3756757,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500-fr.jpg":{"filename":"banner-1544x500-fr.jpg","revision":3756757,"resolution":"1544x500","location":"assets","locale":"fr","width":1544,"height":500},"banner-1544x500.jpg":{"filename":"banner-1544x500.jpg","revision":3756757,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250-fr.jpg":{"filename":"banner-772x250-fr.jpg","revision":3756757,"resolution":"772x250","location":"assets","locale":"fr","width":772,"height":250},"banner-772x250.jpg":{"filename":"banner-772x250.jpg","revision":3756757,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{"blueprint.json":{"filename":"blueprint.json","revision":3757123,"resolution":false,"location":"assets","locale":"","contents":"{\"$schema\":\"https:\\\/\\\/playground.wordpress.net\\\/blueprint-schema.json\",\"meta\":{\"title\":\"JuZ Rich Text Extender demo\",\"author\":\"juzed\"},\"landingPage\":\"\\\/wp-admin\\\/post.php?post=1000&action=edit\",\"preferredVersions\":{\"php\":\"8.3\",\"wp\":\"latest\"},\"features\":{\"networking\":true},\"login\":true,\"steps\":[{\"step\":\"installPlugin\",\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"juz-rich-text-extender\"},\"options\":{\"activate\":true}},{\"step\":\"mkdir\",\"path\":\"\\\/wordpress\\\/wp-content\\\/mu-plugins\"},{\"step\":\"writeFile\",\"path\":\"\\\/wordpress\\\/wp-content\\\/mu-plugins\\\/juz-rte-demo.php\",\"data\":\"<?php\\n\\\/\\\/ Two demo formats for JuZ Rich Text Extender: Marker and Badge.\\nadd_filter( 'juz_rich_text_extender\\\/formats', function ( array $formats ): array {\\n    $formats[] = [ 'name' => 'demo-marker', 'title' => 'Marker', 'className' => 'demo-marker', 'icon' => 'admin-customizer' ];\\n    $formats[] = [ 'name' => 'demo-badge', 'title' => 'Badge', 'className' => 'demo-badge', 'icon' => 'tag' ];\\n    return $formats;\\n} );\\nadd_action( 'enqueue_block_assets', function () {\\n    wp_register_style( 'juz-rte-demo', false );\\n    wp_enqueue_style( 'juz-rte-demo' );\\n    wp_add_inline_style( 'juz-rte-demo', '.demo-marker{padding:0 .15em;background:linear-gradient(transparent 55%,#fff176 55%)}.demo-badge{display:inline-block;padding:.1em .6em;border-radius:999px;background:#111;color:#fff;font-size:.8em}.demo-badge[data-rich-text-format-boundary]{background:#111!important;color:#fff!important}' );\\n} );\\n\"},{\"step\":\"runPHP\",\"code\":\"<?php\\nrequire '\\\/wordpress\\\/wp-load.php';\\n\\\/\\\/ As the admin: without a user, kses strips the markup of the blocks (inputs, attributes).\\nwp_set_current_user( 1 );\\nwp_insert_post( wp_slash( [ 'import_id' => 1000, 'post_type' => 'page', 'post_status' => 'publish', 'post_title' => \\\"JuZ Rich Text Extender demo\\\", 'post_content' => <<<'HTML'\\n<!-- wp:heading -->\\n<h2 class=\\\"wp-block-heading\\\">Try it<\\\/h2>\\n<!-- \\\/wp:heading -->\\n\\n<!-- wp:paragraph -->\\n<p>This demo registers two formats, Marker and Badge (a small must-use plugin, with the <code>juz_rich_text_extender\\\/formats<\\\/code> filter). Select some text: they appear in the formatting toolbar, under the arrow next to bold, italic and link.<\\\/p>\\n<!-- \\\/wp:paragraph -->\\n\\n<!-- wp:paragraph -->\\n<p>A sentence with a <span class=\\\"demo-marker\\\">highlighted passage<\\\/span>, then a <span class=\\\"demo-badge\\\">New<\\\/span> badge.<\\\/p>\\n<!-- \\\/wp:paragraph -->\\nHTML\\n ] ) );\\n\"}]}"}},"all_blocks":[],"tagged_versions":["1.0.0","1.1.0","1.1.1"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3756757,"resolution":"1","location":"assets","locale":"","width":1167,"height":692}},"screenshots":{"1":"Custom inline format buttons added to the Gutenberg toolbar."}},"plugin_section":[],"plugin_tags":[148076,258157,12737,1541],"plugin_category":[],"plugin_contributors":[242698],"plugin_business_model":[],"class_list":["post-290102","plugin","type-plugin","status-publish","hentry","plugin_tags-gutenberg","plugin_tags-inline-formats","plugin_tags-rich-text","plugin_tags-toolbar","plugin_contributors-juzed","plugin_committers-juzed"],"banners":{"banner":"https:\/\/ps.w.org\/juz-rich-text-extender\/assets\/banner-772x250.jpg?rev=3756757","banner_2x":"https:\/\/ps.w.org\/juz-rich-text-extender\/assets\/banner-1544x500.jpg?rev=3756757","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/juz-rich-text-extender\/assets\/icon-128x128.png?rev=3756757","icon_2x":"https:\/\/ps.w.org\/juz-rich-text-extender\/assets\/icon-256x256.png?rev=3756757","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/juz-rich-text-extender\/assets\/screenshot-1.png?rev=3756757","caption":"Custom inline format buttons added to the Gutenberg toolbar."}],"raw_content":"<!--section=description-->\n<p>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.<\/p>\n\n<p>The plugin includes a built-in nesting validation system: buttons are automatically disabled when their use would produce invalid HTML nesting (e.g. <code>&lt;a&gt;<\/code> inside <code>&lt;a&gt;<\/code>). When a format is already active, its button remains enabled so it can always be toggled off.<\/p>\n\n<p><strong>Key features:<\/strong><\/p>\n\n<ul>\n<li>Register custom inline formats (tag + CSS class) via a WordPress filter<\/li>\n<li><code>tagName<\/code> defaults to <code>span<\/code>, which is strongly recommended for compatibility<\/li>\n<li>Smart nesting validation against the full active tag stack (for non-span tags)<\/li>\n<li>Buttons are visually disabled when nesting is not allowed<\/li>\n<li>Exhaustive inline tag ruleset (<code>span<\/code>, <code>a<\/code>, <code>strong<\/code>, <code>em<\/code>, <code>mark<\/code>, <code>code<\/code>, <code>u<\/code>, <code>s<\/code>, <code>i<\/code>, <code>b<\/code>, <code>q<\/code>, <code>cite<\/code>, <code>abbr<\/code>, <code>small<\/code>, <code>sub<\/code>, <code>sup<\/code>)<\/li>\n<li>Inline tag rules are filterable for full customisation by themes or other plugins<\/li>\n<\/ul>\n\n<p><strong>Live demo:<\/strong>\nSee it in action on <a href=\"https:\/\/playground.juzed.dev\/our-plugins\/juz-rich-text-extender\/\">playground.juzed.dev<\/a>, or try it in the editor with <a href=\"https:\/\/playground.wordpress.net\/?blueprint-url=https:\/\/wordpress.org\/plugins\/wp-json\/plugins\/v1\/plugin\/juz-rich-text-extender\/blueprint.json\">WordPress Playground<\/a>, nothing to install.<\/p>\n\n<p><strong>For developers:<\/strong><\/p>\n\n<p>Formats and inline tag rules are both exposed via WordPress filters, making the plugin fully extensible without modifying core files.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>juz-rich-text-extender<\/code> folder to the <code>\/wp-content\/plugins\/<\/code> directory.<\/li>\n<li>Activate the plugin through the <strong>Plugins<\/strong> menu in WordPress.<\/li>\n<li>Use the <code>juz_rich_text_extender\/formats<\/code> filter to register your custom inline formats.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"how%20do%20i%20register%20a%20custom%20format%3F\"><h3>How do I register a custom format?<\/h3><\/dt>\n<dd><p>Use the <code>juz_rich_text_extender\/formats<\/code> filter to add your formats. Each format must be an associative array with the following keys:<\/p>\n\n<ul>\n<li><code>name<\/code> - unique identifier (used as the format type slug)<\/li>\n<li><code>title<\/code> - label shown in the toolbar tooltip<\/li>\n<li><code>tagName<\/code> - HTML inline tag to apply. Optional, defaults to <code>span<\/code>. See the caution note below.<\/li>\n<li><code>className<\/code> - CSS class applied to the tag<\/li>\n<li><code>icon<\/code> - a Dashicon slug or SVG string<\/li>\n<\/ul>\n\n<p>Example:<\/p>\n\n<pre><code>add_filter( 'juz_rich_text_extender\/formats', function( $formats ) {\n    $formats[] = [\n        'name'      =&gt; 'highlight',\n        'title'     =&gt; __( 'Highlight', 'your-text-domain' ),\n        'className' =&gt; 'my-highlight',\n        'icon'      =&gt; 'editor-textcolor',\n    ];\n    return $formats;\n} );\n<\/code><\/pre>\n\n<p><strong>CAUTION:<\/strong> <code>tagName<\/code> is optional and defaults to <code>span<\/code>, which is strongly recommended. Using any other tag may produce invalid HTML nesting or conflict with Gutenberg's internal formats (e.g. <code>core\/link<\/code> for <code>&lt;a&gt;<\/code> 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.<\/p><\/dd>\n<dt id=\"how%20do%20i%20customise%20the%20inline%20tag%20nesting%20rules%3F\"><h3>How do I customise the inline tag nesting rules?<\/h3><\/dt>\n<dd><p>Use the <code>juz_rich_text_extender\/inline_tags<\/code> filter. Each tag entry has three keys: <code>canSelfNest<\/code> (bool), <code>canNestIn<\/code> (array of tag names), and <code>canContain<\/code> (array of tag names).<\/p>\n\n<p>Example:<\/p>\n\n<pre><code>add_filter( 'juz_rich_text_extender\/inline_tags', function( $tags ) {\n    \/\/ Allow &lt;kbd&gt; as a custom inline tag\n    $tags['kbd'] = [\n        'canSelfNest' =&gt; false,\n        'canNestIn'   =&gt; [ 'p', 'li', 'span', 'strong', 'em' ],\n        'canContain'  =&gt; [],\n    ];\n    return $tags;\n} );\n<\/code><\/pre><\/dd>\n<dt id=\"why%20is%20a%20button%20greyed%20out%20in%20the%20toolbar%3F\"><h3>Why is a button greyed out in the toolbar?<\/h3><\/dt>\n<dd><p>The nesting validation system detected that applying this format at the current cursor position would produce invalid HTML. For example, clicking <code>&lt;a&gt;<\/code> while already inside an <code>&lt;a&gt;<\/code> tag is forbidden by the HTML specification, so the button is disabled. This validation only applies to formats using a non-span <code>tagName<\/code>.<\/p><\/dd>\n<dt id=\"can%20i%20override%20nesting%20rules%20for%20an%20existing%20tag%3F\"><h3>Can I override nesting rules for an existing tag?<\/h3><\/dt>\n<dd><p>Yes. Use the <code>juz_rich_text_extender\/inline_tags<\/code> filter and modify the entry for the tag you want to change before returning the array.<\/p><\/dd>\n<dt id=\"how%20do%20i%20apply%20a%20custom%20class%20to%20a%20link%3F\"><h3>How do I apply a custom class to a link?<\/h3><\/dt>\n<dd><p>Gutenberg manages <code>&lt;a&gt;<\/code> tags internally via its own <code>core\/link<\/code> format, which makes it unreliable to apply a custom <code>&lt;a&gt;<\/code>-based format on top of an existing link. The recommended approach is to leave <code>tagName<\/code> unset (defaults to <code>span<\/code>) and target it in CSS using the <code>:has()<\/code> selector:<\/p>\n\n<pre><code>add_filter( 'juz_rich_text_extender\/formats', function( $formats ) {\n    $formats[] = [\n        'name'      =&gt; 'button',\n        'title'     =&gt; __( 'Button', 'your-text-domain' ),\n        'className' =&gt; 'btn',\n        'icon'      =&gt; 'button',\n    ];\n    return $formats;\n} );\n<\/code><\/pre>\n\n<p>Then in your CSS:<\/p>\n\n<pre><code>a:has(&gt; .btn) {\n    display: inline-block;\n    padding: 0.5em 1em;\n    background: #0073aa;\n    color: #fff;\n    border-radius: 4px;\n    text-decoration: none;\n}\n<\/code><\/pre>\n\n<p>This approach produces valid HTML (<code>&lt;a href=\"...\"&gt;&lt;span class=\"btn\"&gt;text&lt;\/span&gt;&lt;\/a&gt;<\/code>), survives the HTML \u2194 visual editor round-trip without issues, and is supported by all modern browsers.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.1.1<\/h4>\n\n<ul>\n<li>Live demo: new address of the demo page, and a WordPress Playground preview that opens the editor on a demo page.<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>Tested with WordPress 7.1.<\/li>\n<li>New banners and icons, link to the live demo.<\/li>\n<li>Requires WordPress 6.6 and PHP 7.4.<\/li>\n<li>Build tooling updated.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release.<\/li>\n<li>Custom inline format registration via filter.<\/li>\n<li>tagName defaults to span for maximum compatibility.<\/li>\n<li>Smart nesting validation on the full active tag stack for non-span tags.<\/li>\n<li>Exhaustive inline tag ruleset covering all standard HTML inline elements.<\/li>\n<li>Filterable inline tag rules for themes and plugins.<\/li>\n<\/ul>","raw_excerpt":"Extends the Gutenberg block editor toolbar with custom inline format buttons, with smart nesting validation based on HTML spec rules.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/290102","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=290102"}],"author":[{"embeddable":true,"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/juzed"}],"wp:attachment":[{"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=290102"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=290102"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=290102"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=290102"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=290102"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=290102"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}