Manage RTL CSS with Sass and Grunt

Share this article

As a native Arabic speaker I have worked on multilingual websites before, some already existed with LTR (Left to Right) CSS support which I would then have to add support for RTL (Right to Left) and some projects have started from scratch. Before I started using Sass and Grunt, supporting both directions was a nightmare and a time wasting process with much code repetition.

The important thing when working on multilingual projects using both directions is to write CSS that supports both RTL and LTR in an effective, automated and dynamic way so that we don’t have to repeat or override CSS.

The differences between the two directions in general and in most cases is the float direction, text alignment, padding and margin values.

The Problem

Let’s see how supporting RTL and LTR in the same project could be a bit cumbersome in practice and how we can solve this. In some cases, before adding direction support, we would add a new CSS file in the header and start to override and repeat code over and over to change some CSS properties like floats, padding-left or text-align for different components.

Let’s say this is the code for the RTL language template, we have added the lang="ar" attribute for the language support, this attribute value would probably be dynamically changed based on the language detection on the server side.

<!DOCTYPE html>
<html lang="ar">
  <head>
    <link rel="stylesheet" href="css/app.css">
    <link rel="stylesheet" href="css/rtl-app.css">
  </head>
  <body>
    <main>Main contant</main>
    <aside>Sidebar</aside>
  </body>
</html>

The layout requires that in the normal direction (LTR) the main section should be floated to the left, while aside should be to the right.

/* app.css */
main  { float: left; }
aside { float: right; }

Now for the RTL direction we should do the same thing above but in the opposite direction or to mirror the layout, this will be the code to override the original style in the same file.

/* app.css */
[lang='ar'] main  { float: right; }
[lang='ar'] aside { float: left; }

or in a new CSS file appended in the header (as in the example above) we can do it like so.

/* rtl-app.css */
main  { float: right; }
aside { float: left; }

The problem here is that we write more code to override the original code, loading more than one CSS file. This is not a good practice and is time consuming.

Now how we can improve this workflow, the solution I have used is to use a CSS preprocessor like Sass and a JavaScript task runner like Grunt to automate and enhance the workflow.

Setup Grunt

By using Grunt and Sass we can automate and solve the problems we have, the theory here is to write our styles in one core file and then generate two other files for each direction then include each file in the header based on the language used.

The Grunt task used for compiling Sass to CSS is grunt-sass.

{
  "name": "rtl-sass-grunt",
  "version": "0.1.0",
  "devDependencies": {
    "grunt": "^0.4.5",
    "grunt-sass": "^0.16.1"
  }
}
module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    sass: {
      dist: {
        files: {
          'css/ltr-app.css': 'scss/ltr-app.scss',
          'css/rtl-app.css': 'scss/rtl-app.scss'
        }
      }
    }

  });

  grunt.loadNpmTasks('grunt-sass');

  grunt.registerTask('default', ['sass']);
};

The Sass task takes two files one for ltr-app.scss and rtl-app.scss then generate the CSS files from them. In both files we will define some variables for floats and directions and then import the core Sass file at the end.

ltr-app.scss file

// LTR languages directions.

$default-float:          left;
$opposite-float:        right;

$default-direction:       ltr;
$opposite-direction:      rtl;

// Import the main style

@import 'style';

And rtl-app.scss

// RTL languages directions.

$default-float:         right;
$opposite-float:         left;

$default-direction:       rtl;
$opposite-direction:      ltr;

// Import the main style

@import 'style';

We can include these variables in external files, but this is just for keeping things more clear and to give you the main idea.

The style.scss will include all our CSS or include other project files. This file will imported in both ltr-app.scss and rtl-app.scss.

And this is an example of what style.scss looks like.

body {
    direction: $default-direction;
}

.media {
    float: $default-float;
    padding-#{$opposite-float}: 10px;
}

.button { background-image: url("images/arror-#{default-float}.png"); }

We have used the Sass variables like $default-float and Sass interpolation in padding-#{$opposite-float}, then Grunt can take care of this and generate two files as:

/* ltr-app.css */

body { direction: ltr; }

.media {
  float: left;
  padding-right: 10px;
}

.button { background-image: url(images/arror-left.png); }
/* rtl-app.css */

body { direction: rtl; }

.media {
  float: right;
  padding-left: 10px;
}

.button { background-image: url(images/arror-right.png); }

A good trick I experienced before is how to add an image with a specific direction as a CSS background, we can create two images arror-left.png and arror-right.png and in the Sass code above the variable will change between left and right.

Server Side Setup

For more flexibility when working on multilingual projects, configuring the back-end to provide some variables to use will be important.

What we need from the back-end is to define similar variables like we have done with Sass but this time we will use them inside templates or views. Every back-end solution should be able to provide a way to create theses variables and pass them in views. Variables like def-float and def-direction.

Setting up back-end configuration will enable us to switch between the generated CSS files for the detected direction.

<!DOCTYPE html>
<html lang="lang">
  <head>
    <%= if def-direction is ltr %>
    <link rel="stylesheet" href="css/ltr-app.css">
    <%= else %>
    <link rel="stylesheet" href="css/rtl-app.css">
    <%= end %>
  </head>
  <body>
    <main>Main contant</main>
    <aside>Sidebar</aside>
  </body>
</html>

Or we can use the variable inside the CSS file name itself.

<link rel="stylesheet" href="css/#{def-direction}-app.css">

The if statement above and the file name variable is just a Pseudocode and it will depend on your template engine and your server side configuration.

Working with Helper Classes and Templates

When working with Helper Classes mixed with template files like HTML, erb or any other template engine, we need to dynamically change the class names based on the direction, let’s see an example.

<div class="text-left">
  <!-- content -->
