logo

Types de blocs supplémentaires (EBT) – Nouvelle expérience de Layout Builder❗

Types de blocs supplémentaires (EBT) – types de blocs stylisés et personnalisables : diaporamas, onglets, cartes, accordéons et bien d’autres. Paramètres intégrés pour l’arrière-plan, la boîte DOM, les plugins JavaScript. Découvrez dès aujourd’hui le futur de la création de mises en page.

Démo des modules EBT Télécharger les modules EBT

❗Types de paragraphes supplémentaires (EPT) – Nouvelle expérience Paragraphes

Types de paragraphes supplémentaires (EPT) – ensemble de modules basé sur les paragraphes analogiques.

Démo des modules EPT Télécharger les modules EPT

Défilement

Ajout de feuilles de style (CSS) et de JavaScript (JS) dans un thème Drupal 8

05/07/2025, by Ivan

Cette documentation est destinée aux thèmes. Pour les informations concernant les modules, voir la section Ajouter des feuilles de style (CSS) et JavaScript (JS) dans un module Drupal 8.

Dans Drupal 8, les feuilles de style (CSS) et JavaScript (JS) sont chargées via le même système pour les modules (code) et les thèmes, appelé : bibliothèques de ressources.

Pour être clair, ces instructions sont destinées UNIQUEMENT à une utilisation dans les thèmes et ne s’appliquent pas aux modules.

Drupal utilise un principe de haut niveau : les ressources (CSS ou JS) ne sont chargées que si vous dites à Drupal de les charger. Drupal ne charge pas toutes les ressources sur chaque page car cela réduirait les performances de l’interface.

Différences avec Drupal 7

Voici six différences importantes par rapport à Drupal 7 pour les amateurs :

  • Le fichier THEME.info.yml a remplacé le fichier THEME.info (avec les mêmes données).
  • La propriété stylesheets (pour ajouter du CSS) dans THEME.info a été supprimée et remplacée par *.libraries.yml, où * est le nom du thème ou du module.
  • La propriété scripts (pour ajouter du JS) dans THEME.info a également été supprimée et remplacée par *.libraries.yml, où * est le nom du thème ou du module.
  • Seuls le CSS et le JS nécessaires sont chargés sur la page. Par exemple, jQuery n’est plus chargé automatiquement sauf si explicitement déclaré dans *.libraries.yml. Si votre thème a besoin de jQuery ou d’autres ressources à charger sur toutes les pages, ajoutez-les dans *.libraries.yml puis incluez la bibliothèque dans THEME.info.yml.
  • Dans Drupal 7, les bibliothèques étaient définies via hook_library_info(). Cela a été remplacé par le fichier *.libraries.yml.
  • Dans Drupal 8, drupal_add_css(), drupal_add_js() et drupal_add_library() ont été supprimés au profit de #attached

Processus

Pour charger des ressources CSS ou JS :

Définition d’une bibliothèque

Définissez toutes vos bibliothèques de ressources dans un fichier *.libraries.yml dans le dossier de votre thème. Si votre thème s’appelle fluffiness, le fichier doit s’appeler fluffiness.libraries.yml. Chaque « bibliothèque » dans ce fichier détaille les fichiers CSS et JS (ressources), par exemple :

# fluffiness.libraries.yml
cuddly-slider:
  version: 1.x
  css:
    theme:
      css/cuddly-slider.css: {}
  js:
    js/cuddly-slider.js: {}

Dans cet exemple, les fichiers JavaScript cuddly-slider.js et CSS cuddly-slider.css se trouvent dans les dossiers js et css respectivement, dans votre répertoire de thème.

Notez que même si cet exemple montre l’ajout d’un seul fichier CSS et JS plus jQuery, la définition des bibliothèques offre beaucoup plus d’options, que vous pouvez consulter dans la section Définition des bibliothèques : options et détails.

Inclusion de jQuery dans votre bibliothèque

Sachez que Drupal 8 ne charge plus jQuery automatiquement sur toutes les pages, donc, par exemple, si cuddly-slider a besoin de jQuery, vous devez déclarer une dépendance à la bibliothèque de base qui contient jQuery (fournie par le core Drupal, pas par un module ou thème). Déclarez la dépendance sous la forme core/jquery. Si une autre bibliothèque dépend de cuddly-slider, elle déclarera fluffiness/cuddly-slider, où fluffiness est le nom du thème et cuddly-slider le nom de la bibliothèque. Vous ne pouvez pas déclarer un fichier isolé comme dépendance, uniquement une bibliothèque.

Ainsi, pour rendre jQuery disponible à cuddly-slider, on met à jour l’exemple précédent :

# fluffiness.libraries.yml
cuddly-slider:
  version: 1.x
  css:
    theme:
      css/cuddly-slider.css: {}
  js:
    js/cuddly-slider.js: {}
  dependencies:
    - core/jquery

Déclaration des dépendances

Pour déclarer une dépendance, la bibliothèque nécessaire est mentionnée sous la forme ressource/bibliothèque. Pour les bibliothèques du core, la ressource est core, pour les autres c’est le nom du module ou du thème. Ainsi, si new_library dépend de jQuery du core, et de my_library défini dans my_theme et my_module, les dépendances doivent être :

# fluffiness.libraries.yml
new_library:
  js:
    js/new_libary.js: {}
  dependencies:
    - core/jquery
    - my_module/my_library
    - my_theme/my_library

Les noms des modules et thèmes assurent un espace de noms pour les bibliothèques portant le même nom.

Attacher une bibliothèque à toutes les pages

La plupart des thèmes utilisent une bibliothèque globale global-styling pour les styles CSS qui doivent être chargés sur chaque page où le thème est actif. Il est aussi possible de faire de même avec le JS via une bibliothèque global-scripts :

# fluffiness.libraries.yml (plusieurs bibliothèques peuvent être ajoutées dans ce fichier, en plus de cuddly-slider)
global-styling:
  version: 1.x
  css:
    theme:
      css/layout.css: {}
      css/style.css: {}
      css/colors.css: {}
global-scripts:
  version: 1.x
  js: 
    js/navmenu.js: {}

Pour qu’elles soient disponibles globalement dans le thème, les bibliothèques global-styling/global-scripts doivent être ajoutées dans le fichier info.yml de votre thème (ici fluffiness.info.yml)

# fluffiness.info.yml
name: Fluffiness
type: theme
description: 'Un thème tout doux qui offre un supplément de moelleux.'
core: 8.x
# en ajoutant global-styling et global-scripts ici, les fichiers css/js de la bibliothèque deviennent
# disponibles sur chaque page affichée par le thème
libraries:
  - fluffiness/global-styling
  - fluffiness/global-scripts
base theme: classy
regions:
  header: Entête
  content: Contenu
  sidebar_first: 'Première barre latérale'
  footer: Pied de page

Attacher une bibliothèque via un template Twig

Vous pouvez attacher une bibliothèque de ressources dans un template Twig en utilisant la fonction attach_library() dans n’importe quel fichier *.html.twig, comme ceci :

{{ attach_library('fluffiness/cuddly-slider') }}
<div>Un peu de contenu tout doux {{ message }}</div>

Attacher une bibliothèque à un sous-ensemble de pages

Parfois, vous ne voulez pas que votre bibliothèque soit active sur toutes les pages, mais seulement sur un sous-ensemble, par exemple quand un certain bloc est affiché ou un certain type de contenu.

Le thème peut faire cela en implémentant une fonction THEME_preprocess_HOOK() dans le fichier .theme, où THEME est le nom machine de votre thème, et HOOK le nom machine du hook de thème.

Par exemple, pour attacher un JavaScript à la page de maintenance, HOOK est « maintenance_page », la fonction ressemblera à :

function fluffiness_preprocess_maintenance_page(&$variables) {
  $variables['#attached']['library'][] = 'fluffiness/cuddly-slider';
}

Vous pouvez faire pareil pour d’autres hooks de thème, et bien sûr votre fonction peut contenir une logique, par exemple pour déterminer quel bloc est pré-traité dans le hook « block », quel type de nœud pour le hook « node », etc.

Note importante ! Dans ce cas, vous devez indiquer les métadonnées de cache correspondant à votre état ! L’exemple ci-dessus fonctionne sans ambiguïté, donc pas besoin de métadonnées de cache. Le cas d’usage le plus fréquent est d’attacher une bibliothèque selon la route courante :

function fluffiness_preprocess_page(&$variables) {
  $variables['page']['#cache']['contexts'][] = 'route';
  $route = "entity.node.preview";
  if (\Drupal::routeMatch()->getRouteName() === $route) {
    $variables['#attached']['library'][] = 'fluffiness/node-preview';
  }
}

Définition des bibliothèques : options et détails

Ajout de propriétés aux fichiers CSS/JS inclus

Les propriétés sont ajoutées entre accolades après chaque fichier ajouté dans le fichier THEMENAME.libraries.yml de votre thème.

Propriétés CSS

Les propriétés suivantes sont optionnelles et s’appliquent à chaque ressource CSS.

attributes Attributs optionnels. Exemple d’utilisation connu : Bootstrap CDN.
{ attributes: { crossorigin: anonymous } }
browsers Charge la ressource conditionnellement selon le navigateur. Notez que cette méthode utilise des commentaires conditionnels, non supportés dans IE10 et versions supérieures.
{ browsers: { IE: 'lte IE 9', '!IE': false } }
group Les ressources sont agrégées par groupes.
Par défaut : groupe SMACSS auquel la ressource appartient.

Rarement utilisé

media Type de média.
{ media: print }
minified Indique si la ressource est déjà minifiée.
Par défaut : false
{ type: external, minified: true }
preprocess Indique si la ressource doit être agrégée.
Par défaut : true
{ preprocess: false }
type Source de la ressource.
Par défaut : fichier
{ type: external, minified: true }
weight Ajuste l’ordre par rapport aux autres ressources (dans le même groupe SMACSS).
Par défaut : 0. Utilisez une valeur numérique entre -50 et +50.
{ weight: 1 }

Propriétés JS

Les propriétés suivantes sont optionnelles et s’appliquent à chaque ressource JS.

attributes Attributs additionnels pour la balise script.
{ type: external, attributes: { async: true } }
browsers Charge la ressource conditionnellement selon le navigateur. Notez que cette méthode utilise des commentaires conditionnels, non supportés dans IE10 et versions supérieures.
{ browsers: { IE: 'lte IE 9', '!IE': false } }
preprocess Indique si la ressource doit être agrégée.
Par défaut : true
{ preprocess: false }
type Source de la ressource.
Par défaut : fichier
{ type: external, minified: true }
weight Non recommandé, préférez les dépendances.
Ajuste l’ordre par rapport aux autres ressources. Doit être négatif.
{ weight: -1 }

Remplacement et extension des bibliothèques

Pour remplacer des bibliothèques définies dans *.libraries.yml, vous devez passer par *.info.yml. Elles peuvent être soit remplacées, soit étendues via des remplacements ou extensions de bibliothèques. Les remplacements ajoutés dans *.info.yml seront hérités par les sous-thèmes.

La propriété stylesheets-remove, utilisée dans les fichiers *.info.yml, est obsolète et sera supprimée dans Drupal 9.0.x. La propriété stylesheets-override est déjà supprimée.

libraries-override

Voici la logique à utiliser pour créer des remplacements :

  • Utilisez l’espace de noms original du module (ou du core) pour le nom de la bibliothèque.
  • Utilisez le chemin du dernier remplacement comme clé.
  • Ce chemin doit être le chemin complet vers le fichier.

