DÍA 15

Organización del CSS en proyectos grandes

¿Te enfrentas a un proyecto grande y no sabes cuál es la mejor manera de organizar tu CSS? Aprende cuál es la metodología que mejor se adapta a tu estilo y tu proyecto.


Así que: Tienes un proyecto pequeño con CSS “casero” que ha ido creciendo y… Sorpresa! Tu código se ha ido enmarañando, llenando de fixes rápidos y reglas que alteran partes de la página inesperadas…

No es nada extraño, con una cantidad de código grande es más que esperable. Por suerte, hay diferentes alternativas para organizar tu código de forma mantenible.

En este artículo repasaremos unas cuantas opciones que se han ido presentando, desde las que ya llevan bastante en el aire (como OOCSS BEM) hasta las más novedosas, como AM (Attribute Module).

El ejemplo: Un panel

Para nuestros ejemplos vamos a usar un componente muy frecuente y genérico: Un panel. Un panel podría estar estructurado de esta manera:

<div class="panel default">
    <div class="heading">
        <h4 class="title"></h4>
    </div>
    <div class="body"></div>
    <div class="footer"></div>
</div>
/* Bloque */
.panel {}

/* Modificador */
.panel.default {}

/* Sub-elementos */
.panel .heading {}
.panel .title {}
.panel .body {}
.panel .footer {}

Al hacerlo así aumentamos la especifidad de nuestros selectores (.panel .title es un selector más específico que una sola clase, y por lo tanto, más difícil de sobreescribir), y nos arriesgamos usando clases poco genéricas (¿Qué pasa si a alguien se le ocurre hacer un .title a nivel de página?).

Bootstrap

Bootstrap fue uno de los primeros frameworks que introdujo cierta idea de encapsulamiento de componentes. El panel, por ejemplo, lo estructura de esta manera:

<div class="panel panel-default">
    <div class="panel-heading">
        <h4 class="panel-title"></h4>
    </div>
    <div class="panel-body"></div>
    <div class="panel-footer"></div>
</div>
/* bloque */
.panel {}

/* modificadores */
.panel-default {}
.panel-primary {}
...

/* sub-elementos */
.panel-heading {}
.panel-title {}
.panel-body {}
.panel-footer {}

Como se puede apreciar, no se distingue bien lo que es un elemento dentro del componente de lo que es un modificador. Esto es más apreciable quizás en los componentes de los formularios (.input-group, .input-xs, .input-group-addon…).

El siguiente nivel: BEM

BEM (Block, Element, Modifier) es una convención ideada en un principio por Yandex, y que ha llegado a tener cierto renombre gracias a un artículo atemporal escrito por Nicolas Gallagher.

La idea es simple: Mediante la convención llegar a saber fácilmente los roles de las clases de una sola pasada, y facilitar la organización en componentes del CSS.

Un ejemplo de sintaxis BEM sería:

<div class="panel panel--default">
    <div class="panel__heading">
        <h4 class="panel__title"></h4>
    </div>
    <div class="panel__body"></div>
    <div class="panel__footer"></div>
</div>
/* Bloque */
/* Hay gente que prefiere utilizar notación PascalCase, como: .Panel {} */
.panel {}

/* Modificadores */
.panel--default {}
.panel--primary {}
.panel--danger {}
...

/* Sub-elementos */
.panel__heading {}
.panel__title {}
.panel__body {}
.panel__footer {}

Yo personalmente uso una sintaxis en la que el elemento tiene el nombre en minúscula, el modificador se indica con un doble guión () y el componente con una doble barra baja (__), no obstante hay una gran cantidad de variantes, y puedes elegir la que más se adapte a tu gusto.

Lo que parece un pequeño cambio en la sintaxis ofrece una gran cantidad de ventajas, sobre todo en proyectos grandes:

  • Encapsulamiento total: Sabes que cualquier clase que comience con panel va a ser usada en dicho componente.
  • Diferenciación entre sub componentes y modificadores: En cuanto veas la clase panel–danger sabes que va a ser aplicada al mismo componente, mientras que una clase que contenga el delimitador de subelemento (__) sabes que siempre va a ser usada en un elemento interno a ese componente.

Experimental: Attribute Modules

Recientemente ha surgido una iniciativa bastante curiosa por parte de Glen Maddern y Ben Schwarz: AMCSS.

La idea es simple: Definir los componentes mediante atributos y los modificadores usando el selector [attr~=”valor”] (que aplicaría la regla si el atributo attr contuviera en una lista separada por espacios el token valor).

Como las cosas se ven más fáciles con un ejemplo:

<div am-Panel="default">
    <div am-Panel-Heading>
        <h4 am-Panel-Title></h4>
    </div>
    <div am-Panel-Body></div>
    <div am-Panel-Footer></div>
</div>
/* Bloque */
[am-Panel] {}

/* Modificadores */
[am-Panel~="default"] {}
[am-Panel~="primary"] {}
[am-Panel~="danger"] {}
...

/* Sub-elementos */
[am-Panel-Heading] {}
[am-Panel-Title] {}
[am-Panel-Body] {}
[am-Panel-Footer] {}

Opinión personal / conclusión

Si queréis un resumen: Usad BEM. Solventa los problemas de especifidad, y la diferenciación de componentes y atributos, reduciendo al selector más intuitivo y simple: la clase.

El sistema tradicional no funciona para proyectos a gran escala. Siempre vas a tener algún problema de especifidad.

La sintaxis de bootstrap es simple, y se puede argumentar a su favor que es muy legible. No obstante… no aporta esa diferenciación entre elementos y modificadores que a mi jucio es imprescindible.

BEM, como he dicho, solventa los problemas a la hora de organizar los componentes. Si lo mezclas con la magia de los preprocesadores es posible una organización muy sólida del proyecto, y la creación de componentes muy personalizables e independientes.

AMCSS, sin embargo (y es una opinión personal), no creo que triunfe. Ofrece las mismas ventajas que BEM en el sentido de encapsulamiento, y es mucho más legible del lado del HTML. Sin embargo, me parece que la complejidad extra que añade al css es demasiada, comparado con las ganancias en legibilidad del HTML.

Leer más: