Bidirectional CSS
Writing direction-agnostic atomic classes
One of the things I enjoy about working for a multinational corporation is getting to work on challenges that result from their large size and broad reach. Recently, my team has been redoing some pages that appear in multiple languages, including some that are read from right to left. We have systems in place to translate and localize the content on these pages, and we have addressed some of the layout issues in a one-off manner (like line wrapping because German words are long, 😄) but adding dir="rtl"to the html element for right-to-left languages showed a whole slew of new layout problems.
Our team uses a custom atomic CSS library, similar to Bootstrap, for most of our styling. A CSS library, no matter how well-written, will never fully replace bespoke styling but ours is pretty useful. It is nice to annotate markup with abbreviated classnames and know that the styles will match the spacing rhythm, font-size and colors throughout the app. Like most atomic CSS libraries, we have classes for adding margin and padding spacing to the left or right (u-mr-2, u-pl-3, etc.), and aligning text to the left or right (u-ta-left, u-ta-right). These classes ended up being the culprits of our RTL layout issues.
The problem was, they didn’t care which direction the text flowed. They were hard-coded to affect only the left or the right. The initial “fix” I used was to litter my code with Javascript logic that would apply the appropriate class to the elements based on the dir attribute of the element itself, or the html element. But then I recalled reading about why recent CSS standards like Flexbox and Grid use the words start and end rather than left and right. It’s for exactly this reason! Check it out:
So, in order to make older standards bidirectional, I decided to add some direction-agnostic margin, padding, and text-alignment classes to our atomic CSS library. Following the Grid and Flexbox standards, I used start and end instead of left and right (or rather, abbreviated the classes with s and e instead of l and r) and defaulted to a left-to-right direction for pages that didn’t implement a dir attribute on the html (or any ancestor) element. My CSS classes looked like this:
/* utility margin-start, size 1 */
[dir="ltr"] .u-ms-1, /* direction specified on ancestor */
[dir="ltr"].u-ms-1, /* direction specified on element */
.u-ms-1 { /* default, no direction specified */
margin-left: 0.15em;
}
[dir="rtl"] .u-ms-1, /* direction specified on ancestor */
[dir="rtl"].u-ms-1 { /* direction specified on element */
margin-right: 0.15em;
}/* utility margin-end, size 1 */
[dir="ltr"] .u-me-1, /* direction specified on ancestor */
[dir="ltr"].u-me-1, /* direction specified on element */
.u-me-1 { /* default, no direction specified */
margin-right: 0.15em;
}
[dir="rtl"] .u-me-1, /* direction specified on ancestor */
[dir="rtl"].u-me-1 { /* direction specified on element */
margin-left: 0.15em;
}
If you think that this looks like a lot of boilerplate, you’re right. Fortunately, it’s easy enough to write some SASS or Stylus (or whatever you use) functions to automate this. The important thing is having these tools in your arsenal. Now when the direction of the page’s text flow changes, you won’t be stuck with a ragged leading edge as unidirected (mono-direction?) classes uselessly add in spacing.
P.S. Don’t use (or rely on) the CSS property direction. See https://www.nczonline.net/blog/2010/08/03/working-with-bidirectional-bidi-text-and-rtl-languages-on-the-web/ and https://www.w3.org/International/questions/qa-bidi-css-markup for why.
