prettyprint

Sunday 22 December 2013

How to build LESS pathways with browser detection

Someone told me I should write more, so here is a new post after a bunch of months.

During the past half year I have been working on building highly optimized responsive front ends. It has been an HTML + CSS intensive project that aimed at building a collection of specialized widgets, which could be easily re-used.

The team I was working with developed some very cool new techniques. I am going to share here one of them here. Which is how to build LESS pathways that generate distinct CSS files for different browsers/device environments.

For this example lets assume that we have two distinct device targets. Mobile and Desktop devices. The detection of the user-agent is not part of this article. There are so many good libraries that it would be pointless. We'll also pretend to be building a blog interface.

Since each widget should be modular and independent. Let's split them into separate files, create a 'sharedVariables.less' file which will include colors, gutter widths and other stuff, then build a main.less file that includes them all:

less/
 - main.less
 - sharedVariables.less
 - widgets/
  - navigationWidget.less
  - blogArticleWidget.less
  - commentBlockWidget.less
  - commentWidget.less


The CSS generated from the main.less would normally be the file to import in the HTML and the less would look something like this:

@import "sharedVariables.less";

@import "widgets/navigationWidget.less";
@import "widgets/blogArticleWidget.less";
@import "widgets/commentBlockWidget.less";
@import "widgets/commentWidget.less";

And in the HTML:

<link type="text/css" rel="stylesheet" href="/css/main.css" />

This would generate a single css file that is applied to any device, but it is not what we want. Anyone who has faced web development has found the need to apply small CSS tweaks to target certain devices or even specific browsers (i.e.: IE8). So an interesting way to solve this would be to import specific CSS files that would include only the CSS needed for that target.

Using some pseudo-templating code:

$if target == DESKTOP:
 <link type="text/css" rel="stylesheet" href="/css/devices/desktop.css" />
$else if target == MOBILE:
 <link type="text/css" rel="stylesheet" href="/css/devices/mobile.css" />

This is actually easy to do using less mixins. Mixins can be triggered depending on states of variables. So if we have a certain widget which needs to have different properties on mobile and desktop, we can use them. This is how they look like:

.widget.navigationWidget {
 
 (...) /* other code */

 .adaptative-section() when (@mobile) {
  background: @mobileBackground;  
 }
   
 .adaptative-section() when (@desktop) {   
  background: @desktopBackground
 }
 .adaptative-section;
}

You probably noticed the variables @mobile and @desktop, and so far we have no reference to where they are defined. Let's change the structure of our .less files, and add two more files "mobile.less" and "desktop.less" under a 'devices' folder. These will be the files which will generate the CSS that we use to import.

less/
 - main.less
 - sharedVariables.less
 - devices/
  - mobile.less
  - desktop.less
 - widgets/
  - navigationWidget.less
  - blogArticleWidget.less
  - commentBlockWidget.less
  - commentWidget.less

We can now have our 'mobile.less' file looking like this:

@desktop: false;
@mobile: true;

@import "../main.less";

and the desktop one:
@desktop: true;
@mobile: false;

@import "../main.less";

By importing these two files directly into the CSS generation we can build CSS files that contain only the CSS required for each specific device.

Hope this is usefull. Leave a comment if it helped you.