Generar dinámicamente CSS: La guía definitiva

Existe una situación muy común cuando desarrollamos un Plugin o Tema: Tenemos una serie de opciones que el usuario puede tocar a su antojo para personalizar una serie de estilos, sean colores, márgenes, fuentes o cualquier otro elemento visual. Sabemos cómo agregar una hoja de estilos en WordPress usando la función wp_enqueue_style pero no podemos generar las reglas dinámicamente, esto es, con PHP dentro de la hoja CSS.

Imaginemos que tenemos unas opciones en nuestra Base de Datos tal que así:

$options = array(
	'background-color' => '#DDD',
	'font-size' => 23
);

¿No sería maravilloso si en nuestra hoja de estilos pudiéramos hacer algo así?

$options = get_option( 'my-options' );
.my-class {
	background-color:<?php echo $options['background-color']; ?>;
	font-size:<?php echo $options['font-size']; ?>px;
}

Obviamente se puede, veamos qué posibilidades tenemos:

La forma brusca: El hook wp_head

No hay en realidad ningún problema en utilizar este hook para añadir algunos estilos:

De esta forma, los estilos quedan encerrados entre nuestras etiquetas <head></head> de nuestra página.

No está mal: Las reglas se generan bien y queda en la cabecera. No obstante, si usamos muchas reglas (por ejemplo, la personalización de un Tema), la cabecera de nuestra página podría quedar demasiado larga, añadiendo carga adicional a nuestra página. Cuando una página HTML llega al usuario, el navegador lee el cuerpo del código HTML mientras carga en paralelo el resto de los elementos como hojas de estilo o ficheros Javascript (no funciona exactamente así pero eso daría para otro post). Lo que nos interesa es que la página cargue lo más rápido posible mientras en paralelo se descargan las hojas de estilo adicionales.

La forma elegante: Hojas de estilo “virtuales”

Con hoja de estilo “virtual” nos referimos a cargar un fichero CSS que no existe. ¿Cómo? Lo vamos a explicar, pero vayamos paso a paso. Primero tenemos que saber cómo va esto del protocolo HTTP (algo básico pero que tendemos a pasarlo por alto).

Cuando nuestro navegador solicita la descarga de una hoja de estilos, pide al servidor mediante un mensaje dicho fichero. El servidor devuelve una respuesta compuesta por una cabecera que contiene los datos identificativos del fichero: longitud en bytes del fichero, tipo de contenido y el tiempo de expiración de la caché entre otros campos. Bajo la cabecera se emite el cuerpo del fichero que contiene pues eso, el contenido del fichero.

Es fácil verlo en Chrome con las herramientas para desarrolladores

headers

En este caso, el fichero noticons.css se descarga de la URL https://s1.wp.com/i/noticons/noticons.css. Nos interesa especialmente la parte de Response Headers. Ésta es la cabecera que el servidor ha devuelto y lo que nos debería llamar más la atención es el campo llamado Content Type que nos dice qué tipo de contenido tiene el fichero, en este caso text/css.

¿Qué pistas nos da todo esto? Bien, si queremos generar dinámicamente nuestro CSS tenemos que tener en cuenta que cuando el navegador solicite nuestro archivo “virtual”, nuestro sitio tiene que devolver una respuesta con una cabecera cuyo campo Content Type sea text/css. Y no hay más. Si el navegador del usuario reconoce dicha URL como un fichero CSS, lo aplicará como tal.

Ahora, sigamos unos pasos básicos que nos lleve a la meta:

  • ¿Qué URL debería tener nuestra hoja de estilos “virtual”? Bueno, la que queramos en realidad. Pongamos un ejemplo: https://holadolly.wordpress.com?dolly-styles=true. Esa URL, que normalmente devolvería un fichero HTML vamos a “engañarla” para que devuelva un fichero CSS en su lugar. En WordPress podemos generar dicha URL fácilmente:
$css_url = add_query_arg( 'dolly-styles', 'true', home_url() );
  • Agregar dicha URL como si fuera un estilo normal usando la función wp_enqueue_style

  • Hasta aquí, más o menos todo bien pero la URL todavía no devuelve un CSS, de hecho devuelve la página HTML de la home de nuestro sitio. Ahora llega el momento del engaño. Cuando llamamos a esa URL, WordPress se carga de forma normal pero hay que saber que no necesitamos cargarlo al completo. La idea es comprobar que existe una variable $_GET llamada ‘dolly-styles’, que tiene valor ‘true’. Si esta condición se cumple le decimos al usuario que estamos cargando un CSS (mediante los campos de cabecera), generamos los estilos y paramos la ejecución. No necesitamos cargarlo todo, sólo la parte que nos interesa. Lo vamos a ver mejor con un ejemplo:

De este código podemos sacar alguna pregunta: ¿Por qué plugins_loaded y no otro? Funcionaría en init por ejemplo, ¿Por qué no init entonces? Bueno, hay que recordar que la idea es cargar lo mínimo del núcleo de WordPress, además debe hacerse antes de que WordPress envíe las cabeceras. Esto se produce justo después de la ejecución del action template_redirect. Es decir, que podríamos utilizar éste último y aún así funcionaría pero ¿Para qué? Lo mejor es hacerlo lo antes posible. Si estamos desarrollando un plugin, el idóneo es justo cuando nuestro plugin ha cargado su código, esto es plugins_loaded pero si lo estamos haciendo para un Tema, el hook apropiado sería after_theme_setup. Aquí podemos ver una lista de actions y el orden de ejecución.

  • ¿Cómo generar la cabecera CSS y generar los estilos? Es muy sencillo generar una cabecera para nuestro supuesto fichero. Se usa la función header de PHP

Los pasos son sencillos: Generamos la cabecera de tipo text/css, recogemos las opciones de nuestro Plugin/Tema de Base de Datos y generamos las reglas CSS con los valores de las opciones.

Ahora ya sólo queda comprobar que todo funciona bien. Para esto, simplemente ponemos en el navegador la URL de nuestro CSS “virtual”. Deberíamos ver un fichero CSS normal y corriente.