A Quick Guide To CSS Logical Properties
Tags: html, css, webdev
Published: 2021-06-16 13:57:47

This is a post about *CSS Logical Properties*, with basic and advanced usage examples. If you think you don't need *CSS Logical Properties*, think again!

---

If you've ever worked on a website for a global client, you'd come across the terms “left-to-right” (`ltr`) and “right-to-left” (`rtl`).

“left-to-right”-languages include most Western languages, like English or French, while “right-to-left”-languages include Arabic, Farsi and Hebrew.

On a website, you switch the (language)-direction by using `dir="ltr"` (or `rtl`) in HTML, or by using the `direction`-property in CSS.

So which properties will be affected, when you switch direction?
Basically all properties, that include the words `left` or `right`.

To get a quick overview, paste this in your `console`:

```js
const logical = ['right', 'left'];
const props = [...getComputedStyle(document.body)].filter(entry => logical.some(key => entry.includes(`-${key}`)));
console.table(props);
```

But that's not all! Some languages are written *vertically*, using the CSS `writing-mode`-property. That means we need to include `top` and `bottom` to our snippet as well:

```js
const logical = ['top', 'right', 'bottom', 'left'];
``` 
**As well as** most properties containing the words `height` or `width`.

If you position an element with `position: absolute;` and `left: 0;`, that will **not work** when you change the direction — or: it will work, but not as intended — since the element will stay `left`, while all the other content change direction.

To fix it, you *could* write some extra CSS for `rtl`, like:

```css
[dir="rtl"] .selector {
  left: auto;
  right: 0;
}
```

But … that would be *way* too much work for a website with many components, and just add a lot of unnessecary CSS to the solution.

Luckily, there's a smarter way. 

---

## CSS Logical Properties

*From MDN*:

> CSS Logical Properties and Values is a module of CSS introducing logical properties and values that provide the ability to control layout through logical, rather than physical, direction and dimension mappings

With *Logical Properties*, `left` and `right` are `inline`, while `top` and `bottom` are `block`. 

To indicate direction within this “box”, we use the words `start` and `end`. 

So, for positioning, the new values are:

| Value    | Logical Value      |
| -------- | ------------------ |
| left     | inset-inline-start |
| right    | inset-inline-end   |
| top      | inset-block-start  |
| bottom   | inset-block-end    |

With logical properties, you can also set all of them in one go, something that was not possible before:

```css
.before {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}
.after {
  position: fixed;
  inset: 0;
}
``` 

`margin-top` becomes `margin-block-start`, while `margin-bottom` becomes `margin-block-end`.

`margin-left` becomes `margin-inline-start`, and `margin-right` becomes `margin-inline-end`.

Etcetera: `border-block`, `border-inline`, `padding-block` … 

The only properties that deviate from this format, are the “border-radii”:

| Value                      | Logical Value             |
| -------------------------- | ------------------------- |
| border-top-left-radius     | border-start-start-radius |
| border-top-right-radius    | border-start-end-radius   |
| border-bottom-left-radius  | border-end-start-radius   |
| border-bottom-right-radius | border-end-end-radius     |


But enough talking. The [documentation is here](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties), now, let's look at some examples!

---

## Examples
Here's a product-card, with a `dir="ltr"`:
![01-ltr](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u644lhit2e7wq4qp3sjf.png)

Here's the same card, but with `dir="rtl"`:
![02-rtl](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gpc8r03i07926d8w4clf.png)
 
Hmm, that “Best value”-caption is placed with `position: absolute` and `left: 0;`, and it will stay there, even if we change the direction.

Let's change that to:
```css
.caption {
  inset-inline-start: 0;
  position: absolute;
}
```

![03-rtlfix](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n8i6hp3qeh25exq2unba.png)
 
Much better! Now, let's add a *splash* with `inset-inline-end: 0`:

![04-splash-ltr](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jlv5j0wxde2sjyx5ig4f.png)

Looks good, but it could do with some spacing. Let's not hardcode the spacing, as an editor might want to position this *splash* as s/he wants, using *Custom Properties* `translate-x: var(--S-tx, -10%)` and `translate-y: var(--S-ty, 10%)`:

![05-splash-txltr](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bjm9w46xjqzstrd5fbse.png)
 
Now, what happens when we switch direction?

![06-splash-txrtl](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r9kd5dezk9iah7aqk0v8.png)

Hmm, not good - the translated values persist. And — as I mentioned — if the editors can dynamically change the position, we **cannot** hardcode the values. 

Unfortunately, `translate-inline` or `translate-block` does not exist, so let's add a property for `scaleX`, `--S-sx`, with a default value of `1`:

```css
.splash {
  transform:
    scaleX(var(--S-sx, 1))
    translateX(var(--S-tx, 0%))
    translateY(var(--S-ty, 0%))
}
```

Then, with `dir="rtl"`, we can simply update that property:

```css
[dir="rtl"] .splash { --S-sx: -1; }
```

That works! `scaleX(1)` becomes `scaleX(-1)` — but it will also flip the text-lines! To fix this, we'll add a wrapper around the text-lines, and flip that too:

```css
[dir="rtl"] .splash-lines { transform: scaleX(-1) translateX(-100%); }
```

![07-splashtxrtlfix](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g62vl19798qit784hqe2.png)

In this example, you could also have used `margin-inline-end: -10%;` and `margin-block-start: 10%;` — but I use the `translate` and `scaleX`- method, so the *splash* can have different shapes, that will then be flipped:

Here's another example in `ltr`:
![12-splashshape1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fqqf1d1eqvpd421xs2qv.png)

And `rtl`:
![13-splashshape2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tlt8cbfrnobp8c5t2nbn.png)

As a final example, let's add an image (and a wrapper) using:

```css
.image {
  transform:
    scaleX(var(--I-sx, 1))
    translateX(var(--I-tx, 0))
    translateY(var(--I-ty, 0));
}
.image-wrapper {
  inset-block-end: 0;
  inset-inline-end: 0;
  transform: scaleX(var(--IW-sx, 1)) translateX(var(--IW-tx, 0));
}
```

We'll translate it like the splash, and add a `drop-shadow`:

![08-imgltr](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pg7887z2jco08jfnz2bu.png)

Using the same principle as before, but using a variable from the `.image` in a `calc()`-ulated property on the `.image-wrapper`, we can flip and adjust the position like this:

```css
[dir="rtl"] .image { --I-sx: -1; }
[dir="rtl"] .image-wrapper { --IW-sx: -1; --IW-tx: calc(-1% * var(--I-tx)); }
```

![09-imgrtl](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1x8yj217i8cdfqc0qgag.png)

If we add a `perspective`-variable and a `rotateY`-variable, we can go even further:

![10-imgrotate](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4phnsmgy0lw7ju7wxlr.png)

And then just update the variables for `rtl`:

![11-imgrotatertl](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7i02j4efah78leg2sem2.png)

## Conclusion

Should you use *CSS Logical Properties* even though your website is only `ltr` now? Yes! You have nothing to lose, and your website is future-proof if you want to add international support later.

Thanks for reading!