Skip to main content
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Adam Tuttle
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Adam Tuttle ( @adamtuttle )

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

By on
Tags:

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).

Want to use code from this post? Check out the license.

Reader Comments

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel