[![Profile picture for user Joachim](/sites/default/files/styles/thumbnail/public/avatars/1776092216762.png.avif?itok=EuiDMMaI)](/user/2)

  Gespeichert von [Joachim](/user/2 "Benutzerprofil anzeigen.") am So. 6 Sep 2026 - 14:52 

  09.06.2026

#  Der Formate-Button, der einfach nicht wollte: Ein unsichtbarer CKEditor-5-Bug und wie du ihn jagst 

 Geschrieben von [Joachim](/user/2)

 

 



 

 [ ckeditor ](/taxonomy/term/63) [ debugging ](/taxonomy/term/64)

  💡 Key Facts

- Das Problem: Ein CKEditor-5-"Formate"-Dropdown mit drei Callout-Styles (Update/Powertipp/Info) blieb dauerhaft grau und deaktiviert – egal, wo der Cursor stand.
- Die Falle: Jede Config-Prüfung sah perfekt aus. JSON-Settings korrekt, Dropdown-Einträge mit den richtigen Labels und Farben generiert. Trotzdem: nichts ging.
- Der eigentliche Bug: Die Styles zielten auf &lt;blockquote&gt; – aber CKEditor 5 kann Blockquote keine freie Klasse verpassen, weil das Element schon von einem eigenen Feature verwaltet wird.
- Der Fix: Ziel auf &lt;p&gt; umgestellt. Button war sofort klickbar, Dropdown zeigte die drei Farbkacheln mit Emoji-Label – fertig.
- Die Lehre: "Sieht in der Config richtig aus" ist keine Bestätigung. Öffne den echten Editor im Browser, bevor du eine Style-Konfiguration für fertig erklärst.
 
 Kennst du das Gefühl? Du baust ein Feature, jede Zeile Config stimmt, du liest deinen eigenen JSON-Output dreimal gegen – und trotzdem tut die UI einfach nicht das, was sie soll. Kein Fehler in der Konsole. Keine rote Meldung. Nur ein Button, der stur grau bleibt.

Genau das ist mir heute beim Bau der Callout-Boxen für diese Seite passiert. Und weil der Bug am Ende so unauffällig war, dass ich ihn fast übersehen hätte, will ich dir zeigen, wie ich ihn gefunden habe – damit du nicht dieselbe Stunde verlierst, wenn du mal selbst CKEditor-5-Styles konfigurierst.

## Die Mission: Drei Callout-Boxen für den Editor

Die Idee war simpel: Redakteur:innen sollen einen Absatz markieren, im Editor auf "Formate" klicken und daraus eine farbige Hinweisbox machen – 🔔 Update, 💡 Powertipp oder ℹ️ Info. Ähnlich wie ein Toast in Bootstrap 5, nur direkt im CKEditor nutzbar.

Drupal bringt dafür das passende Werkzeug schon mit: das CKEditor-5-"Style"-Plugin. Du definierst ein paar Styles in der Editor-Konfiguration, jeder bekommt ein Ziel-Element plus eine oder mehrere CSS-Klassen, und Drupal baut dir daraus automatisch das "Formate"-Dropdown. Kein eigenes JavaScript nötig.

Also los: Drei Styles definiert, jeweils gemappt auf ein **blockquote** mit eigener Klasse:

```yml
ckeditor5_style:
  styles:
    - label: Update
      element: '<blockquote class="callout callout--update">'
    - label: Powertipp
      element: '<blockquote class="callout callout--tip">'
    - label: Info
      element: '<blockquote class="callout callout--info">'
```

Dazu noch **filter\_html** erweitert, damit die drei Klassen auf blockquote überhaupt erlaubt sind, und ein bisschen CSS für Farbe und Emoji-Label. Sah in jeder Config-Ansicht wasserdicht aus.

## Der Endgegner: Ein Button, der nichts tut

Cache geleert, Editor neu geladen – und dann: nichts. Der "Formate"-Button in der Toolbar stand einfach nur grau da. Kein Klick, kein Dropdown, keine Reaktion. Egal, ob der Cursor im Text stand oder nicht.

Das Tückische daran: Es gab absolut keinen Hinweis darauf, *warum*. Kein Fehler in der Browser-Konsole. Die generierten JavaScript-Settings für den Editor zeigten exakt das, was ich erwartet hatte – die drei Styles, korrekt benannt, mit den richtigen Farben. Wenn du nur auf die Konfiguration schaust, würdest du schwören: Das muss funktionieren.

Hier ist der Punkt, an dem viele aufgeben und anfangen, wahllos an der Config herumzuschrauben. Stattdessen bin ich einen Schritt zurückgegangen und habe mir den Button selbst angeschaut – nicht die Config, sondern das tatsächliche DOM-Element im Browser:

document.querySelectorAll('button')  
 .find(b =&gt; b.textContent.trim() === 'Stile')  
 .getAttribute('aria-disabled');  
// → "true"

Da war er, der eigentliche Zustand: **aria-disabled="true"**. Fest verdrahtet, unabhängig von Cursorposition oder Auswahl. Der Button war nicht kaputt – er war *absichtlich* deaktiviert. Nur eben von CKEditor 5 selbst, aus einem Grund, den keine Config-Datei verraten hätte.

## Die Auflösung: Blockquote ist schon vergeben

CKEditor 5 aktiviert den "Formate"-Button nur, wenn mindestens einer der konfigurierten Styles auf das aktuelle Element angewendet werden *kann*. Und genau hier lag der Haken: blockquote wird bereits von einem eigenen, fest eingebauten Feature verwaltet – dem BlockQuote-Feature, das für den normalen "Zitat"-Button in der Toolbar zuständig ist.

Dieses Feature kennt zwar blockquote, aber es ist nicht darauf ausgelegt, dass ihm ein anderes Plugin einfach beliebige CSS-Klassen unterschiebt. Das Style-Plugin und das BlockQuote-Feature reden schlicht nicht dieselbe Sprache – und weil keins von beiden das als Fehler behandelt, bleibt der Button einfach für immer deaktiviert. Kein Absturz, keine Warnung. Er tut einfach: nichts.

Der Fix war am Ende eine einzige Änderung: statt blockquote als Ziel-Element &lt;p&gt; verwenden:

ckeditor5\_style:  
 styles:  
\- label: Update  
 element: '&lt;p class="callout callout--update"&gt;'  
\- label: Powertipp  
 element: '&lt;p class="callout callout--tip"&gt;'  
\- label: Info  
 element: '&lt;p class="callout callout--info"&gt;'

Warum &lt;p&gt;? Weil das Textformat hier schon lange bewiesen hat, dass Klassen-basiertes Styling auf &lt;p&gt; zuverlässig funktioniert – die Textausrichtung (linksbündig, zentriert, …) läuft über exakt denselben Mechanismus. Nach der Umstellung: Button sofort klickbar, Dropdown zeigt alle drei Styles mit Farbvorschau und Emoji-Label, Klick verwandelt den Absatz tatsächlich in die farbige Box.

## Warum eigentlich kein &lt;div&gt;? Die vollständige Liste

Naheliegende Frage, die ich mir selbst gestellt habe: Wenn blockquote schon Ärger macht, warum dann nicht einfach ein neutrales &lt;div class="callout"&gt; nehmen, ganz ohne konkurrierendes Feature?

Um das sauber zu beantworten, bin ich eine Ebene tiefer gegangen, als die Config normalerweise verlangt: direkt in den JavaScript-Quellcode, den Drupal-Core für das Style-Plugin mitliefert (core/assets/vendor/ckeditor5/style/style.js – minifiziert, aber lesbar). Dort steckt die eigentliche Logik, und sie beantwortet die Frage eindeutig.

CKEditor 5 unterscheidet für jedes Style-Ziel-Element drei Situationen:

1. **Kein eigenes Feature, direkt von General HTML Support (GHS) verstanden.** &lt;p&gt; und die Überschriften h1–h6 fallen darunter – GHS behandelt sie als sein eingebautes "paragraphartiges" Standardelement. Kein Konflikt möglich, weil niemand sonst Anspruch auf den Tag erhebt.
2. **Eigenes Feature, aber mit eingebauter Brücke zum Style-Plugin.** Listen, Tabellen und Links werden zwar von eigenen, spezialisierten Features verwaltet (List, Table, Link) – aber CKEditor 5 liefert dafür extra Kompatibilitäts-Plugins mit: ListStyleSupport, TableStyleSupport, LinkStyleSupport. Die verbinden das jeweilige Feature explizit mit dem Style-Plugin. Ich habe das für &lt;li&gt; selbst nachgestellt (Style nur auf &lt;li class="test-li"&gt; konfiguriert, Absatz per Toolbar in eine Liste verwandelt): Button sofort aktiv, keine Spur des Problems von vorhin.
3. **Eigenes Feature, aber keine Brücke.** Genau das ist der Fall bei blockquote. Das BlockQuote-Feature verwaltet den Tag exklusiv, aber es existiert kein BlockQuoteStyleSupport – anders als bei Listen, Tabellen und Links hat hier schlicht niemand die Brücke gebaut. Ergebnis: für immer deaktiviert, ohne jede Fehlermeldung.

