Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
BBC News' RTL (right to left) solution

Right-to-Left (RTL)

Implementation

There are two methods to use in order to flip CSS styles: interpolated properties and the flip() function.

  • Interpolation should be used for any property which has a direction (e.g. padding-left, margin-right, border-right, left, etc.)
  • flip() should be used for all other properties

Which properties need to be flipped?

  • background
  • background-position
  • border
  • border-radius
  • clear
  • cursor
  • direction
  • float
  • left/right
  • margin
  • padding
  • text-align
  • text-indent

How does it work?

// guts/mixins/_rtl.scss
@function flip($value_ltr, $value_rtl) {
  @if $rtl { @return $value_rtl; }
  @else { @return $value_ltr; }
}

$padding-left:    padding-left;
$padding-right:   padding-right;
$margin-left:     margin-left;
$margin-right:    margin-right;
$border-right:    border-right;
$left:            left;
$right:           right;

@if $rtl {
  $padding-left:  padding-right;
  $padding-right: padding-left;
  $margin-left:   margin-right;
  $margin-right:  margin-left;
  $border-right:  border-left;
  $left:          right;
  $right:         left;
}

Flip

To implement, let's take the following style as an example:

// Original Sass
.class {
  float: left;
}

For a RTL layout, float: left; should be flipped to float: right'.

We can use the flip() function to accomplish this.

// Flipped Sass
.class {
  float: flip(left, right);
}

When Sass comes across the flip() function when compiling the code, it will check what the $rtl variable is set to. This variable is set at the top level, for example in sass/intl-arabic-core.scss.

If $rtl is false, the flip() function will take the first parameter. If $rtl is true, the flip() function will take the second parameter.

The Sass will compile out as follows:

// Compiled LTR style
.class {
  float: left;
}

// Compiled RTL style
.class {
  float: right;
}

Interpolation

This method interpolates the property names from variables which are flipped higher up.

In _rtl.scss, the $padding-left variable is declared as padding-left. Then if $rtl is true, it is overridden with padding-right.

To implement, take the following style as an example:

// Original Sass
.class {
  padding-left: 8px;
}

For a RTL layout, padding-left: 8px; should be flipped to padding-right: 8px;.

In order to flip this, we have to interpolate the style property:

// Flipped Sass
.class {
  #{$padding-left}: 8px;
}

This will compile out to:

// Compiled LTR style
.class {
  padding-left: 8px;
}

// Compiled RTL style
.class {
  padding-right: 8px;
}

Markup

Placing the dir=rtl attribute on the <html> tag can cause the scrollbar in certain browsers to be flipped to the left-hand side. This is generally found to be a bad experience for RTL users.

Adding dir=rtl to the <head> tag and to a <div> wrapping the whole page, as recommended by W3C, ensures that the scrollbar isn't flipped.

<!DOCTYPE HTML>
<html>
<head dir="rtl">
    ...
</head>
<body>
    <div dir="rtl">
        ...
    </div>
</body>
</html>

More examples

// Flipped Sass
.class {
  #{$padding-left}: 8px;
  #{$padding-right}: 8px;
  #{$margin-left}: 8px;
  #{$margin-right}: 8px;
  #{$left}: 8px;
  #{$right}: 8px;
  margin: flip(1px 2px 3px 4px, 1px 4px 3px 2px);
  float: flip(left, right);
}

// Compiled LTR style
.class {
  padding-left: 8px;
  padding-right: 8px;
  margin-left: 8px;
  margin-right: 8px;
  left: 8px;
  right: 8px;
  margin: 1px 2px 3px 4px;
  float: left;
}

// Compiled RTL style
.class {
  padding-right: 8px;
  padding-left: 8px;
  margin-right: 8px;
  margin-left: 8px;
  right: 8px;
  left: 8px;
  margin: 1px 4px 3px 2px;
  float: right;
}

Best practices

  • Don't flip everything! Only flip what needs to be flipped. This will help keep the CSS as clean as possible.
  • Styles which are hiding elements by pushing them off the screen (e.g. text-align: -320px; or right: 5000%;) don't need to be flipped unless they are being transitioned or overridden.
  • If left and right properties have the same values in the same selector, they dont need to be flipped (e.g. margin-left: 0; margin-right: 0;)
  • Write long values on separate lines:
// Good
.class {
  padding: flip($gutter/2 $gutter*2.5 $gutter/4 $gutter/2,
                $gutter/2 $gutter/2 $gutter/4 $gutter*2.5);
}

// Bad
.class {
  padding: flip($gutter/2 $gutter*2.5 $gutter/4 $gutter/2, $gutter/2 $gutter/2 $gutter/4 $gutter*2.5);
}
  • Separate background-position from background shorthand
// Good
.class {
  background: $pale-grey image-url('icons-sprite.png') no-repeat;
  background-position: flip(right -792px, left -792px);
}

// Bad
.class {
  background: flip($pale-grey image-url('icons-sprite.png') no-repeat right -792px,
                   $pale-grey image-url('icons-sprite.png') no-repeat left -792px);
}
@7ErAz7

This comment has been minimized.

Copy link

7ErAz7 commented Aug 7, 2019

it might be a good practice to create a transform variable that sets transform to 180 for icons like back arrow

// in global scope
$GLOBALS_ROTATE: 0;
@if $GLOBALS_DIR == rtl {
    $GLOBALS_ROTATE: 180deg;
}


//when using directional icons like back arrow
.some-icon-selector{
    transform: rotate($GLOBALS_ROTATE);
}

Although this is a rare use case, but for example 'rtl' users will consider right as back direction since they write right to left

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.