Frontend Development 20 min read

Comprehensive Guide to RTL Support in CSS and Frontend Development

This article explains the challenges of Right‑To‑Left (RTL) layout adaptation, compares three implementation strategies—including CSS logical properties, transform scaling, and manual RTL code—and demonstrates how tools like rtlcss, Tailwind, and Ant Design can automate or simplify RTL support for modern web projects.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Comprehensive Guide to RTL Support in CSS and Frontend Development

Introduction

Developers who have worked on international projects are familiar with RTL (Right‑To‑Left) languages such as Arabic and Hebrew. To ensure a page works correctly in all languages, you can use the dir attribute or the direction CSS property to set the writing direction.

<!DOCTYPE html>
<html dir="ltr">
  <body style="direction:rtl;">
    This is a text.
  </body>
</html>

Modern browsers can also infer direction automatically when Arabic text appears.

Practical Difficulties

RTL languages require not only reversed text but also mirrored UI elements, such as icon rotation, navigation arrows, loading spinners, scrollbars, and tab order.

Text writing order

Horizontal icon order

Back/forward icons

Page navigation direction

Horizontal scrollbar direction

Tab item order

Solution 1: CSS Logical Properties

Logical properties (and logical values) provide direction‑agnostic layout control, allowing the same stylesheet to work for both LTR and RTL without manual adjustments.

Physical properties : traditional CSS properties such as margin-left , padding-right , top that refer to absolute directions.

Logical properties : map to physical directions based on the writing mode, e.g., margin-inline-start , padding-block-end .

Logical Property Categories

Block and Inline Size

block-size : size in the block (vertical) direction.

inline-size : size in the inline (horizontal) direction.

.element {
  block-size: 50%; /* equivalent to height:50% in LTR */
  inline-size: 100px; /* equivalent to width:100px in LTR */
}

Margin, Padding, Border

margin-block-start / margin-block-end

padding-block-start / padding-block-end

border-block-start / border-block-end

margin-inline-start / margin-inline-end

padding-inline-start / padding-inline-end

border-inline-start / border-inline-end

.element {
  margin-left: 10px;
  padding-right: 20px;
  border-right: 2px solid black;
}

[dir="rtl"] .element {
  margin-inline-start: 10px;
  padding-inline-end: 20px;
  border-inline-end: 2px solid black;
}

Border Radius

border-start-start-radius (top‑left in LTR)

border-start-end-radius (top‑right in LTR)

border-end-start-radius (bottom‑left in LTR)

border-end-end-radius (bottom‑right in LTR)

.element {
  /* LTR: top‑left corner rounded, RTL: automatically becomes top‑right */
  border-start-start-radius: 50%;
}

Position Properties

inset-block-start / inset-block-end

inset-inline-start / inset-inline-end

.element {
  position: fixed;
  /* LTR: bottom‑right, RTL: bottom‑left */
  inset-block-end: 10px;
  inset-inline-end: 20px;
}

Problems with Logical Properties

Browser compatibility: IE does not support them (but IE is being phased out).

Readability: mixing physical and logical properties can make code harder to understand.

Flex, grid, and transform direction properties are not covered, requiring manual overrides.

Solution 2: transform: scaleX(-1)

Applying a horizontal flip can mirror an entire region with almost no cost:

.contains-text {
  transform: scaleX(-1);
}

However, this also flips text and images, which is usually undesirable. Additional classes are needed to re‑flip textual content, and the visual flip does not change the actual click area, leading to interaction issues, SEO concerns, and maintenance overhead.

Solution 3: Manual RTL Adaptation Code

Write separate LTR and RTL rules for each element:

.element {
  display: inline-block;
  padding-left: 10px;
}

[dir="rtl"] .element {
  display: inline-block;
  padding-right: 10px;
}

This approach is error‑prone and costly, so it is usually avoided.

rtlcss – Automatic RTL Generation

Because RTL CSS follows clear patterns, the rtlcss tool can generate RTL counterparts automatically. It provides a playground for previewing results.

Instead of authoring two sets of CSS files, one for each language direction. Now you can author the LTR version and RTLCSS will automatically create the RTL counterpart for you!

Integration with Webpack (postcss‑rtlcss)

rules: [
    {
        test: /.css$/,
        use: [
            { loader: 'style-loader' },
            { loader: 'css-loader' },
            {
                loader: 'postcss-loader',
                options: {
                    postcssOptions: {
                        plugins: [postcssRTLCSS(options)]
                    }
                }
            }
        ]
    }
]

