{# Prefetch Turbo (survol) DÉSACTIVÉ : sur OVH mutualisé (workers FPM limités) le prefetch des cartes de recherche tapait /livres/lookup, qui MATÉRIALISE le livre (import OL/GB, lent). Survoler la grille de résultats lançait donc une rafale d'imports concurrents (~1 min chacun) qui saturait le serveur et bloquait la vraie navigation. App mobile-first = pas de survol de toute façon : aucune perte UX. #} {# Active les View Transitions de Turbo 8. On n'anime QUE la pastille de nav (glissement d'onglet a onglet) ; l'animation racine est coupee en CSS pour rester discret. #} {% block title %}LivreComment{% endblock %} {# Aperçu de lien au partage (Open Graph + Twitter). Défaut générique ; les fiches livre et les critiques surchargent ce block avec leur couverture, titre et extrait. #} {% block og %} {% endblock %} {# Preconnect aux CDNs de covers (utilisés sur les fiches Works pour les images Wikipedia FR / Google Books). Économise le handshake TLS au moment où le browser découvre les . #} {# General Sans servie en local (public/fonts/general-sans/) — plus de dépendance Fontshare CDN. La 400 est preload pour éviter le FOIT sur le texte du corps. Les autres graisses chargent à la demande. #} {% block stylesheets %} {# CSS chargé fichier par fichier, PAS via @import dans app.css : le preload-scanner du navigateur découvre tous les dès le parsing du HTML et les télécharge en parallèle (HTTP/2), au lieu de la cascade app.css -> parse -> @import (1 aller-retour réseau en plus avant le 1er rendu). L'ordre ci-dessous EST le cascade (identique à l'ancien app.css) : tokens d'abord, pages ensuite. Les modules sont des assets digérés individuels (asset() résout chacun) : aucune recompilation nécessaire pour ce changement. #} {% for css in [ 'styles/_tokens.css', 'styles/_base.css', 'styles/_typography.css', 'styles/_a11y.css', 'styles/_layout.css', 'styles/_header.css', 'styles/_buttons.css', 'styles/_forms.css', 'styles/_flash.css', 'styles/_utilities.css', 'styles/pages/home.css', 'styles/pages/search.css', 'styles/pages/work-show.css', 'styles/pages/author-show.css', 'styles/pages/profile-show.css', 'styles/pages/list-show.css', 'styles/pages/list-form.css', 'styles/pages/list-row.css', 'styles/pages/review-show.css', 'styles/pages/notifications.css', 'styles/pages/onboarding.css', 'styles/pages/decouverte.css', 'styles/components/ad-slot.css' ] %} {% endfor %} {% endblock %} {# Theme bootstrap inline AVANT le reste — évite le FOUC et empêche le Force Dark Mode des browsers. On set color-scheme à "only light" ou "only dark" selon le thème actif (jamais "light dark") pour que les browsers respectent strictement notre choix au lieu d'appliquer leur propre auto-dark. #} {% block javascripts %} {% block importmap %}{{ importmap('app') }}{% endblock %} {% endblock %} {# Loader de la régie publicitaire : à coller UNE fois ici (dans ce block, ou en override). Rendu seulement si ADS_ENABLED. #} {% if ads_enabled() %}{% block ads_head %}{% endblock %}{% endif %} Aller au contenu principal {# Header global : logo centré par défaut. Les templates peuvent override le block `app_header` pour un header contextuel (ex. profil propre style Instagram avec @username au centre). #} {% block app_header %} {% endblock %}
{% for label, messages in app.flashes %} {% for message in messages %}
{{ message }}
{% endfor %} {% endfor %} {% block body %}{% endblock %}
{% if app.user %} {% endif %} {# Service worker : app installable (Android) + mode hors-ligne minimal. Network-first sur les pages (jamais de HTML périmé), cache-first sur les assets hashés. Enregistrement inline : la CSP autorise script-src 'self' 'unsafe-inline' (aucun nonce). Fichier servi à la racine (/sw.js) pour un scope global. #} {% if app.environment == 'prod' %} {% else %} {# Dev : ne PAS enregistrer le SW (il fige les assets en cache-first, ce qui masque toute modif CSS/JS) et désenregistrer tout SW déjà présent. #} {% endif %}