Par exemple :

libraries-override:
  contextual/drupal.contextual-links:
    css:
      component:
        /core/themes/stable/css/contextual/contextual.module.css: false

Ici, contextual/drupal.contextual-links est l’espace de noms de la bibliothèque de base, et /core/themes/stable/css/contextual/contextual.module.css est le chemin complet vers le dernier remplacement, désactivé ici (false).

Notez que seule la dernière partie est un chemin de fichier réel, les autres sont des espaces de noms. Les lignes css: et component: reflètent la structure de la bibliothèque à remplacer.

Gardez à l’esprit que dépendre d’un chemin complet peut casser si la structure du site change. C’est pourquoi il existe une problématique pour supprimer la dépendance au chemin complet via les packagers stream.

Voici quelques exemples d’utilisation de libraries-override pour retirer ou remplacer des ressources CSS ou JS, ou des bibliothèques entières héritées par votre thème depuis des modules ou thèmes :

libraries-override:
  # Remplacer une bibliothèque entière.
  core/drupal.collapse: mytheme/collapse
  
  # Remplacer une ressource par une autre.
  subtheme/library:
    css:
      theme:
        css/layout.css: css/my-layout.css

  # Remplacer un fichier de remplacement depuis stable.
  contextual/drupal.contextual-toolbar:
    css:
      component:
        core/themes/stable/css/contextual/contextual.toolbar.css: css/contextual.toolbar.css

  # Remplacer un fichier JavaScript d’un module core.
  toolbar/toolbar:
    js:
      js/views/BodyVisualView.js: js/views/BodyVisualView.js

  # Supprimer une ressource.
  drupal/dialog:
    css:
      theme:
        dialog.theme.css: false
  
  # Supprimer une bibliothèque entière.
  core/modernizr: false
  
  # Remplacer des ressources très spécifiques dans une bibliothèque d’un module contrib.
  # Note : Les bibliothèques des modules contrib disponibles pour remplacement sont dans *.libraries.yml du module. Exemple : /modules/contrib/webform/webform.libraries.yml 

  webform/webform.element.location.places:
    css:
      component:
        css/webform.element.location.places.css: css/my-themes-replacement-file.css
    js:
      js/webform.element.location.places.js: js/my-themes-replacement-file.js

libraries-extend

libraries-extend permet aux thèmes de modifier les ressources d’une bibliothèque en ajoutant des dépendances supplémentaires spécifiques au thème chaque fois que cette bibliothèque est chargée.
libraries-extend est défini en étendant une bibliothèque par un ou plusieurs autres.

Idéal pour styliser certains composants différemment dans votre thème sans ajouter du CSS global. Ainsi, vous pouvez modifier l’apparence d’un composant sans charger du CSS sur chaque page.

# Étendre drupal.user : ajouter des assets des bibliothèques user de classy.
libraries-extend:
  core/drupal.user: 
    - classy/user1
    - classy/user2

Personnalisation supplémentaire du JavaScript

Ordre de chargement des ressources

Comme attendu, l’ordre dans lequel les fichiers sont listés est l’ordre de leur chargement. Par défaut, toutes les ressources JS sont chargées dans le pied de page. Les JS critiques nécessaires à l’interface utilisateur peuvent être chargés dans l’en-tête si besoin, ainsi :

js-header:
  header: true
  js:
    header.js: {}

js-footer:
  js:
    footer.js: {}

Définissez la propriété header à true pour indiquer que le JavaScript de cette bibliothèque est dans le « chemin critique » et doit être chargé dans l’en-tête. Toutes les dépendances directes ou indirectes seront aussi chargées automatiquement dans l’en-tête sans devoir les déclarer individuellement. C’est le sens de « chemin critique » : la ressource et ses dépendances doivent être chargées en premier.

Attacher un JavaScript personnalisé :

Parfois, vous voulez ajouter du JavaScript à une page qui dépend d’informations PHP calculées.

Dans ce cas, créez un fichier JavaScript, définissez et attachez la bibliothèque comme d’habitude, puis attachez aussi des réglages JavaScript via drupalSettings (successeur de Drupal 7 Drupal.settings). Pour que drupalSettings soit accessible dans votre JS, vous devez aussi déclarer sa dépendance.

Par exemple :

cuddly-slider:
  version: 1.x
  js:
    js/cuddly-slider.js: {}
  dependencies:
    - core/jquery
    - core/drupalSettings

et

function fluffiness_page_attachments_alter(&$page) {
  $page['#attached']['library'][] = 'fluffiness/cuddly-slider';
  $page['#attached']['drupalSettings']['fluffiness']['cuddlySlider']['foo'] = 'bar';
}

où 'bar' est une valeur calculée. (Notez que les métadonnées de cache sont aussi nécessaires !)

Le fichier cuddly-slider.js pourra alors accéder à settings.fluffiness.cuddlySlider.foo (qui vaudra 'bar') :

(function ($, Drupal, drupalSettings) {

  'use strict';

  Drupal.behaviors.mybehavior = {
    attach: function (context, settings) {
      
      console.log(settings.fluffiness.cuddlySlider.foo);
      
    }
  };

})(jQuery, Drupal, drupalSettings);

Ajout d’attributs aux éléments script

Pour ajouter des attributs à une balise script, ajoutez une clé attributes dans l’objet JSON après l’URL du script. À l’intérieur, mettez le nom de l’attribut comme clé, et sa valeur comme valeur. Si la valeur est true, l’attribut sera affiché sans valeur.

Par exemple :

https://maps.googleapis.com/maps/api/js?key=myownapikey&signed_in=true&libraries=drawing&callback=initMap:
  type: external
  attributes:
    defer: true
    async: true
    data-test: map-link

Cela génère :

<script src="https://maps.googleapis.com/maps/api/js?key=myownapikey&signed_in=true&libraries=drawing&callback=initMap" async defer data-test="map-link"></script>

JavaScript inline

Le JavaScript inline est fortement déconseillé. Il est recommandé de placer le JavaScript dans un fichier JS pour permettre la mise en cache côté client. Cela permet aussi que le code soit lisible et modifiable.

JavaScript inline qui génère du markup
C’est déconseillé et généralement inutile. Mettez ce JavaScript dans un fichier. Exemples : publicités, boutons de partage social, widgets sociaux. Ce sont juste des types de contenu/markup qui ne servent pas à décorer le contenu ou à l’interactivité du site, mais à extraire du contenu externe via JavaScript.

Vous pouvez les placer dans un bloc personnalisé ou directement dans un template Twig.

Exemple :

<script type="text/javascript"><!--
ad_client_id = "some identifier"
ad_width = 160;
ad_height = 90;
//--></script>
<script type="text/javascript" src="http://adserver.com/ad.js"></script>
<a class="twitter-timeline" href="https://twitter.com/wimleers" data-widget-id="307116909013368833">Tweets by @wimleers</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>

JavaScript inline affectant toute la page

L’utilisation de JavaScript inline est fortement déconseillée. Exemples : analytics (Google Analytics), services de polices hébergées. Le JavaScript inline affectant toute la page appartient soit au front-end/style, soit à la logique.

Dans le cas du front-end/style (ex. : polices hébergées), le JS appartient au thème. Placez-le directement dans votre fichier html.html.twig. Cela permet aussi de bien positionner le JS pour éviter le FOUT (Flash Of Unstyled Text) pendant le chargement des polices (les polices chargées via JS doivent être listées dans le <HEAD> avant le CSS).
(Pour plus d’infos, voir l’excellent article « Async Typekit & Micro-FOUT ».)

