Bard Mutator
This Statamic addon allows you to modify the tags rendered by the Bard fieldtype, giving you full control over the final HTML. You can add, remove and modify attributes, wrap tags and content, or rename and replace tags entirely.
Installation
You can search for this addon in the Tools > Addons
section of the Statamic control panel and click install, or run the following command from your project root:
composer require jacksleight/statamic-bard-mutator
Examples
First of all let's see what this can do with some examples!
Add a class to all bullet lists:
use JackSleight\StatamicBardMutator\Facades\Mutator;
Mutator::tag('bullet_list', function ($tag) {
$tag[0]['attrs']['class'] = 'list';
return $tag;
});
noopener
to all external links:
Add use JackSleight\StatamicBardMutator\Facades\Mutator;
use Statamic\Facades\URL;
Mutator::tag('link', function ($tag) {
if (URL::isExternal($tag[0]['attrs']['href'])) {
$tag[0]['attrs']['rel'] = 'noopener';
}
return $tag;
});
Add an auto-generated ID to all level 2 headings:
use JackSleight\StatamicBardMutator\Facades\Mutator;
Mutator::tag('heading', function ($tag, $data) {
if ($data->attrs->level === 2) {
$tag[0]['attrs']['id'] = str_slug(collect($data->content)->implode('text', ''));
}
return $tag;
});
Add a wrapper div around all tables:
use JackSleight\StatamicBardMutator\Facades\Mutator;
Mutator::tag('table', function ($tag) {
array_unshift($tag, [
'tag' => 'div',
'attrs' => ['class' => 'table-wrapper'],
]);
return $tag;
});
Add a wrapper span around all list item content:
use JackSleight\StatamicBardMutator\Facades\Mutator;
Mutator::tag('list_item', function ($tag) {
array_push($tag, 'span');
return $tag;
});
Convert all images to a custom element:
use JackSleight\StatamicBardMutator\Facades\Mutator;
Mutator::tag('image', function ($tag) {
$tag[0]['tag'] = 'fancy-image';
return $tag;
});
Creating Mutators
You should add mutators in your service provider's boot()
method. They will receive two arguments:
- tag (array): The standard tag value
- data (object): The raw node or mark data
You should return a tag value.
You can add multiple mutators for the same node or mark, they'll be executed in the order they were added.
How it Works
Rendering Process
This is roughly how ProseMirror (the magic behind Bard's content format and rendering) handles the rendering process:
- The raw content is stored as a ProseMirror document in your entries, these documents consist of nodes and marks
- ProseMirror converts the raw node and mark data to their standard tag values
- Bard Mutator jumps in here, allowing you to modify the tag values
- ProseMirror renders the tag values to an HTML string
Tag Values
In its fully expanded format a tag value looks like this:
[
[
'tag' => 'a',
'attrs' => [
'href' => 'http://...'
]
]
]
A fully expanded tag value is an array of items where each item is an associative array with two keys:
- tag (string): The name of the tag
- attrs (array): A name/value array of attributes
An array with multiple items will render nested tags, with the content placed within the innermost (last) tag.
If there are no attributes the item can just be the tag name:
[
['p']
]
And if there's only one item with no attributes the entire tag value can just be the tag name:
'p'
The built-in node and mark classes return a mixture of these formats, but for ease and consistency Bard Mutator normalizes them to the fully expanded format before passing to your mutators. You can return any format you like.
Important: A tag value that's a single associative array is not supported:
// Don't do this, it won't work! Wrap it in another array.
return [
'tag' => 'a',
'attrs' => [
'href' => 'http://...'
]
];
Avaliable Nodes & Marks
Bard Mutator will replace all of the built-in node and mark classes with extended versions that support mutation, except for Statamic's Set node. These are:
- Nodes
- blockquote
- bullet_list
- code_block
- hard_break
- heading
- horizontal_rule
- image
- list_item
- ordered_list
- paragraph
- table
- table_cell
- table_header
- table_row
- Marks
- bold
- code
- italic
- link
- subscript
- underline
- strike
- superscript
🚨
Other Addons & Potential Incompatibilites
Because of the way ProseMirror works Bard Mutator has to replace all of the built-in node and mark classes with its own. And it can only do that reliably if there are no other addons (or user code) trying to do the same thing.
My other Bard addon (Bard Paragraph Style) is fully compatible with this. If Bard Mutator is installed it uses that under the hood instead of it's own node class.
However, if you have other addons (or user code) that replace any of the built-in node or mark classes Bard Mutator probably won't work properly. Unfortunately I don’t think there’s any way I can fix that.
This does not affect custom nodes and marks.