Resuelve tus dolores de cabeza específicos con los módulos CSS
Spanish (Español) translation by CYC (you can also view the original English article)
Los "Módulos CSS" no tienen nada que ver con la W3C, sino que son parte de un proceso de construcción sugerido. Compila tu proyecto, cambiando el nombre de los selectores y las clases para que se vuelvan únicas, con alcance a componentes individuales. Los estilos se bloquean en esos componentes y no se pueden utilizar en otra parte, ¡a menos que lo digas específicamente!
Preámbulo
Hoy en día estamos bastante acostumbrados a la idea de que las tecnologías web sean la fuerza impulsora detrás de las aplicaciones; aplicaciones web, móviles y aplicaciones de escritorio. Pero a diferencia de los simples sitios web estáticos, las aplicaciones suelen ser más dinámicas, complejas y a menudo consisten en componentes incluso más allá de lo que ofrece Bootstrap o ZURB Foundation. A medida que una aplicación crece en complejidad, la gestión de su CSS puede ser una tarea infernal.
Con el tiempo, se han desarrollado múltiples estrategias, como OOCSS, ITCSS, BEM, Atomic CSS, etc. para mantener el CSS organizado, reutilizable y (crucialmente) escalable. Estas estrategias requieren que tú y todos en tu equipo sigan las convenciones diligentemente.
Sin embargo, tarde o temprano, la complejidad se volverá a infiltrar de nuevo y encontrarás reglas de estilo como las siguientes:
1 |
html.progressive-image.js [data-progressive-image],html.progressive-image.js [data-progressive-image] * { |
2 |
background-image: none !important; |
3 |
mask-image: none !important; |
4 |
opacity: 0 |
5 |
}
|
6 |
.main #section-enhanced-gallery-heroes.homepage-section.enhanced-gallery .with-single-item { |
7 |
transform: translate(0, 0) !important |
8 |
}
|
El problema con CSS en muchos sitios web de gran escala y aplicaciones es que es tan difícil mantener la especificidad baja que, en algún momento, la adición de !important no se puede evitar. Y refactorizar CSS en una base de código grande es complicado ya que la eliminación de estilos podría romper otros componentes.
En este tutorial, vamos a ver "Módulos CSS" y cómo puede ayudarnos a minimizar estos notorios problemas CSS.
Nota: Mira el repositorio en Github para ver ejemplos de códigos.
Usando módulos CSS
En pocas palabras, los "Módulos CSS" son una herramienta que cambia el nombre de las clases y los identificadores CSS en selectores únicos, permitiéndote aislar las reglas de estilo localmente a los elementos o componentes asignados. Asumiendo que tenemos un botón, podríamos escribir sus reglas de estilo de la siguiente manera:
1 |
.button { |
2 |
background-color: #9b4dca; |
3 |
border: 0.1rem solid #9b4dca; |
4 |
border-radius: .4rem; |
5 |
color: #fff; |
6 |
cursor: pointer; |
7 |
display: inline-block; |
8 |
}
|
9 |
.button:focus, |
10 |
.button:hover { |
11 |
background-color: #606c76; |
12 |
border-color: #606c76; |
13 |
color: #fff; |
14 |
outline: 0; |
15 |
}
|
Con los Módulos CSS, estas reglas de estilo se renombrarán en algo como:
1 |
._12We30_button { |
2 |
background-color: #9b4dca; |
3 |
border: 0.1rem solid #9b4dca; |
4 |
border-radius: .4rem; |
5 |
color: #fff; |
6 |
cursor: pointer; |
7 |
display: inline-block; |
8 |
}
|
9 |
._12We30_button:focus, |
10 |
._12We30_button:hover { |
11 |
background-color: #606c76; |
12 |
border-color: #606c76; |
13 |
color: #fff; |
14 |
outline: 0; |
15 |
}
|
Si echas un vistazo a sitios grandes como Facebook, Instagram o Airbnb a través de DevTools de tu navegador, verás que las clases CSS y los ID se nombran con este tipo de patrón. He aquí un ejemplo de la página de inicio de Airbnb:



Componentes múltiples
El uso de módulos CSS no tendrá mucho sentido si sólo tenemos un componente, así que vamos a ampliar nuestro ejemplo a tres componentes y veremos cómo configurar nuestro proyecto para implementar Módulos CSS.
Creando un componente
En este segundo ejemplo, vamos a construir tres componentes; un botón con tres estilos diferentes. Los llamaremos el "botón principal", el "botón de contorno" (también conocido como "botón fantasma") y el "botón de borrar". Pondremos estos botones en un directorio aparte. Cada directorio contendrá index.css e index.js.
En el index.js, creamos el elemento y asignamos las clases al elemento, de la siguiente manera:
1 |
// 1. Import the styles from index.css file.
|
2 |
import styles from './index.css'; |
3 |
|
4 |
/**
|
5 |
* 2. Creating a button element and add the class from index.css.
|
6 |
* @type {String}
|
7 |
*/
|
8 |
const button = `<button class="${styles['button']}">Save Changes</button>`; |
9 |
|
10 |
// 3. Export the button to be used in the other files.
|
11 |
export default button; |
Utilizando la nueva directiva import (de importación) en ES6, importamos la hoja de estilo y leemos las clases y las ID como un objeto de JavaScript. Ahora, creamos un elemento y agregamos la clase denominada .button utilizando el JavaScript nativo que también se introdujo en ES6. Por último, exportamos nuestro elemento para que el elemento también pueda ser importado y reutilizado dentro de los otros archivos JavaScript.
Al momento de escribir este artículo, no todos los navegadores han implementado las últimas características y sintaxis JavaScript de la especificación ES6. Por lo tanto, necesitaremos que Babel transforme esos snippets en sintaxis JavaScript para que sean compatibles con la mayoría de los navegadores.
Nuestra hoja de estilos, index.css, es CSS simple. Esta contiene varios selectores para diseñar el elemento de botón.
1 |
/* 1. Primary Button */
|
2 |
.button { |
3 |
background-color: #9b4dca; |
4 |
border: 0.1rem solid #9b4dca; |
5 |
border-radius: .4rem; |
6 |
color: #fff; |
7 |
cursor: pointer; |
8 |
display: inline-block; |
9 |
font-size: 1.1rem; |
10 |
font-weight: 700; |
11 |
height: 3.8rem; |
12 |
letter-spacing: .1rem; |
13 |
line-height: 3.8rem; |
14 |
padding: 0 3.0rem; |
15 |
text-align: center; |
16 |
text-decoration: none; |
17 |
text-transform: uppercase; |
18 |
white-space: nowrap; |
19 |
}
|
20 |
/* More styles of the Primary Button here */
|
Una de las ventajas del uso de Módulos CSS es que no tenemos que preocuparnos por las convenciones de nomenclatura. Puedes utilizar todavía tus metodologías preferidas del CSS como BEM u OOCSS, pero no se aplica nada; puedes escribir las reglas de estilo de la forma más práctica para el componente, ya que el nombre de la clase eventualmente se pondrá en namespaced.
En este ejemplo, nombramos todas las clases de nuestro componente de botón .button en lugar de .button-primary o .button-outline.
Trabajando con CSS en Shadow DOM, todavía tengo el viejo hábito de usar notación BEM aunque no es *necesario*. ¡Estilo de encapsulación FTW!
— Razvan Caliman (@razvancaliman) Julio 31, 2017
Compilando módulos con Webpack
Cuando cargamos el index.js en una página HTML, nada aparecerá en el navegador. En este caso, tendremos que compilar el código para hacerlo funcional. Necesitaremos instalar Babel, Babel Preset para ES2015 (ES6) y Webpack junto con los siguientes "cargadores" para permitir que Webpack procese nuestros archivos de origen.
- Babel-loader: para cargar archivos
.jsy transformar el código fuente con el módulo básico de Babel. - Css-loader: para cargar archivos
.css. - Style-loader: para inyectar estilos internos tomados del
css-loaderen nuestra página HTML usando el<style>.
Instalamos esos paquetes con NPM y los guardamos en el archivo package.json como nuestras dependencias de desarrollo.
Configuración del Webpack
De forma similar a Grunt con su gruntfile.js o Gulp con su gulpfile.js, ahora necesitamos configurar Webpack usando un archivo llamado webpack.config.js. A continuación presentamos la configuración completa de Webpack en nuestro proyecto:
1 |
var path = require('path'); |
2 |
|
3 |
module.exports = { |
4 |
entry: './src/main.js', // 1. |
5 |
output: { |
6 |
path: path.resolve(__dirname, 'dist/js'), |
7 |
filename: 'main.js' |
8 |
},
|
9 |
module: { |
10 |
loaders: [ // 4. |
11 |
{
|
12 |
test: /\.css$/, |
13 |
use: [ |
14 |
{
|
15 |
loader: 'style-loader' |
16 |
}, { |
17 |
loader: 'css-loader', |
18 |
options: { |
19 |
modules: true, |
20 |
localIdentName: '[hash:base64:5]__[local]' |
21 |
}
|
22 |
}
|
23 |
]
|
24 |
}, { |
25 |
test: /\.js$/, |
26 |
exclude: /node_modules/, |
27 |
use: [ |
28 |
{
|
29 |
loader: 'babel-loader', |
30 |
options: { |
31 |
presets: ['es2015'] |
32 |
}
|
33 |
}
|
34 |
]
|
35 |
}
|
36 |
]
|
37 |
}
|
38 |
}
|
La configuración le indica a Webpack que compile nuestro archivo principal de JavaScript, main.js, al directorio /dist/js. También hemos habilitado la opción modules en css-loader, así como hemos establecido la clase y el patrón de nomenclatura de ID en [hash: base64: 5]__[local]. Empleamos babel-loader para compilar nuestros archivos JavaScript ES6.
Una vez que tengamos las dependencias instaladas y configuradas, importaremos nuestros tres botones en el archivo main.js.
1 |
// Import the button elements;
|
2 |
import ButtonPrimary from './button-primary'; |
3 |
import ButtonOutline from './button-outline'; |
4 |
import ButtonClear from './button-clear'; |
5 |
|
6 |
// Add the element to the content;
|
7 |
document.getElementById('content').innerHTML = `${ButtonPrimary}${ButtonOutline}${ButtonClear}`; |
Ahora ejecuta el comando webpack de la siguiente manera:
1 |
./node_modules/.bin/webpack |
Cuando cargamos /dist/js/main.js en el navegador, debemos ver que nuestros botones se agregan con las clases que se nombran siguiendo el patrón que establecemos en css-loader. También podemos encontrar los estilos añadidos a la página con el elemento styles.



