Ahmad Alfy's

Fun with Front-end development and WWW

Let's Talk About RTL

26 Jul 2014

Arabic is the 7th most spoken language in the digital sphere. The number of the Arabic websites is increasing every day and the number of the websites localizing their content to attract more Arabic speaking users is growing. The techniques used to develop RTL websites are still misunderstood or mysterious for some. I will try to shed some light on the problems I have been facing and the tricks I am using to build RTL websites.

Note: Arabic is not the only language that is written from right to left. Persian and Hebrew are also written in the same way so relevant speakers might find the posted information useful.

The semantics

The first step is to declare the direction of the document by attaching the dir attribute to the html tag with the value rtl. This will affect the document rendering in many ways:

  • Text will naturally be aligned to the right.
  • Margin and padding of some elements will be calculated differently.
  • Table directionality.

Note: The value of using the direction attribute in the HTML rather than the CSS is to add a semantic value. The change we are making is not just for presentations. It will also ensure that the document will render using RTL if for any reason the CSS files fails to load. The dir attribute is also used in conjugation with the lang attribute which specify the natural language of the document.

Text alignment

Text natural rendering is different from aligning text to the right. Consider the following:

See the Pen jGkav by Ahmad Alfy (@ahmadalfy) on CodePen.

If you pay close attention, you will find that the full stop that is supposed to come by the end of the sentence is coming at the beginning on the first paragraph. The second paragraph however, placed the full stop at the correct place. That’s not everything, let’s try and mix some Arabic and English text together using the same example we used before:

See the Pen arqLy by Ahmad Alfy (@ahmadalfy) on CodePen.

The first paragraph is corrupted and almost unreadable. This image explain what is wrong with it:

Figure displaying how the first example should be rendered. The first part of the paragraph is placed by the end.

Using the RTL direction defines a (complex) algorithm for determining the proper directionality of text. This will ensure that the whole sentence will be presented as RTL and the embedded English sequences will be properly reversed by the bidirectional algorithm.

Working with numbers

Even in the languages that are written from right to left, numbers are meant to be read from left to right. Applying RTL direction to it corrupts its display. Check out the following example:

See the Pen kGnat by Ahmad Alfy (@ahmadalfy) on CodePen.

If we decided to eliminate the spaces between the numbers and use dashes, we still face the problem of the "+" character that comes at the beginning.

The solution is fairly easy. Usually wrapping it in a span and giving it a class number and styling it using CSS:

span.number {
  direction: ltr;
}

Calculating margin and padding

User agents apply default margin and padding to some elements like ordered, unordered and definition lists. Setting a document’s direction to RTL will produce a mirror image of the element like this:

See the Pen caork by Ahmad Alfy (@ahmadalfy) on CodePen.

The way the browser achieves it is very interesting. Webkit based browsers and Mozilla Firefox uses non-standard CSS properties:

/* Webkit based browsers */
ul {
  -webkit-margin-start: 0px;
  -webkit-margin-end: 0px;
  -webkit-padding-start: 40px;
}

/* Mozilla Firefox */
ul {
  -moz-margin-start: 0px;
  -moz-margin-end: 0px;
  -moz-padding-start: 40px;
}

These properties would have made life easier if they were supported by every browser. The -start will be translated to be right or left according to the document’s direction. It makes perfect sense to use it except that they are not supported by Internet Explorer. When I tried to understand how IE handles this issue I didn’t find anything unusual. It utilizes the regular padding property and the browser itself flips it.

Table directionality

When the browser render a table in RTL mode the starting point of the table will change. Try to change the direction of the table below using the checkbox:

See the Pen Hqmyd by Ahmad Alfy (@ahmadalfy) on CodePen.

The stylesheet

