Maneja las CSS como un profesional

Una parte importante de cualquier aplicación o sitio web son los estilos CSS. Las hojas de estilos se suelen escribir aisladas de la aplicación, sus tecnologías y metodologías. Además, suelen ser servidos como archivos estáticos, de forma que no pasan por la base de datos ni el resto de la aplicación. Existen buenas prácticas relacionadas con la legibilidad y mantenibilidad de las hojas de estilos, pero pocas veces se opta por mezclar las CSS con las aplicaciones, entre otras cosas porque servirlos como ficheros estáticos es más rápido que procesarlos con PHP, y porque a menudo los diseñadores que escriben los estilos no conocen las tecnologías de programación.

No obstante, como se explica en “Supercharge Your CSS with PHP Under the Hood” y demuestran algunas aplicaciones como Plone o phpMyAdmin, mezclar las tecnologías de programación con el lenguaje de estilos CSS puede aportarnos más potencia y comodidad a la hora de escribir, mantener y aumentar el rendimiento de las stylesheets. Y por el mismo precio también te presentaré CssDispatcher, una biblioteca que te ayudará a hacer todo eso casi sin esfuerzo.

Variables y funciones en CSS

En programación, si vas a utilizar varias veces un dato, lo almacenas en una variable para manejarlo más cómodamente. El mismo principio podemos aplicar, por ejemplo, con los colores en una hoja de estilos. De este modo evitamos repetir (es decir, recordar o copiar-pegar) códigos hexadecimales. Por ejemplo, el siguiente estilo:

.paginacion { border: 1px solid red; }
.paginacion a { color: red; }

…quedaría de este modo:

.paginacion { border: 1px solid <?=$rojo ?>; }
.paginacion a { color: <?=$rojo ?>; }

Como ves, he utilizado las etiquetas cortas y el operador de impresión <?= en lugar de echo para abreviar. Establecer variables para colores facilita la tarea de crear esquemas de colores intercambiables (y más legibles):

body { color: <?=$color_texto ?>; }
a { color: <?=$color_enlace ?>; }
h1 { color: <?=$color_primario ?>; }

#divCentral { border: 1px solid <?=$color_primario ?>; }

Como ya he comentado, phpMyAdmin hace uso de esta técnica. Aquí tenemos un pequeño ejemplo:

th {
    font-weight:        bold;
    color:              <? echo $GLOBALS['cfg']['ThColor']; ?>;
    background:         <? echo $GLOBALS['cfg']['ThBackground']; ?>;
}

En este caso no se usa el operador = para imprimir, y sí echo. Es recomendable hacer lo mismo ya que en muchos servidores (sobre todo con las versiones más recientes de PHP) las etiquetas cortas están desactivadas.

Además de colores, puedes utilizar las variables para calcular medidas relativas. Por ejemplo, si tenemos un layout de 800px de ancho con dos columnas, una de 600px y otra de 200, podemos expresarlo con medidas relativas (%) para hacer más accesible nuestras hojas de estilo:

#columna1 { width: <?php echo ($ancho_columna1/800)*100 ?>%; }
#columna2 { width: <?php echo ($ancho_columna2/800)*100 ?>%; }

También podemos utilizar propiedades completas como variables:

h1 { <?php echo $negrita ?>; }

Donde $negrita valdría font-weight: bold.

Llegados a este punto, seguramente estés pensando “muy bonito, pero ¿dónde defino las variables?“. En el tutorial de NetTuts los ejemplos muestran las declaraciones de variables en el mismo fichero que las CSS, pero no creo que sea una buena práctica. phpMyAdmin tiene ficheros separados donde se definen los valores, aunque utilizar el array $_GLOBALS no es muy elegante, sobre todo si tu aplicación es orientada a objetos.

Como ya adelanté al comienzo, vamos a ver una biblioteca llamada CssDispatcher que facilita la gestión de CSS con PHP. Esta librería propone un método concreto de definir las variables de los estilos, de forma que se identifiquen fácilmente y además los nombres no causen conflictos con el resto de variables de la aplicación. Este método es sencillamente añadir las variables a una instancia de la clase Css a través del tipado dinámico:

$general = new Css('general.css.php');
$general->fondo = '#eee';
$general->color_borde = 'red';
$general->cabecera = 2.1;

Aunque las variables son introducidas en el objeto, al usarlas en la plantilla CSS sólamente tenemos que especificar su nombre, como variables “tradicionales”:

body { background: <?php echo $fondo ?>; }

De este modo podemos asignar variables a cuantas hojas de estilos queramos, sin que interfieran los nombres:

$general = new Css('general.css.php');
$general->altura_cabecera = '5em';

$ie_hacks = new Css('ie_hacks.css.php');
$ie_hacks->altura_cabecera = '5.1em';