Composición
Los preprocesadores CSS como LESS y Sass nos permiten reutilizar estilos extendiendo otras clases o IDs de otras hojas de estilo. Con los Módulos CSS, podemos usar la directiva compose para hacer lo mismo. En este ejemplo, he puesto en otro archivo las reglas de estilos comunes que se comparten a través de nuestros tres botones e importé la clase del nuevo archivo, de la siguiente manera:
1 |
.button { |
2 |
|
3 |
/* Extend .button class to apply the basic button styles */
|
4 |
composes: button from "./button.css"; |
5 |
|
6 |
color: #fff; |
7 |
background-color: #9b4dca; |
8 |
border: 0.1rem solid #9b4dca; |
9 |
}
|
Una vez que el código es compilado y cargado en el navegador, podemos encontrar que el botón es procesado con dos clases. También hay cuatro elementos style inyectados en la página que incluyen la clase _3f6Pb__button que contiene las reglas de estilo común de nuestros componentes.



Usando módulos CSS en Vue
En un proyecto real, es probable que no usemos módulos CSS utilizando JavaScript. En su lugar, utilizaríamos un framework de JavaScript como Vue. Afortunadamente, los Móludos CSS han sido integrados a Vue a través de la vue-loader; un cargador Webpack que compila el .vue. Vamos a mostrar un ejemplo de cómo colocaríamos nuestro botón principal en un componente .vue.
1 |
<template>
|
2 |
<button v-bind:class="$style.button"><slot></slot></button> |
3 |
</template>
|
4 |
|
5 |
<script>
|
6 |
export default { |
7 |
name: 'button-primary' |
8 |
}
|
9 |
</script>
|
10 |
|
11 |
<style module> |
12 |
.button { |
13 |
|
14 |
/* Extend .button class to apply the basic button styles */
|
15 |
composes: button from "./button.css"; |
16 |
|
17 |
color: #fff; |
18 |
background-color: #9b4dca; |
19 |
border: 0.1rem solid #9b4dca; |
20 |
}
|
21 |
|
22 |
.button[disabled] { |
23 |
cursor: default; |
24 |
opacity: .5; |
25 |
}
|
26 |
|
27 |
.button[disabled]:focus, |
28 |
.button[disabled]:hover { |
29 |
background-color: #606c76; |
30 |
border-color: #606c76; |
31 |
}
|
32 |
|
33 |
.button:focus, |
34 |
.button:hover { |
35 |
background-color: #606c76; |
36 |
border-color: #606c76; |
37 |
color: #fff; |
38 |
outline: 0; |
39 |
}
|
40 |
</style>
|
En Vue, agregamos el atributo module al elemento style, como se muestra arriba, para habilitar los Módulos CSS. Cuando compilamos este código obtendremos casi el mismo resultado.
Terminando
Para algunos de ustedes, esto será algo completamente nuevo. Es perfectamente comprensible si el concepto de Módulos CSS es un poco rascador de cabeza a primera vista. Así que vamos a recapitular lo que hemos aprendido acerca de los Módulos CSS en este artículo.
- Los "Módulos CSS" nos permite encapsular las reglas de estilos renombrando o
namespacing(clasificando) los nombres de las clases, minimizando los choques en la especificidad del selector a medida que crece la base de código. - Esto también nos permite escribir los nombres de las clases más cómodamente en lugar de atenernos a una metodología en particular.
- Por último, como las reglas de estilo se acoplan a cada componente, los estilos también se eliminarán cuando ya no utilices el componente.
En este artículo, apenas arañamos la superficie de los Módulos CSS y otras herramientas modernas de desarrollo web como Babel, Webpack y Vue. Por lo tanto, he reunido algunas referencias para que busques más sobre estas herramientas.
Referencia adicional
- Echa un vistazo a la demo en Github
- Empieza a codificar en ES6 con Babel: Curso en Tuts+
- Comenzando con Vue: Curso en Tuts+
- Webpack 2 inmediato: Curso en Tuts+
- Estrategias para mantener baja la especificidad CSS
- Daniel Eden: Moverse lento y arreglar cosas



