Bidirectional horizontal rules in CSS

The dir attribute in HTML and the dir property in CSS can be used to set the direction of text and horizontal flow. Some languages are written Left-To-Right (LTR), while others are written Right-To-Left (RTL). So having this level of control is vital for creating documents and interfaces for the web.

Building an interface to support both LTR and RTL layouts is a challenge. Flexbox and Grid certainly makes things easier, but they don’t cover all our styling needs.

There are many CSS rules we write in which we specify a physical direction or side. For instance, when we write CSS to layout horizontal elements, it is common to set a margin only on a single side and set a margin of 0 (on the same side) to the first or last sibling element.

.element {
    margin-right: 1rem;
}

.element:last-child {
    margin-right: 0;
}

Or

.element:not(:last-child) {
    margin-right: 1rem;
}

In the above example we are adding a margin on the right side of each element except the last. That’s how it is written. Though what we mean when we write this is to add a margin after the element in the direction of the horizontal flow of the document (or the parent element).

A common way to style blockquote is to add a border to one side of the quote; the side we consider to come before the quote.

This is Bulma’s <blockquote> styling:

.content blockquote {
    background-color: #f5f5f5;
    border-left: 5px solid #dbdbdb;
    padding: 1.25em 1.5em;
}

And there are many other styling choices we make in which we specify a side/direction (left/right) in CSS, but what we actually mean is before/after.

So when building interfaces that support both LTR and RTL layouts, one option would be to write CSS rules like:

.class {}

html[dir="rtl"] .class {}

Or perhaps even load different stylesheets for LTR and RTL layouts. However, both options require we either write more CSS or set up our tooling to generate the appropriate flipped styles. And there are also tools that convert LTR to RTL styles for you.

Wouldn’t it be nice to work with a lower level of abstraction instead? What if we can tell the browser we are targeting the side before/after an element instead of referring to the physical directions left/right? At the end of the day this is what we mean a lot of the times. This reminds me of this tweet.

CSS has what is called logical properties. We can now tell the browser what we actually mean. Instead of using -left and -right, we can use -inline-start and -inline-end:

.element:not(:last-child) {
    margin-inline-end: 1rem;
}

Similarly instead of using -top and -bottom, we can use -block-start and -block-end.

This means we can write one set of rules that target both LTR and RTL layouts. Here is an example using the margin-inline-end property:

See the Pen Logical properties demo: inline list by Hussein Al Hammad (@hus_hmd) on CodePen.


Using the border-inline-start and padding-inline-start properties:

See the Pen Logical properties demo: blockquote by Hussein Al Hammad (@hus_hmd) on CodePen.


Firefox also supports border-*-*-radius so you can target different corners with border-start-start-radius, border-end-start-radius, etc.

See the Pen Logical properties demo: inline form by Hussein Al Hammad (@hus_hmd) on CodePen.


All demos:

See the Pen Logical properties demo by Hussein Al Hammad (@hus_hmd) on CodePen.


For a deeper explanation, you can refer to Rachel Andrew’s article Understanding Logical Properties And Values. This is not just about RTL interfaces or horizontal bidirectional CSS rules. Rachel’s article also covers logical dimensions.

Browser Support

Can I Use?: