Arquitectura CSS: Soluciones

  • Por
Después de ver en el artículo “Arquitectura CSS – Problemas” los problemas de código y las consecuencias que podemos tener por tenerlos, es hora de darte algunos consejos para mejorar.

Aunque no es amplia, mi experiencia me ha enseñado que apegarse a estos principios te ayudará a lograr tus metas en una buena arquitectura CSS.

Sé intencional

La mejor manera de asegurarte de que tus selectores no estilizan elementos que no quieres estilizar es no darles la oportunidad. Un selector como #main-nav ul li ul li div podría muy fácilmente aplicarse a otros elementos con un par de cambios. Un estilo como .subnav, por otro lado, tendrá la garantía de que no va a haber oportunidad de accidente aplicándose a un elemento indeseado. Aplicando clases directamente a los elementos que quieres estilizar es la mejor manera de mantener tu CSS predecible.

/* Granada */
#main-nav ul li ul { }

/* Rifle de Francotirador */
.subnav { }

Dados esos dos ejemplos, piensa que el primero es como una granada y el segundo como un rifle de francotirador. La granada podría hacer el trabajo bien, pero nunca sabes cuándo un civil inocente podría introducirse en el radio de la explosión.

Separa tus asuntos

Ya he mencionado que una zona de contenidos bien organizada puede ayudar a aflojar el acoplamiento de la estructura HTML con el CSS. Por añadidura, tus componentes CSS deberían ser modulares por ellos mismos. Los componentes deberían saber cómo estilizar por ellos mismos y cómo hacer su trabajo bien, pero no deberían ser responsables de su distribución o posicionamiento, como tampoco deberían asumir cómo debieran ser colocados en relación con los elementos circundantes.

En general, los componentes CSS deberían definir el aspecto visual, no su distribución en la página, ni su posicionamiento. Ten cuidado cuando veas propiedades como "background", "color", y "font" junto a "position", "width", "height", y "margin".

La distribución y la posición deberían ir de la mano en una clase en un elemento contenedor separado (recuerda que separar efectivamente el contenido de la presentación con frecuencia es esencialmente separar el contenido de su contenedor).

Personaliza el nombre de tus clases

Ya habíamos examinado por qué los selectores parentales no son 100% efectivos en la encapsulación y previniendo contaminación a través de los estilos. Una actitud mucho mejor es aplicar nombres de clases específicos a cada clase. Si un elemento pertenece a un componente visual, cada una de sus clases de sub-elementos debería usar como base para el nombre la base de nombre de clase del componente superior. Ejemplo:

/* Alto riesgo de contaminación a través del estilo */
.widget { }
.widget .title { }

/* Bajo riesgo de contaminación a través del estilo */
.widget { }
.widget-title { }

Especificar el nombre de clase hace que tus componentes se mantengan autocontenidos y modulares. Minimiza la probabilidad de que una clase ya existente entre en conflicto con la actual, y reduce la especificidad requerida al estilizar los elementos hijo.

Extender componentes con clases modificadas

Cuando un componente ya existente necesita mostrarse visualmente ligeramente diferente en un cierto contexto, lo mejor es crear una clase modificada para extenderla:

/* Mal */
.widget { }
#sidebar .widget { }

/* Bien */
.widget { }
.widget-sidebar { }

Ya hemos visto los inconvenientes de modificar componentes basados en uno de sus elementos parentales, pero reitero: Una clase modificada puede ser usada donde sea. Modificar dependiendo de la localización hará que solo pueda usarse en ese sitio concreto. Las clases modificadas pueden ser reutilizadas todas las veces que necesites. Por último, las clases modificadas expresan la intención del desarrollador de ser claro en cuanto al código HTML. Las clases basadas en la localización, por otro lado, son completamente invisibles para el desarrollador que solo mira el HTML, incrementando mucho la probabilidad de que lo pase por alto.

Organiza tu CSS en una estructura lógica

Jonathan Snook, en su excelente libro SMACSS, argumenta en favor de organizar tus reglas CSS en cuatro categorías separadas: base, distribución, módulos, y estado. La base consiste en resetear las reglas y en poner por defecto los elementos. La distribución es el posicionamiento genérico de los elementos, así como algún sistema de ayuda al respecto como por ejemplo los 'grids'. Los módulos son elementos visuales reutilizables y el estado se refiere a los distintos estilos que un elemento pueda tener activándose o desactivándose vía JavaScript.

En el sistema SMACSS los módulos (los cuales son equivalentes a lo que llamamos componentes) incluyen la vasta mayoría de todas las reglas CSS, y con frecuencia encontraré necesario romperlas para llevarlas a componentes más abstractos.

Los componentes son elementos visuales independientes. Los patrones, por otro lado, son bloques. No se soportan por sí mismos y raramente describen el aspecto visual, lo que se ve y se siente. En cambio son sencillos y repetibles patrones que pueden ser puestos juntos para formar un componente.

Veamos un ejemplo concreto, un componente que sea una caja modal de diálogo. La parte modal podría tener el sello del sitio como degradado en el fondo de la cabecera, y por ejemplo, una sombra caída alrededor, un botón en lo alto de la esquina derecha, y posicionamiento fijo, además de un centrado vertical y horizontal. Cada una de estas configuraciones podría ser usada repetidamente en cualquier sitio de la web, así que la idea sería no tener que ajustar el código cada vez que así fuera. Cada una de esas configuraciones son un patrón, y juntos componen el componente modal.

Normalmente no uso clases solo de patrones en el HTML a menos que tenga una buena razón. En cambio, uso un preprocesador para incluir los estilos de patrón en la definición del componente. Discutiré ésto detalladamente después.

Usa clases para estilizar y solo para estilizar

Cualquiera que haya trabajado en un gran proyecto ha encontrado un elemento HTML con una clase cuyo propósito era completamente desconocido. Quiere quitarla, pero estás indeciso, no vaya a ser que tenga un propósito de cual no se te ha avisado. Como eso ocurre continuamente, con el paso del tiempo tu HTML se va rellenando con clases que en realidad no tienen propósito, y que los miembros del equipo tienen miedo de borrar.

El problema es que las clases normalmente tienen demasiadas responsabilidades en el desarrollo front-end. Estilizan elementos HTML, actúan como ganchos para el JavaScript hooks, se añaden al HTML como características de detección, se usan en tests automatizados, etc.

Es un problema. Cuando las clases son usadas en demasiadas partes de la aplicación, llegar a dar miedo quitarlas de tu HTML.

Sin embargo, con una convención establecida, este problema puede ser completamente evitado. Cuando ves una clase en el HTML, deberías ser capaz instantáneamente de saber su propósito. Mi recomendación es darle a todas las clases sin estilo un prefijo. Uso .js- para JavaScript y uso .supports- para las clases Modernizr. Todas las clases sin prefijo son para estilizar y solo para estilizar.

Esto hace que encontrar clases sin uso y eliminarlas del HTML sea tan fácil como buscarlas en la estructura de la hoja de estilos. Puedes incluso automatizar este proceso en JavaScript referenciando las clases del HTML con sus respectivas, mediante el objeto document.styleSheets. Las clases que no están en el document.styleSheets pueden ser eliminadas con seguridad.

En general, como una buena práctica el separar el contenido de la presentación, es también importante separar tu presentación de tu funcionalidad. Usando clases estilizadas como ganchos de Javascript profundiza en el acoplamiento entre CSS y JavaScript, de tal manera que hace imposible actualizar el enganche de ciertos elementos sin romper la funcionalidad.

Nombra tus clases con una estructura lógica

En estos días, la mayoría de la gente escribe CSS usando guiones como separadores de palabras. Pero los guiones por sí solos no suelen ser normalmente suficientes para distinguir entre tipos de clases.

Nicolas Gallagher recientemente escribió sobre su solución a este problema, la cual he adoptado (con ligeros cambios) con gran éxito. Para ilustrar la necesidad de una convención en cuanto a la nomenclatura consideremos lo siguiente:

/* Un componente */
.button-group { }

/* La modificación de un componente (modificando .button) */
.button-primary { }

/* Un sub-objeto del componente (independiente de .button) */
.button-icon { }

/* ¿Es ésto una clase de componente o de diseño? */
.header { }