Und &lt;div&gt;? Der landet in einer vierten, eigentlich noch einfacheren Kategorie: Es ist gar kein Tag, das dieses Textformat überhaupt erlaubt. General HTML Support kennt für diesen Editor nur die Elemente, die filter\_html freigibt – &lt;div&gt; steht da nicht drin. Der Style-Eintrag findet dadurch weder ein Block- noch ein Inline-Ziel und bleibt komplett wirkungslos, noch bevor die Frage "eigenes Feature ja/nein" überhaupt eine Rolle spielt.

Die gute Nachricht: Genau deshalb ist &lt;div&gt; kein Sackgassen-Fall. Wenn du unbedingt einen neutralen Container brauchst, kannst du ihn über die "Source Editing"-Konfiguration (Drupals Oberfläche für General HTML Support) explizit erlauben. Dann kennt GHS den Tag, niemand sonst beansprucht ihn – und er verhält sich wie &lt;p&gt;: Kategorie 1, keine Brücke nötig.

Hier die Liste als Tabelle, mit allem, was ich direkt bestätigen konnte (live getestet oder im Core-Quellcode mit Namen gefunden) und allem, was sich logisch aus derselben Mechanik ergibt:

| Element | Funktioniert mit Style? | Warum |
|---|---|---|
| &lt;p&gt; | Ja (live bestätigt) | Kein eigenes Feature, GHS-Standardelement |
| &lt;h1&gt;–&lt;h6&gt; | Ja | Gleiche GHS-Mechanik wie &lt;p&gt; |
| &lt;li&gt; | Ja (live bestätigt) | Eigenes List-Feature, aber ListStyleSupport-Brücke |
| &lt;ul&gt;, &lt;ol&gt; | Ja | Gleiche ListStyleSupport-Brücke |
| &lt;td&gt;, &lt;th&gt; | Ja | Eigenes Table-Feature, aber TableStyleSupport-Brücke |
| &lt;thead&gt;, &lt;tbody&gt; | Ja | Gleiche TableStyleSupport-Brücke |
| &lt;a&gt; | Ja (als Inline-Style) | Eigenes Link-Feature, aber LinkStyleSupport-Brücke |
| &lt;blockquote&gt; | Nein (live bestätigt) | Eigenes BlockQuote-Feature, keine Brücke vorhanden |
| &lt;hr&gt;, &lt;br&gt;, &lt;img&gt;, drupal-media | Nein | Von Drupal-Core offiziell als "bekannt inkompatibel" dokumentiert, inkl. Verweis auf offene Upstream-Issues |
| &lt;div&gt;, &lt;span&gt;, &lt;article&gt;, &lt;section&gt;, &lt;figure&gt; | Nein – aber nachrüstbar | Vom Textformat gar nicht erlaubt; GHS kennt den Tag nicht. Über "Source Editing" freischalten, dann wie &lt;p&gt; nutzbar |

Zeilen ohne "live bestätigt" habe ich nicht selbst durchgeklickt, sondern aus derselben, im Quellcode klar benannten Mechanik abgeleitet (Plugin-Namen wie ListStyleSupport oder TableStyleSupport sind kein Rateergebnis, die stehen wortwörtlich so im Code). Bei &lt;hr&gt;, &lt;br&gt;, &lt;img&gt; und drupal-media ist es noch eindeutiger: Die stehen in einer festen Liste namens KNOWN\_UNSUPPORTED\_TAGS direkt im Drupal-Core-Code, mit Kommentaren, die auf die jeweiligen offenen Tickets verweisen.

## Die größere Lehre

Eine Konfiguration, die auf dem Papier – oder im JSON-Export – korrekt aussieht, ist noch keine funktionierende Konfiguration. Öffne den echten Editor, bevor du einen Haken dran machst. Manche Bugs verstecken sich nicht im Code, sondern genau in der Lücke zwischen "sieht richtig aus" und "tut auch, was es soll". Und wenn du wirklich wissen willst, warum etwas nicht geht: Config-Ebene ist oft nur die halbe Wahrheit – die andere Hälfte steckt manchmal im JavaScript, das der Browser tatsächlich ausführt.



  🔗 Weiterführende Links

- [CKEditor 5 Style-Feature (offizielle Doku)](https://ckeditor.com/docs/ckeditor5/latest/features/style.html)
- [CKEditor 5 General HTML Support (offizielle Doku)](https://ckeditor.com/docs/ckeditor5/latest/features/html/general-html-support.html)
- [Drupal.org: CKEditor 5-Modul](https://www.drupal.org/project/ckeditor5)
- [MDN: aria-disabled](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-disabled)
 
  [Zur Blog-Übersicht](/blog) 

  [ ←  Zurück Jahresrückblick 2025 – Wie die KI Deutsch lernte &amp; was 2026 kommt  ](/blog/jahresrueckblick-2025-wie-die-ki-deutsch-lernte-was-2026-kommt)