Arquitectura CSS: problemas

  • Por
Proponemos contemplar CSS como si fuera un lenguaje de programación, analizando algunas de las inofensivas costumbres, que resultan no serlo tanto.

Para muchos desarrolladores web, ser bueno en CSS significa que puedes echar un vistazo visual a un contenido y reproducirlo perfectamente en código. No usas tablas, y te enorgulleces y usas cuantas más imágenes mejor. Si eres realmente bueno, usas las más avanzadas técnicas como “media queries”, transiciones y transformaciones. Es cierto que todo ésto es usado por los buenos desarrolladores CSS, pero hay una parte completamente separada del CSS que raramente es mencionada cuando se trata de calificar las habilidades de uno mismo.

Normalmente no nos descuidamos de esa manera con otros lenguajes. Un desarrollador de Rails (RubyOnRails) no es considerado bueno solo porque su código respete la especificación. Es lo mínimo. Por supuesto que debe funcionar respetando la especificación; la pregunta es: ¿es el código legible? ¿Es fácil de cambiar o extender? ¿Es una parte desacoplada de otras partes de la aplicación? ¿Será escalable? Estas preguntas surgen de forma natural cuando estamos observando la base del código, y con CSS no debería ser diferente. Hoy en día las aplicaciones web son más grandes que nunca, y un pobre pensamiento sobre lo que significa la arquitectura CSS puede lisiar el desarrollo de un proyecto. Es hora de evaluar CSS de la misma forma con que evaluamos las otras partes de la aplicación. No puede ser algo añadido en el último momento o resuelto como meramente un problema del “diseñador”.

Las Metas de la Arquitectura CSS

En la comunidad CSS es muy difícil lograr un consenso general sobre las mejores prácticas a seguir. Juzgando objetivamente los comentarios en Hacker News con el lanzamiento de CSS Lint queda demostrado que mucha gente está en desacuerdo incluso en los aspectos más básicos de CSS.

Así que, en vez de mostrarte un argumento propio sobre las mejores prácticas, creo que deberíamos empezar por definir nuestras metas. Si podemos ponernos de acuerdo en algunas metas, podemos empezar a desechar algunos conceptos de CSS, no porque rompan nuestras preconcebidas nociones sobre lo que es bueno, sino porque obstaculizan nuestro proceso de desarrollo.

Creo que las metas de la buena arquitectura CSS no deberían ser diferentes de todas las buenas metas del desarrollo de software. Quiero que mi CSS sea predecible, reutilizable, estable y escalable.

Predecible

CSS predecible significa que tus reglas se comportan como tú esperarías. Cuando añades o actualizas una regla, no debería afectar a las partes de tu sitio web en las que no hay intención de que afectara. En los sitios pequeños eso raramente ocurre, no es importante, pero en los sitios grandes con decenas o centenares de páginas, el que el CSS sea predecible es una obligación.

Reutilizable

Las reglas CSS deberían ser abstractas y estar suficientemente desacopladas a la hora de construir rápidamente nuevos componentes en partes ya establecidas sin tener que recodificar configuraciones sobre problemas que ya has solventado.

Estable

Cuando nuevos componentes y capacidades necesitan ser añadidas, actualizadas o reiniciadas en tu sitio, hacer eso no debería requerir modificar demasiado el CSS existente. Añadir un componente X a la página no debería, por su mera presencia, romper el componente Y.

Escalable

Cuando el sitio crece y se vuelve más complejo normalmente requiere mayor mantenimiento por parte de los desarrolladores. CSS escalable significa que puede ser fácilmente administrado por una persona o por un equipo de personas. También significa que la arquitectura CSS de tu sitio es fácilmente accesible sin requerir una enorme curva de aprendizaje. Solo porque seas el único desarrollador que toque el CSS hoy no significa que siempre vaya a ser así.

Malas Prácticas Comunes

Antes de que echemos un vistazo a las maneras de alcanzar las metas de una buena arquitectura CSS en otro artículo especialmente dedicado a ello, pienso que puede ser útil ojear las prácticas comunes que están en medio del camino. Con frecuencia, solo a través de repetidos errores comenzamos a abrazar una ruta alternativa.

Los siguientes ejemplos son todos generalizaciones de código que he escrito, y, aunque el código es técnicamente válido, cada uno de esos trozos de código me ha llevado al desastre o, al menos, a un intenso dolor de cabeza. A pesar de mis mejores intenciones y de la promesa de que esa vez sería diferente, esos “snippets” me metieron en problemas.

Modificando los Componentes Basándonos en Quiénes son Sus Padres

Cuando nos enfrentamos con esta situación todos los nuevos desarrolladores CSS (e incluso algunos experimentados) lo atendemos de la misma manera. Te imaginas un único elemento padre y creas una nueva regla para controlarlo.

.widget {
background: yellow;
border: 1px solid black;
color: black;
width: 50%;
}

#sidebar .widget {
width: 200px;
}

body.homepage .widget {
background: white;
}

Al principio podría parecer un código medianamente inofensivo, pero vamos a examinarlo basándonos en las metas que establecimos antes.

