Assets (CSS/JS)
This content is not available in your language yet.
📚 Système de librairies Drupal
Section intitulée « 📚 Système de librairies Drupal »Drupal utilise un système de librairies pour gérer les assets CSS et JavaScript. Contrairement à l’inclusion classique de fichiers dans le <head>, ce système offre un contrôle granulaire et des optimisations automatiques.
🎯 Pourquoi un système de librairies ?
Section intitulée « 🎯 Pourquoi un système de librairies ? »Problèmes de l’approche classique :
<!-- ❌ Mauvais : Chargement non optimisé #}<link rel="stylesheet" href="/style.css"><script src="/script.js"></script>Avantages du système Drupal :
- ✅ Chargement conditionnel : Ne charger que ce qui est nécessaire
- ✅ Gestion des dépendances : Ordre de chargement automatique
- ✅ Agrégation : Combine plusieurs fichiers en un seul
- ✅ Minification : Réduit la taille des fichiers en production
- ✅ Cache optimisé : Cache navigateur avec busting automatique
- ✅ Prévention des doublons : Un fichier n’est chargé qu’une seule fois
🔄 Cycle de vie d’un asset
Section intitulée « 🔄 Cycle de vie d’un asset »1. Déclaration dans .libraries.yml ↓2. Attachement (global, template, PHP) ↓3. Résolution des dépendances ↓4. Agrégation (si activée) ↓5. Minification (si activée) ↓6. Injection dans le HTML (<head> ou avant </body>) ↓7. Cache navigateur avec version🌐 Vue d’ensemble
Section intitulée « 🌐 Vue d’ensemble »| Fichier | Rôle | Exemple |
|---|---|---|
*.libraries.yml | Déclare les librairies | tailstore.libraries.yml |
*.info.yml | Attache globalement | libraries: - tailstore/global |
*.theme | Attache en PHP | $variables['#attached']['library'][] |
| Templates Twig | Attache dans un template | {{ attach_library('tailstore/slider') }} |
🎨 Déclarer des CSS
Section intitulée « 🎨 Déclarer des CSS »Structure du fichier .libraries.yml
Section intitulée « Structure du fichier .libraries.yml »# tailstore.libraries.yml
global: version: 1.0 css: # Catégories (ordre de chargement) base: css/base/reset.css: {} css/base/typography.css: {} layout: css/layout/grid.css: {} css/layout/regions.css: {} component: css/components/buttons.css: {} css/components/cards.css: {} css/components/forms.css: {} theme: css/tailstore.css: {}Catégories CSS
Section intitulée « Catégories CSS »| Catégorie | Poids | Usage |
|---|---|---|
base | -200 | Reset, normalisation |
layout | -100 | Grilles, mise en page |
component | 0 | Composants |
state | 100 | États (hover, active) |
theme | 200 | Styles finaux, surcharges |
Options CSS
Section intitulée « Options CSS »css: theme: css/style.css: weight: 10 # Poids relatif media: screen # Media query preprocess: true # Agrégation minified: false # Déjà minifié attributes: crossorigin: anonymous📜 Déclarer des JavaScript
Section intitulée « 📜 Déclarer des JavaScript »global: js: js/tailstore.js: {} dependencies: - core/drupal - core/once
cart: js: js/cart.js: attributes: defer: true dependencies: - core/drupalSettings - tailstore/globalOptions JavaScript
Section intitulée « Options JavaScript »js: js/script.js: weight: -10 # Chargement prioritaire minified: true # Fichier déjà minifié preprocess: true # Inclure dans l'agrégation attributes: defer: true # Attribut defer async: true # Attribut async type: module # ES modulesDépendances courantes
Section intitulée « Dépendances courantes »| Dépendance | Description |
|---|---|
core/drupal | Objet Drupal, behaviors |
core/once | Helper pour exécuter une seule fois |
core/jquery | jQuery (éviter si possible) |
core/drupalSettings | Variables PHP → JS |
🔗 Attacher les librairies
Section intitulée « 🔗 Attacher les librairies »1. Globalement (toutes les pages)
Section intitulée « 1. Globalement (toutes les pages) »Dans tailstore.info.yml :
libraries: - tailstore/global2. Dans un template Twig
Section intitulée « 2. Dans un template Twig »{{ attach_library('tailstore/slider') }}
<div class="slider"> {# Contenu du slider #}</div>3. Dans un preprocess PHP
Section intitulée « 3. Dans un preprocess PHP »function tailstore_preprocess_node(&$variables) { if ($variables['node']->bundle() === 'product') { $variables['#attached']['library'][] = 'tailstore/product'; }}4. Dans un contrôleur/service
Section intitulée « 4. Dans un contrôleur/service »public function build() { return [ '#markup' => '<div class="my-component"></div>', '#attached' => [ 'library' => ['tailstore/component'], 'drupalSettings' => [ 'tailstore' => [ 'apiUrl' => '/api/endpoint', ], ], ], ];}🌐 CDN et ressources externes
Section intitulée « 🌐 CDN et ressources externes »Déclarer une librairie externe
Section intitulée « Déclarer une librairie externe »swiper: version: 11.0 remote: https://swiperjs.com/ license: name: MIT url: https://github.com/nolimits4web/swiper/blob/master/LICENSE gpl-compatible: true css: theme: https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css: type: external minified: true js: https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js: type: external minified: true🔄 Surcharger des librairies
Section intitulée « 🔄 Surcharger des librairies »libraries-override
Section intitulée « libraries-override »Dans tailstore.info.yml :
libraries-override: # Désactiver complètement une librairie core/normalize: false
# Remplacer un fichier CSS core/drupal.vertical-tabs: css: component: misc/vertical-tabs.css: css/components/vertical-tabs.css
# Remplacer par une librairie CDN core/jquery: tailstore/jquery-cdnlibraries-extend
Section intitulée « libraries-extend »Ajouter des assets à une librairie existante :
libraries-extend: core/ckeditor5: - tailstore/ckeditor-custom💻 JavaScript moderne
Section intitulée « 💻 JavaScript moderne »Drupal Behaviors
Section intitulée « Drupal Behaviors »// js/tailstore.js
(function (Drupal, once) { 'use strict';
/** * Product gallery behavior. */ Drupal.behaviors.productGallery = { attach: function (context, settings) { // once() s'assure que le code ne s'exécute qu'une fois par élément once('product-gallery', '.product__gallery', context).forEach(function (gallery) { const thumbnails = gallery.querySelectorAll('.product__thumbnail'); const mainImage = gallery.querySelector('.product__main-image img');
thumbnails.forEach(function (thumb) { thumb.addEventListener('click', function () { const img = this.querySelector('img'); mainImage.src = img.src; mainImage.alt = img.alt;
// Active state thumbnails.forEach(t => t.classList.remove('active')); this.classList.add('active'); }); }); }); },
detach: function (context, settings, trigger) { // Nettoyage si nécessaire (avant suppression AJAX) if (trigger === 'unload') { // Cleanup code } } };
/** * Add to cart behavior. */ Drupal.behaviors.addToCart = { attach: function (context, settings) { once('add-to-cart', '.product__add-to-cart', context).forEach(function (button) { button.addEventListener('click', function () { const productId = this.dataset.productId; const quantity = document.getElementById('quantity')?.value || 1;
// Récupérer les options const size = document.querySelector('.size-btn.active')?.dataset.size; const color = document.querySelector('.color-btn.active')?.dataset.color;
// Appel API ou action Drupal.tailstore.addToCart(productId, quantity, { size, color }); }); }); } };
/** * Namespace pour les fonctions TailStore. */ Drupal.tailstore = { addToCart: function (productId, quantity, options) { console.log('Adding to cart:', productId, quantity, options);
// Exemple avec fetch fetch('/api/cart/add', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ product_id: productId, quantity: quantity, options: options, }), }) .then(response => response.json()) .then(data => { if (data.success) { // Mettre à jour l'icône panier document.dispatchEvent(new CustomEvent('cart-updated', { detail: data.cart }));
// Notification Drupal.announce(Drupal.t('Product added to cart')); } }); } };
})(Drupal, once);Utiliser drupalSettings
Section intitulée « Utiliser drupalSettings »PHP (dans .theme ou module) :
function tailstore_preprocess_page(&$variables) { $variables['#attached']['drupalSettings']['tailstore'] = [ 'cartApiUrl' => '/api/cart', 'currency' => '€', 'locale' => 'fr-FR', ];}JavaScript :
(function (Drupal, drupalSettings) { const config = drupalSettings.tailstore;
console.log('API URL:', config.cartApiUrl); console.log('Currency:', config.currency);})(Drupal, drupalSettings);📦 CSS Composants
Section intitulée « 📦 CSS Composants »css/components/buttons.css
Section intitulée « css/components/buttons.css »/* Variables boutons */:root { --btn-padding-x: 1rem; --btn-padding-y: 0.5rem; --btn-border-radius: 0.375rem; --btn-font-weight: 600; --btn-transition: all 0.2s ease;}
/* Base button */.btn { display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem; padding: var(--btn-padding-y) var(--btn-padding-x); font-weight: var(--btn-font-weight); text-align: center; text-decoration: none; border: 1px solid transparent; border-radius: var(--btn-border-radius); cursor: pointer; transition: var(--btn-transition);}
.btn:disabled { opacity: 0.6; cursor: not-allowed;}
/* Variantes */.btn--primary { background-color: var(--color-primary); color: white;}
.btn--primary:hover:not(:disabled) { background-color: var(--color-primary-dark);}
.btn--secondary { background-color: var(--color-secondary); color: white;}
.btn--outline { background-color: transparent; border-color: currentColor;}
.btn--outline:hover:not(:disabled) { background-color: var(--color-primary); border-color: var(--color-primary); color: white;}
/* Tailles */.btn--sm { padding: 0.25rem 0.5rem; font-size: 0.875rem;}
.btn--lg { padding: 0.75rem 1.5rem; font-size: 1.125rem;}
.btn--full { width: 100%;}css/components/cards.css
Section intitulée « css/components/cards.css »/* Product Card */.product-card { position: relative; display: flex; flex-direction: column; background: white; border-radius: 0.5rem; overflow: hidden; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); transition: transform 0.2s, box-shadow 0.2s;}
.product-card:hover { transform: translateY(-4px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);}
.product-card__badges { position: absolute; top: 0.5rem; left: 0.5rem; z-index: 10; display: flex; gap: 0.25rem;}
.product-card__image-link { display: block; aspect-ratio: 1; overflow: hidden;}
.product-card__image-link img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.3s;}
.product-card:hover .product-card__image-link img { transform: scale(1.05);}
.product-card__quick-actions { position: absolute; top: 0.5rem; right: 0.5rem; display: flex; flex-direction: column; gap: 0.25rem; opacity: 0; transform: translateX(10px); transition: opacity 0.2s, transform 0.2s;}
.product-card:hover .product-card__quick-actions { opacity: 1; transform: translateX(0);}
.product-card__info { padding: 1rem; flex-grow: 1; display: flex; flex-direction: column; gap: 0.25rem;}
.product-card__brand { font-size: 0.75rem; color: var(--color-secondary); text-transform: uppercase; letter-spacing: 0.05em;}
.product-card__title { font-size: 1rem; font-weight: 600; line-height: 1.3;}
.product-card__title a { color: inherit; text-decoration: none;}
.product-card__title a:hover { color: var(--color-primary);}
.product-card__price { margin-top: auto; padding-top: 0.5rem;}
.price--current { font-size: 1.25rem; font-weight: 700; color: var(--color-dark);}
.price--sale { color: var(--color-danger);}
.price--old { font-size: 0.875rem; color: var(--color-secondary); text-decoration: line-through; margin-right: 0.5rem;}
.product-card__colors { display: flex; gap: 0.25rem; margin-top: 0.5rem;}
.color-dot { width: 1rem; height: 1rem; border-radius: 50%; border: 1px solid rgba(0, 0, 0, 0.1);}
.product-card__add-to-cart { margin: 1rem; margin-top: 0;}🎯 Exercices pratiques
Section intitulée « 🎯 Exercices pratiques »Exercice 1 : Créer une librairie CSS basique
Section intitulée « Exercice 1 : Créer une librairie CSS basique »Objectif : Créer et attacher une librairie CSS simple pour personnaliser les boutons.
-
Créez le fichier CSS :
Fenêtre de terminal mkdir -p themes/custom/tailstore/css/componentstouch themes/custom/tailstore/css/components/buttons.css -
Ajoutez des styles de test :
/* css/components/buttons.css */.btn-test {background-color: #ff6b6b;color: white;padding: 10px 20px;border-radius: 5px;border: none;} -
Déclarez la librairie dans
tailstore.libraries.yml:buttons:version: 1.0.0css:component:css/components/buttons.css: {} -
Attachez-la globalement dans
tailstore.info.yml:libraries:- tailstore/global- tailstore/buttons -
Videz le cache :
Fenêtre de terminal drush cr -
Testez en ajoutant un bouton quelque part :
<button class="btn-test">Test Button</button>
Validation : ✅ Le bouton doit avoir un fond rouge avec le style appliqué
Exercice 2 : Librairie JavaScript avec Drupal.behaviors
Section intitulée « Exercice 2 : Librairie JavaScript avec Drupal.behaviors »Objectif : Créer un script qui affiche une alerte au clic sur un bouton.
-
Créez le fichier JS :
Fenêtre de terminal mkdir -p themes/custom/tailstore/jstouch themes/custom/tailstore/js/alert.js -
Ajoutez le code :
(function (Drupal, once) {'use strict';Drupal.behaviors.testAlert = {attach: function (context, settings) {once('test-alert', '.btn-alert', context).forEach(function (button) {button.addEventListener('click', function () {alert('Hello from Drupal!');});});}};})(Drupal, once); -
Déclarez dans
tailstore.libraries.yml:alert:version: 1.0.0js:js/alert.js: {}dependencies:- core/drupal- core/once -
Attachez dans un template ou globalement
-
Testez avec :
<button class="btn-alert">Click me!</button>
Validation : ✅ Un clic sur le bouton doit afficher “Hello from Drupal!”
Exercice 3 : Chargement conditionnel
Section intitulée « Exercice 3 : Chargement conditionnel »Objectif : Charger une librairie uniquement sur les pages produit.
-
Créez une librairie pour les produits :
# tailstore.libraries.ymlproduct-zoom:version: 1.0.0js:js/product-zoom.js: {}dependencies:- tailstore/global -
Attachez conditionnellement dans
tailstore.theme:function tailstore_preprocess_node(&$variables) {$node = $variables['node'];if ($node->bundle() === 'product') {$variables['#attached']['library'][] = 'tailstore/product-zoom';}} -
Videz le cache et testez :
Fenêtre de terminal drush cr -
Vérifiez (F12 → Network) :
- Sur une page produit :
product-zoom.jsest chargé - Sur une autre page :
product-zoom.jsn’est PAS chargé
- Sur une page produit :
Validation : ✅ La librairie ne se charge que sur les pages produit
Exercice 4 : Utiliser drupalSettings
Section intitulée « Exercice 4 : Utiliser drupalSettings »Objectif : Passer des données PHP vers JavaScript.
-
Dans
tailstore.theme, ajoutez des settings :function tailstore_preprocess_page(&$variables) {$variables['#attached']['drupalSettings']['tailstore'] = ['siteUrl' => \Drupal::request()->getSchemeAndHttpHost(),'currency' => '€','locale' => 'fr-FR',];} -
Créez
js/settings-test.js:(function (Drupal, drupalSettings) {'use strict';Drupal.behaviors.settingsTest = {attach: function (context, settings) {console.log('Site URL:', drupalSettings.tailstore.siteUrl);console.log('Currency:', drupalSettings.tailstore.currency);console.log('Locale:', drupalSettings.tailstore.locale);}};})(Drupal, drupalSettings); -
Déclarez la librairie et attachez-la
-
Ouvrez la console (F12) et vérifiez les logs
Validation : ✅ Les valeurs PHP apparaissent dans la console JavaScript
🧠 Bonnes pratiques essentielles
Section intitulée « 🧠 Bonnes pratiques essentielles »⚡ Performance
Section intitulée « ⚡ Performance »1. Charger uniquement ce qui est nécessaire
// ❌ Mauvais - Charge partout// Dans tailstore.info.ymllibraries: - tailstore/slider - tailstore/cart - tailstore/filters
// ✅ Bon - Charge conditionnellementfunction tailstore_preprocess_page(&$variables) { // Slider uniquement sur homepage if (\Drupal::service('path.matcher')->isFrontPage()) { $variables['#attached']['library'][] = 'tailstore/slider'; }
// Panier uniquement si utilisateur connecté if (\Drupal::currentUser()->isAuthenticated()) { $variables['#attached']['library'][] = 'tailstore/cart'; }}2. Utiliser defer pour les scripts non critiques
analytics: version: 1.0.0 js: js/analytics.js: attributes: defer: true # Charge après le DOM3. Précharger les ressources critiques
function tailstore_preprocess_html(&$variables) { $variables['#attached']['html_head_link'][][] = [ 'rel' => 'preload', 'href' => '/themes/custom/tailstore/fonts/inter.woff2', 'as' => 'font', 'type' => 'font/woff2', 'crossorigin' => 'anonymous', ];}4. Activer l’agrégation en production
# Mode productiondrush config:set system.performance css.preprocess true -ydrush config:set system.performance js.preprocess true -ydrush cr🔒 Sécurité
Section intitulée « 🔒 Sécurité »1. Toujours déclarer les dépendances
# ❌ Dangereux - Suppose que Drupal est disponiblecart: js: js/cart.js: {}
# ✅ Sécurisé - Déclare explicitementcart: js: js/cart.js: {} dependencies: - core/drupal - core/once2. Valider les données drupalSettings
// ❌ Dangereux - Peut crasher si non définiconst apiUrl = drupalSettings.tailstore.apiUrl;
// ✅ Sécurisé - Vérifie l'existenceconst apiUrl = drupalSettings.tailstore?.apiUrl || '/api/default';3. Éviter eval() et innerHTML
// ❌ Dangereux - Faille XSSelement.innerHTML = userInput;
// ✅ Sécuriséelement.textContent = userInput;// Ouelement.insertAdjacentText('beforeend', userInput);📝 Conventions de code
Section intitulée « 📝 Conventions de code »1. Nommage des librairies
# Format : theme-nom/fonctionnalitetailstore/global # Librairie de basetailstore/product-zoom # Fonctionnalité spécifiquetailstore/cart # Composant2. Structure JavaScript
(function (Drupal, once) { 'use strict';
// 1. Namespace pour éviter les conflits Drupal.tailstore = Drupal.tailstore || {};
// 2. Fonctions privées function privateHelper() { // ... }
// 3. Behavior Drupal Drupal.behaviors.myFeature = { attach: function (context, settings) { once('my-feature', '.selector', context).forEach(function (element) { // Code d'initialisation }); } };
// 4. Fonctions publiques Drupal.tailstore.publicMethod = function () { return privateHelper(); };
})(Drupal, once);3. Commentaires obligatoires
/** * @file * Product gallery functionality. */
/** * Gallery behavior. * * Handles thumbnail clicks and updates the main image. */Drupal.behaviors.productGallery = { // ...};♿ Accessibilité
Section intitulée « ♿ Accessibilité »1. Annoncer les changements dynamiques
// Utiliser Drupal.announce() pour les lecteurs d'écranbutton.addEventListener('click', function () { addToCart(productId); Drupal.announce(Drupal.t('Product added to cart'));});2. Gérer le focus
// Après ouverture d'une modalemodal.querySelector('.modal__close').focus();
// Piège du focus dans la modalemodal.addEventListener('keydown', function (e) { if (e.key === 'Tab') { // Logique de trap focus }});3. Support clavier
element.addEventListener('keydown', function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); this.click(); }});🔧 Vérifier le chargement
Section intitulée « 🔧 Vérifier le chargement »# Mode développementdrush state:set system.performance css_preprocess 0drush state:set system.performance js_preprocess 0drush cr
# Mode productiondrush state:set system.performance css_preprocess 1drush state:set system.performance js_preprocess 1drush crInspectez le code source HTML pour voir les fichiers chargés.
✅ Checklist de validation
Section intitulée « ✅ Checklist de validation »Librairies créées
Section intitulée « Librairies créées »| Librairie | Contenu | Attachement | Statut |
|---|---|---|---|
global | CSS/JS de base | Global (info.yml) | ⬜ |
buttons | Styles boutons | Global | ⬜ |
product-zoom | JS zoom images | Conditionnel (produits) | ⬜ |
cart | JS panier | Conditionnel | ⬜ |
Vérifications techniques
Section intitulée « Vérifications techniques »-
Fichiers déclarés dans .libraries.yml
Fenêtre de terminal # Vérifier la syntaxe YAMLcat themes/custom/tailstore/tailstore.libraries.yml | grep -A 5 "global:"Attendu : Syntaxe YAML valide,
version:obligatoire -
Fichiers existent sur le disque
Fenêtre de terminal # Vérifier que les fichiers CSS/JS existentfind themes/custom/tailstore -name "*.css" -o -name "*.js"Attendu : Tous les fichiers déclarés sont présents
-
Librairies chargées dans le HTML
Fenêtre de terminal # Inspecter le code source (Ctrl+U)# Chercher <link> et <script>curl -s http://localhost/ | grep -E "<link|<script" | head -10Attendu : Fichiers CSS dans
<head>, JS avant</body> -
Pas d’erreurs 404
Fenêtre de terminal # Ouvrir F12 → Network → Filtrer par "CSS" et "JS"# Vérifier que tous les fichiers sont en statut 200 -
JavaScript fonctionnel
Fenêtre de terminal # Ouvrir F12 → Console# Vérifier qu'il n'y a pas d'erreurs -
Agrégation (production uniquement)
Fenêtre de terminal # Vérifier la configdrush config:get system.performance css.preprocessdrush config:get system.performance js.preprocess# En production, les deux doivent être à true
Tests fonctionnels
Section intitulée « Tests fonctionnels »- Les styles CSS sont appliqués (boutons, cartes, etc.)
- Les Drupal.behaviors fonctionnent (interactions)
- Les librairies conditionnelles se chargent au bon moment
- drupalSettings est accessible en JavaScript
- Pas d’erreurs dans la console (F12)
- Performance acceptable (< 3s chargement)
- Responsive fonctionne (mobile, tablette, desktop)
Script de validation complet
Section intitulée « Script de validation complet »#!/bin/bash
echo "=== Vérification Assets TailStore ==="
# 1. Vérifier fichiers CSSecho "\n🎨 Fichiers CSS :"find themes/custom/tailstore -name "*.css" -type f | wc -lecho "fichiers CSS trouvés"
# 2. Vérifier fichiers JSecho "\n📜 Fichiers JavaScript :"find themes/custom/tailstore -name "*.js" -type f | wc -lecho "fichiers JS trouvés"
# 3. Vérifier syntaxe YAMLecho "\n⚙️ Validation YAML :"if command -v yamllint &> /dev/null; then yamllint themes/custom/tailstore/tailstore.libraries.ymlelse echo "yamllint non installé, validation manuelle nécessaire"fi
# 4. Vérifier agrégationecho "\n📦 Agrégation CSS/JS :"drush config:get system.performance css.preprocessdrush config:get system.performance js.preprocess
# 5. Vérifier erreurs logsecho "\n⚠️ Erreurs récentes :"drush watchdog:show --severity=Error --count=5 | grep -i "javascript\|css" || echo "Aucune erreur asset"
echo "\n✅ Vérification terminée"Temps estimé total : 2-3 heures pour créer et tester tous les assets
🚨 Erreurs courantes et solutions
Section intitulée « 🚨 Erreurs courantes et solutions »Erreur 1 : Fichier CSS/JS non chargé (404)
Section intitulée « Erreur 1 : Fichier CSS/JS non chargé (404) »Symptôme : Erreur 404 dans Network (F12)
Causes possibles :
- Chemin incorrect dans
.libraries.yml - Fichier n’existe pas sur le disque
- Permissions incorrectes
Solution :
# 1. Vérifier le chemin exactls -la themes/custom/tailstore/css/style.css
# 2. Vérifier la déclaration dans .libraries.ymlcat themes/custom/tailstore/tailstore.libraries.yml | grep "css/style.css"
# 3. Vérifier les permissionschmod 644 themes/custom/tailstore/css/*.csschmod 644 themes/custom/tailstore/js/*.js
# 4. Vider le cachedrush crErreur 2 : JavaScript ne fonctionne pas
Section intitulée « Erreur 2 : JavaScript ne fonctionne pas »Symptôme : Pas d’erreur mais le code ne s’exécute pas
Causes :
- Dépendances manquantes (
core/drupal,core/once) - Syntaxe JavaScript incorrecte
once()mal utilisé
Solution :
// ❌ Mauvais - Dépendances manquantes(function () { document.querySelectorAll('.btn').forEach(function (btn) { btn.addEventListener('click', function () { // ... }); });})();
// ✅ Bon - Drupal.behaviors + once(function (Drupal, once) { 'use strict';
Drupal.behaviors.myFeature = { attach: function (context, settings) { once('my-feature', '.btn', context).forEach(function (btn) { btn.addEventListener('click', function () { // ... }); }); } };
})(Drupal, once);# Déclarer les dépendancesmy-library: js: js/script.js: {} dependencies: - core/drupal - core/onceErreur 3 : “Drupal is not defined”
Section intitulée « Erreur 3 : “Drupal is not defined” »Symptôme : Erreur console JavaScript
Cause : Librairie chargée avant core/drupal
Solution :
# Toujours déclarer core/drupal en dépendancemy-library: js: js/script.js: {} dependencies: - core/drupal # 👈 Obligatoire !Erreur 4 : Styles CSS ne s’appliquent pas
Section intitulée « Erreur 4 : Styles CSS ne s’appliquent pas »Symptôme : Fichier chargé (200) mais styles invisibles
Causes :
- Spécificité CSS insuffisante
- Cache navigateur
- Ordre de chargement
Solution :
# 1. Vider le cache Drupaldrush cr
# 2. Vider le cache navigateur# Ctrl+Shift+R (Chrome/Firefox)
# 3. Vérifier l'ordre de chargement# Utiliser weight dans .libraries.ymlglobal: css: theme: css/base.css: weight: -10 # Charge en premier css/custom.css: weight: 10 # Charge aprèsErreur 5 : “once is not defined”
Section intitulée « Erreur 5 : “once is not defined” »Symptôme : Erreur JavaScript en production
Cause : Dépendance core/once non déclarée
Solution :
my-library: js: js/script.js: {} dependencies: - core/drupal - core/once # 👈 Nécessaire pour once()Erreur 6 : Agrégation casse le site
Section intitulée « Erreur 6 : Agrégation casse le site »Symptôme : Site fonctionne en dev mais casse en production
Cause : Ordre de chargement changé avec l’agrégation
Solution :
# Utiliser weight pour contrôler l'ordremy-library: js: js/init.js: weight: -10 # Charge en premier js/main.js: weight: 0 # Charge après# Tester avec agrégation activée localementdrush config:set system.performance js.preprocess true -ydrush cr📚 Ressources complémentaires
Section intitulée « 📚 Ressources complémentaires »Documentation officielle
Section intitulée « Documentation officielle »- Asset Libraries - Guide complet
- JavaScript API - Drupal.behaviors, once
- Library Definitions - Format .libraries.yml
- Aggregation - Optimisation
Outils recommandés
Section intitulée « Outils recommandés »- Vite : Bundler moderne ultra-rapide (utilisé dans ce projet)
- PostCSS : Transformations CSS (autoprefixer, etc.)
- ESLint : Linter JavaScript
- Stylelint : Linter CSS
- Prettier : Formateur de code automatique
Exemples de code
Section intitulée « Exemples de code »- Drupal Core Libraries - Exemples officiels
- Olivero Libraries - Thème moderne
✅ Checklist
Section intitulée « ✅ Checklist »- Librairie
globaldéclarée et attachée - CSS organisés par catégories
- JavaScript avec Drupal.behaviors
- Dépendances correctement déclarées
- Librairies externes (CDN) configurées
- drupalSettings utilisé si nécessaire
- Librairies testées en dev et prod
🔜 Prochaine étape
Section intitulée « 🔜 Prochaine étape »Les assets sont configurés ! Passons à l’intégration de Tailwind CSS.