CSS Preprocessors and Parent Selectors

By  on  

Working on web sites and web apps that require RTL support is hard because ensuring correct display in RTL is made more difficult by the fact that we either don't have the CSS properties and values to do so or that the existing support isn't widely used enough yet.  We have values like start for text-align and we have properties like -moz-margin-start, but they aren't supported everywhere despite knowing that RTL is an important aspect of global sites.

So what do we ultimately have to do?  Repeat selectors with a new RTL value and sometimes even an offset:

/* ltr / default */
.some {
	.thing {
		> .is {
			.here {
				margin-left: 20px;
			}
		}
	}
}

/* rtl */
html[dir=rtl] {
	.some {
		.thing {
			> .is {
				.here {
					margin-right: 20px;
					margin-left: 0;
				}
			}
		}
	}
}

I consider this a nightmare for a few reasons:

  1. If you have to change the default nested CSS structure, you must remember to do so for the separate RTL block as well
  2. If you have to offset the original rule, you're required to remember to offset the original and then set the new rule

In tinkering with Stylus, I've found an excellent solution for curing all of these problems with a simple mixin:

/* mixin definition ; sets LTR and RTL within the same style call*/
bidi-style(prop, value, inverse-prop, default-value) {
	{prop}: value;

	html[dir=rtl] & {
		{inverse-prop}: value;
		{prop}: default-value;
	}
}

/* usage */
.some {
	.thing {
		> .is {
			.here {
				bidi-style(margin-left, 20px, margin-right, 0); /* setting LRT and RTL! */
			}
		}
	}
}

The mixin above is a simple but sends me into a state of euphoria.  Instead of having to copy and maintain the nested structure, this mixin allows me to set the property values for LTR and RTL in the same place in code, avoiding the need to create separate RTL blocks.  And this scenario, of simply swapping out properties depending on direction, covers 95% of direction scenarios.

I know that LESS also accommodates for this pattern but I'm not sure if SASS does as well.  What's also nice is that RTL isn't the only scenario where this is useful; you could also use this for feature-based stuff like Modernizr CSS classes:

.gradient-background {
	background-image: linear-gradient(top, #555, #333);

	.no-cssgradients & {
		background: url("background.png")
	}
}

Brilliant!  This simple structure makes coding CSS and organizing different states a million times easier.  In an ideal world, the "start" properties and values would be in place but until then, use this type of strategy to make your life easier!

Track.js Error Reporting

Recent Features

  • Facebook Open Graph META Tags

    It's no secret that Facebook has become a major traffic driver for all types of websites.  Nowadays even large corporations steer consumers toward their Facebook pages instead of the corporate websites directly.  And of course there are Facebook "Like" and "Recommend" widgets on every website.  One...

  • 5 HTML5 APIs You Didn’t Know Existed

    When you say or read "HTML5", you half expect exotic dancers and unicorns to walk into the room to the tune of "I'm Sexy and I Know It."  Can you blame us though?  We watched the fundamental APIs stagnate for so long that a basic feature...

Incredible Demos

  • WebKit Marquee CSS:  Bringin’ Sexy Back

    We all joke about the days of Web yesteryear.  You remember them:  stupid animated GIFs (flames and "coming soon" images, most notably), lame counters, guestbooks, applets, etc.  Another "feature" we thought we had gotten rid of was the marquee.  The marquee was a rudimentary, javascript-like...

  • Create Snook-Style Navigation Using MooTools

    Jonathan Snook debuted a great tutorial last September detailing how you can use an image and a few jQuery techniques to create a slick mouseover effect. I revisited his article and ported its two most impressive effects to MooTools. The Images These are the same...

Discussion

  1. yes, SASS does support that pattern too. even the last time I used less, it doesn’t work but it was a long time ago.

  2. As Bobby said it’s fairly easy in SASS as well. Required a few tweaks but here is a SASS version for those interested http://codepen.io/fleeting/pen/AqHtu.

  3. Denis Borovikov

    You can upgrade bidi-style mixin, if make function like compass opposite-position() http://compass-style.org/reference/compass/helpers/constants/

    There is no need to pass inverse property, because you always can calculate it from property itself.

  4. André Machado

    Nice aproach.

    I am Brazilian but work in Dubai (UAE), have the constant need to work with “rtl / ltr” but had not yet achieved an elegant way to work with exceptions to “rtl” rules.

    Thanks for the tip!

  5. Also have such functions in Sass,like this:

    Sass:

    @mixin bidi-style($prop,$value,$inverse-prop,$default-value){
    	#{$prop}: $value;
    	html[dir=rtl] : $value;
    		#{$prop}: $default-value;
    	}
    }
    
    .some {
    	.thing {
    		> .is {
    			.here {
    				@include bidi-style(margin-left,20px,margin-right,0);
    			}
    		}
    	}
    }
    

    Compiled in the CSS:

    .some .thing > .is .here {
      margin-left: 20px; }
      html[dir=rtl] .some .thing > .is .here {
        margin-right: 20px;
        margin-left: 0; }
    

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!