Neil
Carpenter

Design & front-end development

Blog

Thoughts

General thoughts on web design and front-end development - trends, frameworks, tools, APIs, tips, new techniques, old techniques, good cat videos, funny GIFs etc etc.

Graceful degradation workflow using feature-detection and CSS preprocessors

Wow I haven’t written anything on here in a while. Need to sort that out… Anyhoo, this is just a quick note on how I use CSS preprocessors with browser feature-detection for neat and tidy graceful degradation.

Feature detection basics – Modernizr

First off, when I say ‘feature-detection’, I’m mostly talking about Modernizr. If you’re not familiar with Modernizr then check out this intro post on HTML5 Doctor, or just Google it, there’s a heap of resources out there. And just as an aside (before I’ve actually really started…), I consider the the best practice of feature-detection, and the existence of libraries such as Modernizr a huge step forward for front end development in the battle against outdated browsers. Feature detection lets us do whatever we want, whilst easily providing fallbacks, branching code and degrading experience as part of our standard workflow. We don’t have to provide a ‘one size fits all’ solution, we can write one solution with multiple outcomes based on supported or not-supported features in each environment. Modernizr is awesome and I would never start a non-trivial project without it.

Building on Modernizr

The reason I’m not explicitly saying Modernizr here is because I often like to build on what Modernizr offers ‘out of the box’, and tailor specifically to each project I am working on.

As an example of what I mean by this, I was recently working a project which had some images with separate shadows, the shadows would move depending on the position of the user’s mouse, to make it look like the mouse was casting the shadow kind of thing. Anyway, as this functionality was dependent on mouse input, I decided to only enable this on devices which didn’t have touch input (yes I know some devices could have touch input and mouse input, but as it’s currently too difficult to detect exactly which input a user is using, I decided to limit it here), and the functionality itself was dependent on CSS-transforms being supported by the browser. So, I could have used the .no-touch in conjunction with .csstransforms HTML classes Modernizr adds, but I prefer to do something different – give this functionality an explicit name and use that name to provide a new CSS-hook. Something along the lines of this:

(function() {
  var newClassName = ( !Modernizr.touch && Modernizr.csstransforms ) ?
  ' moving-shadows' :
  ' no-moving-shadows';
  document.documentElement.className += newClassName;
})();

This way, I can directly use the .moving-shadows and .no-moving-shadows HTML classes to branch my CSS, as opposed to the equivalent .no-touch.csstransforms using just Modernizr. This may seem like a trivial change, but it means you have a clear positive/negative CSS hook for an explicit piece of site functionality, and perhaps more importantly, it makes it patently obvious to any future developers that work on the project exactly what the purpose of a given HTML class is. I would also normally create a Browser JavaScript object to build on the Modernizr object to allow JS, as well as CSS, branching – but I think I will save the detail on that for another post.

So now we have our HTML classes on our root element, meaning we can style the page depending on the particular browser’s capabilities, and we have also tailored these classes to our particular projects’ needs using Modernizr’s feature-detection tests, but what is the best way to use these in our CSS?

Well if we were using just CSS, we could do something like this:

.moving-shadows .someElement {
  display: block;
}
.no-moving-shadows .someElement {
  display: none;
}

But let’s face it, these days if you’re not using a CSS-preprocessor to author your CSS as LESS / Sass / Stylus etc, you’re doing it wrong. One of the core features of CSS-preprocessors is the ability to nest selectors. And although this can be dangerous if used incorrectly, leading to unnecessarily bloated CSS, if done right it allows a much more well-structured and easy to read / maintain authored stylesheet.

Structuring LESS / Sass – styling ‘encapsulation’

I say ‘encapsulation’ here but I think what I really mean is ‘organisation’ – I just like the word ‘encapsulation’.

For example of the benefits of code organisation using CSS-preprocessors, ignoring the fact that I am using an element selector here (which although some people disagree with, I think is fine if you are in complete control of generated HTML and are 100% sure you know how the HTML will be used / vary across a project, and if you are confident you understand how it is going to change over the lifespan of the project), you can easily organise CSS as follows:

Before – vanilla CSS

.someBox {
  display: inline-block;
  width: 100%;
}
.someBox h2 {
  font-size: 2em;
  color: #ff0000;
}
.someBox .btn {
  float: left;
  color: #fff;
}
.someBox .btn:hover {
  text-decoration: underline;
}

Refactored using CSS-preprocessor with nested selectors

.someBox {
  display: inline-block;
  width: 100%;

  h2 {
    font-size: 2em;
    color: #ff0000;
  }

  .btn {
    float: left;
    color: #fff;
  	
    &:hover {
      text-decoration: underline;  
    }
  }
}