Ah, por cierto, las variables también pueden ser de tipo función anónima. Por ejemplo…

$general->layout = function($proporcion) {
    return $proporcion / 800;
}

…que se podría aplicar de este modo:

.columna1 { width: <?=$layout(100) ?>em; }

Un último apunte sobre CSS “plantilladas”: te recomiendo que las nombres con extensión .css.php, como se ve en los ejemplos, o bien .css.tpl. Utilizar la extensión simple .css puede llevar a confusiones… pero es sólo un consejo.

Aumentando el rendimiento con CssDispatcher

Tanto si usamos variables en las CSS como si no, podemos mejorar el rendimiento si las procesamos con PHP, tanto en el servidor (tardamos menos en procesarlas) como en el cliente (le llegan antes).

Un ejemplo es el envío de varias hojas de estilo como un solo fichero. Mientras las buenas prácticas de CSS recomiendan separar las hojas de estilo por secciones o por navegadores (por ejemplo, ie-hacks.css, webkit.css, etc), las buenas prácticas de rendimiento nos aconsejan minimizar el número de peticiones HTTP. Así pues, ¿qué escogemos: mantenibilidad o rendimiento? Pues yo me quedo con las dos: escribamos las CSS en ficheros separados y juntémoslas a la hora de servirlas.

CssDispatcher nos permite hacerlo de una forma muy sencilla. Una vez que hemos creado instancias de la clase Css para cada hoja de estilo, las añadimos a un objeto CssDispatcher:

$general = new Css('ejemplo.css.php');
$general->fondo = '#eee';

$otra = new Css('example2.css.php');
$otra->negrita = 'font-weight: bold';

//Creamos un dispatcher
$estilos = new CssDispatcher;

//Añadimos las CSS al dispatcher
$estilos->add($general);
$estilos->add($otra);

$estilos->render();

Cross-browser

Otro de los problemas comunes cuando se escriben estilos es la compatibilidad entre navegadores. No todos renderizan las CSS de igual forma, así que es habitual escribir hojas de estilo con propiedades específicas para Internet Explorer, Safari/Chrome, etc. Dependiendo del que esté utilizando el usuario se envía una u otra.

Se han venido utilizado tres métodos diferentes para cargar hojas de estilos específicas:

  • Comentarios condicionales (sólo funcionan en IE): para añadir CSS específicas de Internet Explorer basta con envolver la línea de llamada (
    <link rel="stylesheet"...) con las directivas<!--[if IE 6]>y<![endif]-->.
  • JavaScript: una vez cargada la página, se ejecuta un script que comprueba el navegador y carga dinámicamente la hoja de estilos correspondiente. En consecuencia, los usuarios que no ejecuten JavaScript (por seguridad o por usar un navegador viejo/limitado) no podrán cargar hojas de estilo específicas.
  • En el servidor:: antes de enviar la página, el script que la genera comprueba la cabecera HTTP User-Agent y escribe las invocaciones correspondientes al generar HTML.

Elegiremos el último método por ser más seguro y compatible. Con CssDispatcher definir CSS específicas de navegador es tan fácil como añadir un segundo parámetro en el constructor de la clase Css:

//Enviará la hoja de estilos si el navegador del usuario es Internet Explorer 6
$ie_hacks = new Css('ie_hacks.css.php', Css::UA_IE6);

Puedes especificar las siguientes familias de navegadores: Internet Explorer 6 (Css::UA_IE6), Internet Explorer >6 (Css::UA_IE), Gecko (Css::UA_GECKO), WebKit (Css::UA_WEBKIT). También puedes utilizar los sinónimos UA_MOZILLA, UA_FIREFOX, UA_SAFARIy UA_CHROME respectivamente. Para más información puedes ver la referencia de CssDispatcher.

En resumen

Hemos visto varias técnicas que nos ayudan a manejar de forma más eficaz y eficiente las hojas de estilos. Puedes utilizar las que necesites, tanto si quieres hacerlo de forma manual como utilizando CssDispatcher (u otras bibliotecas, si las encuentras dímelo!). A saber:

  • Asignación de variables en espacios de nombres seguros.
  • Hojas de estilos específicas de un navegador.
  • Minimización del código CSS.
  • Unión de hojas de estilos para un envío único.

Rendimiento en el servidor con cache

