tl;dr: This custom Drupal module filters out when editors try to add bold within headings.
The Problem
Headings within content are already styled and semantically meaningful as they are. They do not also need to be bolded. Bolding them just makes it more semantically confusing and visually inconsistent. Some editors will bold them anyway, either because they (incorrectly) think it looks better or because they've copied in from Word without even thinking about it.
The Solution
The full module is in my Codeberg. Note that there may be updates there which are not reflected here.
The module will strip out the <strong> tag when it gets included within a heading. The core logic is defined as part of the filter under src/Plugin/Filter:
<?php
namespace Drupal\ckeditor_filter_bold_headings\Plugin\Filter;
use Drupal\Component\Utility\Html;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\filter\Attribute\Filter;
use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
use Drupal\filter\Plugin\FilterInterface;
/**
* Provides a filter to remove strong within headings.
*/
#[Filter(
id: 'ckeditor_bold_headings_filter',
title: new TranslatableMarkup('Remove bold from within headings'),
type: FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE,
weight: -100,
)]
class FilterBoldHeadings extends FilterBase {
/**
* {@inheritdoc}
*/
public function process($text, $langcode): FilterProcessResult {
$dom = Html::load($text);
$xpath = new \DOMXPath($dom);
// Loop through all heading tags.
foreach (['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as $heading_tag) {
$headings = $dom->getElementsByTagName($heading_tag);
foreach ($headings as $heading) {
// Find all <strong> inside this heading and unwrap them.
$strong_within_headings = $xpath->query('.//strong', $heading);
if (is_iterable($strong_within_headings)) {
foreach ($strong_within_headings as $strong) {
if ($strong instanceof \DOMNode) {
$this->unwrapElement($strong);
}
}
}
}
}
$text = Html::serialize($dom);
return new FilterProcessResult($text);
}
/**
* Unwrap an element by replacing it with its children.
*/
protected function unwrapElement(\DOMNode $el): void {
$parent = $el->parentNode;
if (!$parent) {
return;
}
while ($el->firstChild) {
$parent->insertBefore($el->firstChild, $el);
}
$parent->removeChild($el);
}
}
Previous: Google Gem Prompt
Next: Traefik Error Page