As you can see, there is less repetition in the above Sass / LESS, with .someBox only declared once. I consider this much easier to read – you can now imagine the structure of the HTML which is emulating this element hierarchy.

Problem is, when we come to use Modernizr, or our custom-generated HTML classes, we are using the root element (<html>), so branching styles based on differing classes here is impossible using nested selectors like above, unless our first selector is the class name on the root element itself, no?

The ‘&’ selector; CSS-preprocessor secret weapon

Turns out there is a way we can keep out authored styles ‘encapsulated’, and branch styles based on the root element’s class name – the & selector. This is a feature available in Sass, and LESS, I’m not sure about Stylus / other languages as I don’t use them.

I used to call using this selector the ‘parent’ selector, but that’s not entirely correct (even though that’s what the Sass docs call it). To be more accurate, using the & selector will prepend a selector to the CSS generated by the current Sass / LESS declaration you are in – this will mean the selector itself is a ‘parent’, or at least ancestor, but the element you are targeting is not itself a ‘parent’ element. On top of this, the & selector can be used for selector concatenation… This is probably (definitely) more easily illustrated through examples. Note the use of whitespace here can lead to several different results:

The Sass / LESS

// example 1 - 'parent' selector
.container {
  display: block;

  a {
    color: red;

    .class-to-come-first & {
      color: green;
    }
  }
}

// example 2 - 'parent' / multi-class selector
.container {
  display: block;

  a {
    color: red;

    .class-to-be-added& {
      color: green;
    }
  }
}

// example 3 - selector concatenation
.container {

  a {
    color: red;

    &:hover {
      color: green;
    }

    &.class-to-add {
      color: blue;
    }
  }
}

The output

/* example 1 - 'parent' selector - compiled CSS */
.container { display: block; }
.container a { color: red; }
.class-to-come-first .container a { color: green; }

/* example 2 - 'parent' / multi-class selector - compiled CSS */
.container { display: block; }
.container a { color: red; }
.class-to-be-added.container a { color: green; }

/* example 3 - selector concatenation - compiled CSS */
.container a { color: red; }
.container a:hover { color: green; }
.container a.class-to-add { color: blue; }

Practical applications with feature-detection – the point of this whole article…

And so, to finally actually make my point – how can we use this selector to gracefully degrade our CSS based on feature-detection generated HTML classes? Basically, wherever you wish to branch based on features, use the & selector right inside your Sass / LESS declarations – everything is kept neat and tidy, easy to follow and easy to maintain. Once again, best illustrated through examples, these are all from real projects I am, or have been working on –

Basic example

// CSS linear gradient, fall back to repeating JPG where not supported
.bg-gradient {
  height: 1000px;
  background: linear-gradient(to bottom, rgba(138,211,243,1) 0%,rgba(138,211,243,1) 3%,rgba(136,225,239,0) 90%,rgba(136,227,239,0) 100%);

  .no-cssgradients & {
    background: url(../img/bg-gradient-fallback.jpg) repeat-x;
  }
}

My most frequently used implementation

// Only appear hover styles where touch is not supported
.btn {
  text-decoration: none;

  .no-touch & {

    &:hover {
      opacity: 0.8;
    }
  }
}

This may seem a little verbose, but means hover styles are never unnecessarily applied to devices with touch input, which can cause issues on in some instances, where a first ‘click’ will trigger hover state, and second ‘click’ actually triggering native click event.

A little more involved example…

// Apply 3D transforms where possible, fall back to 2D transforms,
// and left / right absolute positioing as last resort
&.sub-content-hidden {
  -webkit-transform: translate(100%, 0);
     -moz-transform: translate(100%, 0);
      -ms-transform: translate(100%, 0);
       -o-transform: translate(100%, 0);
          transform: translate(100%, 0);
  -webkit-transform: translate3d(100%, 0, 0);
     -moz-transform: translate3d(100%, 0, 0);
          transform: translate3d(100%, 0, 0);

  .no-csstransforms & {
    position: absolute;
    left: 100%;
  }
}

This is showing how using the & selector can build on the native error handling / cascading nature of CSS. Yes, you could keep all styles related to the .no-csstransforms class by themselves, but personally I like to keep the CSS for an individual element / piece of functionality together in this way.

The future

The @supports rule looks like it will be with us shortly, which will allow us to do this natively. And I would presume when that happens, CSS preprocessors will allow us to declare @supports statements within a nested selector statement, much as we can do today with media queries, which would be just swell.

Share this

Leave a comment...?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>