We found ourselves in this situation when developing the global multi-market, multi-language master page template in Drupal for one of our clients. Being an international business, the market line-up included several portals with both left-to-right (LTR) and right-to-left (RTL) languages, which require the whole page to adapt to the respective direction. Since our setup for left-to-right was already in place and live on various markets, we needed a way to incorporate the styles and behaviour for RTL to work on the same portals.
Adjusting pages to RTL
The general rule for website layouts when it comes to RTL is to "simply" mirror them – text that is usually left-aligned becomes right-aligned, the image that was on the left side of your teaser will instead be placed on the right side, and your menu animation sliding in from the right now animates from the left side of your screen instead – you get the idea.
When a language with RTL writing mode is selected in Drupal, the direction attribute on the html-tag is automatically adjusted accordingly and can be referenced when applying styles.
The "traditional" way of translating this behaviour to your CSS is to have separate files for LTR and RTL, and load the respective one depending on the direction defined on the page. While it's still an option to do it like that, depending on the project setup it can be quite cumbersome to handle the styles separately. For maintenance, changes that are independent of the reading direction always need to be done in two places, which takes more effort to keep them aligned.
“Logical Properties” to the rescue
Fortunately, CSS supports the concept of "logical properties", which take the writing mode into account when it comes to applying directions. This applies to both the property name itself as well as the property value.
Let’s assume we are using these physical properties to add a spacing of 10px to the right side of an element and align the text to the left:
margin-right: 10px;
text-align: left;
For an LTR page we get the result that we want to achieve, but for RTL that's probably not the case – the content we wanted to separate our element from by adding the spacing should now be on the left and the text should be right-aligned.
Instead of manually overwriting them for RTL, we could simply use the logical property equivalents:
margin-inline-end: 10px;
text-align: start;
This way, the same definition works as intended for both, because "start" and "end" are defined by the direction of the writing mode itself.
While logical properties are quite well-supported by browsers by now, there are some exceptions to be aware of. For once, shorthand properties are not supported for margins and paddings – which is rather an inconvenience than a huge drawback, but still, something we heavily use – and the more problematic one, that the browser support varies for each property.
Since we were using the Sass preprocessor in our project, we were able to make use of the flexibility of mixins, which allow you to define code snippets that you can easily re-use throughout your styles by importing them in the places you need. This way, it's easy to provide fallbacks for a specific property if needed by only adding it in one place.
But even better than that, mixins also accept parameters and default values that are used in case no values are provided. This allowed us to also mimic the behaviour of shorthand properties that we are used to have, for example, on margins and paddings.
// Paddings.
// Simulates shorthand padding declaration using logical instead of directional properties.
@mixin padding($top, $right: $top, $bottom: $top, $left: $right) {
padding-block-start: $top;
padding-inline-end: $right;
padding-block-end: $bottom;
padding-inline-start: $left;
}
// How it is used:
.class-name {
@include padding(10px, 20px);
}
Naming component options
It's worth checking, though, if there are any components where the layout is not exclusively tied to and determined by the reading direction. Let's take, for example, a 50:50 teaser component with an image on one side and text on the other, where editors can choose which side the image should be placed on. How should the different options be named to ensure editors know what to expect from each one?
Option 1: "Image left" / "Image right"
The straightforward approach would probably be to just use directional properties to describe where the image goes. The definition of "right" and "left" doesn't change in RTL languages, after all. What does change however are the implications on which option would be the "default".
For this kind of teaser, it is common for the image to be the first, and the text to be the second element. The inverted option mostly comes into play to create an alternating pattern when multiple consecutive teasers are used.
In LTR, we would therefore expect "Image left" to be the default option, while in RTL it would be "Image right".
Using this approach, it needs to be ensured the option field is "translatable", meaning if a market has languages with different directions, they can select different values for this field, as it has implications on the design. Of course, this could also be done via styles, manually inverting the order for RTL, but this would create kind of a counterintuitive editor experience, where you would have to select the opposite of what you want to achieve – and we definitely don't want that.
It's also important to note that flexbox and CSS grid automatically adjust to the page direction, so they might cause some unexpected behaviour with this approach if the order of elements should actually not change to match the selected option.
The behaviour we can see in this Codepen leads us to our second option to approach the issue:
Option 2: "Image start/first" / "Image end/last"
Another solution would be to adapt the "logical properties" approach, and instead of referencing the physical direction, specify which element should come first, determining the order based on the reading direction. This way, "Image start" would be the default for both, LTR and RTL, and the styling can be based on the direction specified on the page – thus automatically adjusting when translating content from one language direction to the other, without the need to change the value of the field itself.
Both options are valid, and which one fits best can vary depending on the individual use case. The most important part is to provide the expected outcome for each select option, so editors don't have to guess when using our component.
In our case, where we were already using the directional names, (content has been created, editors are already used to working with the components as they are,...) and the support for RTL is added afterwards, we decided it would be best to keep the naming and just make sure the styles for each combination of field selection and page direction are in place.
For future projects, on the other hand, we would also go for using "logical directions" instead, because it can simplify things a lot - especially if you're also using logical properties in your styles, it can help to avoid a lot of the mind-bending that might otherwise be needed to figure out when to apply which styles.
Drupal Modules with RTL support
Luckily, we don't have to do everything ourselves! A lot of the Drupal modules we commonly use already provide RTL support. Slick-Slider for example has a Boolean property called "rtl". If set to "true", the navigation elements and direction in which images slide will automatically adapt. The only thing we have to do is check the page direction and set the "rtl" property accordingly upon initialization. The WYSIWYG editor "CKEditor", which is part of Drupal core since version 8, also supports RTL languages with an adapted UI to make content editing more intuitive.
In conclusion, adjusting our project to support right-to-left languages was a very interesting challenge that gave us the opportunity to question existing patterns and try out new features. Being aware of the implications that come with reading directions changed our way of thinking and how to approach them in our setup, allowing us to build more inclusive and future-proof products.