Conditional RTL and LTR CSS

Vahid Mohammadi
3 min readApr 16, 2023

--

Supporting bidirectional is easily handled with CSS logical properties but they’re not enough. Sometimes we want to apply a style only in LTR and not in RTL or vice versa. A simple trick with CSS custom properties can help us achieve that.

This trick supports all properties even non-directional CSS properties like color. Basically, this is not limited to right-to-left or left-to-right conditions and you can use this for any conditional CSS.

Conditional CSS Using Custom Properties

Before getting to the trick, we should know a few things about CSS custom properties (CSS variables):

  1. You can set a default value for custom properties:
.my-class {
/* if no value is available for --padding-left, 8px will be used */
padding-left: var(--padding-left, 8px);
}

2. If a custom property has an invalid value, browsers will set the invalid value and the default value won’t be used.

.my-class {
--padding-left: invalid-value;

/* a fallback value */
padding-left: 16px;

/* since "invalid-value" has no meaning in CSS,
so now padding-left has an invalid value
therefore it's not 8px or 16px */
padding-left: var(--padding-left, 8px);
}

3. If the value of a custom property is inital or unset it’ll be ignored and the default value will be used.

.my-class {
--padding-left: unset;

/* "unset" is ignored and padding-left will be 8px */
padding-left: var(--padding-left, 8px);
}

Set a value only if it’s RTL or LTR

Of course you can use the html[dir] attribute in CSS like this:

html[dir=rtl] {
/* RTL only CSS */
}

But it’s not always applicable. One example is Angular components which don’t have access to the html tag.

Let’s use the above information:

html {
--is-ltr: unset;
--is-rtl: disabled;
}
html[dir=rtl] {
--is-ltr: disabled;
--is-rtl: unset;
}

.my-div {
color: var(--is-rtl, green);
}

Here.my-div will be green only if it’s RTL. On LTR, it’ll have the default value.

It’s nice, isn’t it? But there’s a caveat. Look at the code below:

html {
--is-ltr: unset;
--is-rtl: disabled;
}
html[dir=rtl] {
--is-ltr: disabled;
--is-rtl: unset;
}

.my-div {
color: blue;
}

.my-div.green-rtl-only {
color: var(--is-rtl, green);
}

If you try this code, in RTL it’ll be green but in LTR it’ll be black! Because the initial value of color is black (in LTR, the color property has been assigned to disabled which is an invalid value so the browser will use the initial value).

To fix this you can set the blue value for LTR:

html {
--is-ltr: unset;
--is-rtl: disabled;
}
html[dir=rtl] {
--is-ltr: disabled;
--is-rtl: unset;
}

.my-div {
color: blue;
}

.my-div.green-rtl-only {
color: var(--is-rtl, green);
color: var(--is-ltr, blue);
}

Let’s improve this:

html {
--is-ltr: unset;
--is-rtl: disabled;
}
html[dir=rtl] {
--is-ltr: disabled;
--is-rtl: unset;
}

.my-div {
--default-color: blue;
color: var(--default-color);
}

.my-div.green-rtl-only {
color: var(--is-rtl, green);
color: var(--is-ltr, var(--default-color));
}

This is not ideal and if you have full control over your project, it’s recommended to use logical properties and the html[dir=rtl] selector. But this trick can be used in special situations.

--

--