LESS: Preprocesador CSS con soporte en Visual Studio 2012

El uso de preprocesadores CSS está bastante de moda últimamente. En los últimos post he estado centrada en modularidad con RequireJS en JavaScript, la compresión y reducción de archivos gracias al sistema de bundles de ASP.NET 4.5 y hoy me gustaría centrarme en el mundo de los estilos donde podemos encontrar cierta relación con estos temas.

¿Qué es un preprocesador CSS?

Cuando eres desarrollador web, normalmente tienes que situarte en diferentes perspectivas: código de servidor, código de cliente (JavaScript) y frecuentemente las hojas de estilos. Me atrevería a decir que cualquier desarrollador web tiene en cuenta algunas premisas cuando trabajamos con estilos: Los mismos se aplican en cascada (lo último siempre sobrescribe a lo anterior), podemos asignar estilos a elementos únicos (por id), crear clases o asignar directamente una serie de propiedades a un tipo de etiqueta en concreto, e incluso a etiquetas dentro de otras, a varios elementos a la vez, etcétera. Pero somos desarrolladores y estamos acostumbrados al dinamismo y es una de las desventajas que a día de hoy, incluso en CSS3, nos encontramos en este mundo.
Hace ya unos cuantos años (alrededor de unos 6) surgieron las primeras ideas de preprocesador CSS con el objetivo no sólo de ofrecer dinamismo a nuestros estilos sino además de aportarnos algunas de las características de las que ya disfrutábamos en otros lenguajes, como las variables, funciones, controles de flujo, etcétera. ¿Significa que los navegadores deben saber de la existencia de los preprocesadores? En absoluto 😀 ya que el resultado final seguirá siendo el mismo CSS con el que solemos trabajar, pero la forma de generarlo será mucho más potente y productiva.
Los preprocesadores más conocidos son Sass, Less y Stylus (aparecidos en ese orden). Para tener una visión de todos ellos, os recomiendo el Hangout de Naiara Abaroa (@nabaroa) en HTML 5 Spain. En este post nos centraremos únicamente en Less, por ser el que tiene mayor acogida y soporte en Visual Studio 2012 desde la última actualización del IDE.

{LESS}

lesslogo
Conociendo cuál es el propósito de los preprocesadores CSS, LESS es uno de los que mayor acogida está teniendo en estos momentos, influenciado sin embargo por la nueva sintaxis de otro preprocesador llamado Sass creado en el año 2007. De hecho, en la última actualización de Visual Studio 2012.2 se ha mejorado el editor del IDE para soportar la edición y «compilación» del mismo. Lo primero que observamos es que disponemos de una plantilla LESS, reconoce la sintaxis y ofrece intellisense dentro de este contexto.

Quick start

En este post no pretendo hacer una introducción exhaustiva del lenguaje pero si me gustaría mostrar algunos ejemplos de lo que supone trabajar con less y el mundo de los preprocesadores:

Uso de variables

Esta es una de las grandes ventajas de los preprocesadores y en less se declaran de la siguiente manera:

//Variables
@headerColor: #cc1e1e;
@base: #f2f0f0;
@baseSize: 14px;
@font-color: #fff;

body {
    font-family: Verdana;
    font-size: @baseSize;
    background-color: @base;
}

Como vemos en el ejemplo, las variables se declaran utilizando la @ seguida del nombre de nuestra variable y dos puntos para asignar el valor que queramos. El mismo puede ser un color, una medida en píxeles, em, porcentajes e incluso valores complejos. La forma de asignar la variable a una propiedad CSS es haciendo uso de la arroba y el nombre. Visual Studio 2012 nos aporta Intellisense para poder visualizar todas las variables que tenemos creadas.

Nota En este ejemplo también he dejado caer la posibilidad de utilizar la doble barra para los comentarios como en otros lenguajes y no permitido en el caso de las hojas de estilos 😀

Mixins

Cuando lo que queremos es inyectar fragmentos de código en distintos sitios de nuestra hoja de estilos, podemos hacer uso de lo que se conoce como mixins.

//Mixins
.centered() {
    width: 60%;
    margin: auto;
}
.rounded-corners(@size: 5px) {
    -moz-border-radius: @size;
    -webkit-border-radius: @size;
    border-radius: @size;
}
header {
    background-color: @headerColor;
    .centered;
    .rounded-corners;
}
article {
    .centered;
}

En este caso se han creado dos mixins, uno para reutilizar el centrado de los elementos en la página, indicando un valor fijo del 60% y el margin auto y otro ejemplo para los bordes redondeados donde no sólo recibe como parámetro cuál será el número de píxeles a aplicar sino que además nos permite establecer un valor por defecto.

Nota En Sass además existe el concepto de extensión (@extend) el cual echo bastante en falta en Less. Su objetivo no es duplicar código, como hacen los mixins, sino que es capaz de agrupar distintos elementos y aplicar el mismo fragmento para todos, lo cual reduce nuestra hoja de estilos.

Reglas anidadas

Con la llegada de Visual Studio 2012 se incluyó una característica que generaba la indentación automática de los diferentes niveles de profundidad de reglas para aplicar un estilo. Por ejemplo, este caso:

CSS indentation

Como se puede ver en la imagen, las reglas se van indentando cada vez más según el nivel de profundidad que tengan las mismas. Sin embargo, si se añade alguna regla entre medias que no esté relacionadas con la misma se rompe este efecto que nos hace conocer cuál es el nivel en el cual nos encontramos.

Todos los preprocesadores CSS nos ofrecen la anidación de reglas de tal forma que siempre seamos conscientes de la relación entre las mismas:

nav {
    float: right;
    ul { /*nav ul { }*/
        list-style-type: none;
        li { /*nav ul li{ }*/
            display: inline;
            a { /*nav ul li a{ }*/
                text-decoration: none;
                color: @font-color;
                &:hover { /*nav ul li a:hover{ }*/
                    color: yellow;
                }
            }
        }
    }
}

De este modo sabremos cuál es la relación entre cada una de las reglas y qué etiquetas (en este ejemplo) deben estar presentes para que las propiedades definidas se apliquen.
En la última regla podemos observar el uso de & para referirnos a la etiqueta anchor. Aunque la regla esté anidada dentro utilizamos el ampersand para desmentir un siguiente nivel.

Funciones

En cualquier preprocesador, es posible realizar operaciones como sumas, restas, multiplicaciones, etcétera sobre propiedades:

h1 {
    font-size: 13px + 10;
}
h2 {
    font-size: @baseSize + 4;
}
h3 {
    font-size: @baseSize + 2;
}

Por otro lado existen un gran conjunto de funciones proporcionadas por LESS, al igual que el resto, que podemos localizar en el siguiente enlace. Las mismas nos permiten alterar la composición de los colores, formatear strings, operaciones matemáticas, etcétera. La forma de utilizar las mismas es la siguiente:

.button {
    padding: 7px;
    background-color: darken(@headerColor, 20%);
    color: @font-color;
    font-size: ceil(19.5px);
}

Import

Si bien no parece una novedad respecto al uso de CSS de manera tradicional, la sentencia @import tiene una gran relevancia en este caso.
Cuando trabajamos con CSS y hacemos uso de @import lo utilizamos de la siguiente manera:

@import 'reset.css';

En situaciones normales no se trata de una práctica recomendada ya que reduce el rendimiento de la página, impidiendo que el archivo a importar y el principal no puedan ser descargados de forma paralela y debe ser el principal, una vez descargado, quien dé la orden de bajada para el segundo. Además en este caso somos conscientes de que es necesario hacer dos peticiones para dos recursos independientes que, por modularidad y mejora del manejo de los estilos, están separados en archivos distintos. Si no usáramos preprocesadores podríamos hacer uso de bundling para solventar este escenario manteniendo nuestra modularidad.
En el caso de los preprocesadores, podemos hacer uso de import de una forma similar:

@import 'reset';

Importante El archivo reset debería cambiar su extensión a .less

Si bien la operación parece la misma, en este caso no estamos solicitando dos archivos desde el lado del cliente, sino que el preprocesador se encargará de hacer esa importación, inyectando en servidor el valor de reset.less en el archivo principal, evitando la solicitud de dos recursos y mejorando de este modo la velocidad de carga de la página.

Configuración

LESS como tal es el lenguaje que utilizamos para generar nuestro CSS resultante, pero es necesario algún «compilador» que nos permita descifrar lo que queremos que finalmente sea el estilo de nuestra página. Existen dos opciones para conseguir nuestro cometido: Hacer uso de less.js (poco recomendable) el cual nos permitirá la traducción de nuestros archivos less a CSS en cliente o, en el mundo de .NET, podemos hacer uso de .less{} (pronunciado dot-less) desde el lado del servidor. Podemos acceder a la página oficial y seguir las instrucciones que se dictan en la parte derecha, o podemos utilizar como atajo NuGet y configurar todos los pasos mencionados en el sitio con un solo clic:
dotless nuget
Una vez instalado, además de la referencia a dotless.Core, tendremos una nueva sección en el Web.Config llamada dotless donde podremos indicar si queremos reducir el tamaño del resultado y habilitar el cacheo.

  <dotless minifyCss="false" cache="false" web="false" />
</configuration>

Por otro lado, cuando se preprocesa desde el lado del servidor tenemos diferentes opciones:

  • Hacer uso de un handler para que intercepte todos los archivos de tipo less para su preprocesamiento. Es la opción por defecto cuando se instala less desde Nuget, agregando el handler directamente en el Web.config:
          <add name="dotless" path="*.less" verb="GET" type="dotless.Core.LessCssHttpHandler,dotless.Core" resourceType="File" preCondition="" />
        </handlers>
  • Utilizar la consola para preprocesar manualmente o a través de un script definido para la pre-build:
    "$(SolutionDir)packagesdotless.1.3.0.0tooldotless.Compiler.exe" "$(ProjectDir)contentlesssite.less" "$(ProjectDir)contentsite.css"
  • Utilizando el sistema de bundling de ASP.NET 4.5 de la siguiente forma:
    En el archivo BundleConfig.cs creamos una instancia del tipo Bundle a la que asignamos las siguientes transformaciones:

                var lessBundle = new Bundle("~/bundle/less").Include("~/Content/less/site.less");
                lessBundle.Transforms.Add(new LessPreProcessor());
                lessBundle.Transforms.Add(new CssMinify());
                bundles.Add(lessBundle);

    La clase CssMinify pertenece a System.Web.Optimization pero debemos crear una nueva clase para LessPreProcessor que implemente la interfaz IBundleTransform:

    using System.Web.Optimization;
    namespace CSSPP.App_Start
    {
        public class LessPreProcessor : IBundleTransform
        {
            public void Process(BundleContext context, BundleResponse response)
            {
                response.Content = dotless.Core.Less.Parse(response.Content);
                response.ContentType = "text/css";
            }
        }
    }
    

    Cuidado Si no estamos trabajando con la opción BundleTable.EnableOptimizations = true; y sólo obtenemos la optimización en modo release es necesario mantener el handler en el archivo web.config para poder trabajar en local.

Extensiones para Visual Studio 2012

Si bien Visual Studio «sabe de LESS», existen algunas extensiones populares que pueden mejorar nuestra experiencia aún más con los preprocesadores:

Mindscape
Es una de las extensiones más utilizadas para trabajar no solo con LESS sino además con CoffeeScript y Sass.
Web Essentials 2012
En cuanto a LESS se refiere nos facilita una versión preliminar del resultado de nuestro código al lado derecho de nuestro archivo less.

Espero que haya sido de utilidad.

¡Saludos!