No hay que olvidar que generar estilos con CssDispatcher provoca una carga en el servidor por los accesos a disco (para obtener las CSS y otro para obtener los scripts) y por el tiempo de compilación y ejecución. Por ello, una buena técnica para aumentar la velocidad y reducir sobremanera el throughput es cachear el resultado final de la ejecución de CssDispatcher. Para cachear la salida de un script existen varias técnicas:

  • Cache en el servidor web: la salida de una petición se almacena en memoria o disco y se sirve sin ejecutar nada de PHP. En Apache esto es posible con el módulo mod_cache y sus motores de almacenamiento, mod_disk_cache y mod_mem_cache.
  • Cache a través de PHP: esta técnica hace básicamente lo mismo que la anterior, pero en vez de estar gestionada por el servidor web se gestiona con scripts PHP. Por ello, aunque no es necesario compilar y ejecutar los scripts de CssDispatcher sí es necesario ejecutar el script de control de cache, por lo que esta solución no es tan buena como mod_cache. No obstante, en servidores compartidos y otros entornos de escaso control es una buena solución. Además, el script de control de cache se puede cachear con los sistemas de cache de bytecodes como APC o eAccelerator, aunque eso ya es un tema aparte.
Artículo relacionado
Por

13 comentarios

  1. @Utopia, te refieres a errores en la redacción del post? Porque lo he revisado varias veces (y no sólo yo) y creo que no hay tal. Si te refieres a errores en los códigos, todos compilan correctamente y hacen lo que pretendo que hagan, aunque quizá haya errores ocultos para mi intelecto…

    Con respecto a LESS, me gusta la idea. Por una parte, CssDispatcher permite utilizar variables y operaciones (funciones anónimas, PHP 5.3) de forma similar. Por otra, el propósito es escribir hojas de estilo con PHP válido, de forma que no sea necesario compilarlas, logrando un mejor rendimiento. De todos modos, sería interesante un port de LESS para PHP. Estoy seguro de que sería más rápido ;-P

  2. Bueno, no creo que llamar compilar a lo que hace LESS, sea el termino adecuado, aunque aveces se use. Adems en Sass (no se como lo hacen en LESS) tienes la opcion de simplemente apuntar las paginas css estaticas. generadas por sass2css

    En ruby se podria haber optado por una opcion mas clasica, como por ejemplo usar erb para insertar codigo en forma de plantilla, pero es mucho mas elegante usar un DSL. Por lo mismo que algunos preferimos rspec en vez de test::unit o por que nos gusta tanto cucumber.

    A ver si saco un poco de tiempo y puedo mirar el codigo del cssDispatcher, aunque mi PHP es bastante limitado.

  3. @utopia,

    Puedes usar Sass (probablemente LESS tb) junto a tus projectos en PHP, solo tienes que usar sass2css, desde la linea de comandos, y te generara un archivo con el css de toda la vida. si ademas uss compass este lo hara automaticamente cada vez que edites uno de los archivos .sass

    compass –watch

  4. @pedro.delgallego Curiosa la riqueza de DSL que hay en las herramientas de desarrollo para Ruby. PHP siempre ha mantenido la simplicidad de los archivos planos (el desarrollo web ha experimentado una gran evolución en los últimos años, y PHP ya tiene 16). Por otra parte, la naturaleza embebida de la sintaxis de PHP hace innecesarios (o, al menos discutibles) los sistemas de plantillas basados en DSL. Esta es la principal ventaja de CssDispatcher: su rendimiento.

    Con respecto al código, es muy corto y sencillo. La única complicación que puede haber es el uso de los métodos mágicos __get() y __set() cuando se agregan atributos a una clase de forma dinámica (que vienen siendo las variables de la hoja de estilos).

    Ah, y muy buena la idea de usar SASS en proyectos PHP, a veces la "ingeniería del software" nos hace olvidar la maravillosa línea de comandos ;-)

  5. bueno en el caso de sass, no es asi, ya que se compila sola la primera vez que se hace una llamada, y luego se usan las paginas estaticas, excepto en el caso de que se modifique elgun archivo sass, que es recompilado y otra vez se empieza a usar el estatico.

    De hecho yo siempre fuerzo a usar el estatico, por lo el rendimiento es exactamente igual al de tirar de un css normal

  6. @pedro.delgallego Es lo bueno del caché, que se puede cachear todo todo, y si es en memoria mejor ;-)

  7. La verdad que haciéndolo así, tardaría 4 veces más haciendo la hoja de estilo, que repitiendo atributos.

  8. @Nacho pero cuando debas realizar cambios, o mantenimiento lo agradecerías ;) es mejor hacer cambios en los valores de variables que buscarlos en toda la hoja de estilos.

  9. No le veo tanta ventaja a la hora del mantenimiento, lo veo más útil para cambiar la hoja de estilo según la hora del día, que los enlaces fueran distintos si vienes desde una determinada página, que muestre iconos dependiendo del dominio al que apunta, que no se muestren las imágenes en determinados dispositivos… etc etc

  10. @kolobo Buenas ideas! En cuanto a mantenibilidad, CssDispatcher respeta que utilices múltiples hojas de estilo (según secciones, etc), pero a la hora de servirlas las une en una sóla petición, para obtener mejor rendimiento.

  11. gracias Israel… me sirve para ir introduciendome en el mundo de la programacion web…
    saludos desde Chile.