# document_engine/rendering/
Rendu HTML du document — point d'entrée unique utilisé à la fois par le
canevas d'édition et par le Mode Aperçu (même fonction, voir
`docs/plan/PLAN.md` — "Aperçu réel").
## `render_document(elements: list[dict[str, Any]]) -> str`
Assemble le document entier à partir de la liste à plat renvoyée par
`document_engine.list_document_elements` : regroupe les éléments par
`parent_id`, puis rend récursivement les éléments top-niveau dans
l'ordre (une rangée rend elle-même ses propres enfants côte à côte).
- **Retour** : le HTML complet du document.
- **Exceptions** : aucune.
## `render_document_element(el: dict[str, Any], children_by_parent: dict[int | None, list[dict[str, Any]]]) -> str`
HTML d'un seul élément — dispatch par dict (table `_RENDERERS`) selon
`el["kind"]`, plutôt qu'un enchaînement de `if` (mirroir de l'esprit de
`game_engine/scenes/render_scene_object.py`). `children_by_parent` est le
regroupement précalculé par `render_document`, transmis pour que les
rangées puissent rendre récursivement leurs enfants sans refaire le
regroupement à chaque appel.
- **Retour** : le HTML de cet élément (et de ses enfants s'il s'agit
d'une rangée).
- **Exceptions** : aucune ; un `kind` inconnu produit un bloc
`docUnknown` visible plutôt qu'une levée d'exception.
### Détail des rendus par catégorie (fonctions privées, table de dispatch)
- **Rangée** (`row`) : conteneur flex (`gap`/`align-items`/
`justify-content` réels depuis `attributes`), enfants rendus
récursivement.
- **Texte** (`titre`/`paragraphe`) : `
` stylé selon `style` (préréglage
taille/graisse/interligne) et `bold`/`italic`/`underline`/`strikethrough`/
`align`/`color`. `underline`/`strikethrough` se combinent dans un seul
`text-decoration` (`"underline line-through"` si les deux sont actifs).
`font_size`/`line_height` (vides par défaut) remplacent les valeurs du
préréglage `style` SANS toucher `font-weight` (toujours piloté par le
préréglage + `bold`). `text_transform` (`"none"` par défaut) ajoute
`text-transform` quand différent de `"none"`. `font_family`/
`letter_spacing`/`text_shadow` (vides par défaut) ajoutent leur
déclaration CSS respective quand non vides. `max_width` (optionnel, ex.
`"60ch"`, `"480px"`) ajoute `max-width` au style inline quand non vide —
pleine largeur de `.docPageContent` par défaut, retour utilisateur du
24/09/2026 (un paragraphe doit pouvoir rester plus étroit que la page,
sans dépendre d'une rangée qui en partagerait la largeur avec un frère).
Termine par `render_box_style(a)` (voir `box_style.py` ci-dessous) pour
`padding`/`margin`/`background_color`/`border_radius`/`border`/
`align_self` — attributs PARTAGÉS avec d'autres kinds, jamais dupliqués
ici (audit du 26/09/2026, réglages manquants à couvrir élément par
élément en réutilisant ce module).
- **Image** : ``, ou un bloc placeholder si `src` est vide — OU, si
`attributes["svg_markup"]` est non vide (prioritaire sur `src`), un
`
` portant directement ce fragment SVG nettoyé par
`sanitize_svg_markup` (voir `sanitize_svg_markup.py` ci-dessous) : un
contenu vectoriel dessiné/collé par le créateur plutôt qu'un fichier
hébergé, sans aucun style qui lui soit propre.
- **Bouton** : `