Dans l’autre cas, le JS appartient au module, et pour cela, voir la section Ajouter des feuilles de style (CSS) et JavaScript (JS) dans un module Drupal 8.

JavaScript inline dans un module d’intégration

Le JavaScript inline est fortement déconseillé. Si vous pouvez utiliser l’un des exemples ci-dessus, préférez-les.

Deux points à considérer pour un champ acceptant du JavaScript inline fourni par un utilisateur :

1. Le champ, formulaire ou page acceptant ce JavaScript doit avoir la permission correspondante.
Exemple : MODULE.routing.yml

MODULE.settings:
  path: /admin/config/services/MODULE
  defaults:
    _title: 'Paramètres MODULE'
    _form: \Drupal\MODULE\Form\MODULESettings
  requirements:
    _permission: 'administer site configuration'

2. La valeur stockée dans un objet de configuration doit notifier le système de rendu de ses métadonnées CacheableMetadata, afin que le cache soit vidé/invalidé lors d’un changement.
Exemple : MODULE.module

<?php

/**
 * @file
 * Intègre MODULE dans un site Drupal.
 */

use Drupal\Core\Render\Markup;

/**
 * Implémente hook_page_bottom().
 */
function MODULE_page_bottom(array &$page_bottom) {
  $settings = \Drupal::config('MODULE.settings');
  $user = \Drupal::currentUser();
  $page_bottom['MODULE'] = [
    '#markup' => Markup::create($settings->get('js_code')),
    '#cache' => [
      'contexts' => ['user'],
      'tags' => ['user:' . $user->id()],
    ],
  ];
  // Ajout des métadonnées de cache pour les settings de config.
  /** @var Drupal\Core\Render\Renderer $renderer */
  $renderer = \Drupal::service('renderer');
  $renderer->addCacheableDependency($page_bottom['MODULE'], $settings);
}

CDN / bibliothèques externes

Vous pouvez vouloir utiliser du JavaScript hébergé sur un CDN (Content Delivery Network), par exemple les polices web sont souvent accessibles uniquement via une URL externe. Ceci se fait en déclarant une bibliothèque externe (avec type: external). Il est aussi utile d’ajouter des informations sur la bibliothèque externe.

(Notez que charger des bibliothèques via CDN n’est pas une bonne pratique en général ; cela crée des points de défaillance supplémentaires, affecte la performance et la stabilité, nécessite plus de connexions TCP/IP et ne profite pas au cache du navigateur. De plus, les bibliothèques tierces ne doivent pas être hébergées sur Drupal.org comme partie de votre dépôt — voir la politique des bibliothèques tierces sur Drupal.org pour plus de détails.)

angular.angularjs:
  remote: https://github.com/angular
  version: 1.4.4
  license:
    name: MIT
    url: https://github.com/angular/angular.js/blob/master/LICENSE
    gpl-compatible: true
  js:
    https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js: { type: external, minified: true }

Si vous souhaitez que votre fichier externe soit appelé avec le même protocole que la page, utilisez une URL relative au protocole :

js:
    //ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js: { type: external, minified: true }

Ou, pour ajouter du CSS, voici un exemple d’intégration de Font Awesome :

font-awesome:
  remote: https://fortawesome.github.io/Font-Awesome/
  version: 4.5.0
  license:
    name: MIT
    url: https://fortawesome.github.io/Font-Awesome/license/
    gpl-compatible: true
  css:
    theme:
      https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css: { type: external, minified: true }

Exemple pour Bootstrap CDN CSS avec attributs personnalisés.

bootstrap-cdn:
  remote: getbootstrap.com
  version: 4.0
  license:
    name: MIT
    url: https://github.com/twbs/bootstrap/blob/master/LICENSE
  css:
    theme:
      'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css':
        type: external
        minified: true
        attributes:
          crossorigin: anonymous
          integrity: "sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"

Plus d’informations