Skip to Content
MoodlePartie 8 — Les thèmes Moodle : le guide completThèmes avancés : performance, mobile, accessibilité, RTL, upgrades, distribution

Chapitre 8.5 — Thèmes avancés : performance, mobile, accessibilité, RTL, upgrades, distribution

Partie 8 : Les thèmes — Chapitre 5/5 Public : dev front visant l’expertise thème. Les sujets qui séparent l’amateur du pro. Version de référence : Moodle 5.2 (Boost / Bootstrap 5.3, scssphp, rtlcss-php, Brickfield).

⏱️ TL;DR

  • Performance : le CSS compilé peut être lourd ; limiter les surcharges, héberger les fontes localement (woff2, font-display: swap, subset), optimiser images/SVG, viser de bons Core Web Vitals (LCP, CLS, TBT).
  • App mobile : le thème web ne s’applique PAS dans l’app officielle (rendu Ionic/Angular propre). On l’influence via mobilecssurl (CSS distant) et les réglages de l’app, pas via le thème.
  • Accessibilité : obligation légale (éducation). Viser WCAG 2.1 AA : contrastes (attention au dark mode et à $primary), focus visible, ARIA, navigation clavier. Outil intégré : Brickfield Accessibility Toolkit.
  • RTL : Moodle flippe automatiquement le CSS pour les langues droite-à-gauche via rtlcss-php ; éviter les valeurs directionnelles en dur, préférer les propriétés logiques.
  • Survivre aux upgrades : à chaque version, diff les templates surchargés contre les originaux, tester visuellement, purger. Tenir une liste des surcharges.
  • Distribuer : GPL, plugin directory (comme tout plugin, ch. 6.7), captures, versions supportées ; marché commercial de thèmes existant mais 100 % GPL.

🎯 Objectifs

  • Optimiser le poids d’un thème et ses Core Web Vitals.
  • Comprendre le découplage thème web / app mobile et mobilecssurl.
  • Atteindre WCAG 2.1 AA et utiliser Brickfield.
  • Gérer le RTL correctement (propriétés logiques).
  • Maintenir un thème à travers les upgrades et le distribuer.

1. Performance : le CSS, les fontes, les images

Un thème mal fait dégrade tout le site (le CSS est global). Les leviers :

1.1 Le poids du CSS compilé

Chaque surcharge SCSS, chaque preset alourdit le CSS final. Bootstrap + Boost sont déjà conséquents ; empiler des règles sans purge accroît le poids et le temps de parsing.

  • Minimiser les surcharges (ch. 8.4) : moins de SCSS custom = CSS plus léger.
  • Le CSS est compilé et mis en cache par scssphp (ch. 8.1) — donc le coût de compilation est amorti, mais le poids transféré au navigateur reste, à chaque première visite. Un CSS de 500 Ko pèse sur le LCP.
  • Servir en gzip/brotli (config serveur) et via CDN si possible (le CSS de thème est un asset statique cacheable).

1.2 Les fontes

Les fontes sont souvent le plus gros poste de poids après le CSS :

  • woff2 uniquement (le format le plus compressé, supporté partout en 2026).
  • font-display: swap (ch. 8.2) : le texte s’affiche en fonte système d’abord, évitant un FOIT (texte invisible) qui plombe le LCP.
  • Subset : ne charger que les caractères nécessaires (latin + latin-ext pour le français) ; un fichier Inter complet avec tous les scripts est inutilement lourd.
  • Précharger la fonte critique : <link rel="preload" as="font" ...> pour la fonte du corps.
  • Local, jamais de CDN (RGPD + fiabilité, ch. 8.2).

1.3 Images et SVG

  • Logo/favicon en SVG quand possible (net, léger, scalable).
  • Compresser les PNG/JPG, servir des tailles adaptées.
  • Éviter les grandes images de fond non optimisées sur le login/landing (impact LCP direct).

⚠️ Piège : un splendide thème avec une grande image de fond de login non compressée (2 Mo) et Inter chargée en entier (tous scripts) peut avoir un LCP catastrophique sur mobile/3G. Le design ne doit pas se payer en performance : mesurez avec Lighthouse sur mobile simulé.


2. Le cas de l’app mobile

Point qui surprend tous les débutants : votre thème web ne s’applique pas dans l’application mobile officielle (ch. 1.4). L’app est une application Ionic/Angular avec son propre rendu ; elle consomme les données via web services, pas le HTML/CSS de votre thème.

Comment influencer l’app :

  • mobilecssurl (Administration → Mobile → Apparence) : une URL de CSS distant que l’app charge pour personnaliser certains styles (couleurs, en-têtes). C’est un CSS séparé de votre thème, aux possibilités limitées (on ne redessine pas l’app).
  • Les réglages de couleur de l’app (dans l’admin mobile) : couleur primaire, etc.
  • Les remote themes : mécanisme pour pousser un CSS de marque à l’app.

Conséquence de conception : un client qui veut « la même identité web et mobile » doit comprendre que l’app mobile se personnalise séparément et partiellement. Ne promettez pas un pixel-perfect mobile via votre thème web — c’est impossible par construction.

💡 Pour un dev React : pensez au thème web et à l’app mobile comme à deux frontends distincts consommant la même API (les web services, Partie 9). Votre CSS web ne traverse pas plus vers l’app native qu’un CSS de site web ne stylise une app React Native. mobilecssurl est un petit hook de branding, pas un pont de thème.


3. Accessibilité : WCAG 2.1 AA et Brickfield

Pour un LMS, l’accessibilité n’est pas optionnelle : c’est une obligation légale dans l’éducation (secteur public notamment). La cible standard est WCAG 2.1 AA.

Ce que votre thème doit garantir :

  • Contrastes suffisants : $primary sur blanc, texte sur fonds colorés (footer, login), et les deux modes (clair et sombre). Un violet de marque peut passer en clair et échouer en sombre — vérifiez les deux.
  • Focus visible : ne jamais supprimer l’outline de focus sans le remplacer. Le focus clavier doit être clairement visible (ch. 7.6).
  • Structure sémantique : vos surcharges de templates doivent conserver les rôles ARIA, les landmarks (<main>, <nav>), la hiérarchie de titres.
  • Cibles tactiles suffisantes, redimensionnement du texte sans casse (jusqu’à 200 %).

Le Brickfield Accessibility Toolkit est intégré à Moodle : il audite le contenu des cours et signale les problèmes. Il concerne surtout le contenu (images sans alt, contrastes de texte saisi), mais votre thème doit fournir une base accessible pour ne pas ajouter de problèmes structurels.

⚠️ Piège du dark mode : un contraste validé en mode clair peut échouer en mode sombre (texte gris clair sur fond gris foncé). Le dark mode double le travail de contraste : testez chaque couleur dans les deux modes avec un vérificateur de contraste (ratio ≥ 4.5:1 pour le texte normal, ≥ 3:1 pour le grand texte).

📚 Aller plus loin : outils — extension axe DevTools, le vérificateur de contraste WebAIM, et Brickfield en interne. La Partie 11 revient sur l’accessibilité côté audit et conformité.


4. RTL : les langues droite-à-gauche

Moodle supporte des langues RTL (arabe, hébreu…). La bonne nouvelle : Moodle flippe automatiquement le CSS via rtlcss-php (les margin-left deviennent margin-right, etc.) quand la langue de l’utilisateur est RTL. Votre thème hérite de ce comportement.

Vos responsabilités :

  • Ne pas coder de directions en dur de façon qui casserait le flip. Préférer, quand c’est pertinent, les propriétés logiques CSS (margin-inline-start au lieu de margin-left, padding-inline-end…), qui s’adaptent nativement à la direction.
  • Tester en RTL si votre public l’exige : passer l’interface en arabe et vérifier que vos surcharges (login, footer, composants) ne cassent pas (icônes directionnelles, alignements, débordements).
  • Attention aux images directionnelles (flèches) et aux valeurs absolues de positionnement.

⚠️ Piège : une surcharge de template avec du style inline style="margin-left: 20px" ne sera pas flippée (rtlcss agit sur les feuilles compilées, pas toujours sur l’inline selon le cas). Mettez vos styles dans le SCSS (flippé automatiquement) ou utilisez des propriétés logiques. Évitez le CSS inline dans les thèmes, aussi pour cette raison.


5. Survivre aux upgrades (le nerf de la guerre)

Rappel du 8.3/8.4 : chaque surcharge est une copie figée qui diverge du core. La procédure d’upgrade d’un thème (5.2 → 5.3) :

  1. Lister toutes vos surcharges (templates, renderers, layouts). Tenir cette liste à jour est un livrable en soi.
  2. Pour chaque template surchargé, diff l’original 5.3 contre l’original 5.2 (celui que vous aviez copié). Reporter les changements pertinents dans votre copie.
  3. Pour chaque renderer/layout surchargé, vérifier que la signature et les méthodes appelées existent toujours (les dépréciations sont dans les upgrade notes, ch. 6.7).
  4. Tester visuellement les écrans clés (login, dashboard, cours, activités, formulaires) dans les deux modes (clair/sombre) et en RTL si concerné.
  5. Purger les caches, revalider les Core Web Vitals.

⚠️ Piège : un thème « livré et oublié » se dégrade en silence au fil des versions (markup obsolète, correctifs d’accessibilité du core non répercutés, styles cassés). Budgétez la maintenance dès le devis (ch. 8.4). Un thème est un produit vivant.


6. Distribuer ou vendre un thème

Un thème se distribue comme tout plugin (ch. 6.7) :

  • GPL v3+ obligatoire (comme tout le directory), même pour un thème vendu.
  • Plugin directory : fiche avec captures d’écran (attendues pour un thème !), versions de Moodle supportées, privacy provider (souvent null_provider pour un thème sans données perso), moodle-cs vert.
  • Versions supportées honnêtes (supported, ch. 6.7) : un thème est très sensible aux versions (Bootstrap, templates).
  • Marché commercial : des thèmes premium se vendent (RemUI, Moove Pro…), sous licence GPL — on vend le service, le support, les mises à jour, pas une exclusivité du code (l’écosystème est 100 % GPL, ch. 1.4). Modèle traité en Partie 11.8.

💡 Pour un dev React : vendre un thème GPL, c’est le modèle « open-core + support/abonnement » que vous connaissez (le code est libre, la valeur est dans les mises à jour, l’assistance, l’installation clé en main). Vous ne vendez pas une licence de code fermé — vous vendez la maintenance de ce produit vivant à travers les upgrades, précisément le coût identifié au 8.4.


7. Synthèse de la Partie 8

La réponse complète à « est-on flexible ? » : oui, totalement en surface (SCSS + templates + renderers + layouts atteignent 100 % de l’UI), mais le prix se paie en maintenance (surcharges à re-diffuser 2×/an), et certaines zones (formulaires, plugins tiers, app mobile, accessibilité des deux modes, RTL) demandent une expertise et un chiffrage honnêtes. Le bon expert thème n’est pas celui qui « fait tout », c’est celui qui atteint l’identité voulue avec le minimum de surcharges et budgète la vie du thème.


✏️ Exercices

Exercice 1 — LCP catastrophique. Le login a une image de fond de 3 Mo et charge Inter avec tous les scripts. Sur mobile, le LCP est à 6 s. Trois correctifs ?

✅ Solution

(1) Optimiser/compresser l’image de fond (format moderne AVIF/WebP, dimension adaptée, ou un dégradé CSS si l’image n’est pas essentielle) ; (2) Subset de la fonte (latin + latin-ext seulement), woff2, font-display: swap, et preload de la fonte du corps ; (3) servir les assets en brotli/gzip et via CDN cacheable. Mesurer à nouveau avec Lighthouse mobile. Le design ne doit pas se payer en LCP.

Exercice 2 — Identité mobile. Un client veut « exactement la même interface » sur web et dans l’app mobile officielle. Que répondez-vous ?

✅ Solution

Que c’est impossible par construction : l’app mobile officielle est une app Ionic/Angular avec son propre rendu, elle ne charge pas le HTML/CSS de votre thème web (elle consomme les web services). On peut la personnaliser partiellement via mobilecssurl (CSS distant limité) et les réglages de couleur de l’app, mais pas la redessiner comme le web. Web et mobile sont deux frontends d’une même API : promettre un pixel-perfect commun est irréaliste ; on aligne les couleurs de marque des deux côtés séparément.

Exercice 3 — Contraste et dark mode. Votre $primary violet passe le contraste en mode clair. Pourquoi tester aussi en sombre, et sur quels ratios ?

✅ Solution

Parce que le dark mode change les couleurs de fond et de texte : un contraste validé en clair peut échouer en sombre (ex. violet sur fond sombre, ou texte gris clair sur gris foncé). WCAG 2.1 AA exige un ratio ≥ 4.5:1 pour le texte normal et ≥ 3:1 pour le grand texte / éléments d’interface. Il faut vérifier $primary et toutes les paires texte/fond dans les deux modes avec un vérificateur de contraste (WebAIM, axe). Le dark mode double le travail d’accessibilité des couleurs.

