Uso de PostCSS con metodologías BEM y SUIT
() translation by (you can also view the original English article)
En este tutorial aprenderemos a usar PostCSS para hacer que el desarrollo de CSS estilo BEM/SUIT sea más fácil y eficiente.
Estas dos metodologías presentan una convención de nomenclatura para las clases que facilita mantener tus estilos estrechamente orientados a las funciones y le ayuda a otros desarrolladores a reconocer el propósito de varias clases solo por la forma en que se nombran.
BEM fue el precursor de este tipo de metodología de nomenclatura de clases, creada por Yandex. La metodología SUIT es un enfoque basado en BEM, pero con algunos ajustes y adiciones realizadas por Nicholas Gallagher. SUIT hace todo lo que BEM hace, pero para muchos usuarios se considera una mejora.
Trabajar con estos métodos definitivamente ayuda a producir CSS mejor y mejor estructurado. Sin embargo, la parte difícil es que puede ser agotador escribir manualmente los nombres de clase requeridos en esta estructura, y realizar un seguimiento de cómo las clases se interrelacionan puede convertirse en un dolor de cabeza.
El plugin postcss-bem de Malte-Maurice Dreyer alivia estos problemas a través de una combinación de atajos y anidamiento, que aprenderás a usar a medida que avanzamos en este tutorial.
Pero primero, hagamos una introducción rápida sobre los métodos BEM y SUIT, para asegurarnos de tener una idea clara de los beneficios de usar el plugin postcss-bem y de la forma en que se usa.
Introducción rápida sobre BEM
Bloque
En BEM, los bloques son fragmentos de alto nivel de un diseño; los bloques de construcción de los que está hecho el sitio. Un bloque debe ser una parte de tu sitio que sea independiente de otras partes y, en teoría, podría colocarse en cualquier lugar de tu diseño, incluso dentro de otro bloque.
Por ejemplo, los "bloques" del formulario de búsqueda en tu sitio pueden usar la clase .search-form
.
Elemento
Un elemento en BEM es una subsección dentro de un bloque. Se indican agregando un separador __
de dos guiones bajos y un nombre de elemento al nombre del bloque principal.
Por ejemplo, un formulario de búsqueda puede incluir elementos de encabezado, campo de texto y botón de envío. Sus nombres de clase pueden ser .search-form__heading
, .search-form__text-field
y .search-form__submit-button
respectivamente.
Modificador
Se aplica un modificador a un bloque o elemento para indicar un cambio en su presentación o un cambio en su estado. Se significan agregando un separador y un nombre de modificador al bloque o elemento en cuestión.
Los documentos oficiales del sitio BEM establecen que los separadores de modificadores deben ser un guión bajo _
. Sin embargo, la convención "similar a BEM" de las Directrices CSS de Harry Roberts emplea dos guiones --
, y probablemente sea más utilizada y conocida que la convención oficial de BEM.
Por ejemplo, en un diseño, es posible que quieras presentar formularios de búsqueda avanzada de manera diferente a los formularios de búsqueda normales y, por lo tanto, crear la clase modificadora .search-form_advanced
(BEM oficial) o .search-form-advanced
(similar a BEM).
En otro ejemplo, es posible que quieras cambiar la apariencia del formulario debido a un cambio de estado, por ejemplo, si se acaba de enviar contenido no válido y, por lo tanto, crear el modificador .search-form_invalid
(BEM oficial) o .search-form--invalid
(similar a BEM).
Introducción rápida a SUIT
SUIT comprende Utilidades y Componentes. Dentro de los componentes puede haber modificadores, descendientes y estados.
SUIT utiliza una combinación de la notación de Pascal (PascalCase), Mayúsculas y minúsculas(camelCase) y guiones. Sus convenciones imponen un límite en el número a veces confuso de guiones y guiones bajos que pueden aparecer en BEM. Por ejemplo, la clase BEM .search-form__text-field
sería .SearchForm-textField
en SUIT.
Utilidad
Las utilidades manejan la estructura y el estilo posicional, y están escritas de tal manera que se pueden aplicar en cualquier parte de un componente. Tienen el prefijo u-
y están escritos en mayúsculas y minúsculas. Por ejemplo, .u-clearFix
.
Componente
Un componente en SUIT ocupa el lugar de un bloque en BEM. Los componentes siempre están escritos en mayúsculas y minúsculas y son solo una parte de SUIT que usa mayúsculas y minúsculas, lo que los hace fáciles de identificar. Por ejemplo, .SearchForm
.
Espacio de nombres de componentes
Opcionalmente, los componentes pueden tener un prefijo con un espacio de nombres y un solo guión nmsp-
para garantizar que se eviten conflictos, por ejemplo .mine-SearchForm
.
Descendiente
Un descendiente en SUIT reemplaza un elemento en BEM. Utiliza un solo guión -
y está escrito en mayúsculas y minúsculas. Por ejemplo .SearchForm-header
, .SearchForm-textField
y .SearchForm-submitButto
.
Modificador
SUIT usa modificadores al igual que BEM, sin embargo, su función está más estrictamente controlada. Un modificador SUIT generalmente solo se aplica directamente a un componente, no a un descendiente. Tampoco debe usarse para representar cambios de estado, ya que SUIT tiene una convención de nomenclatura dedicada para los estados.
Los modificadores están escritos en mayúsculas y minúsculas y están precedidos por dos guiones --
. Por ejemplo, .SearchForm--advanced
.
Estado
Las clases de estado se pueden usar para reflejar los cambios en el estado de un componente. Esto les permite diferenciarse claramente de los modificadores, que reflejan la modificación de la apariencia base de un componente independientemente del estado. Si es necesario, también se puede aplicar un estado a un descendiente.
Los estados tienen el prefijo is-
y están escritos en mayúsculas y minúsculas. También siempre se escriben como clases contiguas. Por ejemplo .SearchForm.is-invalid
.
Configurar el proyecto
Ya que tienes los conceptos básicos de BEM y SUIT, es hora de configurar tu proyecto.
Necesitarás un proyecto vacío usando Gulp o Grunt, dependiendo de tu preferencia. Si aún no tienes preferencia por uno u otro, te recomiendo usar Gulp, ya que necesitarás menos código para lograr los mismos fines, por lo que deberías encontrarlo un poco más simple de trabajar.
Puedes leer sobre cómo configurar proyectos Gulp o Grunt para PostCSS en los tutoriales anteriores
- Guía de inicio rápido de PostCSS: Configuración de Gulp o
- Guía de inicio rápido de PostCSS: Configuración de Grunt
respectivamente.
Sin embargo, si no quieres configurar manualmente tu proyecto desde cero, puedes descargar los archivos de origen adjuntos a este tutorial y extraer el proyecto de inicio Gulp o Grunt proporcionado en una carpeta de proyecto vacía. Luego, con una terminal o símbolo del sistema apuntando a la carpeta, ejecuta el comando npm install
.
Instalar Plugins
Luego, deberás instalar el plugin postcss-bem. También instalaremos un plugin que puede funcionar con él bastante bien: postcss-nested.
Ya sea que estés usando Gulp o Grunt, ejecuta el siguiente comando dentro de la carpeta del proyecto:
1 |
npm install postcss-bem postcss-nested --save-dev |
Ya estamos listos para cargar los plugins en tu proyecto.
Cargar plugins a través de Gulp
Si estás utilizando Gulp, agrega estas variables debajo de las variables que ya están en el archivo:
1 |
var bem = require('postcss-bem'); |
2 |
var nested = require('postcss-nested'); |
Ahora agrega cada uno de esos nuevos nombres de variables a tu matriz processors
:
1 |
var processors = [ |
2 |
bem, |
3 |
nested
|
4 |
];
|
Haz una prueba rápida de que todo está funcionando ejecutando el comando gulp css
y luego verifica que haya aparecido un nuevo archivo "style.css" en la carpeta "dest" de tu proyecto.
Cargar plugins a través de Grunt
Si usas Grunt, actualiza el objeto processors
, que está anidado en el objeto options
, a lo siguiente:
1 |
processors: [ |
2 |
require('postcss-bem')(), |
3 |
require('postcss-nested')() |
4 |
]
|
Haz una prueba rápida de que todo está funcionando ejecutando el comando grunt postcss
y luego verifica que haya aparecido un nuevo archivo "style.css" en la carpeta "dest" de tu proyecto.
Está bien, estás listo para comenzar. Aprendamos a generar la estructura BEM y SUIT.
BEM y SUIT con postcss-bem
Es posible que se desarrolle cierta incomodidad en la estructura BEM o SUIT al escribir el código manualmente, ya que repetir continuamente los mismos identificadores en los nombres de las clases puede resultar tedioso, y realizar un seguimiento de qué elementos y descendientes pertenecen a qué bloques y componentes puede resultar confuso.
Sin embargo, cuando usas postcss-bem, se vuelve fácil dar sentido a la estructura de tu código de un vistazo, y la repetición al escribir los nombres de clase se vuelve prácticamente inexistente.
Generación de la estructura SUIT
A pesar de su nombre, de forma predeterminada, postcss-bem generará resultados de acuerdo con la sintaxis SUIT en lugar de BEM. Puedes generar en la sintaxis BEM, que veremos más adelante, pero el plugin está diseñado principalmente para generar SUIT, por lo que comenzaremos con la sintaxis SUIT.
Generación de un componente
Para crear un componente, usa la sintaxis @component ComponentName {...}
.
Prueba esto agregando un componente SearchForm
a tu archivo "src/style.css":
1 |
@component SearchForm { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
Complétalo y el código resultante debe ser:
1 |
.SearchForm { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
Generación de un descendiente
Para crear un descendiente, usa la sintaxis @descendent descName {...}
anidada dentro del componente primario.
Agrega un textField
descendiente dentro de tu componente SearchForm
de la siguiente manera:
1 |
@component SearchForm { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
|
5 |
/* Nest descendent under component */
|
6 |
@descendent textField { |
7 |
border: 1px solid #ccc; |
8 |
}
|
9 |
|
10 |
}
|
Después de compilar, deberías ver:
1 |
.SearchForm { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
5 |
|
6 |
.SearchForm-textField { |
7 |
border: 1px solid #ccc; |
8 |
}
|
Generación de un modificador
Crea un modificador para un componente con la sintaxis @modifier name {...}
, anidado dentro del componente al que afecta. Los modificadores normalmente deben colocarse en la parte superior de tu componente, encima de los descendientes y estados.
Agrega un modificador denominado advanced
al componente SearchForm
con el siguiente código:
1 |
@component SearchForm { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
|
5 |
/* Typically, place modifiers above descendents */
|
6 |
@modifier advanced { |
7 |
padding: 1rem; |
8 |
}
|
9 |
|
10 |
@descendent textField { |
11 |
border: 1px solid #ccc; |
12 |
}
|
13 |
|
14 |
}
|
Vuelve a compilar tu código y deberías ver tu nuevo modificador de componentes advanced
:
1 |
.SearchForm { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
5 |
|
6 |
.SearchForm--advanced { |
7 |
padding: 1rem; |
8 |
}
|
9 |
|
10 |
.SearchForm-textField { |
11 |
border: 1px solid #ccc; |
12 |
}
|
Generación de un estado
Los estados se crean a través de la sintaxis @when name {...}
y se pueden anidar dentro de un componente o un descendiente.
Agrega un estado denominado invalid
a tu descendiente de textField
con este código:
1 |
@component SearchForm { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
|
5 |
@modifier advanced { |
6 |
padding: 1rem; |
7 |
}
|
8 |
|
9 |
@descendent textField { |
10 |
border: 1px solid #ccc; |
11 |
|
12 |
/* This creates a state for the textField descendant */
|
13 |
@when invalid { |
14 |
border: 1px solid red; |
15 |
}
|
16 |
}
|
17 |
|
18 |
}
|
Ahora, cuando compiles tu código, verás que contiene tu nuevo estado invalid
:
1 |
.SearchForm { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
5 |
|
6 |
.SearchForm--advanced { |
7 |
padding: 1rem; |
8 |
}
|
9 |
|
10 |
.SearchForm-textField { |
11 |
border: 1px solid #ccc; |
12 |
}
|
13 |
|
14 |
.SearchForm-textField.is-invalid { |
15 |
border: 1px solid red; |
16 |
}
|
Componentes del espaciado de nombres
Puedes asignarle un espaciado de nombres a tus componentes, y todos los descendientes, modificadores y estados anidados dentro de ellos, rodeándolos con @component-namespace name {...}
. Puedes, si así lo quieres, envolver toda tu hoja de estilo con este espacio de nombres para que todas tus clases tengan el prefijo automáticamente.
Prueba esto envolviendo todo tu código hasta ahora con @component-namespace mine {...}
:
1 |
@component-namespace mine { |
2 |
|
3 |
@component SearchForm { |
4 |
padding: 0; |
5 |
margin: 0; |
6 |
|
7 |
@modifier advanced { |
8 |
padding: 1rem; |
9 |
}
|
10 |
|
11 |
@descendent textField { |
12 |
border: 1px solid #ccc; |
13 |
|
14 |
@when invalid { |
15 |
border: 1px solid red; |
16 |
}
|
17 |
}
|
18 |
|
19 |
}
|
20 |
|
21 |
}
|
Después de compilar, verás que ahora cada uno de tus componentes está prefijado con mine-
:
1 |
.mine-SearchForm { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
5 |
|
6 |
.mine-SearchForm--advanced { |
7 |
padding: 1rem; |
8 |
}
|
9 |
|
10 |
.mine-SearchForm-textField { |
11 |
border: 1px solid #ccc; |
12 |
}
|
13 |
|
14 |
.mine-SearchForm-textField.is-invalid { |
15 |
border: 1px solid red; |
16 |
}
|
Generación de una utilidad
Las utilidades se crean con la sintaxis @utility utilityName {...}
. Recordarás que al configurar tu proyecto, instalaste el plugin postcss-nested. Hicimos esto ya que puede ser muy útil usarlo al unísono con postcss-bem, como verás en este ejemplo donde creamos una utilidad clearFix
:
1 |
@utility clearFix { |
2 |
&:before, &:after { |
3 |
content: ""; |
4 |
display: table; |
5 |
}
|
6 |
&:after { |
7 |
clear: both; |
8 |
}
|
9 |
/* If supporting IE 6/7 */
|
10 |
*zoom: 1; |
11 |
}
|
Después de agregar el código anterior, compila y verás que se creó esta nueva utilidad:
1 |
.u-clearFix { |
2 |
/* If supporting IE 6/7 */
|
3 |
zoom: 1; |
4 |
}
|
5 |
|
6 |
.u-clearFix:before, .u-clearFix:after { |
7 |
content: ""; |
8 |
display: table; |
9 |
}
|
10 |
|
11 |
.u-clearFix:after { |
12 |
clear: both; |
13 |
}
|
Generación de la estructura BEM
Para activar la salida de sintaxis BEM en postcss-bem, pasa la opción style: 'bem'
en tu Gulpfile o Gruntfile así:
1 |
/* Gulpfile */
|
2 |
var processors = [ |
3 |
bem({style: 'bem'}), |
4 |
nested
|
5 |
];
|
6 |
|
7 |
/* Gruntfile */
|
8 |
processors: [ |
9 |
require('postcss-bem')({style: 'bem'}), |
10 |
require('postcss-nested')() |
11 |
]
|
Por defecto, postcss-bem usará el separador oficial para un modificador de un solo guión bajo _
. Si es importante para tu proyecto que uses el separador más común de dos guiones --
, en su lugar, puedes cambiar la configuración del plugin postcss-bem yendo a la carpeta node_modules/postcss-bem de tu proyecto, abriendo index.js, ubicando la línea 15 y cambiando esto:
1 |
bem: { |
2 |
separators: { |
3 |
namespace: '--', |
4 |
descendent: '__', |
5 |
modifier: '_' |
6 |
}
|
7 |
}
|
... a esto:
1 |
bem: { |
2 |
separators: { |
3 |
namespace: '_', |
4 |
descendent: '__', |
5 |
modifier: '--' |
6 |
}
|
7 |
}
|
Generación de un bloque
Debido a que un "bloque" en BEM se correlaciona con un "componente" en SUIT, usa la sintaxis @component block-name {...}
para generar un bloque.
Para crear un bloque search-form
, agrega este código:
1 |
@component search-form { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
Luego compila y deberías ver:
1 |
.search-form { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
Generación de un elemento
Como un "elemento" en BEM se correlaciona con un "descendiente" en SUIT, se pueden crear con la sintaxis @descendent element-name {...}
anidada dentro del bloque padre.
Para crear un elemento text-field
, agrega lo siguiente:
1 |
@component search-form { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
|
5 |
@descendent text-field { |
6 |
border: 1px solid #ccc; |
7 |
}
|
8 |
|
9 |
}
|
En la compilación, verás que se creó el nuevo elemento:
1 |
.search-form { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
5 |
|
6 |
.search-form__text-field { |
7 |
border: 1px solid #ccc; |
8 |
}
|
Generación de un modificador
A pesar de que BEM permite modificadores tanto a bloques como a elementos, el plugin postcss-bem solo los procesará si están anidados dentro de bloques y no elementos, debido a la convención SUIT de modificadores que se aplican a componentes no descendientes. Se pueden crear con la sintaxis @modifier name {...}
, anidada dentro de su bloque padre.
Agrega un modificador advanced
a tu componente de search-form
de la siguiente manera:
1 |
@component search-form { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
|
5 |
@modifier advanced { |
6 |
padding: 1rem; |
7 |
}
|
8 |
|
9 |
@descendent text-field { |
10 |
border: 1px solid #ccc; |
11 |
}
|
12 |
|
13 |
}
|
Y en la compilación producirá:
1 |
.search-form { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
5 |
|
6 |
.search-form_advanced { |
7 |
padding: 1rem; |
8 |
}
|
9 |
|
10 |
.search-form__text-field { |
11 |
border: 1px solid #ccc; |
12 |
}
|
No hay utilidades ni estados, pero los espaciados de nombres están en
Mientras está en modo BEM, las sintaxis @utility
y @when
no se compilarán en nada, dado que BEM no usa utilidades o estados.
Sin embargo, aunque generalmente no forma parte de BEM, la sintaxis @component-namespace
seguirá funcionando si quieres usarla en tu hoja de estilos BEM. Prefijará tus clases con name--
:
1 |
.mine--search-form { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
5 |
|
6 |
.mine--search-form_advanced { |
7 |
padding: 1rem; |
8 |
}
|
9 |
|
10 |
.mine--search-form__text-field { |
11 |
border: 1px solid #ccc; |
12 |
}
|
Recapitulemos
Ahora ya sabes todo sobre cómo acortar el desarrollo de BEM y SUIT y hacer que el proceso general sea más fácil. Resumamos todo lo que cubrimos:
- BEM y SUIT son convenciones de nomenclatura de clases que ayudan a mantener las funciones de las hojas de estilo orientadas y organizadas, además de ayudar a otros desarrolladores a reconocer el propósito de varias clases.
- SUIT es como BEM, pero con algunos extras añadidos y ajustes realizados
- El plugin postcss-bem proporciona accesos directos para crear clases BEM y SUIT, como
@component
,@descendent
,@modifier
, entre otros. - El plugin también permite anidar el código de una manera útil, por ejemplo, los modificadores se anidan dentro del componente o bloque que modifican.
- El espaciado de nombres se puede hacer automáticamente al envolver las clases con
@component-namespace name {...}
En el siguiente tutorial
A continuación, veremos otra excelente manera de aprovechar PostCSS, y es armando un conjunto de herramientas de taquigrafía y atajos que podemos tomar para hacer que nuestra codificación sea más rápida y eficiente.
¡Nos vemos!