¿Recuerdas cuando trabajar con CSS era añadir un <style>/* */ </style> a tu index.html justo encima del <script>/* */ </script> ?
Por suerte esos días han quedado muy atrás ¿No? todo lo relativo a las hojas de estilo ha evolucionado mucho, es algo que siempre pasa con las cosas de frontend que nos encanta cambiarlas, actualizarlas y/o complicarlas. A veces hasta se nos va un poco de las manos y creamos CSS funcional.
@CKGrafico Functional CSS https://t.co/aWyFgpRW5G
— Alex Casquete (@acasquete) March 22, 2016
Entonces llega un cliente y nos pide un nuevo proyecto ¿Qué hacemos ahora? Planear una mínima estructura y unas convenciones básicas, en las siguientes líneas voy a intentar mostrar un poco cómo me gusta hacerlo y cómo creo que debe hacerse.
Algo que lógicamente no ha salido solo de mí, es gracias horas de discusión y cerveza (o té) con Borja, Edu, Carlos, Carrillo o Álex entre otros.
Por supuesto, no tratamos de reinventar la rueda, nos basamos en convenciones existentes, en este caso BEM y en convenciones de otras personas y lógicamente en nuestras pequeñas aportaciones.
BEM – Block Element Modifier
Es una de las convenciones que más me han gustado últimamente, lo hemos podido poner en práctica en muchos proyectos reales (con algunas modificaciones) y los resultados son excelentes, a medida que vas trabajando empiezas a ver problemas o cosas que necesitas cambiar para adaptarlo a tus necesidades, pero es genial.
Se basa en una estructura muy sencilla, tenemos los bloques que dentro tienen elementos y estos elementos pueden tener modificadores.
/* Author block */
.author { ... }
/* Image element */
.author__image { ... }
/* Rounded image */
.author__image--rounded { ... }
En HTML tendríamos algo de este estilo
<div class="author">
<img class="author__image author__image–rounded" alt="" />
</div>
Lo interesante de esto es que .author se ha vuelto un ‘componente’ bastante reutilizable y fácil de cambiar.
Cuando empiezas a trabajar con diferentes librerías que utilizan esta convención podría llegar a darse que hay repeticiones, para ello se usan los módulos, un prefijo que pondremos al principio.
/* Author block */
.ck-author { … }
/* Image element */
.ck-author__image { … }
/* Rounded image */
.ck-author__image–rounded { … }
Si trabajas con SASS o cualquier otro, el aspecto mejora un poco.
.ck {
/* Author block */
&-author {
…
/* Image element */
&__image {
…
/* Rounded image */
&–rounded {
…
}
}
}
}
Algunas consideraciones sobre BEM
Al trabajr con BEM puedes elegir dos caminos, seguir la convención de una manera totalmente estricta o basarte en ella para adaptarla a tus necesidades e ideas para el proyecto o el equipo.
Considero mejor el segundo, pero sin que se nos vaya de las manos y sabiendo adaptarlo a cada momento. Si trabajas en producto es muy probable que acabes buscando una manera que os guste y poco a poco vaya evolucionando. Nosotros en consultoría llegamos a la conclusión que lo mejor es ver en cada proyecto y cada equipo las convenciones que nos parecen correctas, que suelen ser las mismas o parecidas.
Estas son algunas que me gustan:
- Cambiar ‘__’ por ‘-‘ en bloque-elemento: Me parece mucho más legible y cómodo de utilizar.
/* Author block */
.ck-author { … }
/* Image element */
.ck-author-image { … }
/* Rounded image */
.ck-author-image–rounded { … }
- Utilizar ‘_’ para los espacios: En vez de rompernos la cabeza 15 minutos para poder escribir algo como ‘my page’ en una sola palabra, los ‘_’ existen ‘my_page’.
/* No */
.ck-authorBox { … }
/* Yes */
.ck-author_box { … }
- No utilizar nietos: Es decir todos los elementos dentro de un bloque son hijos del padre, independientemente de si están dentro de otro hijo.
<!– No –>
<div class="ck-author">
<div class="ck-author-info">
<span class="ck-author-info-email"></span>
</div>
</div>
<!– Yes –>
<div class="ck-author">
<div class="ck-author-info">
<span class="ck-author-email"></span>
</div>
</div>
- Utilizar utilidades, sufijos responsive y estados: No todo son bloques y elementos, hay más herramientas que nos pueden ayudar.
Utilidades
A medida que vas trabajando con BEM empiezas a ver que hay ciertas cosas que hacen al componente más dependiente y menos reutilizable. Ahí es donde entran las utilidades.
Siguiendo con el ejemplo del componente ‘author’, al crearlo vamos a definir una serie de estilos que se le van a aplicar.
.ck-author {
background-color: white;
box-shadow: 0px 0px 50px -10px rgba(0, 0, 0, .4);
display: table;
width: 400px;
}
.ck-author-image {
display: table-cell;
max-width: 100px;
}
.ck-author-info {
color: #333333;
display: table-cell;
padding: 2%;
vertical-align: top;
}
.ck-author-name {
color: #0EAD87;
font-size: 1.2em;
font-weight: 300;
}
.ck-author-email {
text-decoration: underline;
}
<div class="ck-author">
<img class="ck-author-image" src="https://i.imgur.com/V2Pb5B1.png">
<div class="ck-author-info">
<span class="ck-author-name">Quique Fdez. Guerra</span>
<span class="ck-author-email">quique@mycompany.com</span>
</div>
</div>
En un mundo ideal sería suficiente, pero cuando ponemos este elemento en un contexto real siempre acaban faltando algunos detalles. Por ejemplo que situemos este componente en una columna que no tiene padding interior y que todo su texto está alineado a la derecha.
.ck-author {
…
float: left;
padding: 5px;
text-align: right;
}
Pero estos detalles no son necesarios para el propio componente, los necesito por la situación en la que se encuentra, de hecho si lo moviera a otro sitio seguramente no lo necesitaría. Además esto no va a pasar solo en este componente, es muy probable que pase con varios de los componentes que creamos. Con lo que es mucho más sencillo tener un módulo con una serie de clases de ayuda o utilidades, de tal manera que si en este caso creamos estas clases.
.u-float-left {
float: left;
}
.u-p-5 {
padding: 5px;
}
.u-text-right {
text-align: right;
}
Las usamos de este modo.
<div class="ck-author u-float-left u-text-right u-p-5″>
…
</div>
Obtendremos el mismo resultado.
Si utilizamos SASS seguramente tengamos algo así
.ck {
&-author {
background-color: $color-background-light;
box-shadow: 0px 0px 50px -10px rgba($color-background-dark, .4);
display: table;
width: 400px;
&-image {
display: table-cell;
max-width: 100px;
}
&-info {
color: $color-foreground-semidark;
display: table-cell;
padding: 2%;
vertical-align: top;
}
&-name {
color: $color-primary;
font-size: 1.2em;
font-weight: 300;
}
&-email {
text-decoration: underline;
}
}
}
// utilities.scss
.u {
&-float {
…
&-left {
float: left;
}
}
// Padding with generator like https://gist.github.com/CKGrafico/0864109a512c1cc44110
&-text {
…
&-right {
text-align: right;
}
}
}
Sufijos Responsive
Algo muy interesante que vimos en el artículo sobre BEMIT son los sufijos responsive lo que nos puede ayudar a estructurar todavía más todo nuestro css, para este caso los ejemplos están directamente en SASS.
Definimos unos tamaños con los que vamos a trabajar, me gusta bastante la manera en lo que lo hace Bootstrap.
$screen-xs: 480px;
$screen-sm: 768px;
$screen-md: 992px;
$screen-lg: 1200px;
O el estilo camisetas:
$screen-xs: 480px;
$screen-s: 768px;
$screen-m: 992px;
$screen-l: 1200px;
Ahora podemos utilizarlos de dos modos diferentes, para componentes o para utilidades.
- En el caso de componentes lo haría de la siguiente manera:
.ck {
/* Author block */
&-author {
@media screen and (min-width: $screen-lg) {
width: 700px;
}
}
}
Es decir, entiendo que son cosas propias de ese elemento y las pongo en el mismo.
- Pero si quiero cosas específicas, que son dependientes de la situación y no del propio componente:
// utilities.scss
.u {
&-hidden{
display: none;
&\@sm {
@media screen and (min-width: $screen-xs) and (max-width: $screen-sm) {
display: none;
}
}
}
}
<div class="ck-author">
<img class="ck-author-image u-hidden@sm" src="https://i.imgur.com/V2Pb5B1.png">
…
</div>
Estados
Llegado un punto puede ser que empieces a notar que las utilidades no son suficientes y que hay ciertas cosas que pueden separarse y entrar en una nueva convención, esto pasa con los estados.
Algunos ejemplos de estados podrían ser:
-
- Un componente está desabilitado .is-disabled
-
- Un componente está oculto .is-hidden
- Un componente está activo .is-active
Se puede observar en estos casos anteriores que al final esto es otro módulo que en vez de llamar u de ‘utilities’ pasa a ser is que nos ayuda a remarcar el estado.
Volviendo al ejemplo del artículo podríamos definir estilos para este componente en dos sitios diferentes, primero el estilo genérico de desabilitado que va a ser para todos los componentes, y luego algunas cosas específicas para el nuestro.
// states.scss
.is {
&-disabled {
opacity: .5;
}
}
// author.scss
.ck {
&-author {
…
&.is-disabled{
font-size: .7em;
}
}
}
Clases js-*
Estas clases nos vienen bien cuando queremos acceder mediante JavaScript a un elemento del DOM y no queremos que eso afecte a la estructura de componentes que hemos creado.
Gracias a este tipo de clases si trabajamos en proyectos en los cuales más de una persona va a tocar esos mismos componentes y la lógica que los asocia vamos a tener menos posibilidades de pisarnos entre nosotros.
Por otro lado, con la llegada de los componentes, he intentado reducir la utilización de estas clases y ponerlas solo cuando es un tercero quien necesita el selector, es decir si el código JavaScript que necesita ese selector es lógica del propio componente prefiero utilizar las clases del mismo como selector y no poner estas clases especiales, ya que esta lógica está totalmente acoplada al componente y no es independiente del mismo.
<div class="ck-author">
<img class="ck-author-image" src="https://i.imgur.com/V2Pb5B1.png">
<div class="ck-author-info">
<span class="ck-author-name js-name">Quique Fdez. Guerra</span>
<span class="ck-author-email js-email">quique@mycompany.com</span>
</div>
</div>
No lo olvides
Todo son recomendaciones, convenciones e ideas que tienen algunas personas, lo más importante siempre es hacer lo mejor para el proyecto y su equipo. Si te han gustado coge algunas ideas y piensa las tuyas para seguir mejorando e intentando mejorar los proyectos (aunque sabes de sobra que no siempre se puede, yo también 🙂 poco a poco)
Nota: Si usas librerías modernas cómo Vue o Angular es muy probable qué uses ‘Scoped CSS’, con lo cuál usar préfijos cómo los de este artículo ‘ck-‘ deja de ser realmente necesario.
También he traducido este artículo al inglés en medium.
Algunos enlaces de interés:
– https://www.leemunroe.com/css-sass-scss-bem-less
– https://hashnode.com/post/why-do-you-prefer-what-you-prefer-css-class-names-hyphenated-camelcase-or-any-other-style-cj34dtr3z00l91xk9zxoar3lr/answer/cj34gd1e1000u2xk8wnjh07ec
– http://getbem.com
– http://bradfrost.com/blog/post/atomic-web-design/
– https://www.smashingmagazine.com/2011/12/an-introduction-to-object-oriented-css-oocss/
– https://smacss.com/