logo

Extra Block Types (EBT) - Nueva experiencia con Layout Builder❗

Extra Block Types (EBT): tipos de bloques con estilo y personalizables: Presentaciones de diapositivas, Pestañas, Tarjetas, Acordeones y muchos más. Configuraciones integradas para fondo, DOM Box y plugins de JavaScript. Experimenta hoy el futuro de la construcción de diseños.

Módulos de demostración EBT Descargar módulos EBT

❗Extra Paragraph Types (EPT) - Nueva experiencia con Paragraphs

Extra Paragraph Types (EPT): conjunto de módulos basado en párrafos de forma análoga.

Módulos de demostración EPT Descargar módulos EPT

Scroll

Agregar hojas de estilo (CSS) y JavaScript (JS) en un tema de Drupal 8

19/06/2025, by Ivan

Esta documentación es para temas. Para información sobre módulos, consulte la sección Agregar hojas de estilo (CSS) y JavaScript (JS) en un módulo Drupal 8.

En Drupal 8, las hojas de estilo (CSS) y JavaScript (JS) se cargan mediante el mismo sistema para módulos (código) y temas para todo: las bibliotecas de recursos.

Para mayor claridad, estas instrucciones están destinadas SOLO para trabajar en temas y no se aplican en módulos.

Drupal utiliza un principio de alto nivel: los recursos (CSS o JS) se cargan solo si se indica explícitamente a Drupal que deben cargarse. Drupal no carga todos los recursos en cada página, ya que esto reduce el rendimiento de la interfaz.

Diferencias con Drupal 7

Hay seis diferencias importantes respecto a Drupal 7 para entusiastas:

  • El archivo THEME.info.yml reemplazó al archivo THEME.info (con los mismos datos).
  • La propiedad stylesheets (para agregar CSS) en THEME.info fue eliminada y reemplazada por *.libraries.yml, donde `*` es el nombre del tema o módulo.
  • La propiedad scripts (para agregar JS) en THEME.info fue eliminada y también reemplazada por *.libraries.yml, donde `*` es el nombre del tema o módulo.
  • Solo se cargan los CSS y JS necesarios en la página. Por ejemplo, jQuery ya no se carga automáticamente a menos que se declare explícitamente en *.libraries.yml. Si tu tema requiere jQuery u otros recursos que quieras cargar en todas las páginas, agrégalos en *.libraries.yml y luego incluye la biblioteca en THEME.info.yml.
  • En Drupal 7 las bibliotecas debían definirse con hook_library_info(). Esto fue reemplazado por archivos *.libraries.yml.
  • En Drupal 8, drupal_add_css(), drupal_add_js() y drupal_add_library() fueron eliminados en favor de #attached

Proceso

Para cargar recursos CSS o JS:

Definición de biblioteca

Define todas tus bibliotecas de recursos en un archivo *.libraries.yml dentro de la carpeta de tu tema. Si tu tema se llama fluffiness, el archivo debe llamarse fluffiness.libraries.yml. Cada "biblioteca" dentro del archivo es una entrada que detalla los archivos CSS y JS (recursos), por ejemplo:

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

En este ejemplo, el JavaScript cuddly-slider.js y el CSS cuddly-slider.css se encuentran en los directorios correspondientes js y css de tu directorio.

Nota: aunque este ejemplo muestra la inclusión de un archivo CSS y JS + jQuery, hay muchas más opciones disponibles al definir bibliotecas. Consulta la sección "Definición de bibliotecas: opciones y detalles" para más información.

Incluir jQuery en tu biblioteca

Recuerda que Drupal 8 ya no carga jQuery en todas las páginas por defecto, por lo que si cuddly-slider necesita jQuery, debes declarar la dependencia de la biblioteca base que contiene jQuery (Drupal core provee jQuery, no un módulo o tema). Declara la dependencia con el nombre de extensión seguido por una barra y el nombre de la biblioteca, en este caso core/jquery. Si otra biblioteca depende de cuddly-slider, declarará: fluffiness/cuddly-slider (nombre del tema seguido por el nombre de la biblioteca). No puedes declarar un archivo individual como dependencia, solo bibliotecas.

Así que para hacer jQuery disponible para cuddly-slider, actualizamos el ejemplo anterior:

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

Declaración de dependencias

Para declarar una dependencia, la biblioteca requerida se declara en forma recurso/biblioteca. Para las bibliotecas principales, el recurso es core, para otras es el nombre del módulo o tema. Por lo tanto, si new_library depende de jQuery de core, my_library declarada en my_theme, y my_library declarada en my_module, debes declarar las dependencias así:

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

Los nombres de módulos y temas proporcionan un espacio de nombres para bibliotecas con nombres similares.

Adjuntar biblioteca a todas las páginas

La mayoría de los temas usan una biblioteca de recursos llamada global-styling para CSS que debe cargarse en cada página donde el tema está activo. También es posible hacer esto para JS usando la biblioteca global-scripts.

# fluffiness.libraries.yml (se pueden agregar múltiples bibliotecas en un archivo libraries.yml, estas aparecerán debajo de la biblioteca cuddly-slider añadida anteriormente)
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: {}   

Para que estén disponibles globalmente en el tema, las bibliotecas global-styling/global-scripts deben agregarse en el info.yml de tu tema (en este caso fluffiness.info.yml):

#fluffiness.info.yml
name: Fluffiness
type: theme
description: 'Un tema adorable que ofrece extra suavidad.'
core: 8.x
# al agregar global-styling y global-scripts aquí, los archivos css/js de la biblioteca
# estarán disponibles en cada página que presente el tema
libraries:
  - fluffiness/global-styling
  - fluffiness/global-scripts
base theme: classy
regions:
  header: Header
  content: Content
  sidebar_first: 'Sidebar first'
  footer: Footer

Adjuntar biblioteca desde una plantilla Twig

Puedes adjuntar una biblioteca de recursos a una plantilla Twig usando la función attach_library() en cualquier archivo *.html.twig, por ejemplo:

{{ attach_library('fluffiness/cuddly-slider') }}
<div>Algún marcado suave {{ message }}</div>

Adjuntar biblioteca a un subconjunto de páginas

En algunos casos, no necesitas que tu biblioteca esté activa en todas las páginas, sino solo en un subconjunto. Por ejemplo, puedes querer que tu biblioteca esté activa solo cuando se muestra un bloque específico o un tipo específico de nodo.

El tema puede hacerlo implementando una función THEME_preprocess_HOOK() en el archivo .theme, reemplazando "THEME" con el nombre de máquina de tu tema y "HOOK" con el nombre de máquina del hook del tema.

Por ejemplo, para adjuntar JavaScript a la página de mantenimiento, la parte "HOOK" es "maintenance_page", y tu función sería así:

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

Puedes hacer algo similar para otros hooks del tema, y por supuesto tu función puede contener lógica, por ejemplo para determinar qué bloque se está preprocesando en el hook "block", qué tipo de nodo en el hook "node", etc.

¡Nota importante! En este caso, debes proporcionar metadatos de caché que correspondan a tu estado actual. El ejemplo anterior funciona incondicionalmente, por lo que no necesita metadatos de caché. El uso más común es adjuntar alguna biblioteca de recursos basada en la ruta actual:

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';
  }
}

Definición de bibliotecas: opciones y detalles

Agregar propiedades a los CSS/JS adjuntos

Las propiedades se agregan en llaves {} tras cada archivo añadido en el archivo THEMENAME.libraries.yml de tu tema.

Propiedades CSS

Las siguientes propiedades son opcionales y aplican para cada activo CSS.

attributes Atributos opcionales. Un caso de uso conocido es Bootstrap CDN.
{ attributes: { crossorigin: anonymous } }
browsers Carga condicional basada en navegador. Este método usa comentarios condicionales no soportados en IE10 y superiores.
{ browsers: { IE: 'lte IE 9', '!IE': false } }
group Los activos se agregan por grupos.
Por defecto: el grupo SMACSS donde el activo está ubicado.

Raramente usado

media Tipo de medio.
{ media: print }
minified Indica si el activo ya está minificado.
Por defecto: false
{ type: external, minified: true }
preprocess Si los activos deben ser agregados.
Por defecto: true
{ preprocess: false }
type Origen del activo.
Por defecto: archivo
{ type: external, minified: true }
weight Ajusta el orden relativo a otros activos (dentro del mismo grupo SMACSS).
Por defecto: 0. Usa valores numéricos entre -50 y +50.
{ weight: 1 }

Propiedades JS

Las siguientes propiedades son opcionales y aplican para cada activo JS.

attributes Atributos adicionales para el script.
{ type: external, attributes: { async: true } }
browsers Carga condicional basada en navegador. Usa comentarios condicionales no soportados en IE10 y superiores.
{ browsers: { IE: 'lte IE 9', '!IE': false } }
preprocess Si los activos deben ser agregados.
Por defecto: true
{ preprocess: false }
type Origen del activo.
Por defecto: archivo
{ type: external, minified: true }
weight No se recomienda usar dependencias para esto.
Ajusta el orden relativo a otros activos. Debe ser negativo.
{ weight: -1 }

Sobrescribir y extender bibliotecas

Debes ir a *.info.yml para sobrescribir bibliotecas definidas en *.libraries.yml. Estas pueden ser sobrescritas o extendidas mediante overrides o extensiones de bibliotecas. Las sobrescrituras que añadas en *.info.yml serán heredadas por subtemas.

La propiedad stylesheets-remove usada en archivos *.info.yml está obsoleta y será eliminada en Drupal 9.0.x. La propiedad stylesheets-override ya fue eliminada.

libraries-override

La lógica para crear sobrescrituras es:

  • Usar el espacio de nombres original del módulo (o core) para el nombre de la biblioteca.
  • Usar la ruta del último override como clave.
  • La ruta debe ser la ruta completa al archivo.

Ejemplo:

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

Aquí contextual/drupal.contextual-links es el espacio de nombres de la biblioteca base y /core/themes/stable/css/contextual/contextual.module.css es la ruta completa del último override de esta biblioteca. En este caso, el archivo fue eliminado con false.

Nota que solo la última parte es la ruta real del sistema de archivos, las otras partes son espacios de nombres. Las líneas css: y component: reflejan la estructura de la biblioteca sobrescrita.

Ten en cuenta que depender de la ruta absoluta puede romperse si la estructura de archivos cambia. Por eso existe la cuestión de eliminar esta dependencia usando empaquetadores.

A continuación, algunos otros ejemplos de uso de libraries-override para eliminar o reemplazar recursos CSS o JS o bibliotecas enteras heredadas por tu tema desde módulos o temas.

libraries-override:
  # Reemplazar una biblioteca entera.
  core/drupal.collapse: mytheme/collapse
  
  # Reemplazar un activo con otro.
  subtheme/library:
    css:
      theme:
        css/layout.css: css/my-layout.css

  # Reemplazar un activo override de stable.
  contextual/drupal.contextual-toolbar:
    css:
      component:
        core/themes/stable/css/contextual/contextual.toolbar.css: css/contextual.toolbar.css

  # Reemplazar un activo JavaScript de un módulo core.
  toolbar/toolbar:
    js:
      js/views/BodyVisualView.js: js/views/BodyVisualView.js

  # Eliminar un activo.
  drupal/dialog:
    css:
      theme:
        dialog.theme.css: false
  
  # Eliminar una biblioteca entera.
  core/modernizr: false
  
  # Reemplazar activos muy específicos de la biblioteca de un módulo contribuido.
  # Nota: Las bibliotecas disponibles para sobrescribir se encuentran en el archivo *.libraries.yml del módulo. En este ejemplo, el archivo libraries.yml estaría en: /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 permite a los temas modificar recursos de una biblioteca agregando recursos dependientes adicionales en cada inclusión de la biblioteca.
libraries-extend se definen extendiendo una biblioteca con otras bibliotecas.

Esto es ideal para estilizar algunos componentes de forma diferente en tu tema sin cargar CSS global. Es decir, ajustar la apariencia del componente sin cargar CSS en todas las páginas.

# Extender drupal.user: añadir activos de las bibliotecas user de classy.
libraries-extend:
  core/drupal.user: 
    - classy/user1
    - classy/user2

Configuración adicional de Javascript

Orden de carga de activos

Como era de esperar, el orden en que los archivos están listados es el orden de carga. Por defecto, todos los recursos JS ahora se cargan en el pie de página. JS para elementos críticos de UI, que no pueden mostrarse sin su JS correspondiente, puede cargarse en el encabezado si es necesario, así:

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

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

Establece header en true para indicar que los recursos JS en esta biblioteca están en el "camino crítico" y deben cargarse desde el encabezado. Cualquier dependencia directa o indirecta de bibliotecas declaradas así también se cargará automáticamente desde el encabezado, no es necesario declararlas por separado. Esto es el significado de "camino crítico": cuando un recurso se declara en el encabezado, es "crítico" que este recurso y sus dependencias se carguen primero.

Adjuntar JavaScript personalizado:

En algunos casos, puedes querer agregar JavaScript a una página que depende de información calculada en PHP.

En ese caso, crea un archivo JavaScript, define y adjunta la biblioteca como antes, pero también adjunta configuraciones JS y haz que ese archivo JavaScript lea esas configuraciones vía drupalSettings (sucesor de Drupal 7's Drupal.settings). Para que drupalSettings esté disponible en tu JS, debes declarar dependencia de core/drupalSettings.

Así que se convierte en:

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

y

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

donde 'bar' es un valor calculado. (Recuerda que los metadatos de caché también son necesarios aquí.)

Entonces cuddly-slider.js podrá acceder a settings.fluffiness.cuddlySlider.foo (que será === 'bar'):

(function ($, Drupal, drupalSettings) {

  'use strict';

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

})(jQuery, Drupal, drupalSettings);

Agregar atributos a etiquetas script

Si deseas agregar atributos a la etiqueta <script>, debes añadir una clave attributes en JSON tras la URL del script. Dentro del objeto bajo attributes, agrega el nombre del atributo como clave y su valor como valor. Si el valor es true, el atributo se mostrará solo (sin valor explícito).

Por ejemplo:

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

Esto generará el siguiente marcado:

<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

El JavaScript inline es muy desaconsejado. Se recomienda colocarlo en un archivo JS separado para permitir el caché en el cliente. Esto también permite que el código JS sea legible y mantenible.

JavaScript inline que genera marcado
No se recomienda y generalmente no es necesario. Coloca el JS en un archivo. Ejemplos son publicidad, botones para compartir en redes sociales, widgets sociales. Usan JS inline pero son un tipo especial de contenido/markup, no destinados a decorar o interactuar con el sitio, sino para incluir contenido externo vía JS.

Querrás ponerlos en un bloque personalizado o directamente en una plantilla Twig.

Por ejemplo:

<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 que afecta toda la página

El uso de JavaScript inline es muy desaconsejado. Ejemplos incluyen analíticas (como Google Analytics) y servicios de fuentes alojadas. El JavaScript inline que afecta toda la página puede ser de dos tipos: front-end/estilos o lógico.

En el caso de front-end/estilos (como fuentes alojadas), el JS pertenece al tema. Pon el JS directamente en tu archivo html.html.twig. Para fuentes esto ayuda a colocarlo donde el usuario final tiene la mejor (y más rápida) experiencia, evitando FOUT (Flash Of Unstyled Text), mientras la fuente aún carga (las fuentes cargadas vía JS deben ir en el HTML <HEAD> antes que el CSS).
(Para más información, revisa el excelente artículo "Async Typekit & Micro-FOUT".)

En el otro caso, pertenece a un módulo; para eso consulta "Agregar hojas de estilo (CSS) y JavaScript (JS) a un módulo Drupal 8".

JavaScript inline en un módulo de integración

El uso de JavaScript inline está muy desaconsejado. Considera usar los ejemplos anteriores antes de intentarlo.

Dos cosas a tener en cuenta al proporcionar un campo que acepta JavaScript inline proporcionado por un usuario:

1. El campo, formulario o página que acepte este JavaScript inline debe tener permiso adjunto.
Ejemplo: MODULE.routing.yml

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

2. Si el valor se guarda en un objeto de configuración, debe informar al sistema de renderizado sobre su CacheableMetadata, para que cuando cambie, el caché de renderizado se limpie o expire.
Ejemplo: MODULES.module

<?php

/**
 * @file
 * Integra MODULE en un sitio Drupal.
 */

use Drupal\Core\Render\Markup;

/**
 * Implementa 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()],
    ],
  ];
  // Agrega metadata de cacheabilidad de configuración.
  /** @var Drupal\Core\Render\Renderer $renderer */
  $renderer = \Drupal::service('renderer');
  $renderer->addCacheableDependency($page_bottom['MODULE'], $settings);
}

CDN / bibliotecas externas

Quizás desees usar JavaScript alojado en un CDN (Content Delivery Network), por ejemplo, las fuentes web normalmente solo están disponibles mediante URL externas. Esto se hace declarando la biblioteca como externa (type: external). También es útil incluir información sobre la biblioteca externa en la definición.

(Ten en cuenta que generalmente no es una buena idea cargar bibliotecas desde CDN; para evitarlo, si es posible, pues introduce más puntos de fallo, afecta rendimiento y estabilidad, requiere más conexiones TCP/IP y usualmente no está cacheado por el navegador. Sin embargo, las bibliotecas externas no deben alojarse en Drupal.org como parte de tu repositorio — revisa la política de bibliotecas externas en Drupal.org para detalles.)

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 deseas que el archivo externo se solicite usando el mismo protocolo que la página, usa una URL relativa al protocolo:

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

O si deseas agregar CSS, aquí tienes un ejemplo integrando 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 }

Ejemplo para Bootstrap CDN CSS con atributos personalizados.

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"

Más información

Drupal’s online documentation is © 2000-2020 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License.