The CSS that dir="rtl" doesn't cover. ~10KB, MIT, no build step, no dependencies.
Setting dir="rtl" mirrors your text and your flex rows. Then it leaves a long
tail of things that silently don't mirror — and the result is a page that a
native Arabic reader will tell you feels broken without being able to say why.
This file is that long tail.
<link rel="stylesheet" href="rtl-core.css"> <!-- before your own CSS -->
<html dir="rtl" lang="ar">That's the whole installation.
Each of these is a real bug that ships to production constantly. rtl-core.css
fixes all of them.
The select arrow lands on your text.
It's a background-image pinned to the right, not a glyph — direction can't move
a painted background. In RTL it sits directly on top of the label.
Angled gradients don't mirror.
A 96deg hero wash keeps its physical angle, so in RTL the dark end covers the
headline instead of framing it. The mirrored angle is 360 − original.
Icons get flipped that shouldn't be. Arrows, chevrons and undo need mirroring. Search, clock, user, play and checkmarks must not — a mirrored clock runs backwards. Flipping everything is exactly as wrong as flipping nothing.
box-shadow offsets stay physical.
There is no logical box-shadow. A 12px offset lights the scene from the wrong
side in RTL. Same trap for translateX and background-position.
Phone numbers reorder themselves.
+962 6 500 0000 renders with the + at the wrong end, because the Unicode bidi
algorithm resolves each run by its content. It isn't wrong — it's guessing. The
fix is to isolate the run.
Charts read backwards. A time axis flows with the reading direction, so in RTL the plot must mirror — and then the labels need counter-flipping, or they render mirrored too.
font-weight: 300 looks broken. Arabic is drawn, not stacked: the strokes
that connect letters are thinner than the letters themselves, so they're the
first thing to vanish when you thin the font. 400 is the floor.
letter-spacing is actively harmful. Arabic is a joined script. Spacing
the letters apart doesn't read as airy, it reads as misspelled. Same for
text-transform — Arabic has no letter case.
Arabic needs more leading. Dots sit above and below the line, so 1.5
(tuned for an alphabet with neither) collides. Use 2, and size Arabic slightly
larger than Latin at the same optical size.
Most of this becomes free if you write logical CSS in the first place:
| physical | logical |
|---|---|
margin-left |
margin-inline-start |
padding-right |
padding-inline-end |
border-left |
border-inline-start |
left: 0 |
inset-inline-start: 0 |
text-align: left |
text-align: start |
rtl-core.css is, more than anything, a catalogue of the exceptions that
logical properties don't cover.
demo/index.html — the page flips to Arabic when you hit the toggle, so you can
watch each fix apply instead of taking my word for it.
Live: https://rtl-ready.higgsfield.app/free/
Logical properties are supported everywhere current (Chrome 87+, Firefox 66+, Safari 14.1+). No build step, no preprocessor, no JS.
MIT — use it in commercial work, no attribution required.
There's a larger component library and an RTL admin dashboard built on this foundation at https://rtl-ready.higgsfield.app. This file is free forever and works completely on its own.