Sveltia CMS für 11ty: die Redaktionsoberfläche, die dem Generator fehlt

Ein Git-basiertes CMS als eine HTML-Datei und ein Script-Tag: Was Sveltia CMS für einen 11ty-Blog leistet, wie es sich ohne CDN selbst hosten lässt — und warum zwei Seiten der eigenen Doku beim Pfadaufbau Verschiedenes behaupten.

von ·

Vor zwei Tagen habe ich einen Absatz geschrieben, der mich seitdem beschäftigt:

Redaktionelle Teams ohne Terminal. 11ty setzt Git, eine Kommandozeile und einen Editor voraus. Für Kolleginnen und Kollegen, die einen Beitrag schreiben und auf ‚Veröffentlichen' klicken wollen, ist das die falsche Bauform."

Der Satz stimmt, und er zeigte sogar schon in die richtige Richtung: Git-basierte Redaktionsoberflächen und Build Awesome Pro stehen im Original zwei Zeilen später. Nur habe ich beide bloß genannt und nicht nachgesehen. Als Ratschlag hört der Absatz damit genau dort auf, wo es interessant wird — und genau das mache ich anderen Artikeln sonst zum Vorwurf.

Also habe ich mir angesehen, was diese Grenze tatsächlich verschiebt. Der Kandidat heißt Sveltia CMS, und ich habe mich damit ernsthaft beschäftigt: Dokumentation gegen Quelltext geprüft, das ausgelieferte Paket vermessen, und die Konfiguration, die diese Knowledge Base bräuchte, geschrieben und gegen das offizielle JSON-Schema validiert. Was ich nicht getan habe: es in Produktion nehmen. Wo ich also messe, sage ich es; wo ich einordne, auch.

Vorweg das Ergebnis, damit du nicht bis unten scrollen musst: Es ist ein bemerkenswert gutes Stück Software, und der erste ernsthafte Stolperstein war nicht das CMS. Er war meine eigene Verzeichnisstruktur.

Was Sveltia CMS ist

Ein Git-basiertes Headless-CMS. Die eigene Beschreibung ist selbstbewusst und, soweit ich das prüfen kann, zutreffend:

„Sveltia CMS is a leading Git-based headless CMS for Jamstack sites. It's open source, free, and a complete modern rewrite of Netlify CMS, now known as Decap CMS."

„Git-basiert" ist dabei das entscheidende Wort, und es bedeutet etwas anderes als bei einem klassischen CMS. Es gibt keine Datenbank und keinen Server. Die Inhalte sind und bleiben Markdown-Dateien mit YAML-Frontmatter in deinem Repository. Das CMS ist eine Einzelseiten-Anwendung, die im Browser läuft, sich über die GitHub-Schnittstelle an dein Repository hängt und beim Speichern einen Commit macht. Danach läuft dein normaler Build.

Anders gesagt: Es ist ein Formular über deinen Dateien. Nicht mehr, und das ist der ganze Trick.

Hinter dem Projekt steht eine Person: Kohei Yoshino aus Toronto, nach eigener Angabe UX-Engineer mit über zwanzig Jahren Weberfahrung und ehemals langjähriger Lokalisierer bei Mozilla. Die Lizenz ist MIT. Gebaut ist es mit Svelte 5 — bewusst nicht mit SvelteKit, und die Begründung sagt viel über die Prioritäten:

„It's not a SvelteKit application, as we need to maintain its architecture as a single-file JavaScript bundle to ensure compatibility with Netlify/Decap CMS."

Ein Bündel, eine Datei, ein Script-Tag. Ich habe nachgemessen, was da tatsächlich über die Leitung geht — Version 0.197.1, abgerufen am 24. August 2026 vom offiziellen CDN:

Wert Gemessen
dist/sveltia-cms.js roh 1.969.605 Bytes (1,88 MiB)
dasselbe, gzip-komprimiert 601.579 Bytes (588 KiB) mit gzip -9; über die Leitung liefert unpkg rund 601.800
CSS-Dateien im Paket 0
Sprachdateien 26, de.json mit 46.419 Bytes

Knapp 600 KiB für eine komplette Redaktionsoberfläche sind in Ordnung — und sie treffen ausschließlich die Redaktion, nie die Besucher deiner Seite. Die Null bei den CSS-Dateien ist kein Zufall, sondern eine Designentscheidung, auf die ich gleich zurückkomme.

Die Vorgeschichte, die du kennen solltest

Um einzuordnen, warum es dieses Projekt gibt, muss ich kurz ausholen — und dabei einen Punkt geraderücken, den ich im Netz regelmäßig falsch dargestellt sehe.

Am Anfang stand Netlify CMS, jahrelang die Standardantwort auf „ich will ein Formular über meinem Jekyll-Blog". Irgendwann blieb die Entwicklung liegen. Sveltia CMS entstand nach eigener Darstellung „in November 2022, when the progress of Netlify CMS was stalled for more than six months". Im Februar 2023 übernahm ein anderes Team das Original und nannte es Decap CMS — mit der bemerkenswert offenen Begründung: „We took this step because we didn't like that this awesome project was slowly dying, and we wanted to ensure that it would continue to be supported and improved."

Zwei Nachfolger, dieselbe Wurzel. Beide leben, und Decap ist entgegen mancher Erzählung nicht tot: Version 3.15.1 stammt vom 24. Juli 2026, Commits gab es noch letzte Woche, und im Mai 2026 wurde mit „Decap Turbo" eine kommerzielle Ergänzung angekündigt. Wer Decap heute abschreibt, liegt falsch.

Und jetzt der Punkt, den fast alle vermischen: Netlify Identity ist nicht abgekündigt — Git Gateway schon. Netlify hatte die Abkündigung von Identity angekündigt und sie am 19. Februar 2026 ausdrücklich zurückgenommen: „Netlify Identity will continue as a supported authentication option on Netlify." Der Baustein, der wirklich stirbt, ist Git Gateway, und den braucht der klassische Anmeldeweg von Netlify/Decap CMS. Die Netlify-Dokumentation ist unmissverständlich:

„Git Gateway is deprecated. While Git Gateway continues to function for sites that currently have it enabled, new Git Gateway configurations are not recommended."

Das ist keine Wortklauberei, sondern eine Planungsgrundlage: Wer heute eine neue Git-CMS-Installation aufsetzt, sollte den Git-Gateway-Weg gar nicht erst einschlagen. Sveltia CMS unterstützt ihn konsequenterweise nicht — und, ebenso konsequent, auch das Netlify-Identity-Widget nicht, „it's not useful without Git Gateway".

Zwei Dateien, und du bist drin

Die Installation ist der Teil, bei dem ich beim ersten Lesen dachte, ich hätte etwas überblättert. Es sind wirklich zwei Dateien in deinem statischen Verzeichnis. Erstens admin/index.html, unverändert aus der offiziellen Anleitung:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="robots" content="noindex" />
    <title>Sveltia CMS</title>
  </head>
  <body>
    <script src="https://unpkg.com/@sveltia/cms/dist/sveltia-cms.js"></script>
  </body>
</html>

Zweitens admin/config.yml daneben — die Datei wird automatisch aus demselben Verzeichnis geladen, ohne dass du einen Pfad angeben musst.

In einem 11ty-Projekt gehört beides in dein statisches Verzeichnis; in diesem hier wäre das src/static/, das eins zu eins in die Ausgabewurzel kopiert wird. Die Sveltia-Dokumentation führt Eleventy in ihrer Tabelle der statischen Verzeichnisse übrigens ausdrücklich auf — und nennt an anderer Stelle sogar 11tys Entwicklungsport 8080 beim Namen. Das Projekt weiß, wer es benutzt.

Zwei Dinge, die du nicht tun sollst, stehen ausdrücklich in der Anleitung — und der erste hat mich zum Grinsen gebracht:

„Some AI agents, namely Claude, include a stylesheet <link> tag in Sveltia CMS setups, apparently due to confusion with Static CMS, a now-discontinued fork of Netlify CMS. However, Sveltia CMS does not require any additional CSS files, as all the necessary styles are bundled within the JavaScript file."

Das ist die Null aus meiner Tabelle oben: Ich habe die Dateiliste des veröffentlichten Pakets abgefragt, 37 Dateien, keine einzige CSS-Datei. Die Warnung ist also berechtigt, und sie ist ein hübsches Lehrstück darüber, wie sich ein Irrtum über Trainingsdaten fortpflanzt. Der zweite Hinweis: kein type="module" am Script-Tag. Ein ES-Modul liegt dem Paket zwar bei, nur ist es nicht das, was der CDN-Schnipsel lädt — und das Attribut kann beim Einsatz der JavaScript-Schnittstelle zu unerwartetem Verhalten führen.

Das Schema, das mir einen Fehler gefunden hat

Beim Konfigurieren gibt es einen Kniff, den ich in dieser Form von kaum einem anderen Projekt kenne: Zu jeder Version wird ein JSON-Schema mit veröffentlicht. Eine Zeile am Kopf deiner Konfiguration, und dein Editor vervollständigt und prüft mit:

# yaml-language-server: $schema=https://unpkg.com/@sveltia/cms/schema/sveltia-cms.json

Ich habe das nicht geglaubt, sondern nachgesehen: Die Datei ist da, 294.117 Bytes, HTTP 200. Und dann habe ich meine eigene Konfiguration dagegen validiert — mit einem Ergebnis, das ich so nicht erwartet hatte. Ein Feld war falsch, und zwar dieses:

- { name: kind, label: Typ, widget: hidden, default: article, i18n: duplicate }

Das Schema lehnt es ab — aber nicht dort, wo ich es vermutet hätte. Das hidden-Widget kennt genau vier Schlüssel: name, widget, default und i18n. Ein label ist nicht darunter, denn ein unsichtbares Feld hat nichts zu beschriften. i18n: duplicate dagegen ist ausdrücklich erlaubt und hier sogar richtig — es kopiert den Wert der Default-Sprache in alle übrigen. Falsch war also nur die Beschriftung, und darauf kommst du selbst nicht, wenn du gerade zwanzig Felder mit Beschriftungen versiehst. Richtig ist:

- { name: kind, widget: hidden, default: article, i18n: duplicate }

Ein Konfigurationsfehler, gefunden bevor die Anwendung überhaupt einmal gestartet war. Das ist genau die Art von Prüfung, die ich mir von mehr Projekten wünschen würde, und für mich einer der stärksten Punkte überhaupt: Ein CMS, dessen Konfiguration maschinenprüfbar ist, verlagert eine ganze Klasse von Fehlern aus der Laufzeit in den Editor.

Der Test an meinem eigenen Projekt

Jetzt der Teil, der wehtut, und der Grund, warum dieser Artikel nicht mit einer Empfehlung endet.

Diese Knowledge Base ist zweisprachig. Deutsch ist Default und liegt unter src/content/…, Englisch spiegelt alles unter src/content/en/…. Das ist die allererste Architekturentscheidung dieses Projekts gewesen, und sie folgt der Hauptseite.

Mehrsprachigkeit ist bei Sveltia CMS kein nachgerüstetes Beiwerk, sondern der Gründungsgrund:

„Sveltia CMS was originally built for @kyoshino's Japanese clients, who needed a CMS that could handle multilingual content efficiently. The maintainer is a former long-time localizer for Mozilla and lives in the most diverse city in the world where 150+ languages are spoken."

Das merkst du sofort. Fünf Strukturen für mehrsprachige Inhalte stehen zur Wahl, und übersetzte Fassungen werden automatisch verknüpft: Im Editor siehst du einen Eintrag mit mehreren Sprachreitern, nicht zwei getrennte Dateien. Genau das wollte ich haben. Also habe ich die dokumentierten Pfadmuster nebeneinandergelegt:

Struktur Erzeugter Pfad
single_file /<folder>/<path>.<extension>
single_file_default_root /<folder>/<path>.<extension> (wie single_file, nur liegen die Felder der Default-Sprache auf der obersten Ebene)
multiple_files /<folder>/<path>.<locale>.<extension>
multiple_folders /<folder>/<locale>/<path>.<extension>
multiple_root_folders /<locale>/<folder>/<path>.<extension>

Und dann fiel der Groschen — der falsche. Meine Struktur ist src/content/en/blog/artikel.md, die Sprache sitzt also mittendrin, zwischen content/ und dem Bereichsordner. Setze ich folder: src/content/blog, erzeugt multiple_folders daraus src/content/blog/en/artikel.md und multiple_root_folders ein en/src/content/blog/artikel.md. Keins von beidem ist meine Struktur. Ich hatte den Absatz schon geschrieben, in dem das Projekt an meiner Verzeichnisentscheidung von 2026 scheitert.

Der Fehler war meiner. Ich hatte <folder> für den ganzen Pfad gehalten, und das ist er nicht. Die Dokumentation der Collections sagt es deutlich, nur eben an einer anderen Stelle: folder ist die Basis, und path darf Schrägstriche enthalten („It can contain slashes to create a nested folder structure", Standardwert {{slug}}). Der Sprachanteil wird nicht zwischen Bereich und Datei eingefügt, sondern zwischen folder und path. Damit lässt sich der Schnitt frei legen:

folder: src/content      # Basis
path: blog/{{slug}}      # der Rest — darf Schrägstriche enthalten

Rechne es durch: multiple_folders ergibt src/content/de/blog/artikel.md und src/content/en/blog/artikel.md. Die englische Seite stimmt damit exakt. Nur meine deutschen Dateien liegen ohne de/ direkt unter src/content/blog/ — und genau dafür gibt es omit_default_locale_from_file_path: true.

Wo die Dokumentation sich selbst widerspricht

Und hier wurde es interessant, denn an dieser Stelle sagt die Dokumentation zweierlei. Die Seite über Entry-Collections schreibt für multiple_folders mit gesetztem omit_default_locale_from_file_path:

/<folder>/<path>.<extension>          # default locale
/<locale>/<folder>/<path>.<extension> # other locales

Nach dieser Tabelle würde die Sprache für alle anderen Sprachen nach vorn springen, ganz an den Anfang des Pfades — aus src/content/en/blog/ würde en/src/content/blog/. Damit wäre ich wieder am Ausgangspunkt.

Die eigene i18n-Seite sagt an derselben Stelle aber etwas anderes:

/<folder>/<path>.<extension>          # default locale
/<folder>/<locale>/<path>.<extension> # other locales

Zwei Seiten derselben Dokumentation, zwei verschiedene Pfade. Entschieden wird das nicht durch Lesen, sondern durch Nachsehen. In dist/sveltia-cms.mjs steht der Pfadbau als schlichtes switch; hier mit den zurückübersetzten Namen aus dem minifizierten Bündel:

switch (structure) {
  case "multiple_folders":
    return omitLocale ? `${basePath}/${path}.${ext}`
                      : `${basePath}/${locale}/${path}.${ext}`;
  case "multiple_folders_i18n_root":
  case "multiple_root_folders":
    return omitLocale ? `${basePath}/${path}.${ext}`
                      : `${locale}/${basePath}/${path}.${ext}`;
  // …
}

Da steht es schwarz auf weiß: Bei multiple_folders bleibt der Sprachanteil hinter der Basis, omit_default_locale_from_file_path lässt ihn für die Default-Sprache lediglich weg — es verschiebt ihn nicht. Nur multiple_root_folders zieht ihn nach vorn. Der Ausdruck, mit dem das CMS vorhandene Dateien wiedererkennt, ist genauso gebaut: Das Sprach-Segment wird dort schlicht optional ((?:(?<locale>en)\/)?), nicht umgehängt.

Damit ist der Fall klar: Die i18n-Seite hat recht, die Collections-Seite hat einen Fehler — dort ist offensichtlich die Zeile aus dem multiple_root_folders-Block einen Absatz zu weit oben gelandet. Das ist keine Grauzone und kein undokumentiertes Verhalten, sondern ein Tippfehler in einer von zwei Doku-Seiten. Gemeldet gehört er trotzdem, denn er kostet genau die Entscheidung, die ich hier fast falsch getroffen hätte.

Für mein Projekt heißt das: Es passt. Diese Konfiguration validiert gegen das offizielle Schema mit null Fehlern

i18n:
  structure: multiple_folders
  locales: [de, en]
  default_locale: de
  omit_default_locale_from_file_path: true

collections:
  - name: blog
    label: Blog
    label_singular: Beitrag
    folder: src/content
    path: blog/{{slug}}
    create: true
    i18n: true
    fields:
      - { name: title, label: Titel, widget: string, i18n: true }
      - { name: description, label: Beschreibung, widget: text, i18n: true }
      - { name: translationKey, label: Übersetzungsschlüssel, widget: string, i18n: duplicate }
      - { name: kind, widget: hidden, default: article, i18n: duplicate }
      - { name: date, label: Datum, widget: datetime, i18n: duplicate }
      - name: tags
        label: Themen
        widget: select
        multiple: true
        i18n: duplicate
        options: [eleventy, ssg, cms, node, npm, markdown, security, accessibility, seo, geo]
      - { name: body, label: Inhalt, widget: markdown, i18n: true }

— und die nachgerechnete Pfadlogik liefert src/content/blog/artikel.md für Deutsch und src/content/en/blog/artikel.md für Englisch. Also genau den Baum, der hier seit dem ersten Tag liegt, ohne dass eine einzige Datei umzieht. Inklusive automatischer Verknüpfung: Beide Sprachfassungen wären ein Eintrag mit zwei Reitern.

Ein Vorbehalt gehört dazu, und ich will ihn nicht kleinreden: Ich habe das aus Schema, Doku und Quelltext hergeleitet, nicht in einer laufenden Installation gegen mein Repository getestet. Drei übereinstimmende Belege sind keine Inbetriebnahme. Vor einem echten Redaktionsstart würde ich es an einem Wegwerf-Branch ausprobieren, und das solltest du auch.

Wer trotzdem lieber auf Nummer sicher geht, hat einen konservativen Ausweg: zwei getrennte Sammlungen mit folder: src/content/blog und folder: src/content/en/blog, ganz ohne i18n-Block. Das läuft garantiert, kostet aber die automatische Verknüpfung — der translationKey, der bei mir DE und EN zusammenhält, wird wieder ein Feld, das jemand von Hand richtig setzen muss.

Nebenbei ist das select-Feld mit fester Optionsliste die kleine Freude in dieser Konfiguration: Diese Seite pflegt ihre Themen-Tags in einem kontrollierten Vokabular, und ein frei erfundener Tag rendert hier nur den nackten Slug statt eines gepflegten Labels. Als Auswahlliste im Editor kann das gar nicht mehr passieren. Eine Regel, die bei mir bisher in einer Dokumentationsdatei stand, wird zu einer, die das Werkzeug durchsetzt.

Was ich aus dem Nachmittag mitnehme, ist trotzdem die Lehre, die ich schon aufgeschrieben hatte, bevor sich der Befund gedreht hat — sie stimmt jetzt sogar doppelt: Sobald ein Git-basiertes CMS auf dein Repository schaut, ist dein Verzeichnisbaum eine Schnittstelle. Er war es vorher schon, nur hat es niemand gemerkt, weil bis dahin nur der Generator hineingeschaut hat, und der liest alles. Ein CMS hat Meinungen. Und die zweite Hälfte der Lehre habe ich mir selbst verpasst: Ich hätte diesen Artikel um ein Haar mit einem falschen Fazit veröffentlicht, weil ich eine Doku-Tabelle gelesen habe — ausgerechnet die fehlerhafte — statt zwei Seiten zu vergleichen und im Zweifel nachzurechnen. Wenn du vor einem zweisprachigen 11ty-Projekt sitzt und auch nur ahnst, dass irgendwann jemand anders darin schreiben soll: Sieh dir die fünf Strukturen an, bevor du das erste Verzeichnis anlegst — und prüf im Zweifel, was das Werkzeug tut, nicht nur, was es über sich schreibt.

Was mich sonst überzeugt hat

Jenseits meines Sonderfalls ist die Funktionsliste ernsthaft beeindruckend, und ich nenne nur, was ich für einen Blog wie diesen tatsächlich brauchen würde.

Der Umstieg von Decap ist wirklich eine Zeile. Die Migrationsseite zeigt sie als Diff:

-<script src="https://unpkg.com/decap-cms@^3.0.0/dist/decap-cms.js"></script>
+<script src="https://unpkg.com/@sveltia/cms/dist/sveltia-cms.js"></script>

Das Projekt bleibt dabei erfreulich ehrlich und verspricht nicht zu viel: „In some casual use cases, Sveltia CMS can be used as a drop-in replacement for Netlify/Decap CMS with just a one-line code update." Und zwei Absätze weiter: „However, 100% feature parity is never planned, and some features are still missing or will not be added due to deprecation and other factors."

Bildoptimierung schon beim Hochladen. Rasterbilder werden im Browser nach WebP gewandelt, SVGs optimiert, alles über ein paar Zeilen Konfiguration:

media_libraries:
  default:
    config:
      transformations:
        raster_image:
          format: webp
          quality: 85
          width: 2048
        svg:
          optimize: true

Wer je hinter einer Redaktion hergeräumt hat, die 6-Megapixel-Handyfotos direkt ins CMS lädt, weiß, was das wert ist. Und es passiert im Browser, bevor der Commit entsteht — das Riesenbild kommt gar nicht erst in die Git-Historie, wo du es nie wieder los wirst.

Redaktionelle Freigaben als Pull Requests. Mit publish_mode: editorial_workflow bekommt jeder Entwurf einen eigenen Branch nach dem Muster cms/<collection>/<slug> und einen offenen Pull Request; Veröffentlichen heißt Mergen. Das ist die eleganteste Lösung dieses Problems, die ich kenne, weil sie kein neues Konzept erfindet, sondern eines benutzt, das ohnehin da ist — und das dir als Entwickler den vollen Git-Werkzeugkasten für den Freigabeprozess offenhält.

Commits werden signiert. Über die GitHub-Schnittstelle erzeugte Commits sind automatisch GPG-signiert und als „verified" markiert.

Die Content-Security-Policy wird einfacher, nicht schwerer. Anders als Decap braucht Sveltia CMS weder unsafe-eval noch unsafe-inline in script-src. Ein Detail für den deutschen Fall: Eine nicht-englische Oberfläche lädt ihre Sprachdatei zur Laufzeit vom CDN nach — die de.json mit ihren 46 KB, die ich oben gemessen habe. Dafür brauchst du connect-src https://unpkg.com in deiner Policy. Nicht dramatisch, aber genau die Zeile, die du beim ersten Anlauf vergisst und dann eine Stunde suchst.

Die Oberfläche gibt es auf Deutsch, in 26 Sprachen insgesamt, mit automatischer Erkennung der Browsersprache. Für den Anwendungsfall aus meinem Ausgangsabsatz — Kolleginnen und Kollegen, die kein Terminal wollen — ist das kein Luxus, sondern die halbe Miete.

Barrierefreiheit: Anspruch und Stand

Das ist mein Thema, deshalb schaue ich hier genauer hin — und deshalb formuliere ich es vorsichtiger, als das Marketing es täte.

Der Anspruch ist explizit: „Sveltia CMS is built with accessibility as a core principle." Genannt werden vollständige Tastaturbedienung samt Alternativen für Drag-and-Drop, „Full WAI-ARIA support for screen readers like NVDA and VoiceOver", angesagte Seitenwechsel, ausreichende Kontraste, standardmäßig unterstrichene Links und respektierte Einstellungen für prefers-reduced-motion und prefers-reduced-transparency. Die Oberflächenkomponenten stammen aus einer eigenen Bibliothek, die ausdrücklich „for optimal keyboard usability without compromising accessibility" gebaut ist.

Und jetzt die Einordnung, die dazugehört. Das Projekt sagt selbst:

„We'll conduct an accessibility self-audit and address any issues before the 1.0 release to make sure Sveltia CMS meets the WCAG 2.2 standard."

Im Klartext: Es gibt heute keine veröffentlichte Prüfung — weder eine externe noch die eigene. Der Audit ist ein Punkt auf der Roadmap für Version 1.0. Unterstützung für prefers-contrast fehlt noch. Wer eine Redaktionsoberfläche in einem Umfeld einsetzen muss, in dem das BFSG greift, hat damit einen Anspruch und einen Terminplan in der Hand, aber keinen Nachweis. Das ist immer noch mehr, als die meisten CMS-Oberflächen bieten — es ist nur eben kein Häkchen, das du in einer Ausschreibung setzen kannst.

Fair ist auch: Redaktionsoberflächen fallen oft nicht unter dieselben Verpflichtungen wie die öffentliche Website. Wenn deine Redaktion aber Menschen mit Behinderungen einschließt — und das sollte sie —, ist das keine juristische, sondern eine praktische Frage.

Der lokale Weg, und warum er bei mir hakt

Es gibt einen zweiten Betriebsmodus, der ohne jeden Dienst auskommt: „Work with Local Repository". Du öffnest die Oberfläche, wählst dein Projektverzeichnis, und das CMS bearbeitet die Dateien direkt auf deiner Platte — über die File System Access API des Browsers, ohne Hilfsserver. Decap braucht dafür einen Proxy-Prozess; Sveltia lehnt den ausdrücklich ab: „For security and performance reasons, we don't support netlify-cms-proxy-server or decap-server."

Elegant. Und für mich bislang nicht nutzbar, aus zwei dokumentierten Gründen.

Erstens funktioniert die File System Access API nur in Chromium-basierten Browsern. Firefox und Safari fallen raus.

Zweitens, und das trifft mich direkt:

„If you're using Windows Subsystem for Linux (WSL), you may get an error saying ‚Can't open this folder because it contains system files.' This is due to a limitation in the browser, and you can try some workarounds mentioned in this issue and this thread."

Ich entwickle unter WSL2. Meine Projekte liegen im Linux-Dateisystem, der Browser läuft unter Windows. Genau die Konstellation, die der Absatz beschreibt. Es gibt Umgehungen in den verlinkten Diskussionen, aber „es gibt Umgehungen" ist bei einem Redaktionswerkzeug kein Satz, mit dem ich ein Team allein lasse.

Und selbst wenn es liefe, bliebe eine Einschränkung, die du kennen musst: Der lokale Modus macht keine Git-Operationen. „You have to manually fetch, pull, commit and push all changes using a Git client." Das ist als Vorschau-Modus für Entwickler großartig und als Redaktionswerkzeug für Nicht-Techniker das Gegenteil dessen, was du suchst.

Für den eigentlichen Anwendungsfall bleibt also der GitHub-Weg. Und der ist bequemer, als ich dachte: Für dich selbst genügt ein persönlicher Zugangstoken, ganz ohne Konfigurationsänderung. Erst wenn sich Menschen anmelden sollen, die keinen GitHub-Zugang haben, brauchst du einen OAuth-Client — das Projekt liefert dafür ein eigenes Cloudflare-Workers-Skript, dessen README gleich unter der Überschrift einen fettgedruckten Warnkasten trägt: „In most cases, you don't need this authenticator."

Die ehrliche Kehrseite

Ein Fan-Artikel ohne Grenzen ist Werbung, hier wie im letzten Artikel. Also der Reihe nach.

Es ist Beta, und das steht groß über der Anleitung. „Sveltia CMS is still in beta. Although it's already being used in production by many users, there might still be breaking changes before the stable 1.0 release." Die aktuelle Version ist 0.197.1. Version 1.0 ist für „late 2026" avisiert, mit dem Zusatz „this date may change".

Die Veröffentlichungsfrequenz ist ungewöhnlich, und sie schneidet in beide Richtungen. Das Projekt sagt es selbst: „usually multiple times per week or even multiple times per day." Nachgezählt: fünf Versionen in vier Tagen, zehn Releases in acht Tagen, und die 0.197.1 kam am Tag, an dem ich diesen Artikel geschrieben habe. Für einen Ein-Personen-Betrieb ist das eine beachtliche Schlagzahl. Für dich heißt es aber auch: Das Standard-Script-Tag lädt immer die neueste Version — inklusive möglicher Breaking Changes in einer Minor-Version, denn vor 1.0 erlaubt semantische Versionierung das ausdrücklich. Wenn dir stabile Redaktionsarbeit wichtiger ist als jede Neuerung, pinne die Version in der URL.

Ein Script-Tag ohne Netz

Und damit zu dem Punkt, an dem ich als jemand, der über npm-Sicherheit schreibt, nicht vorbeikomme.

Schau dir das empfohlene Script-Tag noch einmal an: Es lädt eine unversionierte Datei von einem fremden CDN, ohne Integritätsprüfung. Jeder Aufruf deiner Redaktionsoberfläche holt sich das aktuellste Bündel — 1,88 MiB fremdes JavaScript, das in dem Moment vollen Zugriff auf ein Fenster mit deinem GitHub-Token hat. Das ist genau die Angriffsfläche, die ich beim Build-Prozess so ernst nehme, nur eine Etage tiefer: Sie ist nicht bei npm ci, sondern bei jedem Login.

Ich will das nicht dramatisieren. unpkg ist etabliert, das Paket wird mit npm-Provenance veröffentlicht, und der Adminbereich trägt noindex. Aber es ist eine Standardeinstellung, die sich mit zwei Attributen deutlich verbessern lässt — und ich habe nachgeprüft, dass es tatsächlich geht:

<script
  src="https://unpkg.com/@sveltia/cms@0.197.1/dist/sveltia-cms.js"
  integrity="sha384-Ey0tcaM4hcMi78saAC2mMD/+2UoxzNc3iX/8n9w4Q34yqosJHst5cU00ozCzDd2p"
  crossorigin="anonymous"
></script>

Drei Voraussetzungen mussten dafür stimmen, und alle drei tun es: unpkg liefert access-control-allow-origin: *, setzt für die versionsgenaue URL cache-control: max-age=31536000 — die Datei ist also unveränderlich —, und der Prüfwert lässt sich verifizieren. Den sha384-Wert habe ich selbst über die geladene Datei berechnet; zur Gegenprobe habe ich zusätzlich den sha256-Wert gebildet und mit dem verglichen, den unpkg in seinen Metadaten und im content-digest-Header veröffentlicht — identisch. Der sha384-Wert oben stammt aus meiner eigenen Berechnung vom 24. August 2026 und gilt ausschließlich für 0.197.1 — bei jeder neuen Version musst du ihn neu bilden, sonst lädt die Oberfläche gar nicht mehr.

Und genau das ist der Grund, warum das Projekt es nicht als Standard vorgibt: Bei einer Software, die mehrmals pro Woche erscheint, wäre ein fester Hash eine dauerhafte Wartungsaufgabe. Trotzdem ist die Abwägung deine, nicht die des Projekts. Für eine Redaktionsoberfläche, die nur alle paar Monate angefasst wird, ist eine gepinnte Version mit Prüfsumme die richtige Wahl — und du bekommst die Stabilität aus dem vorigen Absatz gratis dazu.

Selbst hosten — und was trotzdem beim CDN bleibt

Der Script-Tag oben lädt aus einem fremden Netz, und in Deutschland ist das keine Geschmacksfrage, sondern eine, die du beantworten können musst. Jeder Aufruf der Redaktionsoberfläche schickt IP-Adresse und User-Agent an unpkg und, wie sich zeigen wird, auch an ein zweites CDN. Das ist genau das Muster, das bei Google Fonts zu jahrelangen Abmahnwellen geführt hat.

Vorweg die Verhältnismäßigkeit, denn die wird in solchen Diskussionen gern übersprungen: Es geht hier nicht um deine Besucher. Sveltia CMS läuft ausschließlich im Adminbereich, den nur angemeldete Redakteure öffnen; die öffentliche Seite bleibt statisches HTML ohne eine einzige Drittanfrage. Der Kreis der Betroffenen ist also klein und bekannt. Klein heißt aber nicht egal — auch deine Redaktion sind Betroffene im Sinne der DSGVO, und wenn du ein Verzeichnis von Verarbeitungstätigkeiten führst, gehört diese Verbindung hinein.

Die gute Nachricht: Du musst das CDN nicht nehmen. Die Dokumentation nennt den Weg ausdrücklich — Installation über den Paketmanager statt über den Script-Tag, mit anschließender manueller Initialisierung über die JavaScript-Schnittstelle:

npm install @sveltia/cms

Dann liegt das Bündel in deinem eigenen Repository, wird mit deinem Build ausgeliefert und kommt vom selben Host wie der Rest deiner Seite. Die Aktualisierung wird damit zu deiner Aufgabe, worauf die Projektdokumentation auch hinweist — dafür verschwindet die Abhängigkeit von einem fremden Netz zur Laufzeit, und die Frage nach Subresource Integrity aus dem vorigen Abschnitt erledigt sich gleich mit.

Die schlechte Nachricht: Ganz weg ist das CDN damit nicht. Ich habe im ausgelieferten Bündel nachgesehen, und dort stehen fest verdrahtete Adressen:

Was Wohin
Modulare Abhängigkeiten https://unpkg.com/@sveltia/cms
Syntax-Highlighting (Shiki), bei Bedarf nachgeladen https://unpkg.com/@shikijs, https://unpkg.com/@sveltia/ui
Drei Schriften (Source Sans 3, Noto Mono, Material Symbols) https://cdn.jsdelivr.net/fontsource/…
Oberflächensprache außer Englisch https://unpkg.com (die gemessene de.json)

Das Projekt beschreibt das selbst als Architekturentscheidung — „Dynamically loads additional dependencies from UNPKG rather than bundling everything into one large file" — und seine Beispiel-Richtlinie für die Content-Security-Policy bestätigt es: Dort stehen script-src und connect-src mit https://unpkg.com und zusätzlich font-src 'self' https://cdn.jsdelivr.net. Eine Konfigurationsoption, mit der du diese Adressen auf deinen eigenen Host umbiegst, habe ich nicht gefunden: Im offiziellen JSON-Schema kommen weder unpkg noch jsdelivr vor, und die dortigen CDN-Einträge betreffen ausschließlich externe Medienspeicher.

Wer also wirklich null Drittanfragen braucht — Behörde, Klinik, Betriebsrat mit klarer Ansage —, kommt mit dem npm-Paket allein nicht ans Ziel. Dann bleibt: Schriften selbst ausliefern und per CSS überschreiben, die nachgeladenen Module spiegeln oder auf sie verzichten, und das Ergebnis im Netzwerk-Tab gegenprüfen, statt es zu glauben. Machbar, aber Arbeit — und ein guter Punkt für ein Issue beim Projekt, denn eine konfigurierbare CDN-Basis wäre für den europäischen Markt ein echtes Argument.

Ehrlich bleiben will ich auch hier: Ich habe die npm-Variante nicht selbst in Betrieb genommen. Die Adressen oben habe ich aus dem ausgelieferten Bündel gelesen und gegen die CSP-Empfehlung des Projekts gehalten — welche davon bei einer selbst gehosteten Installation tatsächlich noch feuern, gehört im konkreten Fall gemessen.

Es ist ein Ein-Personen-Projekt, und es ist für Beiträge geschlossen. „At this point, the codebase still requires significant refactoring and improvements in test coverage, so pull requests from the community are not being accepted yet." Ich habe im 11ty-Artikel geschrieben, warum mir bezahlte Vollzeitarbeit an Open Source lieber ist als Ehrenamts-Verschleiß. Der Satz gilt hier genauso, nur zeigt er in die andere Richtung: Sveltia CMS finanziert sich über GitHub-Sponsoren. Das ist der Fall, den ich dort als Risiko beschrieben habe — mit dem Unterschied, dass die Ausfallfolgen bei einem Redaktionswerkzeug milder sind. Wenn es morgen stehen bleibt, bleiben deine Inhalte trotzdem als Markdown in deinem Repository liegen. Genau das ist der Punkt dieser ganzen Bauform.

Und jetzt die Pointe, an der ich hängen geblieben bin. Ich bin wegen redaktioneller Teams hier gelandet. Und dann steht in der Anleitung:

„Sveltia CMS does not officially support multi-user scenarios yet. Be cautious when multiple users are editing content simultaneously, as it may lead to merge conflicts or unintended overwrites."

Für den Fall, den ich vor zwei Tagen als Grenze von 11ty benannt habe — mehrere Menschen, die gleichzeitig schreiben — ist das keine Randnotiz, sondern der Kern. Das Werkzeug schließt die Lücke also für eine Redakteurin, nicht für eine Redaktion. Das ist ein realer Fortschritt und trotzdem nicht das, wonach ich gesucht habe. In der Praxis lässt es sich entschärfen: unterschiedliche Bereiche, abgesprochene Zeiten, editorial workflow mit getrennten Branches. Aber das ist Organisation, kein Feature.

Größe ist eine bewusste Grenze. Das Projekt beantwortet die Frage nach großen Installationen mit einem klaren Nein: „Sveltia CMS is primarily designed for small to medium-sized projects, such as personal blogs, portfolios, and small business websites." Diese Knowledge Base hat 966 gebaute Seiten. Ich bin nicht sicher, auf welcher Seite dieser Linie ich stehe, und ich müsste es ausprobieren, statt es zu raten.

Kleingedrucktes zum Mitnehmen: Verschachtelte Collections fehlen noch. Git LFS wird auf dem GitHub-Backend nicht unterstützt. Bitbucket und Azure DevOps gibt es nicht — aus Performance-Gründen, aber mit der ausdrücklichen Zusage, sie zu unterstützen, sobald deren Schnittstellen Sammelabfragen erlauben. Git Gateway wird es in dieser Form nicht geben; für 1.0 ist stattdessen ein eigener Ersatz angekündigt, Commits ohne Git-Dienstkonto. Die Oberfläche braucht einen sicheren Kontext, läuft also nur über HTTPS oder localhost. Ein paar Standardwerte weichen von Decap ab — Dateinamen werden nicht automatisch slugifiziert, leere optionale Felder nicht weggelassen, Markdown nutzt weiche statt harter Zeilenumbrüche. Und es gibt ausdrücklich keine kostenlose Einrichtungshilfe: „Questions about installation or initial configuration may go unanswered." Ob dieses Projekt zu unterschiedlichen Pfadpräfixen passt — diese Seite liegt unter /db/ — ist in der Dokumentation nirgends behandelt. Das ist für mich eine offene Frage, keine Behauptung in die eine oder andere Richtung.

Das Feld daneben

Damit du die Wahl kennst, kurz und ohne Rangliste. Alle Stände vom 24. August 2026:

Werkzeug Was es ist Stand
Sveltia CMS Neuentwicklung von Netlify CMS, Svelte, CDN-Skript 0.197.1, Beta, sehr aktiv
Decap CMS Das umbenannte Netlify CMS, gleiche Bauform 3.15.1 (24.07.2026), aktiv
Tina CMS Git-Inhalte plus visuelles Bearbeiten, optionale Cloud 3.12.1 (24.08.2026)
Keystatic Schema in TypeScript, bearbeitet Dateien im Repo 0.6.8 (20.08.2026)
Pages CMS GitHub-Repository als einzige Datenquelle, gehostet oder selbst betrieben 2.1.8 (08.06.2026)
Front Matter CMS Läuft komplett in Visual Studio Code 10.12.0 (21.08.2026)
CloudCannon Kommerziell und gehostet, nennt 11ty ausdrücklich SaaS, keine Version

Und dann ist da noch der Kandidat aus dem eigenen Haus. Build Awesome Pro — der kommerzielle Aufsatz, den Font Awesome für 11ty baut — zielt exakt auf dieselbe Lücke:

„Build Awesome Pro is a new service we're building to help anyone launch and maintain a web site— no installation required. It's a web site builder boasting visual editing and collaboration, zero lock-in (ever), while ultimately helping to fund continued Build Awesome (Eleventy) maintenance for the entire community."

Der Kickstarter war erfolgreich, nach Angabe der Produktseite mit 838 Unterstützern. Nur steht auf derselben Seite auch: „Build Awesome Pro Coming Soon!" Ein Erscheinungsdatum gibt es nicht, ein Paket unter dem angekündigten Namen existiert auf npm nicht, und die im April 2026 skizzierte Bearbeitungs-Schnittstelle steht dort unter der Überschrift „One Possible Editable API", illustriert von einem Beispiel, das der Beitrag selbst „hypothetical" nennt.

Das ist die Lage, nüchtern betrachtet: Die offizielle Antwort auf mein Problem ist angekündigt und finanziert, aber nicht da. Sveltia CMS ist da. Wer heute entscheiden muss, entscheidet zwischen einem Versprechen und einer Beta — und die Beta hat den Vorteil, dass du sie in zehn Minuten ausprobieren kannst, ohne irgendetwas zu unterschreiben.

Über den Tellerrand: ein CMS, das sich wieder abnehmen lässt

Der Grund, warum mich diese Bauform begeistert, ist nicht die Funktionsliste. Es ist eine Eigenschaft, die sich aus ihr ergibt: Du kannst dieses CMS wieder entfernen, und es passiert nichts.

Lösch die zwei Dateien aus admin/, und dein Projekt ist exakt das, was es vorher war: Markdown-Dateien mit YAML-Frontmatter in einem Git-Repository, gebaut von 11ty. Keine Migration, kein Export, kein Datenbank-Dump, keine Wiederherstellung von Inhalten aus einem proprietären Feldformat. Das CMS war nie der Ort, an dem deine Inhalte lagen — es war eine Ansicht darauf.

Genau das ist die Umkehrung des Handels, den ich in den WordPress-Jahren eingegangen bin. Dort gehörten meine Inhalte in einem sehr praktischen Sinn dem System: in seinen Tabellen, in seinem Serialisierungsformat, in den Feldern seiner Plugins. Der Weg heraus war Arbeit — deshalb steht der gestrige Artikel über @11ty/import überhaupt in diesem Blog. Bei einem Git-basierten CMS gibt es keinen Weg heraus, weil es keinen Weg hinein gab. Die Dateien lagen schon immer bei dir.

Und noch etwas passt hier zusammen, das mich als Betreiber dieser Seite besonders freut. Sveltia CMS liefert zu jeder Dokumentationsseite eine .md-Spiegelung aus, dazu eine llms.txt als Index und eine ausführliche Langfassung für Sprachmodelle — und es veröffentlicht eine offizielle Agent-Skill für Claude Code, samt einem Skript, das deine Konfiguration validiert. Das ist exakt das Experiment, für das diese Knowledge Base gebaut ist: Inhalte zusätzlich nativ als Markdown vorhalten, damit KI-Systeme sie rauschfrei lesen können. Dass ein CMS-Projekt dieselbe Idee unabhängig entwickelt und dann sogar noch vor fremden, veralteten Skills warnt, halte ich für ein gutes Zeichen — und für einen kleinen Beleg, dass der Ansatz nicht meine Privatmarotte ist.

Fazit

Würde ich Sveltia CMS auf diese Seite setzen? Heute noch nicht — aber aus anderen Gründen, als ich beim Schreiben erwartet hatte. Der eine Grund, den ich erwartet hatte, ist nämlich weggefallen: Meine Verzeichnisstruktur passt, und sie passt dokumentiert. Was bleibt, ist unspektakulärer: Der lokale Weg scheitert an WSL, ich schreibe hier ohnehin allein, und für einen einzelnen Autor sind Editor und Commit weiterhin der schnellere Weg. Mit 966 Seiten liege ich außerdem jenseits dessen, wofür das Projekt sich selbst empfiehlt. Nichts davon ist ein Vorwurf an das Werkzeug — es ist schlicht ein anderer Anwendungsfall.

Würde ich es dem Fall empfehlen, den ich vorgestern als Grenze von 11ty beschrieben habe? Ja, und ohne zu zögern. Eine überschaubare Firmenseite, ein Vereinsblog, ein Portfolio — gebaut mit 11ty, gepflegt von jemandem, der nie ein Terminal sehen will, mit sauber definierten Feldern, Bildoptimierung beim Hochladen und einer deutschsprachigen Oberfläche: Das ist genau der Punkt, an dem mein Satz von vorgestern zu pessimistisch war — und, wie sich herausgestellt hat, auch mein erster Entwurf dieses Artikels. Ich würde die Version pinnen, den redaktionellen Freigabeprozess einschalten und mir vorher zwei Stunden für die Konfiguration nehmen. Dann steht das.

Was ich mitnehme, ist aber etwas Grundsätzlicheres. Ich habe gestern über ein Werkzeug geschrieben, das Inhalte hereinholt, und heute über eines, das sie bearbeitbar macht. Beide funktionieren nur deshalb so unaufgeregt, weil dazwischen nichts ist außer Dateien in einem Verzeichnis. Das ist keine technische Feinheit — das ist die eigentliche Entscheidung, die du beim Bau einer Website triffst. Alles andere ist austauschbar. Deine Dateien sind es nicht.

Weiterlesen