Stack Frontend â Telaria
Ce document dĂ©crit ce qui tourne rĂ©ellement en production, pas les alternatives Ă©cartĂ©es. Choix dĂ©libĂ©rĂ©ment sobre : pas de framework JS Ă gouverner â la qualitĂ© d'implĂ©mentation comme diffĂ©renciateur.
Vue d'ensemble
| Couche | Outil | Version |
|---|---|---|
| Gestion assets | Symfony AssetMapper | (Symfony 7) |
| Modules JS | Import Maps (natif navigateur) | â |
| Composants UI | Bootstrap | 5.3.8 |
| IcĂŽnes | Bootstrap Icons | 1.13.1 |
| Navigation SPA-like | Turbo Drive + Turbo Frames | 7.3.0 |
| Comportements JS | Stimulus | 3.2.2 |
| Graphiques | Chart.js | 3.9.1 |
| Templates | Twig | (Symfony 7) |
| CSS | CSS natif (BEM partiel) + Bootstrap utilities | â |
Pas de build step (webpack, vite, esbuild). AssetMapper + importmap = assets servis directement, versionnés par hash.
AssetMapper + importmap
Symfony AssetMapper remplace Webpack Encore. Les modules sont déclarés dans importmap.php et résolus par le navigateur via <script type="importmap">.
// importmap.php (extrait) return [ 'app' => ['path' => './assets/app.js', 'entrypoint' => true], '@hotwired/turbo' => ['version' => '7.3.0'], '@hotwired/stimulus' => ['version' => '3.2.2'], 'bootstrap' => ['version' => '5.3.8'], 'bootstrap/dist/css/bootstrap.min.css' => ['version' => '5.3.8', 'type' => 'css'], 'bootstrap-icons/font/bootstrap-icons.min.css' => ['version' => '1.13.1', 'type' => 'css'], 'chart.js' => ['version' => '3.9.1'], 'canvas-confetti' => ['version' => '1.9.4'], ];
Ajout d'une dĂ©pendance : php bin/console importmap:require nom-du-package â met Ă jour importmap.php automatiquement.
Point d'entrée : assets/app.js (seul entrypoint). Tous les autres modules sont importés depuis ce fichier ou via Stimulus.
Turbo Drive + Turbo Frames
Turbo Drive
Navigation SPA-like sans JS cĂŽtĂ© serveur. Le clic sur un lien charge la rĂ©ponse en AJAX et remplace le <body> â pas de rechargement complet. ActivĂ© globalement via l'import dans app.js :
import '@hotwired/turbo'; // démarre Turbo Drive globalement
Turbo Frames â navigation partielle
Les frames chargent et mettent à jour une portion de page indépendamment. Usage en production :
<turbo-frame id="doc">â arborescence de documentation (navigation gauche/contenu)<turbo-frame id="config">â hub de configuration BO (panneau gauche/panneau droit)
Navigation pilotée explicitement via app.js (l'observer de clic Turbo Drive n'intercepte pas les data-turbo-frame dans cette version) :
document.addEventListener('click', (event) => { const link = event.target.closest('a[href]'); if (link?.dataset.turboFrame) { const frame = document.getElementById(link.dataset.turboFrame); if (frame && 'TURBO-FRAME' === frame.tagName) { event.preventDefault(); frame.src = link.href; // Synchronise l'URL du navigateur pour /docs et /admin/config (F5/partage) if (link.dataset.turboFrame in FRAME_NAV_PREFIXES) { window.history.pushState({}, '', link.href); } } } });
Popstate (window.history) synchronise le cadre avec le bouton Précédent.
Auto-refresh conditionnel
Le cadre #config se recharge toutes les 10 s tant qu'une ré-indexation RAG est en cours (marqueur data-config-reindex-running dans le DOM) :
setInterval(() => { if (!document.querySelector('[data-config-reindex-running]')) return; document.getElementById('config')?.reload(); }, 10000);
Stimulus â comportements JS ciblĂ©s
Stimulus ajoute des comportements sans framework global. Chaque controller est un ES module chargé à la demande.
Chargement
// assets/stimulus_bootstrap.js (gĂ©nĂ©rĂ© par Symfony) // assets/controllers.json â controllers UX Symfony (chart, turbo)
{ "controllers": { "@symfony/ux-chartjs": { "chart": { "enabled": true, "fetch": "eager" } }, "@symfony/ux-turbo": { "turbo-core": { "enabled": true } } } }
CSRF Protection (Stimulus controller)
// assets/controllers/csrf_protection_controller.js // GénÚre et double-soumet un token CSRF (champ formulaire + cookie) // Compatible Turbo : écoute turbo:submit-start / turbo:submit-end export function generateCsrfToken(formElement) { /* ... */ } export function generateCsrfHeaders(formElement) { /* ... */ }
/* stimulusFetch: 'lazy' */ â chargĂ© seulement quand le contrĂŽleur est prĂ©sent dans le DOM.
Sélection groupée (sources de veille)
Gestion de la sélection de masse via délégation globale d'événements (pas de Stimulus controller dédié ici) :
// délégation sur 'click' et 'change' pour les checkboxes .src-check // opérant que la liste soit en pleine page ou dans le cadre #config
Bootstrap 5.3 + Bootstrap Icons
Bootstrap est importé comme module ES (pas en CDN) :
import 'bootstrap'; // JS (composants : modal, dropdown, collapseâŠ) import 'bootstrap/dist/css/bootstrap.min.css'; // CSS import 'bootstrap-icons/font/bootstrap-icons.min.css'; // IcĂŽnes (font)
IcÎnes référencées dans le code PHP (interface ConfigSectionInterface::icon()) :
public function icon(): string { return 'bi-rss'; } // VeilleSourcesSection public function icon(): string { return 'bi-chat-dots'; }
Les icÎnes Bootstrap Icons sont utilisées directement dans les templates Twig via <i class="bi bi-rss">.
Organisation CSS
assets/styles/ âââ app.css â rĂšgles globales : chrome CMS Ă©ditable, skip link, utilitaires âââ admin.css â styles back-office (chargĂ© conditionnellement si utilisateur connectĂ©) âââ front.css â styles surface publique
Chargement conditionnel
import 'bootstrap/dist/css/bootstrap.min.css'; import './styles/app.css'; if (isLoggedIn()) { import('./styles/admin.css'); // dynamic import â pas chargĂ© pour les visiteurs } export function isLoggedIn() { return window.app?.user !== null; }
Chrome CMS éditable (app.css)
Les blocs nav/footer sont édités en Markdown au BO et rendus en HTML en front. Le CSS neutralise les marges et stylise les liens pour qu'un [Lien](/url) ressemble au chrome natif :
.cms-chrome > :last-child { margin-bottom: 0; } .cms-chrome p { margin: 0; } .cms-chrome ul { list-style: none; display: flex; flex-wrap: wrap; gap: .75rem; } .cms-chrome-nav a { text-decoration: none; }
Skip link (accessibilité)
.skip-link { position: absolute; top: -1px; transform: translate(-50%, -200%); /* hors-écran par défaut */ transition: all 0.1s; &:focus { transform: translate(-50%, 0%); } /* visible au focus clavier */ }
Organisation des templates Twig
templates/ âââ admin/ â âââ body/nav.html.twig â navigation BO globale â âââ cms/ â CRUD CMS (sites, contenus, mĂ©dias) â âââ config/ â hub de configuration (index + sections par feature) â âââ dashboard.html.twig â tableau de bord avec partials _metrics/_rag/_veille â âââ metrics/ â alertes, graphiques consommation IA â âââ veille/ â proposals (validation), sources, stats â âââ users/ âââ cms/ â âââ index.html.twig â layout CMS (Ă©tendu par les sites multisite) â âââ show.html.twig â rendu d'une page CmsContent (Markdown â HTML) âââ components/ â âââ _pagination.html.twig â âââ _source_tree.html.twig âââ contact/ âââ bundles/TwigBundle/Exception/ â âââ error.html.twig â âââ error403.html.twig â âââ error404.html.twig âââ (docs/, reader/, assistant/, âŠ)
Convention : les partials commencent par _ (ex. _list.html.twig, _metrics.html.twig).
Multisite â hĂ©ritage de layout
Chaque Site pointe vers son layout_template (ex. cms/index.html.twig). Les pages CMS d'un site vitrine étendent ce layout et n'ont accÚs qu'à leur propre chrome éditorial (blocs nav/footer scopés au site).
PWA â Web App Manifest
// assets/favicon/site.webmanifest
// Fournit les métadonnées pour "Ajouter à l'écran d'accueil"
// IcĂŽnes : 192Ă192 + 512Ă512 (android-chrome-*.png)
// Apple touch icon : 180Ă180
Pas de Service Worker actif en production â manifest seul, pas de mode offline.
Voir aussi
architecture-symfony.mdâ Stimulus (AssetMapper), Twig, Bootstrap cĂŽtĂ© archipatterns-symfony.mdâ patterns PHP cĂŽtĂ© backendtutos/ia/accessibilite-chatbot.mdâ accessibilitĂ© interface IAaccessibility/interface-utilisateur.mdâ critĂšres accessibilitĂ© UI