Für alles eine API
Alles, was Sumaq kann, könnt ihr steuern. Konfiguration, Methoden, Events, Style-Tokens: alles da, und alles funktioniert so, wie ihr es erwarten würdet.
Schnellstart
import Sumaq from './src/sumaq.js';
const editor = new Sumaq('#my-container', {
toolbar: ['h1', 'h2', 'p', '|', 'bold', 'italic', '|', 'link'],
onChange: (html) => console.log(html)
});
Der Konstruktor nimmt einen Container (CSS-Selektor oder DOM-Element) und ein optionales Konfigurationsobjekt. Er gibt eine Editor-Instanz zurück. Der bestehende HTML-Inhalt des Containers wird beibehalten und in den Editor geladen.
Konfiguration
| Option | Typ | Standard | Beschreibung |
|---|---|---|---|
toolbar |
string[] |
Alle 25 Buttons + Trennzeichen | Button-IDs in Reihenfolge. '|' fügt ein Trennzeichen ein. |
styleOverrides |
string | null |
null |
CSS-String, der als zweites <style> im Shadow DOM eingefügt wird, nach den Basis-Styles. |
onChange |
function | null |
null |
Callback bei entprellter Inhaltsänderung. Erhält bereinigtes HTML. |
extensions |
array |
[] |
Extension-Objekte. Werden durchgereicht. Siehe Extensions. |
Methoden
getHTML() → string
Gibt den Inhalt des Editors als bereinigtes HTML zurück. Im Quellcode-Modus wird der rohe Textarea-Wert vor der Rückgabe bereinigt.
const html = editor.getHTML();
setHTML(html) → void
Setzt den Inhalt des Editors. Die Eingabe wird bereinigt, dann normalisiert (semantische Tag-Konvertierung, Inline-Style-Bereinigung). Löst ein 'change'-Event aus.
editor.setHTML('<p>Hallo <strong>Welt</strong></p>');
getText() → string
Gibt den reinen Textinhalt zurück (ohne HTML-Tags).
const text = editor.getText();
on(event, callback) → void
Registriert einen Event-Listener.
editor.on('change', (html) => { /* ... */ });
off(event, callback?) → void
Entfernt einen Event-Listener. Wird callback weggelassen, werden alle Listener für dieses Event entfernt.
editor.off('change', myHandler); // bestimmten Listener entfernen
editor.off('change'); // alle 'change'-Listener entfernen
destroy() → void
Vollständiger Aufräumprozess. Trennt den Mutation-Observer, schließt offene Dialoge, entfernt den selectionchange-Listener, zerstört Extensions, baut das Shadow DOM ab und stellt den bereinigten HTML-Inhalt im ursprünglichen Container wieder her.
editor.destroy();
Events
'change'
Entprellt (150ms). Feuert, nachdem sich der Inhalt stabilisiert hat. Erhält bereinigtes HTML.
editor.on('change', (html) => saveToServer(html));
'input'
Sofort. Feuert bei jedem Tastendruck / jeder Content-Mutation. Erhält HTML (roh im Quellcode-Modus).
editor.on('input', (html) => updatePreview(html));
'drop'
Feuert, wenn Inhalte in den Editor gezogen werden. Das Standard-Drop-Verhalten des Browsers wird unterbunden. Der Editor fügt nichts selbst ein. Er reicht die Daten zur externen Verarbeitung weiter (z.B. CMS-Asset-Upload, Base64-Konvertierung).
editor.on('drop', ({ files, imageFiles, html, text, originalEvent }) => {
if (imageFiles.length) {
uploadToAssetManager(imageFiles).then(urls => {
urls.forEach(url => {
const img = document.createElement('img');
img.src = url;
img.alt = '';
// Editor-Methoden oder setHTML zum Einfügen verwenden
});
});
}
});
| Eigenschaft | Typ | Beschreibung |
|---|---|---|
files | File[] | Alle abgelegten Dateien |
imageFiles | File[] | Abgelegte Dateien mit image/* MIME-Typ |
html | string | HTML-Inhalt aus den Drop-Daten (falls vorhanden) |
text | string | Klartext-Inhalt aus den Drop-Daten (falls vorhanden) |
originalEvent | DragEvent | Das native Drop-Event, für Koordinaten oder zusätzlichen dataTransfer-Zugriff |
'imagePaste'
Feuert, wenn Bilder aus der Zwischenablage eingefügt werden (z.B. Screenshot einfügen, kopiertes Bild). Das Standard-Paste-Verhalten wird unterbunden. Wenn keine Bilder in der Zwischenablage sind, fällt der Editor auf Klartext-Einfügung zurück.
editor.on('imagePaste', ({ files }) => {
files.forEach(file => {
const reader = new FileReader();
reader.onload = () => {
const img = document.createElement('img');
img.src = reader.result; // base64
img.alt = '';
editor.setHTML(editor.getHTML() + img.outerHTML);
};
reader.readAsDataURL(file);
});
});
| Eigenschaft | Typ | Beschreibung |
|---|---|---|
files | File[] | Eingefügte Bilddateien (nur image/* MIME-Typ) |
Sowohl drop als auch imagePaste sind bewusst CMS-agnostisch. Der Editor liefert die Daten, die Integration entscheidet, was damit geschieht. Keine Dateien werden vom Editor selbst eingefügt, hochgeladen oder konvertiert.
Toolbar-Buttons
25 Kern-Buttons plus '|' als Trennzeichen.
| ID | Funktion | Ausgabe |
|---|---|---|
h1 | Überschrift 1 | <h1> |
h2 | Überschrift 2 | <h2> |
h3 | Überschrift 3 | <h3> |
h4 | Überschrift 4 | <h4> |
h5 | Überschrift 5 | <h5> |
h6 | Überschrift 6 | <h6> |
p | Absatz | <p> |
pre | Codeblock | <pre> |
blockquote | Zitat | <blockquote> mit <cite> |
bold | Fett | <strong> |
italic | Kursiv | <em> |
underline | Unterstrichen | <u> |
strikethrough | Durchgestrichen | <s> |
superscript | Hochgestellt | <sup> |
subscript | Tiefgestellt | <sub> |
mark | Hervorhebung | <mark> |
code | Inline-Code | <code> |
link | Link einfügen/bearbeiten | <a href>, Dialog mit drei Typen: URL (mit optionalem target="_blank"), E-Mail (mailto: mit optionalem Betreff), Telefon (tel:) |
anchor | Anker einfügen/bearbeiten | <a id>: ID muss mit einem Buchstaben beginnen, nur Buchstaben/Zahlen/Bindestriche/Unterstriche erlaubt. Wird live im Dialog validiert. |
alignLeft | Linksbündig | entfernt Ausrichtungsklasse |
alignCenter | Zentriert | .text-center |
alignRight | Rechtsbündig | .text-right |
ul | Aufzählung | <ul> |
ol | Nummerierung | <ol> |
hr | Horizontale Linie | <hr> |
table | Tabelle | <table>: Dialog für Zeilen/Spalten (max. 10). Steht der Cursor in einer bestehenden Tabelle, öffnet sich der Bearbeitungsmodus mit vorausgefüllten Maßen und Optionen zum Ändern oder Löschen. |
image | Bild | <img> (Dialog für src, alt) |
undo | Rückgängig | Browser-Undo |
redo | Wiederherstellen | Browser-Redo |
source | Quellcode-Ansicht | wechselt zu <textarea> mit rohem HTML. Beim Zurückwechseln zur Rich-Text-Ansicht wird der Inhalt bereinigt. |
'|' | Trennzeichen | optischer Teiler, keine Funktion |
Style-Overrides
Die styleOverrides-Konfiguration fügt ein zweites <style>-Element im Shadow DOM ein, nach den Basis-Styles. Euer CSS gewinnt durch die normale Kaskaden-Reihenfolge. Kein !important nötig.
const editor = new Sumaq('#editor', {
styleOverrides: `
.sumaq-content h1,
.sumaq-content h2,
.sumaq-content h3 {
font-family: Georgia, 'Times New Roman', serif;
}
.sumaq-content a {
color: oklch(.45 .15 260);
}
.sumaq-content blockquote {
border-left-color: oklch(.6 .1 260);
}
`
});
Der einfachste Weg: --sumaq-* Custom Properties auf :host ändern.
const editor = new Sumaq('#editor', {
styleOverrides: `
:host {
--sumaq-accent: oklch(.5 .2 150);
--sumaq-border-blockquote: oklch(.5 .2 150);
}
`
});
CSS Custom Properties
Alle Tokens verwenden das --sumaq--Präfix. Auf :host definiert, kaskadieren sie in jede Komponente innerhalb des Shadow DOM.
Oberfläche
| Token | Standard | Zweck |
|---|---|---|
--sumaq-bg | oklch(.985 .003 107) | Editor-Wrapper-Hintergrund |
--sumaq-bg-secondary | transparent | Toolbar-, Tabellenkopf-Hintergrund |
--sumaq-bg-tertiary | oklch(.947 .007 81) | Escape-Hinweis-Hintergrund |
--sumaq-bg-active | oklch(.947 .007 81) | Button-Aktiv-Status |
--sumaq-bg-content | oklch(.985 .003 107) | Inhaltsbereich-Hintergrund |
--sumaq-bg-content-focus | oklch(.985 .003 107) | Inhaltsbereich bei Fokus |
--sumaq-bg-code | oklch(.96 .005 95) | Inline-<code>-Hintergrund |
--sumaq-bg-pre | oklch(.2 .01 255) | <pre>- und Quellansicht-Hintergrund |
--sumaq-bg-mark | oklch(.94 .035 90) | <mark>-Hervorhebungs-Hintergrund |
--sumaq-bg-dialog | oklch(.985 .003 107) | Dialog-Hintergrund |
--sumaq-bg-backdrop | oklch(0 0 0 / .35) | Dialog-Backdrop-Overlay |
Text
| Token | Standard | Zweck |
|---|---|---|
--sumaq-text | oklch(.423 .003 107) | Primäre Textfarbe |
--sumaq-text-secondary | oklch(.55 .003 107) | Sekundärer Text (Zitat, Cite) |
--sumaq-text-tertiary | oklch(.7 .003 107) | Tertiärer Text (Platzhalter) |
--sumaq-text-code | oklch(.45 .04 255) | Inline-<code>-Textfarbe |
--sumaq-text-pre | oklch(.9 .005 255) | <pre>- und Quellansicht-Textfarbe |
Akzent
| Token | Standard | Zweck |
|---|---|---|
--sumaq-accent | oklch(.308 .051 253) | Links, Cursor, primäre Buttons, Aktiv-Zustände |
--sumaq-accent-hover | oklch(.25 .05 253) | Akzent-Hover-Status |
--sumaq-accent-text | oklch(.985 .003 107) | Text auf akzentfarbigen Hintergründen |
Gefahr
| Token | Standard | Zweck |
|---|---|---|
--sumaq-danger | oklch(.5 .13 30) | Löschen-/Entfernen-Buttons |
--sumaq-danger-hover | oklch(.43 .13 30) | Gefahr-Hover-Status |
Rahmen
| Token | Standard | Zweck |
|---|---|---|
--sumaq-border | oklch(.925 .008 92) | Editor-Wrapper-Rahmen, Dialog-Eingaben |
--sumaq-border-hover | oklch(.363 .039 255) | Rahmen-Hover-Status |
--sumaq-border-subtle | oklch(.947 .007 81) | Dialog-Kopf-/Fußzeilen-Trennlinien |
--sumaq-border-table | oklch(.925 .008 92) | Tabellenzellen-Rahmen, horizontale Linie |
--sumaq-border-blockquote | oklch(.308 .051 253) | Zitat-Linker-Rahmen |
Radius
| Token | Standard | Zweck |
|---|---|---|
--sumaq-radius | .1875rem | Standard-Radius (Buttons, Eingaben, Wrapper) |
--sumaq-radius-sm | .125rem | Kleiner Radius (Code, Mark) |
--sumaq-radius-lg | .375rem | Großer Radius (Dialog) |
Schatten
| Token | Standard | Zweck |
|---|---|---|
--sumaq-shadow-dialog | 0 .25rem 1rem oklch(0 0 0 / .08) | Dialog-Schatten |
--sumaq-focus-ring | 0 0 0 .125rem oklch(.363 .039 255 / .35) | Fokus-Ring auf Dialog-Eingaben |
Sicher zu überschreiben
Diese Styles sind für Anpassung vorgesehen. Frei änderbar.
Inhalts-Typografie: Schriftfamilien, Schriftgrößen, Zeilenhöhen, Abstände bei Überschriften/Absätzen/Listen. Die Überschriften-Staffelung (h1–h6 Schriftgrößen) ist ein guter Startpunkt.
Zitat-Styling: Rahmenfarbe, Hintergrund, Innenabstand, Cite-Formatierung.
Tabellen-Styling: Zellen-Innenabstand, Rahmenfarbe, Kopfzeilen-Hintergrund.
Code-Styling: <code>- und <pre>-Schriften, Hintergründe, Textfarben.
Link-Styling: Farbe, Unterstreichung, Hover-Status.
Button- und Dialog-Farben: Hintergrund, Textfarbe, Rahmenfarbe auf .sumaq-btn, .sumaq-btn-primary, .sumaq-dialog__cancel, .sumaq-link-remove. Bei Farben bleiben. Maße sind strukturell.
Nicht überschreiben
Der Editor liefert ein barrierefreies Fundament: wandernder Tabindex in der Toolbar, aria-pressed-Button-Zustände, Fokus-Management, Dialog-Fokus-Falle, Screen-Reader-Beschriftungen. Toolbar- oder Dialog-Styles zu überschreiben riskiert das zu brechen. Zwei Kategorien:
Strukturell: bricht das Layout
.sumaq-editorFlexbox (display,flex-direction).sumaq-toolbarFlex und Umbruch (display,flex-wrap,gap).sumaq-contentOverflow-/Resize-Verhalten (overflow-y,resize,min-height).sumaq-dialogZentrierung und Overlay (display,align-items,justify-content,height,width).sumaq-btnMaße (height,width), Icon-Ausrichtung und Touch-Targets hängen davon ab.sumaq-dialog__boxmax-width und Zentrierung
Barrierefrei: bricht Barrierefreiheit
.sumaq-visually-hidden: Screen-Reader-Text. Diese Klasse entfernen oder ändern versteckt Inhalte vor assistiver Technologie.- Fokus-Indikatoren (
outline,box-shadowauf:focus-visible), sehende Keyboard-Nutzer sind darauf angewiesen. aria-pressed-Button-Zustände, der visuelle Gedrückt-Zustand (.sumaq-btn[aria-pressed="true"]) muss unterscheidbar bleiben.- Dialog-Backdrop (
.sumaq-dialog::backdrop), fängt den visuellen Fokus. Transparent machen bricht das Modal-Pattern. - Escape-Hinweis (
.sumaq-esc-hint) Positionierung, absolut positioniert relativ zum Wrapper.
Farben und Rahmen könnt ihr bei all diesen umstylen. Die Empfehlung: Layout- und Sichtbarkeitsregeln in Ruhe lassen, es sei denn, ihr testet das Ergebnis mit Keyboard und Screen Reader.
Statische Eigenschaften
Sumaq.defaultConfig // Standard-Konfigurationsobjekt (toolbar, styleOverrides, onChange)
Sumaq.buttonConfig // Button-Definitionen: label, title, command, value/action
Sumaq.icons // SVG-Icon-Strings nach Button-ID
Sumaq.styles // Basis-CSS Template-Literal
Diese werden nach der Klassen-Definition zugewiesen. Ihr könnt sie lesen und technisch auch vor dem Erstellen einer Instanz modifizieren, aber buttonConfig und icons werden über alle Instanzen geteilt, Änderungen sind also global.
defaultConfig wird flach mit eurer Instanz-Konfiguration zusammengeführt ({ ...defaultConfig, ...config }), also ersetzt jede übergebene Option den Standard für diesen Schlüssel.
Bereinigung
Allowlist-basiert. Jedes Mal, wenn HTML in den Editor rein- oder rausgeht (setHTML(), getHTML(), Quellcode-Modus-Wechsel), durchläuft es die Bereinigung.
Was überlebt: Elemente in ALLOWED_ELEMENTS (31 semantische Tags: p, h1–h6, blockquote, pre, cite, ul, ol, li, table, thead, tbody, tr, td, th, hr, strong, em, u, s, sup, sub, mark, code, a, br, img) und Attribute in ALLOWED_ATTRS (pro Element: href/target/rel auf Links, src/alt auf Bildern, colspan/rowspan auf Tabellenzellen, start auf geordneten Listen, data-placeholder auf Cite, id global).
Was entfernt wird: <script>, <style>, <iframe>, <video>, <audio>, <svg>, <math>, Formularelemente werden mitsamt Inhalt entfernt. Unbekannte Elemente werden entpackt (Kinder bleiben). Alle on*-Event-Handler und javascript:-URIs werden entfernt. Inline-style- und class-Attribute werden durch einen separaten Bereinigungs-Schritt behandelt.
Extensions
Der Editor unterstützt eigene Extensions für Buttons, Dialoge und Formatierungen, die nicht zum Kern gehören. Extensions registrieren Buttons, deklarieren geschützte CSS-Klassen und erhalten Lifecycle-Hooks (init, onCommand, destroy).
Vollständige Dokumentation: Extensions