Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at RIA Unleashed (Nov. 2010) with: Sumit Verma
Ben Nadel at RIA Unleashed (Nov. 2010) with: Sumit Verma

Less CSS, Relative Paths, Nested Quotes, Url() Constructs, And Post-Processing

By Ben Nadel on
Tags: HTML / CSS

This is a really tiny post, but it's something that had our team baffled for weeks. We were using an older version of RetinaJS which defined an .at2x() mixin for inserting media-query rules for retina-sized images. Everything was working great until we upgraded our Less CSS compilers; then, everything started breaking in a really weird way. After a lot of digging, we discovered that Less CSS gets a little funky when you have nested quotes that contain relative paths inside of url() constructs.

To demonstrate, here is some Less CSS that isolates the problem:

  • // -- Working. -- //
  •  
  • h1 {
  • @single-quotes: "../../Meep.png" ;
  •  
  • background-image: url( @single-quotes ) ;
  • content: @single-quotes ;
  • }
  •  
  • h2 {
  • @single-quotes: '../../Meep.png' ;
  •  
  • background-image: url( @single-quotes ) ;
  • content: @single-quotes ;
  • }
  •  
  • // -- Breaking. -- //
  •  
  • h3 {
  • @nested-quotes: "'../../Meep.png'" ;
  •  
  • background-image: url( @nested-quotes ) ;
  • content: @nested-quotes ;
  • }
  •  
  • h4 {
  • @nested-quotes: '"../../Meep.png"' ;
  •  
  • background-image: url( @nested-quotes ) ;
  • content: @nested-quotes ;
  • }

Notice that the second set of CSS rules contain values that have nested quotes - double quotes within single quotes or single quotes within double quotes. Also notice that the image paths are all relative paths that go above the current file. When we compile this Less CSS with CodeKit 2 or LiveReload (the ones I happen to use), we get the following CSS:

  • h1 {
  • background-image: url("../../Meep.png");
  • content: "../../Meep.png";
  • }
  • h2 {
  • background-image: url('../../Meep.png');
  • content: '../../Meep.png';
  • }
  • h3 {
  • background-image: url("Meep.png'");
  • content: "'../../Meep.png'";
  • }
  • h4 {
  • background-image: url('Meep.png"');
  • content: '"../../Meep.png"';
  • }

Notice that the h3 and h4 paths are completely broken, but only when used in conjunction with the url() construct; the relative paths lose their dots and one of the quotes is stripped out (leaving the string value unevenly quoted). Based on what I can gather from this GitHub thread, it appears that Less CSS is trying to do some sort of post-processing on url() values (which I don't fully understand).

Ultimately, the problem had to do with the way the older version of RetinaJS was building the paths - it was accidentally nesting quotes. The newer version of RetinaJS gets around this by not quoting its input during the JavaScript source interpolation; of course, this breaks if you don't quote your URLs. But, that's OK - if you're one of those people that doesn't quote URL values, breaking code is what you get (he says from high up on his soap box).




Reader Comments