drupal theme creation

Bonnes pratiques de création de thèmes pour Drupal 10 et 11

La création de thèmes pour Drupal 10 et 11 nécessite une approche structurée et l’adhésion à de bonnes pratiques pour garantir des sites web performants, accessibles, sécurisés et faciles à maintenir.

Ce guide vous accompagnera à travers les principes fondamentaux et les applications pratiques pour devenir un développeur de thèmes Drupal expert.

1. Accessibilité (A11y)

L’accessibilité web (a11y), un numéronyme pour « accessibility » (il y a 11 lettres entre le ‘a’ et le ‘y’), vise à rendre les sites web utilisables par le plus grand nombre, y compris les personnes en situation de handicap.

Cela inclut des utilisateurs naviguant au clavier, utilisant des lecteurs d’écran ou ayant des déficiences visuelles ou cognitives.

Drupal 10 et 11 s’alignent sur les recommandations internationales telles que les WCAG 2.1 (Web Content Accessibility Guidelines), le RGAA 4.1 (Référentiel Général d’Amélioration de l’Accessibilité) pour la France, et les spécifications ARIA (Accessible Rich Internet Applications) pour les interfaces utilisateur dynamiques.

1.1. Sauter au contenu principal

Twig

<a href="#main-content" class="visually-hidden focusable">
Aller au contenu principal
</a>

Explication : Ce lien « sauter au contenu » est une fonctionnalité essentielle pour les utilisateurs de clavier et de lecteurs d’écran.

La classe visually-hidden rend le lien invisible à l’œil nu mais accessible aux technologies d’assistance. La classe focusable le rend visible uniquement lorsque l’utilisateur y navigue avec la touche Tab.

Le lien dirige vers l’identifiant #main-content, qui doit être l’identifiant de la balise <main> de votre page, permettant ainsi à l’utilisateur de passer directement au contenu principal, évitant la navigation répétitive à travers les éléments de navigation.

1.2. Utiliser les landmarks HTML5 + ARIA

HTML

<header role="banner">...</header>
<nav role="navigation" aria-label="Navigation principale">...</nav>
<main id="main-content" role="main">...</main>
<footer role="contentinfo">...</footer>

Explication : Les landmarks HTML5 (comme <header>, <nav>, <main>, <footer>) fournissent une structure sémantique à votre page.

Les rôles ARIA (comme role= »banner », role= »navigation », role= »main », role= »contentinfo ») complètent cette sémantique, en particulier pour les navigateurs plus anciens ou les technologies d’assistance qui pourraient ne pas encore reconnaître pleinement les nouvelles balises HTML5.

L’attribut aria-label est crucial pour fournir un nom accessible aux éléments de navigation, aidant les utilisateurs de lecteurs d’écran à comprendre la fonction de la navigation.

1.3. Bouton accessible avec ARIA

HTML

<button aria-expanded="false" aria-controls="menu-content" id="menu-toggle">Menu</button>
<div id="menu-content" hidden>...</div>

Explication : Lorsqu’il s’agit de composants interactifs comme des boutons qui contrôlent la visibilité d’autres éléments (par exemple, un menu déroulant), les attributs ARIA sont indispensables. aria-expanded indique si le contenu contrôlé est actuellement étendu ou non (true ou false). aria-controls pointe vers l’identifiant de l’élément qu’il contrôle.

L’attribut hidden masque initialement le contenu du menu, et il sera géré dynamiquement par JavaScript pour l’afficher ou le masquer, mettant à jour aria-expanded en conséquence.

2. Performances Front-end

Un thème performant est crucial pour le référencement naturel (SEO), l’expérience utilisateur et la conversion. Les performances front-end se concentrent sur la réduction du temps de chargement des pages en optimisant les ressources (CSS, JavaScript, images, polices) et en les chargeant de manière stratégique.

Drupal offre des outils pour le chargement conditionnel des assets et l’optimisation des médias.

2.1. Chargement conditionnel du CSS

YAML

# mon_theme.libraries.yml
homepage-style:
css:
theme:
css/homepage.css: {}

Twig

{# page.html.twig #}
{% if node.bundle == 'page_accueil' %}
{{ attach_library('mon_theme/homepage-style') }}
{% endif %}

Explication : Le chargement conditionnel des assets signifie que vous ne chargez les fichiers CSS ou JavaScript que lorsque c’est absolument nécessaire.

Dans cet exemple, le fichier homepage.css n’est chargé que si le type de contenu (node.bundle) de la page actuelle est page_accueil. Cela réduit la taille totale des assets téléchargés sur d’autres pages, améliorant ainsi la vitesse de chargement.

Le fichier mon_theme.libraries.yml déclare la bibliothèque, et attach_library est une fonction Twig de Drupal pour l’inclure dynamiquement.

2.2. Lazy Loading des images

Twig

<img src="{{ image_url }}" loading="lazy" alt="{{ image.alt }}" />

Explication : Le lazy loading (chargement paresseux) des images est une technique où les images ne sont chargées que lorsqu’elles entrent dans la fenêtre d’affichage de l’utilisateur (ou sont sur le point d’y entrer).

L’attribut loading= »lazy » est une fonctionnalité native du navigateur qui permet d’implémenter facilement ce comportement. Cela réduit considérablement le temps de chargement initial de la page, car toutes les images ne sont pas téléchargées immédiatement. image_url est la source de l’image, et image.alt est le texte alternatif pour l’accessibilité.

2.3. Préchargement des polices

Twig

<link rel="preload" href="{{ path_to_theme }}/fonts/SourceSans.woff2" as="font" type="font/woff2" crossorigin="anonymous"

Explication : Le préchargement des polices (ou « font preloading ») informe le navigateur de télécharger les fichiers de polices les plus importants en priorité, avant qu’ils ne soient réellement nécessaires pour le rendu du texte.

Cela aide à éviter le « Flash of Unstyled Text » (FOUT) ou le « Flash of Invisible Text » (FOIT), où le texte s’affiche avec une police par défaut avant que la police personnalisée ne soit chargée.

L’attribut as= »font » indique le type de ressource, type= »font/woff2″ précise le format, et crossorigin= »anonymous » est nécessaire pour les requêtes de polices depuis une origine différente (même si c’est le même domaine, c’est une bonne pratique de l’inclure).

3. Sécurité des Thèmes

Un thème Drupal ne doit jamais introduire de failles de sécurité. Cela implique de toujours échapper les données provenant d’entrées utilisateur pour prévenir les attaques par injection (comme le Cross-Site Scripting ou XSS) et d’éviter de gérer directement les permissions ou des logiques sensibles dans les fichiers Twig.

3.1. Utiliser les filtres Twig sécurisés

Twig

{{ label|escape }}
{{ content|render }}

Explication : Dans Twig, le filtre |escape est fondamental pour la sécurité. Il convertit les caractères spéciaux en entités HTML, empêchant ainsi l’exécution de code malveillant si label contient du texte saisi par un utilisateur.

C’est la première ligne de défense contre les attaques XSS. Le filtre |render est utilisé pour rendre des éléments de rendu complexes de Drupal (comme des tableaux ou des formulaires) qui ont déjà été traités et sécurisés par l’API de rendu de Drupal.

N’utilisez jamais |raw sauf si vous êtes absolument certain que le contenu est sécurisé et provient d’une source fiable.

3.2. Éviter le JS ou CSS inline non filtré

Twig

{# À faire : #}
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%3E%0Abody.theme--light%20%7B%20background%3A%20%23fff%3B%20%7D%0A%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object mce-object-style" width="20" height="20" alt="&lt;style&gt;" />
{# À éviter : #}
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%3E%7B%7B%20some_user_input%20%7D%7D%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object mce-object-style" width="20" height="20" alt="&lt;style&gt;" />

Explication : L’intégration de code CSS ou JavaScript directement dans les balises <style> ou <script> du template est risquée si le contenu provient d’une entrée utilisateur non filtrée.

Le premier exemple est sécurisé car le CSS est statique et défini par le développeur.

Le second exemple est dangereux : si some_user_input contient du code malveillant, il sera exécuté directement par le navigateur.

Pour insérer du code dynamique, il faut toujours passer par les APIs de Drupal qui assurent le nettoyage et la validation des données.

4. TWIG & CSS : Architecture, Modularité et Intégration

La synergie entre Twig, SASS, et les méthodologies CSS est fondamentale pour construire des thèmes Drupal modulables, maintenables et performants.

4.1 Architecture CSS (SMACSS + BEM)

Une architecture CSS bien pensée est essentielle pour la maintenabilité et la scalabilité d’un thème. Drupal recommande fortement SMACSS (Scalable and Modular Architecture for CSS) pour organiser les fichiers CSS et BEM (Block, Element, Modifier) pour nommer les classes.

Cette combinaison favorise la réutilisabilité, la clarté et réduit les conflits de styles.

 Arborescence recommandée

css/

├── base/

│   └── base-typography.css

├── layout/

│   └── layout-grid.css

├── component/

│   └── component-card.css

├── state/

│   └── state-dropdown.css

├── theme/

│   └── theme-brand.css

Explication :

  • Base : Contient les styles de base non spécifiques aux composants, comme la typographie globale (base-typography.css) ou les réinitialisations CSS.
  • Layout : Définit la structure globale de la page (grille, entête, pied de page) à l’aide de fichiers comme layout-grid.css.
  • Component : Regroupe les styles des éléments d’interface utilisateur réutilisables et autonomes, tels que les cartes (component-card.css), les boutons, les formulaires.
  • State : Gère les styles qui décrivent l’état d’un élément (actif, désactivé, caché, hover, etc.), par exemple state-dropdown.css pour un menu déroulant.
  • Theme : Contient les styles visuels spécifiques au thème, tels que les couleurs de la marque (theme-brand.css), les fonds, les ombres, etc., qui peuvent être superposés aux composants de base.

Exemple BEM

HTML

<div class="card card--highlighted">
<h2 class="card__title">Titre</h2>
<p class="card__text">Contenu</p>
</div>

SCSS

.card {
&__title { font-weight: bold; }
&__text { color: #333; }
&--highlighted { background-color: #eee; }
}

Explication : BEM est une convention de nommage qui aide à écrire du CSS plus modulaire et réutilisable.

  • Block (.card) : Un composant autonome et réutilisable.
  • Element (.card__title, .card__text) : Une partie du bloc qui ne peut pas être utilisée indépendamment du bloc. Le double underscore __ relie le bloc à son élément.
  • Modifier (.card–highlighted) : Un drapeau sur un bloc ou un élément qui modifie son apparence ou son comportement. Le double tiret — indique un modificateur. L’utilisation de SASS (ou SCSS) avec le nesting (&__title, &–highlighted) rend l’écriture des sélecteurs BEM plus concise et lisible.

4.2. SASS & Modularité

SASS (Syntactically Awesome Style Sheets) est un préprocesseur CSS qui étend les capacités du CSS standard avec des fonctionnalités comme les variables, les mixins, les fonctions, l’imbrication (nesting) et les imports.

Il améliore considérablement la maintenabilité, la réutilisabilité et l’organisation du code CSS dans les projets de grande envergure.

Exemple :

SCSS

// _variables.scss
$primary-color: #0074d9;
$padding: 1rem;

// _button.scss
.button {
background: $primary-color;
padding: $padding;
border: none;
&:hover { background: darken($primary-color, 10%); }
}

YAML

# mon_theme.libraries.yml
mon_theme.global:
css:
theme:
css/component/component-button.css: {}

Explication :

  • Variables ($primary-color, $padding) : Permettent de définir des valeurs réutilisables (couleurs, espacements, polices, etc.). Changer une variable mettra à jour toutes les instances où elle est utilisée, facilitant les modifications globales et la cohérence du design.
  • Mixins (non montré ici mais courant) : Permettent de réutiliser des groupes de déclarations CSS complexes (par exemple, pour des préfixes de navigateur ou des styles de responsive design).
  • Partials (_variables.scss, _button.scss) : Fichiers SASS préfixés par un underscore, qui ne sont pas compilés directement en CSS mais sont importés dans un fichier SASS principal (ex: style.scss). Cela aide à organiser le code en petits modules logiques.
  • Nesting (&:hover) : Permet d’imbriquer des sélecteurs CSS, reflétant la structure HTML et améliorant la lisibilité, tout en générant des sélecteurs plats en CSS final.
  • Fonctions (darken($primary-color, 10%)) : SASS offre des fonctions pour manipuler les couleurs, les nombres, etc., rendant le CSS plus dynamique.

Après la compilation SASS, le fichier _button.scss générera un fichier CSS comme component-button.css qui est ensuite inclus dans Drupal via le fichier de bibliothèque (mon_theme.libraries.yml).

4.3. Intégration Twig + Classes CSS

Twig, le moteur de templates de Drupal, offre des fonctionnalités puissantes pour manipuler dynamiquement les attributs HTML, y compris les classes CSS.

L’utilisation de l’attribut attributes et de la fonction addClass() est une bonne pratique pour ajouter des classes CSS aux éléments de rendu de Drupal.

Twig

<div{{ attributes.addClass('custom-block') }}>
  <h2 class="custom-block__title">{{ label }}</h2>
  {{ content }}
</div>

Résultat :

HTML

<div class="block block-system custom-block">...</div>

Explication : Dans Drupal, la plupart des templates Twig reçoivent une variable attributes qui représente l’ensemble des attributs HTML de l’élément en cours de rendu.

En utilisant {{ attributes.addClass(‘custom-block’) }}, vous pouvez ajouter une ou plusieurs classes CSS personnalisées (custom-block dans cet exemple) sans écraser les classes existantes que Drupal pourrait déjà avoir ajoutées (comme block et block-system).

Cette approche est cruciale pour la flexibilité et la compatibilité avec le système de rendu de Drupal. label et content sont des variables Twig courantes représentant le titre et le contenu d’un bloc ou d’un nœud.

4.4. Layout Builder + CSS Grid

Layout Builder est un module central de Drupal qui permet aux utilisateurs de créer et de gérer des mises en page complexes pour différents types de contenu, en plaçant des blocs dans des sections et des régions.

Pour les développeurs de thèmes, combiner Layout Builder avec CSS Grid offre une flexibilité inégalée pour créer des layouts responsives et performants, en séparant la structure du contenu.

YAML

# mon_theme.layouts.yml
mon_theme.two_column:
label: '2 colonnes'
category: 'Custom'
template: layouts/two-column
regions:
left: { label: 'Colonne gauche' }
right: { label: 'Colonne droite' }

Et le fichier layouts/two-column.html.twig :

Twig

<div class="layout layout--two-column">
  <div class="layout__left">{{ content.left }}</div>
  <div class="layout__right">{{ content.right }}</div>
</div>

Explication :

  • Le fichier mon_theme.layouts.yml déclare un nouveau layout personnalisé appelé two_column. label est le nom affiché dans l’interface utilisateur de Layout Builder. template pointe vers le fichier Twig qui définit la structure HTML de ce layout. regions définit les zones où les blocs de contenu peuvent être placés.
  • Le fichier layouts/two-column.html.twig contient la structure HTML du layout. Les classes layout et layout–two-column sont des conventions BEM pour la structure du layout. content.left et content.right sont des variables Twig qui représenteront le contenu placé par l’utilisateur dans les régions « Colonne gauche » et « Colonne droite » via Layout Builder.
  • CSS Grid (à définir dans votre fichier CSS) serait ensuite utilisé pour styliser .layout–two-column, en créant une grille à deux colonnes pour les éléments enfants .layout__left et .layout__right. Par exemple :

CSS

/* Dans votre fichier CSS (ex: layout-grid.css) */
.layout--two-column {
  display: grid;
  grid-template-columns: 1fr 1fr; /* Deux colonnes de taille égale */
  gap: 20px; /* Espacement entre les colonnes */

  @media (max-width: 768px) {
    grid-template-columns: 1fr; /* Une seule colonne sur mobile */
  }
}

Cette approche permet une grande flexibilité : les administrateurs de site peuvent choisir des mises en page prédéfinies et glisser-déposer des blocs, tandis que les développeurs de thèmes définissent la structure CSS Grid sous-jacente, garantissant une flexibilité et une réactivité optimales.