</div>
.text-left   { text-align: left; }
.text-right  { text-align: right; }

The div content will always align the content to the left as it takes the text-left class, but we need a way to dynamically change the -left part to -right for RTL.

We can solve this issue by using one of our template variables.

<div class="text-#{default-float}">
  <!-- content -->
</div>

Sass Mixins

We have used Sass variables and interpolations already, another way for making this simpler is to build another set of Sass mixins.

@mixin float($dir) {
  @if $dir == left {
    float: $def-float;
    } @else if $dir == right {
      float: $opp-float;
    } @else {
      float: $dir;
  }
}

@mixin text-align($dir) {
  @if $dir == left {
    text-align: $def-float;
    } @else if $dir == right {
      text-align: $opp-float;
    } @else {
      text-align: $dir;
  }
}

@mixin padding-left($unit) {
  padding-#{$def-float}: $unit;
}

@mixin padding-right($unit) {
  padding-#{$opp-float}: $unit;
}

We can later use these mixins like this

.media {
  @include float(left);
  @include padding-right(10px);
  @include text-align(left);
}

If you’re wanting more mixins for different CSS that is affected by LTR / RTL you can take a look at bi-app-sass.

Conclusion

Here we have discussed how making multilingual sites shouldn’t have to mean we are repeating chucks of code. We learned how we can tackle RTL and LTR languages using Sass. We’ve created a few mixins to demonstrate how we to do this and discussed how we can get the back-end to help make things even easier.

You can use whatever tool other than Grunt and Sass for doing the same thing, getting the idea of using Sass variables for manipulating directions and changing variables in template files is the core idea. I’ve created a Github repo for this article. You can check out the code used in rtl-grunt-sass.

Frequently Asked Questions (FAQs) about Managing RTL CSS with Sass and Grunt

What is the significance of managing RTL CSS with Sass and Grunt?

Managing RTL (Right-to-Left) CSS with Sass and Grunt is crucial for creating websites that cater to languages written from right to left, such as Arabic, Hebrew, and Persian. Sass, a preprocessor scripting language, allows developers to use variables, nested rules, and mixins, making CSS more maintainable, themeable, and extendable. Grunt, on the other hand, is a JavaScript task runner used for automation of repetitive tasks like minification, compilation, unit testing, and linting. Together, they provide a powerful toolset for managing RTL CSS effectively and efficiently.

How can I support both RTL and LTR in Sass?

Supporting both RTL (Right-to-Left) and LTR (Left-to-Right) in Sass involves creating a mixin that can generate both RTL and LTR styles. This mixin can be used to flip properties and values that are direction-dependent, such as padding, margin, float, text-align, and others. By using this approach, you can maintain a single codebase for both RTL and LTR styles, reducing duplication and potential errors.

What is the role of Grunt in managing RTL CSS with Sass?

Grunt plays a significant role in managing RTL CSS with Sass by automating the tasks involved in the process. It can be used to compile Sass to CSS, minify the CSS, and generate source maps. Additionally, Grunt can be used to watch for changes in your Sass files and automatically recompile them, saving you time and effort.

How can I use the grunt-sass package?

The grunt-sass package is a Grunt plugin for compiling Sass to CSS. To use it, you first need to install it using npm (Node Package Manager). Once installed, you can load the plugin in your Gruntfile.js and configure it to compile your Sass files. The configuration can specify the source and destination files, as well as options such as output style and source map generation.

What is rtl-sass and how can I use it?

rtl-sass is a Sass library for handling RTL (Right-to-Left) styles. It provides a set of mixins and functions that make it easier to write RTL-compatible CSS. To use rtl-sass, you need to import it into your Sass files and then use its mixins and functions as needed. This allows you to write direction-agnostic CSS, which can be flipped to RTL or LTR as required.

How can I use the grunt-contrib-sass package?

The grunt-contrib-sass package is another Grunt plugin for compiling Sass to CSS. It differs from grunt-sass in that it requires Ruby and the Sass Ruby gem to be installed. The usage of grunt-contrib-sass is similar to grunt-sass: you install it using npm, load it in your Gruntfile.js, and configure it to compile your Sass files.

What is the best way to RTL my website with Sass?

The best way to RTL your website with Sass involves using a combination of Sass’s features and third-party libraries. You can use Sass’s variables, mixins, and control directives to write direction-agnostic CSS. You can also use libraries like rtl-sass or rtlcss to handle the flipping of styles. Additionally, using a task runner like Grunt can automate the process and make it more efficient.

How can I automate the process of managing RTL CSS with Sass and Grunt?

Automating the process of managing RTL CSS with Sass and Grunt can be achieved by setting up a Grunt task that watches for changes in your Sass files and automatically compiles them to CSS. This task can also minify the CSS and generate source maps. By automating these tasks, you can focus on writing your Sass code and let Grunt handle the rest.

How can I handle direction-dependent styles in Sass?

Handling direction-dependent styles in Sass can be done by creating a mixin that flips these styles based on the direction. This mixin can take a property and its value as arguments, and output the flipped property and value for RTL. You can use this mixin wherever you have direction-dependent styles, making your CSS more maintainable and less error-prone.

How can I make my CSS more maintainable with Sass and Grunt?

Making your CSS more maintainable with Sass and Grunt involves using the features of Sass and the automation capabilities of Grunt. Sass allows you to use variables, mixins, and nested rules, which can make your CSS more readable and easier to manage. Grunt, on the other hand, can automate tasks like compilation, minification, and linting, which can help prevent errors and improve the quality of your code.

Ahmad AjmiAhmad Ajmi
View Author

Ahmad Ajmi is a self-taught front-end developer passionate about the Web, open source, and programming.

multilingual sasssass mixinsStuR
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week