Echando un vistazo a las clases de arriba es imposible saber qué tipo de regla aplicar. Esto no solo incrementa la confusión durante el desarrollo, también hace más difícil testar tu CSS y tu HTML de una manera automática. Una convención estructurada sobre la nomenclatura permitiría ojear un nombre de clase y saber exactamente qué relación tiene con otras clases y dónde debería aparecer en el HTML — fomentando una nomenclatura más sencilla y un testeo posible donde antes no lo era.

/* Reglas de patrón (usando marcadores Sass) */
%template-name
%template-name--modifier-name
%template-name__sub-object
%template-name__sub-object--modifier-name

/* Reglas de Componente */
.component-name
.component-name--modifier-name
.component-name__sub-object
.component-name__sub-object--modifier-name

/* Reglas de Diseño */
.l-layout-method
.grid

/* Reglas de Estado */
.is-state-type

/* Enganches No-Estilizados JavaScript */
.js-action-name
El primer ejemplo rehecho:
/* Un componente */
.button-group { }

/* Un componente modificado (modificando .button) */
.button--primary { }

/* Un sub-objeto de componen (independiente de .button) */
.button__icon { }

/* Una clase de diseño */
.l-header { }

Herramientas

Mantener una efectiva y bien organizada arquitectura CSS puede ser muy difícil, especialmente en grandes equipos. Unas cuantas malas reglas aquí y allí pueden convertirse, cual bolas de nieve, en enredos inmanejables. Una vez que el CSS de tu aplicación ha entrado en la guerra del campo de la especificidad y en el triunfo de los "!important" , puede ser imposible para el siguiente poder analizarlo sin preferir en cambio comenzar de nuevo. La clave es evitar esos problemas desde el comienzo.

Afortunadamente, hay herramientas que pueden controlar la arquitectura CSS de tu sitio de fácil manera.

Preprocesadores

En los días que vivimos es imposible hablar sobre herramientas CSS sin mencionar los preprocesadores, y en este artículo no será diferente. Pero antes de que alabe su utilidad, os ofreceré algunas palabras de cautela.

Los preprocesadores te ayudan a escribir CSS más rápido, no mejor. En última instancia lo que se obtiene es CSS plano, y por lo tanto se aplican las mismas reglas que antes del preprocesador. Si un preprocesador te permite escribir tu CSS más rápido también te permite escribir tu CSS defectuoso más rápido, así que es importante comprender bien la arquitectura CSS antes de pensar que un procesador va a solventar tus problemas.

Muchas de las llamadas “capacidades” de los preprocesadores pueden actualmente ser muy perjudiciales para la arquitectura CSS. Las siguientes son algunas de las “capacidades” que yo evitaría a toda costa (y aunque estas ideas generales se aplican a todos los preprocesadores, estas guías deberían aplicarse específicamente en Sass):

  • Nunca anides reglas por pura organización de código. Únicamente anida cuando el CSS procesado sea lo que quieres.
  • Nunca uses un 'mixin' si no le vas a pasar un argumento. Los 'mixins' sin argumentos es mejor usarlos como 'templates', ya que pueden ser extendidos.
  • Nunca uses @extend en un selector que no es una clase de hijo. No tiene sentido desde una perspectiva de diseño e hincha el CSS compilado.
  • Nunca uses @extend para componentes UI en modificaciones de reglas de componente porque perderás la cadena de herencia.

Lo mejor de los preprocesadores son las funciones como @extend y %placeholder. Ambas te permiten manejar la abstracción CSS fácilmente sin añadir hinchazón a tu CSS o sin añadir una enorme lista de clases base en tu HTML, que podría ser difícil de manejar.

@extend debería ser usado con precaución porque alguna vez querrás esas clases en tu HTML. Por ejemplo, cuando aprendes por primera vez sobre @extend podría ser tentador usarlo con todos tus clases modificadas de la siguiente manera:

.button {
/* Estilos de botón */
}

/* Mal */
.button--primary {
@extend .button;
/* Estilos de Modificación */
}

El problema de hacer eso es que pierdes la cadena de herencia con respecto al HTML. Ahora es muy difícil seleccionar todas las instancias del botón con JavaScript.

Por norma general nunca extiendo componentes UI o ni nada que se le parezca. Eso es lo para lo que sirven los patrones ('templates') y es otra manera de distinguirlos de los componentes. Un patrón es algo a lo que nunca necesitarías apuntar desde la lógica de tu aplicación, y por consiguiente puede ser extendido con un preprocesador.

Aquí vemos ahora cómo se mostraría el ejemplo modal que vimos arriba:

.modal {
@extend %dialog;
@extend %drop-shadow;
@extend %statically-centered;
/* otros estilos */
}

.modal__close {
@extend %dialog__close;
/* otros estilos del botón */
}

.modal__header {
@extend %background-gradient;
/* otros estilos de la cabecera */
}

CSS Lint

Nicole Sullivan y Nicholas Zakas crearon CSS Lint como una herramienta de calidad de código para ayudar a los desarrolladores a detectar malas prácticas en su CSS. Su sitio lo describe como:

CSS Lint señala los problemas de tu código CSS. Hace un chequeo básico de tu sintaxis, además de aplicar una serie de ajustes al código que parece mostrar signos de ineficiente o de problemática. Las reglas son todas opcionales, así que puedes editar u omitir sencillamente las que no te gusten.

Mientras que el set de reglas generales podría no ser perfecto para la mayoría de los proyectos, la mejor capacidad de CSS Lint es su habilidad para ser personalizado exactamente como tú quieres que sea. Eso significa que puedes elegir de entre todas las reglas que vienen por defecto y escoger las que quieras para poder escribirlas por tu cuenta.

Una herramienta como CSS Lint es esencial para cualquier gran proyecto a la hora de asegurar al menos una conformidad común en cuanto a consistencia y las convenciones de lo que se trata. Y como esbocé previamente, una de las grandes razones para asumir convenciones es que permiten a herramientas como CSS Lint identificar cualquier cosa que las rompa.

Basándonos en las convenciones que he propuesto a lo largo de estos dos artículos sobre Arquitectura CSS, vemos que llega a ser muy fácil escribir reglas que detecten las configuraciones que no queremos. Aquí aporto algunas sugerencias:

  • No permitas los ID en tus selectores.
  • No uses selectores de tipo no semántico (div, span) en ninguna regla.
  • No uses más de dos combinadores en un selector.
  • No permitas ningún nombre de clase que empiece por “js-”.
  • Mantente alerta si frecuentemente usas diseño y posicionamiento usas en las reglas sin prefijos “l-” .
  • Mantente alerta si una clase definida en un primer momento es luego redefinida como hijo de alguna otra clase.

Son obviamente solo sugerencias, pero son propuestas que te hacen pensar en cómo reforzar los estándares que quieres hacer cumplir en tus proyectos.

HTML Inspector

Hace rato sugerí que sería fácil buscar las clases en tu HTML y enlazarlas con sus correspondientes en la hoja de estilos y dije que deberías tener cuidado si tu clase estaba definida en el HTML pero no en el CSS. He desarrollado una herramienta llamada HTML Inspector para hacer este proceso más sencillo.

HTML Inspector atraviesa tu HTML y (de forma parecida a CSS Lint) te permite escribir tus propias reglas que lanzarán errores y avisos cuando alguna convención se rompa. Actualmente uso estas normas:

  • Mantente alerta si el mismo ID es usado más de una vez en una página.
  • No uses ninguna clase que no haya sido mencionada en alguna hoja de estilos (como “js-”).
  • Las clases modificadas no deberían ser usadas sin su clase de base.
  • Las clases de sub-objetos no deberían ser usadas cuando ningún ancestro contiene a la clase base.
  • Los elementos planos DIV o SPAN, sin clase adjuntada, no deberían ser usados en el HTML.

Resumen

CSS no es solo diseño visual. No deseches las mejores prácticas de la programación solo porque estés escribiendo CSS. Conceptos como OOP, DRY, el principio abierto/cerrado, separación de asuntos, etc., se pueden aplicar a CSS.

Lo importante es que debes organizar el código, asegurándote de que juzgas tus métodos según lo que puedan ayudarte actualmente a desarrollar de una manera más sencilla y sostenible a largo plazo.

Philip Walton

Autor

OldMith

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

Compartir

Comentarios

Zeneke

21/1/2013
Cojonudisimo
Muy interesante!