Primero, el widget del ejemplo no es predecible. Un desarrollador que haya hecho muchos de estos widgets esperará que tengan un cierto aspecto, esté colocado en el sidebar o en la página principal. Con el código actual, el aspecto del widget variará y no será siempre exactamente el mismo.

Tampoco es muy reutilizable o escalable. ¿Qué ocurre cuando deseemos que el aspecto que tiene en la homepage lo tenga también en otra página? Habrá que añadir más reglas.

Por último, no es demasiado estable porque si el widget es rediseñado habría que estar actualizándolo en muchos sitios del CSS, y como dijimos antes, es difícil que esta rara configuración que de momento funciona le parezca acertada al próximo que la mire.

Imagina que este tipo de código fuera hecho en cualquier otro lenguaje. Esencialmente estás haciendo una definición de clase, y en otra parte del código estás alcanzando esa definición de clase cambiándola por un particular uso local. Esto directamente viola el principio abierto/cerrado de desarrollo software:

Las entidades Software (clases, módulos, funciones, etc.) deberían ser abiertas para extensión, pero cerradas para modificación.

Selectores Demasiado Complicados

Ocasionalmente un artículo se mueve por Internet mostrando el poder de los selectores CSS y proclamando que puedes estilizar un sitio por completo sin usar clases ni IDs.

Aunque es técnicamente cierto, cuanto más desarrollo con CSS, más me alejo de los selectores complejos. Cuanto más se complica un selector, más se acopla al HTML. Aunque mantengas limpio y claro el código HTML, los selectores complejos ensucian tu CSS.

#main-nav ul li ul li div { }
#content article h1:first-child { }
#sidebar > div > h3 + p { }

Estos ejemplos que vemos tienen sentido lógico. El primero probablemente está dando estilo a un menú 'dropdown', el segundo indica que el contenido de cabecera principal del artículo tendría que mostrarse de forma distinta a la de los restantes elementos h1, y el último ejemplo está añadiendo un poco de espacio extra para el primer párrafo en las secciones del sidebar.

Si este HTML no fuera nunca a cambiar, se podrían argumentar sus méritos, pero...¿cómo de realista es asumir que el HTML nunca cambiará? Los selectores demasiado complicados pueden ser imponentes, pero raramente nos ayudan en nuestras metas para una buena arquitectura CSS.

Estos ejemplos no son reutilizables en absoluto. El selector está señalando a un punto muy concreto del maquetado, ¿cómo podría otro componente con distinta estructura HTML reutilizar ese estilo? Tomando el primer selector (el del menú 'dropdown') como ejemplo, ¿qué pasa si un 'drowdown' similar fuera necesitado en una página distinta que no estuviera dentro del elemento #main-nav? Tendrías que recrear el estilo por completo.

Estos selectores son también muy impredecibles si el HTML necesita cambiar. Imagina que un desarrollador quisiera pasar el div en el tercer ejemplo a una etiqueta 'section' de HTML5. La regla entera se rompería.

Finalmente, puesto que estos selectores solo trabajan cuando el HTML permanece constante, no son por definición ni escalables ni estables.

En muchas aplicaciones tienes que fabricar compensaciones y concesiones en cuanto a código. La fragilidad de los selectores complejos raramente es peor que el precio de poder decir con honestidad que tu HTML es “limpio”.

Nombres de Clase Demasiado Genéricos

Cuando creamos componentes de diseño reutilizables es muy común sumergir los sub-elementos del componente dentro del nombre de clase de este. Por ejemplo:

<div class="widget">
<h3 class="title">...</h3>
<div class="contents">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
In condimentum justo et est dapibus sit amet euismod ligula ornare.
Vivamus elementum accumsan dignissim.
<button class="action">Click Me!</button>
</div>
</div>
.widget {}
.widget .title {}
.widget .contents {}
.widget .action {}

La idea es que los sub-elementos de clase .title, .contents, y .action puedan ser estilizados sin tener que preocuparse sobre cómo puedan desparramarse sobre otros elementos estilizados con las mismas clases. Eso es cierto, pero no previene contra el estilizado de clases con aquellos mismos nombres que se desparramen hacia el propio componente.

En un gran proyecto es muy probable que un nombre de clase como .title sea usado en otro contexto. Si eso ocurriera, el título del widget parecería de pronto muy difícil de relacionar concretamente con el estilo que se pretendía.

Los nombres de clase demasiado genéricos conducen a un CSS muy impredecible.

Hacer que una Regla Haga Demasiado

A veces creas un contenido que necesita estar a 20 píxeles de distancia del límite superior y de la izquierda partiendo de la esquina de su correspondiente sección:

.widget {
position: absolute;
top: 20px;
left: 20px;
background-color: red;
font-size: 1.5em;
text-transform: uppercase;
}

Después necesitas usar exactamente ese mismo contenido para ponerlo en un lugar diferente. El CSS de arriba no funciona porque no es reutilizable en diferentes contextos.