Exercice 4 — RTL cassé. En arabe, votre footer surchargé a ses éléments mal alignés (marges à gauche au lieu de droite). Cause et correctif ?

✅ Solution

Vous avez probablement utilisé des directions en dur non flippées : du CSS inline (style="margin-left:…", que rtlcss ne traite pas toujours) ou des propriétés directionnelles dans un contexte non compilé. Correctif : mettre les styles dans le SCSS du thème (flippé automatiquement par rtlcss-php) et/ou utiliser des propriétés logiques (margin-inline-start, padding-inline-end) qui s’adaptent nativement à la direction. Éviter le CSS inline dans les surcharges, et tester l’interface en langue RTL.

Exercice 5 — Plan d’upgrade. Votre thème surcharge 12 templates. Décrivez la procédure pour l’upgrade 5.2 → 5.3.

✅ Solution

(1) Lister les 12 surcharges. (2) Pour chaque template, récupérer l’original 5.3 et le diff contre l’original 5.2 (celui copié) ; reporter les changements pertinents (nouveaux champs de contexte, correctifs d’accessibilité, restructurations) dans votre copie. (3) Vérifier d’éventuels renderers/layouts contre les upgrade notes (dépréciations). (4) Tester visuellement login, dashboard, cours, activités, formulaires, en clair + sombre (+ RTL si concerné). (5) Purger les caches, revalider les Core Web Vitals. Documenter les changements pour la prochaine fois.


🧠 Quiz de révision

Q1. Quels sont les principaux postes de poids d’un thème et comment les optimiser ?

Réponse

Le CSS compilé (minimiser les surcharges, gzip/brotli, CDN) et surtout les fontes (woff2, subset latin, font-display: swap, preload, hébergées localement) et les images (SVG pour logo, compression, tailles adaptées). Objectif : bons LCP/CLS/TBT, mesurés en Lighthouse mobile.

Q2. Le thème web s’applique-t-il dans l’app mobile officielle ?

Réponse

Non. L’app est une application Ionic/Angular avec son propre rendu, consommant les web services. On la personnalise séparément et partiellement via mobilecssurl (CSS distant limité) et les réglages de couleur de l’app — jamais via le thème web.

Q3. Quelle est la cible d’accessibilité et le piège majeur du dark mode ?

Réponse

WCAG 2.1 AA (obligation légale en éducation). Piège : un contraste validé en mode clair peut échouer en mode sombre ; il faut tester chaque paire texte/fond dans les deux modes (ratios ≥ 4.5:1 texte normal, ≥ 3:1 grand texte). Outil interne : Brickfield.

Q4. Comment Moodle gère-t-il le RTL et quelle est votre responsabilité ?

Réponse

Moodle flippe automatiquement le CSS compilé via rtlcss-php pour les langues RTL. Votre responsabilité : ne pas coder de directions en dur (éviter le CSS inline), préférer les propriétés logiques (margin-inline-start…), et tester en langue RTL (icônes directionnelles, alignements).

Q5. Que faut-il faire à chaque upgrade de Moodle pour un thème avec des surcharges ?

Réponse

Diff chaque template surchargé contre l’original mis à jour et reporter les changements, vérifier les renderers/layouts contre l’API (dépréciations), tester visuellement les écrans clés en clair/sombre (+ RTL), purger les caches et revalider les Core Web Vitals. Tenir une liste des surcharges à jour. Le thème est un produit vivant à maintenir.

Fin de la Partie 8. Vous savez comment fonctionne un thème (héritage + pipeline SCSS), en construire un enfant de Boost complet, surcharger templates/renderers/layouts, mesurer honnêtement la flexibilité et son coût, et gérer les sujets d’expert (performance, mobile, accessibilité, RTL, upgrades, distribution). La réponse à « est-on flexible ? » est désormais nuancée et opérationnelle : oui, mais budgétez la maintenance.

Partie suivante : Partie 9 — Web Services : l’API externe de Moodle. On ouvre Moodle vers l’extérieur : architecture des web services, tokens, REST, fonctions externes du core, créer sa propre fonction externe, et consommer l’API depuis Next.js — le pont de données entre Moodle et votre écosystème JavaScript.