How to make your multilingual website suitable for RTL with only HTML and CSS

Arabic, Hebrew and Persian are the most widespread Right to Left writing systems these days. When you’re making a multilingual website, where you want to combine LTR languages with RTL languages, there are some things you have to know.

You might think it’s complicated, but it’s quite easy to apply. It’s actually a gifted standard in HTML and CSS. That’s something we love about the web. No need for extra plugins. So use this knowledge to your advantage. Without further ado, here are 3 knowledgeable things to make your multilingual website suitable for RTL and LTR.

1. Define the text direction on your website

Same as you can define a language, you’re able to define a text direction on your website. Using the dir attribute to set the base direction of text for display. When the language on your website is like English, Spanish or Dutch use dir=”ltr”. When it’s written in Arabic, Hebrew or Persian use dir=”rtl”. 

<html lang=”ar-MA” dir=”rtl”>

There is also a CSS property called direction which has the same effect as the dir attribute. I would recommend the dir attribute, because it has an implicit semantic value since the order of elements change when the text direction changes. Both the HTML attribute and the CSS property cascade down and can be overwritten at a lower level.

2. Use CSS logical properties

When you’re changing your website from LTR to RTL some objects may lose the margins or paddings you defined. All CSS properties with left or right in them, need to be switched to the other side. In the old days we needed to do somethiing like this:

.absolute-element {
  position: absolute;

[dir="rtl"] .absolute-element {
  right: 50px;

[dir="ltr"] .absolute-element {
  left: 50px;

But nowadays CSS logical operators can help you solve most problems.

You can use inline for the axis on which the text is flowing and block for the axis at the right angle of the one on which the text is flowing.

I recommend you start using ‘inline’ instead of, left/right, ‘block’ instead of top/bottom, ‘start’ instead of top and end instead of bottom (reasoning from LTR languages). This way supporting different text directions will come naturally.

If you want to support top-to-bottom languages as well you might even want to reconsider using width and height and use inline-size instead of width and block-size instead of height. These CSS properties will adhere to the CSS writing-mode property.

TIL: Dmitry Belyaev pointed out to me on Twitter that there are also logical properties for absolute, relative, fixed or sticky elements. So left becomes inset-inline-start, right becomes inset-inline-end, top becomes inset-block-start and bottom becomes inset-block-end.

Here are some examples to illustrate further:

border-block-start: 10px;     	// border-top: 10px;
padding-inline-end: 10px;     	// padding-right: 10px;
margin-block-start: 10px;      	// margin-top: 10px;
text-align: start;            	// text-align: left;
inline-size: 100px;            	// width: 100px;
max-block-size: 100px;         	// max-height: 100px;

3. Give unicode ranges to fonts

The font you picked for your website might not be suitable for all languages. You can define unicode ranges to set the specific range of characters to use from a font defined by @font-face. By doing so, you make sure the font is only loaded when a character on the screen is within that specific unicode range. See below an example of rendering Arabic characters in a different font than Latin characters:

@font-face {
  font-family: "My Latin Font";
  src: url("MyLatinFont.woff2") format("woff2-variations"),
       url("MyLatinFont.woff2") format("woff2"),
       url("MyLatinFont.woff") format("woff");
  unicode-range: U+0000-007F; // Latin

@font-face {
  font-family: "My Arabic Font";
  src: url("MyArabicFont.woff2") format("woff2-variations"),
       url("MyArabicFont.woff2") format("woff2"),
       url("MyArabicFont.woff") format("woff");
  unicode-range: U+0600-06FF; // Arabic
← All blog posts