diff --git a/docs/superpowers/notes/2026-09-28-etappe-4-divi-formate.md b/docs/superpowers/notes/2026-09-28-etappe-4-divi-formate.md index 12918a9..82d241b 100644 --- a/docs/superpowers/notes/2026-09-28-etappe-4-divi-formate.md +++ b/docs/superpowers/notes/2026-09-28-etappe-4-divi-formate.md @@ -28,6 +28,23 @@ Geprüft gegen den installierten Quelltext `includes/builder-5/server/Packages/G - **Neuer Typ `gradients`** (`GLOBAL_VARIABLE_TYPES = numbers, strings, images, links, fonts, gradients`); `strings` und `gradients` behalten ihren Wert zeichengetreu. `get_global_variables()` liefert zusätzlich die leere Gruppe `colors`, wandelt Objekt-Gruppen in Arrays und fügt `allowedActions` und die Customizer-Schriften hinzu (jede Gruppe als `stdClass`); beim Zurückschreiben bleiben nur Speicherformat-Felder. - **Archivierte Variablen:** `_sort_global_variable_snapshot()` behandelt `order` archivierter Variablen als `''`; der Builder setzt beim Archivieren `order: null`, das Sanitize macht daraus `''`. +## Nachtrag Divi 5.15.1 – Theme-Optionen und Schriften (Etappe 4d, 2026-10-19) + +Zeilenangaben und Belege: `2026-10-19-etappe-4d-divi-recherche.md` (Abschnitt 0 und A–D). + +1. **`FontsUtility::font_add()` ist im Prozess aufrufbar:** mit `['action' => 'wp_handle_upload_rest']` nimmt `wp_handle_upload()` serverseitige Temp-Dateien (`copy`/`unlink` statt `move_uploaded_file`), so wie Divis KI-Pfad `AiAgentImageUtility::install_to_custom_fonts()`; ein Nachbau des `et_uploaded_fonts`-Eintrags entfällt. +2. **Divi-Rolle für Schriften ist `custom_fonts_management`** (plus `upload_files`), nicht `use_visual_builder`. +3. **Divis UI kennt nur `ttf`/`otf`** (`et_pb_get_supported_font_formats()`); `font_add` und das Frontend verarbeiten auch `woff`, `woff2` und `eot` (`mime_types_font()`). +4. **Die Standardschrift steht im Customizer als `false`, nicht als `'none'`;** ein gespeichertes `'none'` ist in Divi 5 ein echter Schriftname, „Standard“ heißt deshalb Schlüssel entfernen (`et_delete_option`). +5. **Divis Sanitizer klemmen keine Bereiche** (`absint`, `intval`, `floatval`) und liefern bei ungültiger Auswahl `false` statt eines Fehlers; Bereiche gehören ins Plugin. +6. **`body_header_style` ist im Control mehrfach wählbar, der Sanitizer nimmt aber genau einen Wert;** Kombinationen und `''` werden zu `false`. +7. **`et_builder_get_google_fonts()` kann einen HTTP-Abruf auslösen** (einmal je 24 h, nur mit API-Schlüssel) und lädt sonst 1,3 MB JSON; die Liste wird je Request einmal geholt. +8. **`font_add` prüft den Namen nur gegen Google-Fonts und `et_uploaded_fonts`,** nicht gegen Web-Safe-Schriften, und entfernt unzulässige Zeichen stillschweigend. +9. **`font_add` meldet Teilerfolg als Erfolg;** das Plugin vergleicht die gelieferten `font_file`-Schlüssel mit der Eingabe und entfernt die Schrift bei Abweichung. +10. **`ET_Core_PageResource::remove_static_resources` hat sieben Parameter** (neu: `$skip_external_cache`) und ist ohne angemeldeten Benutzer wirkungslos. +11. **`GlobalData::get_customizer_fonts()` cacht per `static` ohne Reset;** Zurücklesen läuft über `get_option('et_divi')`. +12. **`et_update_option` schreibt das ganze `et_divi`-Array aus einem Request-Global** (Lost-Update-Risiko bei gleichzeitigen Änderungen); `design_rev` und Sperre mindern es. + ## 0. Grundlagen: wo Divi speichert `et_get_option()`/`et_update_option()` (`epanel/custom_functions.php:211`, `:272`) haben drei Ziele: diff --git a/docs/superpowers/notes/2026-09-28-etappe-4-entscheidungen-entwurf.md b/docs/superpowers/notes/2026-09-28-etappe-4-entscheidungen-entwurf.md index 542087a..7a1ec93 100644 --- a/docs/superpowers/notes/2026-09-28-etappe-4-entscheidungen-entwurf.md +++ b/docs/superpowers/notes/2026-09-28-etappe-4-entscheidungen-entwurf.md @@ -80,3 +80,43 @@ Ansätze vorschlagen → Entwurf abschnittsweise freigeben → Spec schreiben - DesignLock: zwei gleichzeitige Übernahmen einer veralteten Sperre möglich (sehr kleines Fenster); Schreibvorgang > 120 s kann Sperre verlieren - Verwaister @param-Docblock in DiviPresetsDeleteAbility - Smoke divi-presets-smoke.sh läuft nur mit deaktivierten Plugins postlos (Monolog 2) und asynclistry (Deprecated-Ausgabe) + +## Etappe 4d (Plan 2026-10-19-etappe-4d-theme-optionen-schriften.md) + +Belege: `2026-10-19-etappe-4d-divi-recherche.md`; Formate-Nachtrag „Etappe 4d“. Die Rulings amendieren Spec §8 und §10. + +- **W1:** Drei neue Fähigkeiten `theme-option-read`, `theme-option-write` und `font-upload`; der Mirror `customizer-typography` trägt Lesen und Schreiben, die Schriftlisten-Aufrufe tragen `theme-option-write`, `font-upload` und `variable-write`. +- **W2:** Whitelist von 20 Theme-Optionen (vier Schriften, zwei Gewichte, Größen, Zeilenhöhen, Abstände, `body_header_style`, vier Layout-Werte); Standards, Bereiche und Sanitizer sind Literale in `ThemeOptionSpec`/`ThemeOptionsAdapter`, gesichert durch den Mirror. +- **W3:** „Nicht gesetzt“ ist der Divi-Standard; `null` setzt zurück (`et_delete_option`), `'none'` und `false` werden abgelehnt; Tablet-/Phone-Größen erben, solange sie nicht gesetzt sind. +- **W4:** Das Plugin prüft Bereiche, Schritte und Auswahl selbst (Divis Sanitizer klemmen nicht); `body_header_style` nimmt genau einen Wert; Zahlen als Zahl oder Ziffern-String. +- **W5:** Schriftnamen gelten exakt (Groß-/Kleinschreibung) gegen Divis gemergte Liste; unbekannt → `validation_failed` mit bis zu fünf ähnlichen Namen; die Liste wird je Request einmal geladen. +- **W6:** Snapshot `theme_options` hält die vorhandenen Whitelist-Schlüssel mit Rohwert und Typ; Restore ersetzt die Whitelist vollständig (`et_update_option`/`et_delete_option`) und leert die Caches wie der Customizer. +- **W7:** Theme-Optionen haben keinen Seitenbezug; der Probelauf warnt „wirkt auf die gesamte Website“ (bei Schriften auch auf den Builder), `usages` bleibt `null`. +- **W8:** Der Upload installiert im Prozess über `FontsUtility::font_add()` mit `wp_handle_upload_rest`; Mirror `font-install`; Formate `woff2`, `woff`, `ttf`, `otf`; Teilerfolg wird erkannt und mit `font_remove` zurückgenommen. +- **W9:** Name 1–100 Zeichen aus `A–Z a–z 0–9`, Leerzeichen, `_`, `-`, nicht in Google-, Web-Safe- oder hochgeladenen Schriften; je Datei höchstens `min(Upload-Limit, 5 MB)`, 1–4 Dateien, Magic Bytes passend zur Endung, URL-Regeln über den geteilten `RemoteFileFetcher`. +- **W10:** Der Probelauf lädt wirklich herunter (nur Temp-Dateien) und meldet Dateien mit Größe und SHA-256; mit `confirm: true` erneut laden, prüfen, installieren; kein Snapshot. +- **W11:** Restore kennt den Bereich `theme_options` (`ThemeOptionsRestorePlanner`); Rechte wie beim Schreiben. +- **W12:** Variablen vom Typ `fonts` werden zusätzlich gegen den Schriftkatalog geprüft; `VariableValue` bleibt rein. +- **W13:** Divi-Grenzen, die dokumentiert statt behoben sind: + - `et_update_option` schreibt das ganze `et_divi`-Array aus dem Request-Global (Lost-Update-Risiko; `design_rev` und Sperre mindern es). + - `body_header_style` nimmt in Divi nur einen Wert. + - Divi prüft keine Bereiche; das Plugin tut es. + - `font_add` meldet Teilerfolg als Erfolg (Plugin vergleicht und nimmt zurück). + - `remove_static_resources` wirkt nur mit angemeldetem Benutzer (Smokes laufen mit `--user`). + - Divis Leser kennen für `tablet_*`/`phone_*` feste Standards 14 bzw. 30; bei „gleich dem Größeren“ erzeugt Divi keine CSS-Regel. + - Zwischen `RemoteFileFetcher::allows()` und dem cURL-Abruf bleibt ein DNS-Rebinding-Fenster (das „IPv4-Pinning“ ist nur `CURL_IPRESOLVE_V4`); Thema für das Security-Audit in 4e. + - Schema-Fehler (Bereich, unbekannter Schlüssel) lehnt der MCP-Adapter mit dem Eingabeschema ab, bevor die Tools laufen; sie tragen dann nicht die `validation_failed`-Feldpfade des Plugins. +- **W14:** `DesignRights::themeOptions()` verlangt `edit_theme_options` und `theme_customizer` (der Parameter `$writer` ist ungenutzt, B2), `DesignRights::fonts()` verlangt `upload_files`, `edit_theme_options` und `custom_fonts_management`; die Fehlertexte nennen das fehlende Recht und die Divi-Rolle. + +**Folgepunkte (außerhalb 4d):** Schriftprüfung in Preset- und Modul-Attributen; `lit/divi-fonts-list` bzw. Schriftsuche (Vorschläge gibt es nur in Fehlern); Entfernen hochgeladener Schriften (A2); Format `eot`. + +### Offene Kleinpunkte aus 4d (Reviews, nicht merge-relevant) +- Theme-Optionen: `ThemeOptionSpec` Zahlen-Regex ohne `/D` („15\n“); Werte neben dem Schritt (1.70000001) werden nicht gerundet; `rev()` von INF/NAN; `effective()` gibt gespeicherte Unwerte bei Auswahlfeldern unverändert zurück; `FontSuggest` rechnet bytebasiert. +- Update-Tool: unnötiger Schreibvorgang bei gespeichertem „16“ gegen gesetzte 16 (`ThemeOptionsArea::same` nutzen); Schriftname wird vor dem Trimmen geprüft (" Roboto"); `DesignRights::themeOptions` mit ungenutztem `$writer` samt Test-Mock; kein Hinweis zu Schriftstärken im Builder. +- Gespeichertes `false`: Tests für `get`/Planer/Restore und `design_rev`-Vergleich fehlen; ungesetzte, gespeicherte `false`-Schlüssel entfernt `write()` still. +- Schrift-Variablen: Test, dass reine Status-/Label-Änderungen den Katalog nicht laden; ein erneut gesendeter Wert einer Schrift, die Divis Liste verlassen hat, wird `validation_failed`. +- Upload: `confirm` bindet die im Probelauf gezeigte SHA-256 nicht (optionale Eingabe); der Probelauf prüft den Rohnamen, `install()` nutzt `sanitize_file_name`; `FontInstaller::install()` prüft die Endung nicht selbst gegen `FORMATS`; Meldung bei fehlendem `fileinfo` irreführend; Tests für Fetcher-Ausnahme/Aufräumen und leeres `wp_tempnam` fehlen; nur vier Magic-Byte-Signaturen; gleichzeitiger Upload desselben Namens in anderer Schreibweise. +- `FontInstaller` lässt die verschobene Datei in `et-fonts`, wenn `font_add` wirft (nicht-Array `et_uploaded_fonts`); Aufräumen mit `catch Throwable` ergänzen. +- Smoke-Skripte: fehlende `file_exists`-Prüfung in Schritt 7, Format-Grep im Probelauf vakuos (stattdessen `sha256`/`bytes` prüfen), Namenskonflikt prüft nicht auf `conflict`, `'none'` nicht isoliert, kein Zurücklesen von `body_font`, EXIT-Trap stellt ganz `et_divi` wieder her (Admin-Änderungen während des Laufs gehen verloren) und räumt Snapshots sowie Dateien fälschlich akzeptierter Schriften nicht auf, Sentinel-Prüfung vakuos, `restore()` verbirgt stderr. +- Protokoll: `src/Admin/LogObjects.php` kennt den Objekttyp `divi_font` nicht (Anzeige als Code). +- Kleinkram: Reihenfolge der `use`-Blöcke; `DiviLocked`-Rethrow setzt voraus, dass die Sperre vor jedem Schreiben greift. diff --git a/docs/superpowers/notes/2026-10-18-divi-faehigkeiten.md b/docs/superpowers/notes/2026-10-18-divi-faehigkeiten.md index af15f58..1ae90a1 100644 --- a/docs/superpowers/notes/2026-10-18-divi-faehigkeiten.md +++ b/docs/superpowers/notes/2026-10-18-divi-faehigkeiten.md @@ -91,4 +91,4 @@ beeinflussen, ohne dass ein Test ihn bräuchte. - Der Admin-Hinweis berechnet den Zustand auf jeder Admin-Seite (Reflection und Dateihashes je Aufruf); bei Bedarf kurzlebig cachen. -- `DesignArea::feature()`: Farben (`color-write`) und Variablen (`variable-write`) haben seit Etappe 4c eigene Fähigkeiten; Theme-Optionen bekommen ihre mit 4d. +- `DesignArea::feature()`: Farben (`color-write`) und Variablen (`variable-write`) haben seit Etappe 4c eigene Fähigkeiten; Theme-Optionen bekommen ihre mit 4d. **Erledigt (4d):** `ThemeOptionsArea::feature()` = `theme-option-write`; neu sind `theme-option-read`, `theme-option-write` und `font-upload` („Theme-Optionen lesen“, „Theme-Optionen schreiben“, „Schriften hochladen“). diff --git a/docs/superpowers/notes/2026-10-19-etappe-4d-divi-recherche.md b/docs/superpowers/notes/2026-10-19-etappe-4d-divi-recherche.md new file mode 100644 index 0000000..b9aa2fa --- /dev/null +++ b/docs/superpowers/notes/2026-10-19-etappe-4d-divi-recherche.md @@ -0,0 +1,184 @@ +# Etappe 4d – Divi 5.15.1: Theme-Optionen und Schriften (Code-Recherche) + +Stand: 2026-10-19. Grundlage der Rulings W1–W14 (siehe `2026-09-28-etappe-4-entscheidungen-entwurf.md`, Abschnitt „Etappe 4d“). Die Befunde in Abschnitt 0 sind in den Formate-Nachtrag „Etappe 4d“ übernommen. + +Quelle: installiertes Divi 5.15.1 (`style.css:4`), WordPress 7.1.3. Alle Pfade relativ zu `wp-content/themes/Divi`, sofern nicht anders angegeben. Nur aus Code abgeleitet; die Zeilen der Formate-Notiz §4 (5.2.1) sind durchweg um ca. +80 verschoben. + +## 0. Überraschungen gegenüber Spec §8 (Kurzfassung) + +1. **`FontsUtility::font_add()` ist in-process aufrufbar.** `wp_handle_upload()` nimmt `$overrides['action']` als Action (`wp-admin/includes/file.php:1110`); nur bei Action `'wp_handle_upload'` prüft `_wp_handle_upload` `is_uploaded_file()`/`move_uploaded_file()` (`file.php:936`, `:1019-1027`), sonst `is_readable()` + `copy()`/`unlink()`. Divi übergibt selbst `['action' => 'wp_handle_upload_rest']` (`CustomFontController.php:68-75`) und installiert so **serverseitige Temp-Dateien**: `AiAgentImageUtility::install_to_custom_fonts()` (`includes/builder-5/server/VisualBuilder/AiAgent/AiAgentImageUtility.php:860-1009`, `wp_tempnam` → `copy` → `font_add([...'tmp_name'=>$temp...], …, ['action'=>'wp_handle_upload_rest'])`). → Kein eigener Sideload und kein Nachbau des `et_uploaded_fonts`-Eintrags nötig; Spec §8.2 („nur echte Browser-Uploads“, „Nachbau mit Fingerabdruck“) ist überholt. Mirror höchstens für die *Aufrufkonvention* (`install_to_custom_fonts`). +2. **Divi-Rolle für Schriften ist `custom_fonts_management`**, nicht `use_visual_builder` (`CustomFontController.php:141-152`, `:214-225`; Rollen-Editor `includes/builder/functions.php:9595`). Plus `upload_files`. Spec §10 nachziehen. +3. **Standard-Formate nur `ttf`, `otf`:** `et_pb_get_supported_font_formats()` (`includes/builder/core.php:5090`, Filter `et_pb_supported_font_formats`). Der REST-Store nimmt nur diese Felder an; `font_add` selbst akzeptiert alles aus `mime_types_font()` (otf, ttf, woff, woff2, eot), das Frontend (`et_builder_enqueue_user_fonts`, `includes/builder/functions.php:3813ff`) gibt alle fünf aus. woff/woff2 sind also technisch möglich, aber über Divis UI nicht vorgesehen → Entscheidung nötig. +4. **„Standard-Schrift“ wird im Customizer als `false` gespeichert, nicht als `'none'`.** `et_sanitize_font_choices('none')` → `false` (`'none'` steht nicht in `et_builder_get_fonts()`), WP speichert `false` (nur `null`/`WP_Error` werden verworfen, `wp-includes/class-wp-customize-manager.php:1848-1851`). Leser: Divi-4-CSS/Google-URL `et_pb_get_specific_default_font()` behandelt `false`/`''`/`'none'` gleich (`functions.php:1287-1290`, lose `in_array`); Divi 5 `Fonts::enqueue()` und `GlobalData::get_customizer_fonts()` nehmen bei *fehlendem* Schlüssel `'Open Sans'`, bei `false` ebenfalls `'Open Sans'` (`FrontEnd/Module/Fonts.php:100-129`), bei `'none'` aber `self::add('none')` (`:138-144`). → „Standard“ = Schlüssel entfernen (`et_delete_option`), nie `'none'` schreiben. +5. **Divis Sanitizer prüfen keine Bereiche.** `absint`, `intval`, `floatval` klemmen nicht auf min/max der Controls; `et_sanitize_*_choices` liefern bei Unbekanntem `false` (kein Fehler). Plugin muss Bereiche aus den Control-`input_attrs` selbst prüfen und `false` als Ablehnung werten. +6. **`body_header_style` ist im Control mehrfach wählbar (`italic|uppercase`, Checkbox-Liste mit `|`, `functions.php:5457-5481`), der Sanitizer `et_divi_global_font_style` akzeptiert aber nur genau einen Schlüssel** → jede Kombination und auch `''` (alles abgewählt) wird zu `false`. Divi-Bug; Tool sollte nur einen Wert (oder „unset“) zulassen. +7. **`et_builder_get_google_fonts()` kann einen HTTP-Request auslösen** (`et_builder_google_fonts_sync()`, `includes/builder/core.php:4868-4905`: einmal je 24 h per Transient `fonts_cache_status`, nur wenn Google-API-Key gesetzt). Sonst Option `et_google_fonts_cache` bzw. 1,3-MB-JSON (`core/json-data/google-fonts.json`, 1945 Familien), statisch gecacht. `et_sanitize_font_choices` ruft das bei jedem Aufruf (ksort über ~1950 Einträge) → Liste je Request einmal holen. +8. **Namens-Eindeutigkeit in `font_add` prüft nur Google-Fonts und `et_uploaded_fonts`, nicht Web-Safe-Fonts** (`FontsUtility.php:125-131`) – „Arial“ wäre hochladbar. Plugin-Regel strenger halten. +9. **`font_add` meldet Teilerfolg als Erfolg:** schlägt eine von mehreren Dateien fehl, wird die Schrift mit den übrigen gespeichert, die Fehler gehen verloren (`FontsUtility.php:157-190`). Plugin: vorab jede Datei mit `HooksRegistration::check_filetype_and_ext_font()` prüfen (wie Divis AI-Pfad) und Ergebnis `font_file`-Schlüssel gegen Eingabe vergleichen. +10. **`ET_Core_PageResource::remove_static_resources` hat jetzt 7 Parameter** (`+ $skip_external_cache`, `core/components/PageResource.php:1398`); Signatur-Fingerabdruck prüfen. +11. **`GlobalData::get_customizer_fonts()` cached per `static` ohne Reset** (`GlobalData.php:446-451`) → Zurücklesen nach dem Schreiben im selben Request über `get_option('et_divi')`/`et_get_option`, nicht darüber. +12. **`et_update_option` schreibt das ganze `et_divi`-Array aus dem Request-Global `$et_theme_options`** (`epanel/custom_functions.php:297-310`); wurde das Global früher im Request geladen und die Option inzwischen von einem anderen Prozess geändert, werden dessen Änderungen überschrieben (Lost Update). Für `design_rev`-Vergleich und Snapshot `get_option('et_divi')` lesen; Restrisiko dokumentieren. + +## A. Theme-Optionen + +Allgemein für alle 20 Settings (Registrierung in `et_divi_customizer_theme_settings()`, `functions.php:1371ff`, Hook `customize_register` → `et_divi_customize_register` `functions.php:1306`/`:1363`): +- `type => 'option'`, ID `et_divi[]` → Speicher `get_option('et_divi')[]` (Divi: `$shortname = 'divi'`, `$et_store_options_in_one_row = true`, `functions.php:16-17`). +- `capability => 'edit_theme_options'`, `transport => 'postMessage'`. +- Sanitizer sind **globale Funktionen**, immer geladen: `includes/functions/sanitization.php` und `choices.php` werden in `et_setup_theme()` (`after_setup_theme`, `functions.php:50-52`, `:150`) ohne Bedingung eingebunden; Builder-Listen in `includes/builder/core.php` (immer geladen). WP ruft sie als Filter `customize_sanitize_{id}` mit `($value, $setting)` (`class-wp-customize-setting.php:581`); alle Divi-Sanitizer nehmen nur `$value`, also direkt mit 1 Argument aufrufbar. Kein Customizer-Kontext nötig. +- Rückgabe `false` bei ungültiger Auswahl (`et_sanitize_key_based_option($choosen, $options, $default = false)`, `sanitization.php:45-52`; `isset($options[$choosen])`, also **case-sensitiv**, numerische String-Schlüssel ≙ int). +- Gespeicherte Typen: `absint` → int, `et_sanitize_float_number` → float, `et_sanitize_int_number` → int, Auswahl-Sanitizer → der übergebene Wert (String) oder `false`. + +| Schlüssel | Setting (file:line) | Standard (Setting) | sanitize_callback (Definition) | Control / Choices / min–max–step | Zusammenspiel / Leser | +|---|---|---|---|---|---| +| `heading_font` | `functions.php:2563-2572`, Control `:2574-2586` | `'none'` | `et_sanitize_font_choices($choosen)` `sanitization.php:59-66` (Google+Websafe via `et_builder_get_fonts()` + `et_builder_get_custom_fonts()`) | `ET_Divi_Select_Option`, `type select`, Choices `$font_choices` (`functions.php:1407-1440`: `'none'` + alle Fonts, im UI nach Locale-Zeichensatz gefiltert, Sanitizer filtert **nicht** nach Locale) | `'none'` → `false` gespeichert (§0.4). Builder/D5-Sicht `'Open Sans'` (`GlobalData::$customizer_fonts` `GlobalData.php:108-119`, `Fonts.php:100`). Speist `--et_global_heading_font` (`Fonts.php:148-175`), D4-Font-URL `functions.php:432`, `:7216`, `et_builder_get_font_family('--et_global_heading_font')` `includes/builder/core.php:4683-4686`. Alias `Source Sans Pro`→`Source Sans 3` nur im D5-Enqueue (`Fonts.php:113-127`). | +| `body_font` | `functions.php:2636-2645`, Control `:2647-2659` | `'none'` | `et_sanitize_font_choices` | wie oben | wie oben, `--et_global_body_font`; zusätzlich `functions.php:334`, `:474`, `includes/builder/functions.php:4254`, `Fonts.php:134-136` (`'none'`/`''` = „Theme lädt Open Sans selbst“). | +| `primary_nav_font` | `functions.php:3477-3486`, Control `:3488-3500` | `'none'` | `et_sanitize_font_choices` | Select, `$font_choices` (Section `et_divi_header_primary`) | nur D4-Header/Google-URL (`functions.php:435`); kein D5-Global. | +| `secondary_nav_font` | `functions.php:3601-3610`, Control `:3612-3624` | `'none'` | `et_sanitize_font_choices` | Select, `$font_choices` (Section `et_divi_header_secondary`) | `functions.php:436`; kein D5-Global. | +| `heading_font_weight` | `functions.php:2588-2597`, Control `:2599-2611` | `'500'` | `et_sanitize_global_font_weight($choosen)` `sanitization.php:345-347` | `ET_Divi_Custom_Select_Option`, Choices `et_builder_get_font_weight_list()` (`includes/builder/core.php:4792-4807`: `'100'…'900'`, Filter `et_builder_all_font_weights`) | `--et_global_heading_font_weight` (`Fonts.php:151`, leer → `'500'`), D4-CSS `functions.php:5827`, `:6943ff` (nur wenn ≠ 500). | +| `body_font_weight` | `functions.php:2661-2670`, Control `:2672-2684` | `'500'` | `et_sanitize_global_font_weight` | wie oben | `--et_global_body_font_weight`, `functions.php:5828`, `:5992ff`. | +| `body_font_size` | `functions.php:2287-2296`, Control `:2298-2313` | `'14'` | `absint` (WP) | `ET_Divi_Range_Option`, `range` 10–32, step 1 | `--et_global_body_font_size` (`Fonts.php:104-107`, `''` → 14), D4 `functions.php:5819`, VB-Settings `SettingsDataCallbacks.php:2379`. | +| `tablet_body_font_size` | `functions.php:2371-2380`, Control `:2382-2397` | `et_get_option('body_font_size','14')` (dynamisch) | `absint` | Range 10–32, step 1 (Section `et_divi_mobile_tablet`) | Leser nehmen fest `'14'` (`functions.php:5841`, `Fonts.php:108`), CSS nur wenn `≠14 && ≠ body` (`functions.php:7019`) → unset ≙ erbt Desktop. | +| `phone_body_font_size` | `functions.php:2343-2352`, Control `:2354-2369` | `et_get_option('tablet_body_font_size','14')` | `absint` | Range 10–32, step 1 (`et_divi_mobile_phone`) | Leser: Standard = Tablet-Wert (`functions.php:5846`, `Fonts.php:109`); CSS nur wenn `≠14 && ≠ tablet` (`:7112`). | +| `body_header_size` | `functions.php:2399-2408`, Control `:2410-2425` | `'30'` | `absint` | Range 22–72, step 1 | `functions.php:5821`, VB `SettingsDataCallbacks.php:2391`. | +| `tablet_header_font_size` | `functions.php:2535-2544`, Control `:2546-2561` | `et_get_option('body_header_size','30')` | `absint` | Range 22–72, step 1 | Leser fest `'30'` (`functions.php:5840`), CSS nur wenn `≠30 && ≠ body_header_size` (`:7039`). | +| `phone_header_font_size` | `functions.php:2507-2516`, Control `:2518-2533` | `et_get_option('tablet_header_font_size','30')` | `absint` | Range 22–72, step 1 | Standard = Tablet (`functions.php:5845`), CSS `:7132`. | +| `body_font_height` | `functions.php:2315-2324`, Control `:2326-2341` | `'1.7'` | `et_sanitize_float_number($number)` `sanitization.php:7-9` (`floatval`) | Range 0.8–3, step 0.1 | `--et_global_body_font_height` (`Fonts.php:105-107`, Einheit `em`), D4 `functions.php:5820`, VB `:2378`. | +| `body_header_height` | `functions.php:2455-2464`, Control `:2466-2481` | `'1'` | `et_sanitize_float_number` | Range 0.8–3, step 0.1 | D4 `functions.php:5824`, `:6604`. | +| `body_header_spacing` | `functions.php:2427-2436`, Control `:2438-2453` | `'0'` | `et_sanitize_int_number($number)` `sanitization.php:16-18` (`intval`) | Range −2–10, step 1 | D4 `functions.php:5823`, `:6604`. Negativ erlaubt. | +| `body_header_style` | `functions.php:2483-2492`, Control `:2494-2505` | `''` | `et_divi_global_font_style($choosen)` `sanitization.php:358-360` | `ET_Divi_Font_Style_Option` (`functions.php:5457`, Checkboxen, Wert `a|b`), Choices `et_divi_global_font_style_choices()` (`includes/functions/choices.php:23-33`: `italic`, `uppercase`, `underline`; Filter `et_divi_global_font_style_choices`) – **kein `bold`** (das gibt es nur bei `et_divi_font_style_choices`/`et_sanitize_font_style`) | Sanitizer nimmt nur genau einen Wert, `''` und Kombinationen → `false` (§0.6). Leser `et_get_option('body_header_style','','',true)` + `et_pb_print_font_style()` (`functions.php:5822`, `:6616`). | +| `content_width` | `functions.php:1903-1912`, Control `:1914-1929` | `'1080'` | `absint` | Range 960–1920, step 1 (`et_divi_general_layout`) | D4 `functions.php:5833`, `:6133`; VB `SettingsDataCallbacks.php:2387`. | +| `gutter_width` | `functions.php:1931-1940`, Control `:1942-1957` | `'3'` | `absint` | Range 1–4, step 1 | Standard für Section/Row-`gutter_width` (D4 `includes/builder/main-structure-elements.php:239`, `:1953`), Body-Klasse `functions.php:8581`. In D5-Servercode kein Leser gefunden. | +| `section_padding` | `functions.php:2006-2015`, Control `:2017-2032` | `'4'` | `absint` | Range 0–10, step 1 | D4 `functions.php:5837`. | +| `row_padding` | `functions.php:2090-2099`, Control `:2101-2116` | `'2'` | `absint` | Range 0–10, step 1 | D4 `functions.php:5838`. | + +Abhängige Standards: `tablet_*` / `phone_*` werden im Customizer beim Registrieren aus dem aktuellen Größeren berechnet; die CSS-Leser nutzen feste 14/30 und „gleich dem Größeren = keine Regel“. Für das Tool: Standard von `tablet_body_font_size` = effektiver `body_font_size`, `phone_*` = effektiver `tablet_*`; „unset“ heißt „erbt“. + +### A.1 Builder-Globals (`--et_global_*`) + +- `GlobalData::$customizer_fonts` (`includes/builder-5/server/Packages/GlobalData/GlobalData.php:108-119`): `--et_global_heading_font` → `heading_font`, `--et_global_body_font` → `body_font`, beide Standard `'Open Sans'`. `get_customizer_fonts()` (`:446-470`) liefert `{id,label,value,status:'active',order}`, `static` gecacht. +- Frontend-Variablen (`FrontEnd/Module/Fonts.php:99-191`, `Fonts::enqueue()`): `--et_global_heading_font`, `--et_global_body_font`, `--et_global_heading_font_weight`, `--et_global_body_font_weight`, `--et_global_body_font_size` (+ responsive aus `tablet_/phone_body_font_size`), `--et_global_body_font_height` – also speisen **heading_font, body_font, heading_font_weight, body_font_weight, body_font_size, tablet_body_font_size, phone_body_font_size, body_font_height** Divi-5-Globals; inline ausgegeben (`