Mode Options

The mode option controls how RTL code is generated.

combine : produces two independent, complete files (LTR + RTL). Safe but larger.

override : extracts only RTL overrides, resulting in less code.

.test1, .test2 {
  background-color: #FFF;
  background-position: 10px 20px;
  border-radius: 0 2px 0 8px;
  color: #666;
  padding-right: 20px;
  text-align: left;
  transform: translate(-50%, 50%);
  width: 100%;
}

[dir="ltr"] .test1, [dir="ltr"] .test2 {
  border-radius: 0 2px 0 8px;
  padding-right: 20px;
  text-align: left;
  transform: translate(-50%, 50%);
}

[dir="rtl"] .test1, [dir="rtl"] .test2 {
  border-radius: 2px 0 8px 0;
  padding-left: 20px;
  text-align: right;
  transform: translate(50%, 50%);
}

processUrls

When enabled, rtlcss rewrites image URLs that contain direction‑specific folder names (e.g., ltr → rtl ).

const options = { processUrls: true };

Escaping / Ignoring Sections

/*rtl:ignore*/      /* disable all rules inside selector */
.test1, .test2 {
  text-align: left;
  left: 10px;
}

.test3, .test4 {
  text-align: left;
  /*rtl:ignore*/    /* disable next line only */
  left: 10px;
}

/* RTL‑only value */
.test1, .test2 {
  font-family: Arial, Helvetica/*rtl:"Droid Arabic Kufi"*/;
  left: 10px;
}

Inline Styles

rtlcss does not process inline styles or dynamically generated CSS; use JavaScript to add direction‑specific classes instead.

Setting the Direction Attribute

To activate RTL CSS, add dir="rtl" to the <html> element as early as possible.

Read user preference from cookies or app settings on the server and render the appropriate dir value.

Detect Accept-Language header and enable RTL for languages like ar , he , fa , ur .

Run a small script before page render: const userLang = navigator.language || navigator.userLanguage; const rtlLanguages = ['ar','he','fa','ur']; const isRtl = rtlLanguages.some(lang => userLang.includes(lang)); if (isRtl) { document.documentElement.setAttribute('dir', 'rtl'); }

Third‑Party Library Adaptation

Tailwind CSS

Tailwind 3.0 introduced RTL support via utility prefixes ( ltr: , rtl: ). Tailwind 3.3 adds logical‑property utilities such as ms-0 (margin‑inline‑start) and me-0 (margin‑inline‑end).

<html dir="rtl">
  <body>
    <div class="group flex items-center">
      <div class="ltr:ml-3 rtl:mr-3">
        <p class="text-sm font-medium text-slate-300">...</p>
        <p class="text-sm font-medium text-slate-500">...</p>
      </div>
    </div>
  </body>
</html>

Tailwind also offers the @tailwindcss/rtl plugin, which converts LTR utilities to logical equivalents and generates RTL overrides.

.container {
  margin-left: 20px;
  padding-right: 30px;
  justify-content: flex-start;
  text-align: left;
}

.button {
  margin-right: 10px;
  padding-left: 20px;
}

.navbar {
  float: left;
}

After processing:

.container {
  margin-inline-start: 20px;
  padding-inline-end: 30px;
  text-align: start;
}

[dir="rtl"] .container {
  justify-content: flex-end;
}

.button {
  margin-inline-end: 10px;
  padding-inline-start: 20px;
}

.navbar {
  float: inline-start;
}

Ant Design (antd)

Ant Design provides a ConfigProvider component that can switch the whole application to RTL mode.

import { ConfigProvider } from 'antd';
import arEG from 'antd/es/locale/ar_EG'; // Arabic locale
{/* your components */}

Ant Design internally uses CSS‑in‑JS, logical properties, and runtime class replacement to achieve RTL.

Conclusion

RTL styling requires careful handling of text direction, layout mirroring, and interaction behavior. CSS logical properties offer a standards‑based solution but suffer from compatibility and coverage gaps. Transform scaling is quick but introduces visual and functional side effects. Manual overrides are reliable but labor‑intensive. Tools like rtlcss and framework‑specific solutions (Tailwind, antd) automate most of the work, allowing developers to maintain a single LTR source while delivering correct RTL experiences.

frontendWeb DevelopmentCSSRTLAnt DesigntailwindLogical Propertiesrtlcss
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.