5 prácticas cuestionables usando CSS y/o preprocesadores
0 comentarios
Llevo algunos años usando CSS y unos pocos menos usando preprocesadores para la escritura de CSS. Incluso he cometido el sacrilegio de escribir CSS con JavaScript usando styled-components. Después de esta frase de abuelo cebolleta, debo reconocer que, como todos, en el camino he cometido infinidad de errores que ni preprocesadores como Less o styled-components han evitado. ¿He aprendido algunas cosas de estos errores, malas prácticas o prácticas cuestionables? La respuesta es sí, y las razones son básicamente dos:
- Leer sobre arquitecturas CSS y aplicarlas, aunque no sea en su totalidad.
- Haber trabajado en equipo con maquetadores web o front-end developers (de los que disfrutan, también, con HTML y CSS).
Ninguna de las razones tiene que ver con tecnologías como styled-components o SASS. Más bien es un regreso a las cavernas de CSS.
¿Es recomendable adoptar una arquitectura CSS actualmente?
Pues en un proyecto grande habría que decir que "seguro". En proyectos pequeños hay quien piensa que no es necesario. A mi parecer, los proyectos pequeños es un buen lugar para comenzar a usar estas arquitecturas si nunca has usado una e, inconscientemente, adquirir un flujo de trabajo natural con estas arquitecturas, aunque no sea en su totalidad.
¿Cuáles son esas arquitecturas de las que hablas?
Pienso que las más importantes a lo largo de los años han sido:
- Object Oriented CSS, conocida por OOCSS.
- Scalable and Modular for CSS, conocida por SMACSS.
- Block Element Modifier, conocida por BEM.
- Inverted Triangle CSS, conocida por ITCSS.
De éstas se ha escrito muchísimo, tanto en inglés como en español. Solo hay que darse una vuelta por Google y comprobarlo.
Entonces ¿cuáles son esas prácticas cuestionables?
Vamos a ver las que considero más relevantes.
1.- Sobreespecificidad CSS
Tengo que decir que ésta la considero mi archienemigo desde hace 7 u 8 años. Además, con la popularización de los preprocesadores fue a peor. To quisqui empezó a anidar todos los elementos dentro de otros elementos sin saltarse uno. Así he visto que la hoja de estilos de un proyecto pequeño sea de un tamaño de ¡¡500 kb!!. Pues menos mal que era un proyecto pequeño ¿no?.
Para ello veamos la definición de Especificidad.
Es la manera mediante la cual los nagevadores deciden qué valores de una propiedad CSS son más relevantes para un elemento y, por lo tanto, serán aplicados. La especificidad está basada en las reglas de coincidencia que están compuestas por diferentes tipos de selectores CSS
¿A qué llamo sobreespecificidad CSS? Pues mejor ver un ejemplo ¿no? Lo voy a llamar De verdad, no había necesidad.
.wrapper.exits .item.case .box .title_box h3 span,
.wrapper.texts .item.case .box .title_box h3 span,
.wrapper.password .item.case .box .title_box h3 span,
.wrapper.other .item.case .box .title_box h3 span,
.wrapper.contact .item.case .box .title_box h3 span,
.wrapper.more .item.case .box .title_box h3 span,
.wrapper.faqs .item.case .box .title_box h3 span,
.wrapper.titles .item.case .box .title_box h3 span,
.wrapper.others .item.case .box .title_box h3 span {
font-family: "Arial";
}
Me voy a arriesgar, pero estoy casi seguro que éste es un ejemplo que se ha generado con un preprocesador, donde se ha anidado hasta la saciedad y se ha utilizado @extend de forma indiscriminada, sin tener en cuenta cual sería el resultado final en la hoja de estilos.
Creo que las personas que nos dedicamos a escribir CSS debemos tener muy presente, sobretodo cuando usamos preprocesadores, cuál será el resultado en la hoja de estilos final y el tamaño de ésta. De esta manera podremos alardear de tener un CSS optimizado. Cosa que te agradecerán los usuarios y Google por entregar ficheros CSS con un tamaño menor.
Una solución sin demasiado coste para el caso anterior sería aislar en el fichero correspondiente los tres últimos elementos de selección de cada linea para crear un componente, añadiendo tanto al tag h3 y span un class. Partiendo de un HTML tal que así:
<div class="title_box">
<h3 class="title_box-title">
<span class="title_box-text">El texto en negrita</span>
</h3>
</div>
Veamoslo con un ejemplo con un fichero SCSS:
// module or element title_img_box
.title_box {
// h3
&-title {}
// span
&-text {
font-family: "Arial";
}
}
El resultado sería el siguiente:
.title_box-text {
font-family "Arial";
}
Hemos pasado de once líneas, con un número elevado de caracteres para un elemento, a tres. ¡Imaginad la de bytes que podemos ahorrar en el CSS si la situación anterior se repite constantemente en cada elemento!
También ahorramos complejidad si quisieramos modificar el estilo de ese elemento en diferentes contextos.
Por último, aunque no tengo delante el fichero fuente (SASS, scss, Less, etc) donde está el código, me atrevo a decir que será más sencillo leer para cualquier mortal, incluso para la persona que lo escribió pasadas unas semanas.
Resumiendo, será facilmente mantenible, modicable, entendible y extensible.
Para conocer más aspectos sobre la especificidad y cómo calcularla recomiendo leer Especificidad CSS.
2.- Desconocer los valores implícitos de propiedades CSS y tags HTML
Por defecto, cada tag HTML tiene unos estilos implícitos. Esto quiere decir que si usas un elemento div y le indicas un display: block estás siendo redundante, ya que el elemento div implícitamente es de tipo bloque. No está demás conocer las propiedades implícitas de los elementos HTML más comunes, sobretodo si son elementos en bloque o en línea. Podemos comprobarlo en:
Comúnmente también ocurre que indicamos explicitamente algunas propiedades CSS que no son necesarias. Os pongo un ejemplo:
.title {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
¿Veis algo raro? ¿no? En el caso anterior, al aplicar display: flex explícitamente, no es necesario indicar las dos propiedades siguientes con sus valores. Tanto la propiedades como los valores se aplican de forma implícita, es decir, por defecto, al aplicar la primera regla. Evidentemente, otro tema es que queramos modificar los valores por defecto por otros que sean necesarios para la regla CSS.
Esto es bastante habitual encontrarlo, incluso entre desarrolladores con experiencia contrastada. No quiero mirar a nadie ya que es algo a lo que yo mismo comencé a prestarle atención no hace tanto.
Con esto tampoco nos vamos a hinchar de ahorrar tamaño en el CSS como pasaba con el punto anterior, aunque todo suma. En todo caso, es redundante el añadir este tipo de propiedades explícitamente sin necesidad, tanto como añadir un display: block a un elemento div.
3.- Media queries: Puntos de ruptura con límites descendentes
Pienso que aquí entran en juego diferentes elementos del flujo de trabajo. Cada uno con su grado de responsabilidad. Personalmente pienso que, el que mayor grado tiene es el cliente o jefe de proyecto que primero solicita ver una web o app en tamaño desktop. Esto puede ser por desconocimiento o por considerarlo habitual. Sin entrar a valorar mucho más este asunto, voy a hacer una lista de deseos.
En un mundo perfecto el diseñador nos entregaría primero el diseño para smartphone, tablet y por último pantallas grandes. Bien, este sueño raramente se cumple, así que no queda más que insistir en cambiar el flujo de trabajo sin ser un drama queen.
Partiendo desde los postulados anteriores, es común el uso de media queries con el atributo max-width, ya que generalmente comenzaremos a aplicar estilos para el escritorio. Esto entra en conflicto con la filosofía mobile-first.
Pienso que han corrido rios de caracteres desde hace años sobre este tema. No quiero entrar en detalle sobre lo que significa mobile-first. Lo que si es cierto, el cambio anunciado hace tiempo e implementado por Google hace poco provaca que este palabrejo haya tomado otra dimensión por motivos obvios: el omnipresente SEO.
¿Qué sería lo ideal? Desde hace años se ha comentado en infinidad de blogs y foros sobre CSS que una buena práctica es aplicar puntos de ruptura ascendentes en media queries. Para ello se usa la propiedad min-width en lugar de max-with:
/** Examples **/
@media (min-width: 50em) {}
@media all and (min-width: 50em) {}
@media only screen and (min-width: 50em) {}
¿La razón? Principalmente, la limitación del tamaño de la pantalla de móviles o smartphones hace que, en líneas generales, el contenido no cambie su situación física, mostrandose de forma secuencial a su ubicación en el HTML. ¿Ocurre siempre? Evidentemente no, siempre habrá excepciones, pero son eso ¡excepciones!.
Como comentaba anteriormente, el primer diseño que se aprueba y nos entregan es en desktop (y rezaremos para que nos entreguen una versión móvil) y seguro que una vez ese diseño está aprovado por tu jefe / cliente / coordinador / [añade otro superior tuyo aquí] te dirá - ¡ya puedes comenzar a crear una maqueta HTML y CSS! - Y eso haremos.
Esto no debería de ser un impedimento para que más adelante, cuando nos entreguen la versión smartphone y/o tablet (o tengamos que inventárnosla) de la web o app en pantallas pequeñas, mover los estilos correspondientes respectivamente a los puntos de ruptura que hayamos definido para este fin.
Aunque no debería juzgar únicamente por mis experiencias, debo decir que, en la totalidad de proyectos que he participado, este sistema me ha ahorrado bastantes líneas de código CSS. Generalmente, para la versión de pantallas pequeñas, se aplican menos número de estilos por las razones explicadas más arriba. Justamente lo contrario a lo que ocurre cuando aplicamos puntos de ruptura descendentes, salvo para casos excepcionales.
Como es 2018, y pienso que esto está muy manido, dejo un gran artículo (¡2013!) sobre este tema, escrito por Brad Frost y que lo explica mejor que yo: 7 Habits of Highly Effective Media Queries
4.- Exceso de declaraciones de media queries
Otro efecto colateral de las facilidades que dan los preprocesadores. Para ilustrarlo un ejemplo que podrías implementar en un fichero a procesar con Less o SASS:
.modulo {
color: blue;
p {
text-aling: left;
@media all and (min-width: 520px) {
text-aling: center;
}
span {
font-weigth: bold;
@media all and (min-width: 520px) {
font-weight: bolder;
}
}
}
img {
border: 2px solid yellow;
@media all and (min-width: 520px) {
border-width: 5px;
}
}
@media all and (min-width: 520px) {
color: skyblue;
}
}
Creo que tampoco había necesidad de esto, pero me he encontrado este tipo de declaraciones más a menudo de lo que esperaba. Con alguién que esté empezando pues nada que decir, pero con alguién que tiene muchisima experiencia pues ya me parece algo más raro.
Ese tipo de declaración de media queries para cada elemento, puede parecer muy potente, pero al igual que en el punto de sobreespecificación, esto también aumenta el tamaño de la hoja de estilos resultante de forma incontrolada.
El modo recomendado para aplicar algo de optimización en nuestro SASS o Less al resultado de nuestro CSS sería algo así:
.modulo {
color: blue;
p {
text-align: left;
span {
font-weigth: bold;
}
}
img {
border: 2px solid yellow;
}
@media all and (min-width: 520px) {
color: skyblue;
p {
text-align: center;
span {
font-weight: bolder;
}
}
img {
border-width: 5px;
}
}
}
A priori, entre los ficheros fuentes no parece haber mucha diferencia. Pero me voy a repetir: No hay que perder de vista cual será el resultado en nuestro fichero compilado. Esta forma de hacerlo reducirá algún que otro kilobyte de nuestro CSS, que es el que usará finalmente la web.
En mi mundo de arcoiris y piruletas, lo mejor sería incluir todos los selectores que necesiten ser modificados para los distintos tamaños dentro de tres o cuatro media queries definidas. El problema de esta forma de trabajar, si usamos un preprocesador y construimos modulos con ficheros scss o less que serán procesados, perdemos el contexto del comportamiento de cada uno de ellos. Esta forma es más optima para nuestra hoja de estilos final, ya que tendríamos muy pocas líneas que abrieran un breakpoint, pero aumentaría la complejidad para los desarrolladores. Aquí se impone un criterio de equilibrio entre la optimización y el mantenimiento.
Ha habido varios intentos de que un postprocesador una los puntos de ruptura idénticos. El problema que encontré, es que la mayoría falla al ordenar las declaraciones de reglas CSS, con lo que me encontré con aplicación de reglas CSS incorrectas. No hay que olvidar que CSS es declarativo y el orden de las declaraciones !important. (Festival del humor).
5.- Ausencia de comentarios o comentarios inservibles
En este punto tengo poco que añadir al titular... ¡Por favor, añadid mejores comentarios! De los que dan información importante. Si que me gustaría compartir algo que aprendí de mi compañera y amiga Enma. Ella ¡creaba un índice en los ficheros fuentes para el scss! Me pareció una idea fantástica para archivos SASS, scss o less de gran tamaño. Un ejemplo:
/**
Module overview
---------------
0.- module
1.- properties
2.- states
3.- elements
4.- mediaqueries
**/
// 0.- module
.overview {
// 1.- properties
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
background-color: fade(@white, 97%);
box-sizing: border-box;
padding: 1rem;
overflow: hidden;
overflow-y: auto;
transform: translateY(200%);
opacity: 0;
z-index: -1;
transition: transform .4s ease .2s,
opacity .4s ease 0s;
display: flex;
align-items: center;
justify-content: center;
flex-flow: row wrap;
// 2.- states
&.open {
transform: translateY(0%);
opacity: 1;
z-index: 100;
transition: transform .4s ease 0s,
opacity .4s ease .2s;
}
&.not-animated {
transition: opacity .2s ease 0s;
}
// 3.- elements
.button-close {
position: absolute;
top: 0;
right: 0;
margin: 0.625rem 0.625rem 0 0;
border: 0;
padding: 0.625rem;
&:hover,
&:focus {
background: none;
}
}
.icon-close {
fill: @grey;
}
// 4.- mediaqueries
@media only screen and (min-width: @medium-screen) {
.button-close {
margin: 2rem 2rem 0 0;
}
}
}
Una forma sencilla e inteligente de indizar los distintos puntos que forman un componente o módulo. Facilitaba, y mucho, buscar algo sobre ficheros enormes.
En este caso diréis - Ya, muy bien, pero esto incrementa el número de líneas de nuestro fichero CSS - Al compilar el CSS solo tienes que indicar que elimine los comentarios. De esta forma se resuelve el problema.
Conclusión
Esto no son normas, son recomendaciones que personalmente me han sido útiles y que aplican muy bien con conceptos como mobile-first, responsive development o WPO. Antes de tomar esto como normas es mucho mejor aplicar el sentido común y en algunas situaciones quizá ninguna de estos trucos sea aplicable.