Monatelang hatte der Konverter einen Fehler, den ich nicht sehen konnte: Jeder erzeugte Box-Schatten wurde verworfen. Nicht falsch gerendert – verworfen. Die Konvertierung war erfolgreich, der Import war erfolgreich, die Elemente kamen an, und die Schatten waren einfach nicht da.
Keine Ausnahme. Keine Konsolenwarnung. Keine fehlgeschlagene Anfrage. Nichts in einem Log. Hätten Sie die Seite nicht selbst gebaut, wüssten Sie nicht, dass etwas fehlt; Sie würden nur denken, die Konvertierung sei mittelmäßig.
Es lohnt sich, das aufzuschreiben, weil das Muster des Problems allgemein ist. Elementor ist nur der konkrete Fall. Der allgemeine Fall ist: Sie schreiben in ein System, das Schlüssel ignoriert, die es nicht kennt, und ein Tippfehler oder eine veraltete Annahme wird zu dauerhaftem, stillem Datenverlust.
Warum es unsichtbar ist
Elementor speichert jedes Element als JSON – einen Elementtyp plus ein settings-Objekt. Beim Rendern geht es seine registrierten Controls durch und fragt im Settings-Objekt jedes einzelne ab. Ein Schlüssel, der keinem registrierten Control entspricht, wird nie nachgeschlagen. Er wird nicht abgelehnt. Er wird einfach nie gelesen.
Das ist ein vernünftiges Design: Alte Daten überleben Plugin-Updates, und unbekannte Schlüssel aus einer neueren Version bringen eine ältere nicht zu Fall. Es ist auch der Grund, warum ein falscher Schlüsselname exakt null Rückmeldung erzeugt. Bei einer validierenden API ist ein falscher Schlüssel ein Fehler zur Schreibzeit, der beim nächsten Testlauf auffällt. Bei einer ignorierenden API herrscht Stille – bis ein Nutzer es Monate später bemerkt, wenn überhaupt.
Warum Sie die Namen nicht einfach nachschlagen können
Die naheliegende Antwort ist, die Control-Namen mit der Dokumentation abzugleichen. Das Problem ist, dass viele von ihnen nirgendwo aufgeschrieben sind – sie werden generiert.
Elementor hat Gruppen-Controls: wiederverwendbare Cluster wie Typografie, Rahmen, Hintergrund, Box-Schatten. Sie hängen eines unter einem Namen an ein Widget, und die einzelnen Control-IDs werden zur Laufzeit zusammengesetzt – der Gruppenname, ein Unterstrich, dann jeder Feldname. So erzeugt eine Typografie-Gruppe, die als title registriert ist, das Control title_font_size. Es gibt keine Datei, in der diese Zeichenkette vorkommt. Sie können nicht danach greppen. Sie müssen die Regel kennen und anwenden.
Das ist genau die Situation, in der Erinnerung, Forenantworten und alte Blogbeiträge schlimmer als nutzlos sind: Sie sind selbstsicher und konkret, häufig falsch – und die API korrigiert Sie nicht.
Der Fehler: Ein Schatten ist ein Control, nicht fünf
Der Konverter schrieb für einen Schatten sieben Schlüssel – einen zum Einschalten, dann Farbe, horizontal, vertikal, Unschärfe, Ausbreitung und Position als separate Werte. Das sieht sorgfältig aus. Es war fast komplett falsch.
Die Box-Shadow-Gruppe registriert genau zwei Felder. Der Schatten selbst ist ein zusammengesetztes Control, dessen Wert ein einzelnes Objekt ist, das horizontal, vertikal, Unschärfe, Ausbreitung und Farbe zusammen enthält; das zweite Feld ist die Outline-/Inset-Position. Rechnet man den Popover-Schalter hinzu, besteht der tatsächliche Schlüsselsatz aus drei Namen, die alle ein doppeltes Präfix tragen, weil die Gruppe selbst box_shadow heißt.
Von den sieben geschriebenen Schlüsseln existierten fünf nicht. Der sechste hatte den richtigen Namen, bekam aber die Zeichenkette "yes" statt des erwarteten Objekts. Der siebte war nah dran, aber es fehlte ein Präfix.
Ein Detail, auf das man durch noch so viel Raten nie gekommen wäre: Der Wert für „Outline“ ist ein einzelnes Leerzeichen, kein leerer String. Das steht im Options-Array und nirgendwo sonst.
Der zweite Fehler: der richtige Name, der trotzdem nichts bewirkte
Dieselbe Durchsicht förderte flex_grow und flex_shrink zutage, gesetzt auf Elementen mit expliziter Breite, damit ein Flex-Elternelement sie weder dehnt noch staucht. Wieder falsch – diese Controls gehören zu einer Gruppe, die unter einem anderen Namen registriert ist, also brauchten sie ein Präfix.
Aber das Präfix zu korrigieren hätte trotzdem nicht funktioniert, und das ist der Teil, den man sich merken sollte. Beide Felder tragen eine Bedingung: Sie greifen nur, wenn das Größen-Control der Gruppe auf „custom“ gesetzt ist. Bedingungen steuern die CSS-Erzeugung, nicht nur die Sichtbarkeit im Panel. Ein korrekter Schlüsselname ist notwendig, aber nicht hinreichend.
Die eigentliche Lösung erwies sich als einfacher als beide Versuche, und sie lag die ganze Zeit im Quellcode: Das Größen-Control hat eine Option „none“, die genau „nicht wachsen, nicht schrumpfen“ bedeutet – ein einziges bedingungsloses Control statt drei bedingter. Das Lesen des Quellcodes hat nicht nur den Fehler behoben, sondern auch eine bessere Implementierung hervorgebracht als die ursprünglich angestrebte.
Den Rest systematisch prüfen
Zwei Fehler, gefunden durch Lesen. Es gab noch gut neunzig weitere Schlüssel, die niemand gelesen hatte. Der nächste Schritt war also mechanisch: den offiziellen Plugin-Quellcode parsen, für jedes Widget die maßgebliche Menge an Control-Namen ermitteln und sie mit jedem Schlüssel abgleichen, den der Konverter ausgeben kann.
Das Audit-Skript gab vier falsche Antworten, bevor es eine richtige gab. Jeder Fehlschlag lässt sich verallgemeinern:
- Der Name steht oft in der nächsten Zeile. Control-Registrierungen setzen den Namen häufig in die Zeile nach dem Aufruf. Ein einzeiliges Muster übersah sie, und jedes Widget schien fünf ungültige Schlüssel auszugeben – den gesamten Tab „Erweitert“. Ein Prüfer, der ein Standard-Control als unbekannt meldet, meldet keinen Fehler, sondern seinen eigenen blinden Fleck.
- Zwei Schreibweisen für dasselbe. Eine Gruppe baut ihre Felder unter einem Variablennamen auf, eine andere verwendet einen anderen. Wer nur auf die erste prüfte, verlor stillschweigend ganze Gruppen – darunter, mit einer gewissen Ironie, genau die untersuchte Gruppe.
- Argument-Arrays verschachteln sich. Endete der Treffer an der ersten schließenden Klammer, wurde er beim ersten verschachtelten Array abgeschnitten und die Registrierung komplett übersprungen. So tauchten mehrere völlig gültige Layout-Schlüssel zuerst als „ungültig“ auf.
- Ein breites Erfassungsfenster frisst mit. Den vorigen Punkt durch ein großes Erfassungsfenster zu beheben führte dazu, dass jeder Treffer alle Registrierungen darin mit verschluckte. Die Anzahl bekannter Controls eines Widgets fiel von 281 auf 60, und der Diff meldete trotzdem Erfolg.
Und noch einer, spezifisch für diese Codebasis: Die Datei des Button-Widgets enthält null Control-Registrierungen. Sie stecken alle in einem Trait. Der erste Lauf meldete, Button habe überhaupt keine Controls.
Achten Sie auf die Richtung dieser Fehler. Drei erzeugten Fehlalarme – ärgerlich, aber selbstkorrigierend: Sie gehen der Sache nach und stellen fest, dass das Werkzeug falsch liegt. Einer erzeugte ein falsches „Bestanden“, und das ist die gefährliche Sorte. Weniger bekannte gültige Namen bedeuten weniger gemeldete Probleme; der Fehlermodus ist still und beruhigend.
Die Prüfung, die die anderen vertrauenswürdig macht
Was zu der einen Idee führt, die man aus der ganzen Übung behalten sollte.
Ein Prüfer, der „alles sauber“ meldet, ist nichts wert, solange Sie nicht bewiesen haben, dass er auch „nicht sauber“ melden kann.
Das Audit endet also mit einer Negativkontrolle: Es speist die fünf als falsch bekannten Schlüsselnamen erneut ein und lässt den gesamten Lauf fehlschlagen, wenn einer davon akzeptiert wird. Wenn der Extraktor später kaputtgeht – ein erweitertes Muster, eine übersprungene Datei, ein fehlerhafter Cache –, wird diese Assertion rot, bevor die Zeile „alles bestanden“ die Chance bekommt zu lügen.
Zwei weitere Anker gehören dazu: Eine Liste von Control-Namen, die sicher existieren, muss vollständig gefunden werden, und die Gesamtzahl darf niemals stillschweigend schrumpfen. Genau der zweite hätte den Zusammenbruch von 281 auf 60 abgefangen.
Dafür sorgen, dass es behoben bleibt
Endzustand, nachdem der Diff sauber zurückkam: 205 Schlüssel über neun Widget-Typen geprüft, keiner davon unbekannt. Dann ein Continuous-Integration-Job, damit es so bleibt – er konvertiert eine Seite mit einem Schatten und prüft in der Ausgabe per Assertion, dass der zusammengesetzte Schlüssel vorhanden ist und dass keiner der alten separaten Namen zurückgekehrt ist.
Kein Unit-Test einer reinen Funktion, sondern eine Assertion auf das Artefakt, das tatsächlich ausgeliefert wird – und sie prüft genau den Fehler, der schon einmal passiert ist.
Die allgemeine Version
Wenn Sie in irgendetwas schreiben, das unbekannte Schlüssel ignoriert – ein Page-Builder, eine CMS-Metatabelle, eine Analytics-Nutzlast, ein Feature-Flag-Blob, die Hälfte der existierenden Konfigurationsformate – dann:
- Leiten Sie die gültige Schlüsselmenge aus der maßgeblichen Quelle ab, nicht aus dem Gedächtnis oder der Dokumentation. Dokumentation beschreibt Absicht; generierte Namen existieren nur zur Laufzeit.
- Gleichen Sie das, was Sie ausgeben, maschinell damit ab. Neunzig Schlüssel sind nicht mit dem Auge prüfbar, und die falschen sehen genauso aus wie die richtigen.
- Misstrauen Sie zuerst Ihrem Prüfer. Wenn ein Lauf etwas bemängelt, bei dem Sie sich sicher sind, liegt das Werkzeug weit häufiger falsch als der Code.
- Ergänzen Sie eine Negativkontrolle. Beweisen Sie, dass der Prüfer fehlschlagen kann, sonst bedeuten seine Erfolgsmeldungen nichts.
- Prüfen Sie in der CI per Assertion das ausgelieferte Artefakt, denn diese Fehlerklasse kehrt in dem Moment zurück, in dem jemand den ausgebenden Code refaktoriert.
Die Schatten funktionieren jetzt. Das nützlichere Ergebnis war die Erkenntnis, dass „kein Fehler“ monatelang als Beleg für Korrektheit behandelt worden war – bei einer API, die strukturell nicht in der Lage ist, einen zu erzeugen.