Changing an existing website to become RTL is achieved by writing CSS that overrides the original stylesheet to make a mirror image of the original layout. The changes mainly affect the following properties:

  1. Text aligning. If it was specifically declared, it should be flipped.
  2. Floats. It's easy to flip floats; right becomes left and vice versa.
  3. Margin and padding. Values should change from clockwise to counter-clockwise. Individual horizontal properties should be resetted and the counter property should be set.
    .selector-1 {
      margin: 5px 10px 0 15px
    }
    /* RTL override */
    .selector-1 {
      margin: 5px 15px 0 10px
    }
    .selector-2 {
      margin-left: 15px;
    }
    /* RTL override */
    .selector-2 {
      margin-left: 0; /* resetting the value */
      margin-right: 15px
    }
    
  4. Positioned elements. The horizontal property should be set to auto and the counter property takes its value. For example:
    .selector {
      position: absolute;
      left: 50px;
      top: 10px;
    }
    /* RTL override  */
    .selector {
      left: auto;
      right: 50px;
    }
    
  5. Border radius. It follows a pattern similar to the clockwise approach. If it isn't set to a single value, it should change. For example:
    .selector {
      border-radius: 0 5px 5px 0;
    }
    /* RTL override  */
    .selector {
      border-radius: 5px 0 0 5px;
    }
  6. Transforms. Some values of transform like rotate should be changed manually. The following arrows are rotated to point to the beginning of the text. Check the CSS tab:

    See the Pen JFput by Ahmad Alfy (@ahmadalfy) on CodePen.

  7. Background position. This is one of the most complicated issues when you modify a design to become RTL. The main problem is that the starting point for the horizontal positioning of a background is always set from the left. This has several implications. For simplicity, let's start with the condition where the horizontal position of the background is set to 0. The override should change that to 100% as follow:

    .selector {
      background-position: 0 5px;
    }
    /* RTL override  */
    .selector {
      background-position: 100% 5px;
    }
    

    If we have an element without a defined width that has a background image starts after 5px from the left, there is no way to make its background start 5px from right. If the element width is set to a fixed value, then we could define a specific x position using pixels. Consider the following example

    .selector {
      width: 100px;
      padding-right:15px;
      background-position: 5px 5px;
    }
    /* RTL override  */
    .selector {
      padding-right:0;
      padding-left: 15px;
      background-position: 195px 5px;
    }
    

    There is an ugly solution to fix this issue. The image could be created with the desired amount of empty pixels and set the horizontal background position to be 100%.

    What's the case if we are using sprites? That's another disaster! Setting the horizontal background position to 100% will not work because the total image dimensions are different from the single sprite we are trying to use. For example if we are trying to use Pintrest icon on the following sprite, setting the horizontal background position to 100% will display a blank space:

    Figure displaying a sample sprite image.

    The ultimate fix to overcome all this headache is to use pseudo-elements and create inline blocks using the background dimensions and position it wherever we like.

The implementation

There are several approaches to change an existing design to make it RTL. None of the approaches are 100% automatic.

  1. Using different stylesheets for the RTL version

    This method became popular recently due to the rise of different tools that automatically parse the CSS files and produce an RTL version.

    Pros:

    • Very easy to use and to maintain.

    Cons:

    • The generated files contain all the properties even those that shouldn't be overridden. When the user tries to switch between the RTL and LTR versions, he will load a lot of unnecessary CSS again. 200KB of CSS could be overridden only by 10KB of CSS. This approach will create a new 200KB file.
  2. Loading an additional file that contains the overriding information

    Creating a file that contains only the necessary properties that override the original CSS and load it last.

    Pros:

    • The result file size is usually small.
    • It is easy to maintain.

    Cons:

    • Makes an extra HTTP request.
  3. Adding overrides to the same CSS file.

    This is by far the best approach. The overriding CSS is added to the same file. To make is specific to the RTL pages, we either add a specific class to the html tag of these pages or use the attribute selector like this:

    .selector {
      property: value;
    }
    /* Using class to override  */
    html.rtl .selector {
      property: value;
    }
    /* Using data attribute to override  */
    [dir="rtl"] .selector {
      property: value;
    }
    

    We could even do better by adding SCSS to the flavor! Using the parent selector the overriding information could be added near the original declaration granting us better readability and easier maintenance.

    /* SCSS version */
    .element {
      padding: 0 0 10px 15px;
      margin-left: 25px;
      [dir="rtl"] & {
        padding: 0 15px 10px 0;
        margin-left: 0;
        margin-right: 25px;
      }
    }
    /* The produced CSS will be: */
    .element {
      padding: 0 0 10px 15px;
      margin-left: 25px;
    }
    [dir="rtl"] .element {
      padding: 0 15px 10px 0;
      margin-left: 0;
      margin-right: 25px;
    }
    

Final thoughts

I have helped converting a lot of existing websites to become RTL over the past few years. There is a Chrome extension called My Style that facilitated the process for me. This extension add a textarea to the pages the user visit. It is toggled by pressing ctrl + m. Whenever CSS declaration is added to it, it will affect all the pages on the this domain. The CSS isn’t removed until the user remove it manually. Using this extension, I start adding the overrides till I get the prefect result.

If you created RTL websites before or use a different approaches, please feel free to leave comments or questions.

I would like to express my gratitude for the awesome David Walsh and Kareem El Ansary for taking time to review this article.

comments powered by Disqus