El problema está en que le estás pidiendo demasiado a tu selector. Estás definiendo el diseño así como la posición en la misma regla. El diseño es reutilizable pero la posición no. Y puesto que se usan juntas la regla entera está comprometida.

Aunque esto pueda parecer inofensivo al comienzo, con frecuencia conduce al 'copy/paste' por parte de los desarrolladores CSS menos eruditos. Si un nuevo equipo de miembros quiere poner un nuevo contenido como por ejemplo un .infobox, probablemente comiencen intentando usar esa clase. Pero si no funciona porque la posición de ese nuevo infobox es errónea, ¿qué es lo más probable que hagan? Mi experiencia me dice que la mayoría de nuevos desarrolladores no romperán la regla en reglas reutilizables. En vez de eso ello simplemente copiarán y pegarán las líneas de código necesarias para su particular nuevo selector.

La Causa

Todas estas malas prácticas comparten una similitud: no son una carga para el CSS.

Suena raro, ¿eh? Después de todo...¿no sería lo ideal que tuviéramos una hoja de estilos con poca carga? ¿No es lo que queremos?

La respuesta sencilla a esta pregunta es “sí”, pero, como es habitual, las cosas no son tan sencillas. Separar el contenido de la presentación es una buena idea, pero solo porque tu CSS esté separado de tu HTML no quiere decir que tu contenido deba estar separado de tu presentación. Dicho de otra manera, poner barreras al código HTML no ayuda a cumplir la meta si tu CSS requiere un íntimo conocimiento de tu estructura HTML a la hora de trabajar.

Además, HTML es raramente solo contenido; es casi siempre estructura también. Y con frecuencia esa estructura consiste en elementos contenedores sin más propósito que permitir al CSS aislar un cierto grupo de elementos. Incluso sin clases cuyo propósito único sea el 'embellecimiento' es difícil separar la presentación estética que subyace en el propio código. ¿Es necesario mezclar por lo tanto la presentación estética con el contenido?

Yo creo que dado el actual estado del HTML y del CSS es necesario y razonable hacer que el HTML y el CSS trabajen juntos en una única capa. La capa de contenido puede ser abstraída vía plantilla o algo parecido.

La Solución

Si tu HTML y tu CSS van a trabajar juntos para formar la capa de presentación de una aplicación web, necesitan hacerlo de forma que promueva todos los principios de una buena arquitectura CSS.

La mejor aproximación que he encontrado es que para cada CSS, su HTML sea tan pequeño como sea posible. El CSS debería definir cómo un conjunto de elementos visuales deben mostrarse y (en orden de minimizar el acoplamiento con el HTML) esos elementos deberían aparecer tal y como son definidos aparezcan donde aparezcan en el HTML. Si un cierto contenido necesita ser mostrado diferente en un diferente escenario, debería ser nombrado de manera diferente y es la responsabilidad del HTML llamarlo así.

Como ejemplo, el CSS podría definir un componente botón vía clase .button . Si el HTML quiere que un elemento particular se asemeje a un botón, debería usar esa clase. Si hay una situación en la que el botón necesita mostrarse diferente (quizá más grande, con anchura al 100%), entonces el CSS necesita definir ese aspecto en una nueva clase, y el HTML puede incluir esa nueva clase para usar el nuevo aspecto.

El CSS define cómo se muestran tus componentes, y el HTML les asigna el aspecto visual. Cuanto menos necesite saber el CSS sobre la estructura HTML, mejor.

Un enorme beneficio de declarar exactamente lo que quieres en el HTML es permitir a otros desarrolladores echar un vistazo a la maquetación y saber exactamente qué elemento están buscando. La idea es intentarlo. Sin esta práctica es imposible saber si el aspecto de un elemento es intencional o accidental, y eso conlleva confusión para el equipo.

Una queja común al poner un montón de clases en el maquetado es el esfuerzo extra que requiere hacerlo. Una regla CSS simple podría dirigir miles de instancias de un particular componente. ¿Es realmente peor escribir esas clases miles de veces sólo para tenerlas explícitamente declaradas en el maquetado?

Aunque la idea está clara, puede despistar. Para usar un selector parental en CSS no tienes que preocuparte sobre las mil clases que imagino que ahora piensas que tendrías que escribir a mano, hay obviamente otras alternativas. Viendo los niveles de abstracción en "Rails" o en otros frameworks nos hace fijarnos en cómo podríamos declarar explícitamente el HTML sin tener que escribir las mismas clases una y otra vez.

Pero eso lo veremos ya en el siguiente artículo, en el que plantearemos la arquitectura CSS del lado de las soluciones.

Philip Walton

Autor

OldMith

Desarrollador Web. jQuery. Responsive Design. Wordpress. Friki por naturaleza.

Compartir

Comentarios

Jose A. M

24/9/2014
Gracias OldMith
Gracias OldMith por este revelador y didáctico artículo, realmente me ha gustado y espero ponerlo en práctica en mis futuros trabajos para mejor mis conocimientos de CSS

Shiver

11/4/2015
muy bueno
xzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz