RTL (right-to-left) adalah tulisan yang dibaca dari kanan ke kiri: Arab, Ibrani, Persia, dan Urdu. Mendukung RTL artinya seluruh halaman ikut dicerminkan, bukan cuma hurufnya. Bayangin Bu Sri mencetak brosur Homestay Kabut versi Arab untuk tamu dari Mesir. Brosur Arab dibuka dari sisi yang buat kita adalah sampul belakang, fotonya pindah ke kanan, dan tiap baris mulai dari kanan. Tapi di dalamnya, harga "Rp 700.000" dan tanggal "2026-12-12" tetap ditulis dari kiri ke kanan. Dua aturan itu yang dipelajari di sini: tata letaknya dicerminkan, angkanya enggak.
🧪 Tebakan cepat: CSS kartu Bu Sri berisi
.card { padding-left: 20px; border-left: 4px solid #15803d; }, dan tiga tombol lama menginap disusun dengandisplay: flex. Untukar-EG, Bu Sri menambahdir="rtl"di<html>. Mana yang pindah ke kanan dengan sendirinya: teksnya, urutan tombolnya, atau garis hijaunya? Jawabannya ada di bagian "CSS logical properties: awal dan akhir, bukan kiri dan kanan".
dir="rtl" di tag html
Arah halaman diatur atribut HTML dir. Nilainya ltr (kiri ke kanan, bawaan), rtl (kanan ke kiri), atau auto (browser menebak dari isinya). Taruh di <html>, bersama lang:
<html lang="ar-EG" dir="rtl">
langmenyebut bahasanya,dirmenyebut arahnya. Dua-duanya perlu:lang="ar-EG"saja gak membalik apa pun, danlang="rtl"bukan kode bahasa.- Pakai atribut
dir, bukan CSSdirection: rtl. Arah tulisan adalah bagian dari isi dokumen, jadi spesifikasi CSS sendiri menyarankan atribut HTML. Atribut itu juga dibaca screen reader dan selector:dir(). - Begitu
dir="rtl"terpasang, teks rata kanan, baris mulai dari kanan, dan tanda baca di akhir kalimat Arab ada di ujung kiri.
Di aplikasi yang bisa ganti bahasa, set keduanya dari locale:
const RTL = ['ar', 'he', 'fa', 'ur'];
function setDirection(locale) {
const language = new Intl.Locale(locale).language; // 'ar-EG' jadi 'ar'
document.documentElement.lang = locale;
document.documentElement.dir = RTL.includes(language) ? 'rtl' : 'ltr';
}
setDirection('ar-EG'); // <html lang="ar-EG" dir="rtl">
- Daftar
RTLdisimpan sendiri karena browser belum seragam:new Intl.Locale('ar-EG').getTextInfo().directionmemberi"rtl"di Chrome 147 dan Safari 26.4, tapi Firefox 146 belum punyagetTextInfo. - Kalau memakai i18next,
i18n.dir('ar-EG')memberi"rtl"dani18n.dir('ja-JP')memberi"ltr".
Flex dan grid ikut arah sendiri
Kabar baiknya: layout modern sudah membaca arah. display: flex dengan flex-direction: row menaruh anak pertama di awal baris, dan di dir="rtl" awal baris adalah kanan. Grid sama: kolom pertama dari grid-template-columns muncul di kanan.
Tombol lama menginap Bu Sri (.nights { display: flex; gap: 8px; }) di ar-EG tersusun ليلة واحدة, ليلتان, ٣ ليالٍ dari kanan ke kiri. Kami mengukur posisi tombolnya di Chromium 147, Firefox 146, dan WebKit 26.4: di ketiganya tombol pertama paling kanan, tanpa CSS tambahan. Yang justru merusak adalah menulis flex-direction: row-reverse untuk "membalik" layout Arab: flex sudah membaliknya, jadi hasilnya terbalik dua kali.
CSS logical properties: awal dan akhir, bukan kiri dan kanan
Masalahnya ada di properti yang menyebut sisi fisik: left, right, margin-left, padding-right, border-left, text-align: left. Kiri tetap kiri, apa pun arah tulisannya. Logical properties menyebut sisi menurut arah tulisan. Inline adalah arah baris (kiri ke kanan, atau kanan ke kiri), block adalah arah tumpukan baris (atas ke bawah). start adalah awal, end adalah akhir.
Ini CSS kartu Bu Sri, sebelum dan sesudah. Kami membaca hasilnya dengan getComputedStyle di Chromium 147, Firefox 146, dan WebKit 26.4, dan ketiganya sama:
| Bu Sri menulis | Di ar-EG |
Versi logical | Di ar-EG |
|---|---|---|---|
padding-left: 20px |
padding 20px tetap di kiri | padding-inline-start: 20px |
padding 20px di kanan |
border-left: 4px solid |
garis hijau tetap di kiri | border-inline-start: 4px solid |
garis hijau di kanan |
right: 12px (badge Favorit) |
badge tetap di kanan, menimpa awal judul | inset-inline-end: 12px |
badge di kiri |
margin-right: 6px (jarak ikon) |
jaraknya di sisi yang salah | margin-inline-end: 6px |
jarak di kiri ikon |
Jadi teksnya dan urutan tombol flex pindah sendiri, tapi garis hijaunya enggak, karena border-left menyebut kiri. Itu jawaban tebakannya.
/* Sebelum: kiri dan kanan fisik */
.card { position: relative; padding: 16px 16px 16px 20px; border-left: 4px solid #15803d; }
.badge { position: absolute; top: 12px; right: 12px; }
.icon { margin-right: 6px; }
/* Sesudah: awal dan akhir, satu CSS untuk kedua arah */
.card { position: relative; padding-block: 16px; padding-inline: 20px 16px; border-inline-start: 4px solid #15803d; }
.badge { position: absolute; top: 12px; inset-inline-end: 12px; }
.icon { margin-inline-end: 6px; }
padding-inline: 20px 16pxartinya awal 20px, akhir 16px.padding-block: 16pxuntuk atas dan bawah.- Pasangan lainnya:
margin-leftjadimargin-inline-start,padding-rightjadipadding-inline-end,leftjadiinset-inline-start,text-align: leftjaditext-align: start,float: leftjadifloat: inline-start,widthjadiinline-size. topdanbottomboleh tetap: atas ke bawah gak berubah untuk Arab, Ibrani, Indonesia, dan Jepang yang ditulis mendatar.- Tailwind 4.2 (yang dipakai BelajarWeb.dev) punya kelasnya:
ms-*,me-*,ps-*,pe-*,border-s-*,text-start,end-*, plus varianrtl:danltr:.
Ikon: panah dicerminkan, jam dan logo jangan
Ikon yang menunjuk arah harus dicerminkan, karena "kembali" di RTL artinya ke kanan. Ikon yang menggambar benda jangan.
| Ikon | Di RTL | Kenapa |
|---|---|---|
| Panah "Semua kamar" | dicerminkan, menunjuk ke kanan | Kembali artinya ke awal, dan awal ada di kanan |
| Jam di baris check-in | tetap | Jarum jam berputar searah di seluruh dunia. Kalau dicerminkan, jam 2 jadi jam 10. |
| Logo Homestay Kabut | tetap | Logo adalah merek; tulisannya jadi terbalik |
| Centang, tombol play | tetap | Bentuknya gak menunjuk arah baca |
.back .icon:dir(rtl) { transform: scaleX(-1); }
:dir(rtl)cocok untuk elemen yang arahnya RTL, termasuk yang mewarisi arah dari<html>atau yang arahnya dipilihdir="auto". Didukung Chromium 147, Firefox 146, dan WebKit 26.4. Selector[dir="rtl"] .iconjuga jalan, tapi cuma melihat atribut yang ditulis.- Jangan pasang
transform: scaleX(-1)di semuasvg: jam check-in jadi menunjuk jam 10 dan logo jadi terbalik. - Teks di dalam SVG ikut arah halaman. Di
dir="rtl",<text x="28">di logo Kabut berakhir di x=28, jadi sebagian tulisannya keluar kotak (diukur di Chromium 147, Firefox 146, dan WebKit 26.4). Kunci teks logo dengandirection="ltr".
Angka tetap kiri ke kanan: algoritma bidi
Teks yang mencampur arah kanan ke kiri dan kiri ke kanan disebut bidi (bidirectional). Browser mengurutkannya dengan Unicode Bidirectional Algorithm (UAX #9). Tiap karakter punya tipe arah: huruf Arab kanan ke kiri, huruf Latin kiri ke kanan, sedangkan angka dan tanda baca ikut tetangganya. Satu deret angka selalu ditulis dari kiri ke kanan, tapi letak deret itu di kalimat ditentukan tetangganya.
Kami menaruh tiga kalimat di halaman dir="rtl", lalu mengurutkan karakternya menurut posisinya di layar, dari kiri ke kanan (huruf Arab diganti _). Hasilnya sama di Chromium 147, Firefox 146, dan WebKit 26.4:
| Ditulis | Yang tampil, dibaca dari kiri | Kenapa |
|---|---|---|
الدفع عند الوصول: Rp 700.000 |
Rp 700.000 :_ _ _ |
"Rp" huruf Latin, jadi "Rp 700.000" satu potongan kiri ke kanan. Aman. |
تسجيل الوصول: 2026-12-12 |
12-12-2026 :_ _ |
Angka sesudah huruf Arab diperlakukan sebagai angka Arab. Tiap kelompok tetap kiri ke kanan, tapi kelompoknya diurutkan kanan ke kiri, jadi tahunnya pindah ke ujung kanan. |
Catatan tamu: Arriving around 22.00, thanks! |
!Arriving around 22.00, thanks |
Paragrafnya mewarisi rtl, jadi tanda seru di akhir kalimat Inggris pindah ke ujung kiri. |
Hasil Intl untuk ar-EG juga aman: "٧٠٠٬٠٠٠ IDR" dan "١٢/١٢/٢٠٢٦" membawa tanda tak terlihat U+200F (RLM, right-to-left mark) yang menjaga urutannya. Yang rusak adalah tanggal ISO polos dan teks Latin dari tamu yang diletakkan langsung di halaman RTL.
bdi dan dir="auto" untuk teks dari tamu
<bdi> (bidi isolate) mengurung isinya: arahnya ditentukan isinya sendiri, dan isinya gak menarik tetangganya. dir="auto" membuat sebuah elemen memilih arah dari huruf kuat pertamanya: huruf Latin berarti ltr, huruf Arab berarti rtl.
<p>تسجيل الوصول: <bdi>2026-12-12</bdi></p>
<p dir="auto">Arriving around 22.00, thanks!</p>
<p dir="auto">سأصل حوالي الساعة 22.00، شكرًا!</p>
- Dengan
<bdi>, tanggalnya tampil2026-12-12 :_ _, sama seperti<span dir="ltr">. - Catatan Inggris dengan
dir="auto"jadiltr: tampilArriving around 22.00, thanks!, rata kiri. Catatan Arab dengandir="auto"tetaprtl. Kamu gak perlu tahu bahasa tamunya. - Nama tamu juga teks dari tamu.
الضيف: Alex · 3 ليالٍtampil_ Alex · 3 :_: angka 3 menempel ke "Alex", jauh dari ليالٍ ("malam"). Dengan<bdi>Alex</bdi>jadi_ 3 · Alex :_. Kalau angkanya diformatIntluntukar-EG(٣), urutannya sudah benar tanpa<bdi>.
Cek di tiga browser
Algoritma bidi di tiga mesin browser memberi urutan yang sama untuk semua contoh di atas, tapi font Arab, sambungan huruf, dan pemotongan baris bisa beda. Jadi lihat hasilnya di Chromium, Firefox, dan WebKit (mesin Safari). Cara cepat: di DevTools, tambah dir="rtl" di <html>. Cara yang bisa diulang: satu script Playwright, tiga screenshot.
// cek-rtl.mjs, jalankan dengan: node cek-rtl.mjs
import { chromium, firefox, webkit } from 'playwright';
for (const engine of [chromium, firefox, webkit]) {
const browser = await engine.launch();
const page = await browser.newPage({ locale: 'ar-EG' });
await page.goto('http://localhost:5173/ar/kamar-teras');
await page.screenshot({ path: `kartu-ar-${engine.name()}.png`, fullPage: true });
await browser.close();
}
engine.name() memberi chromium, firefox, atau webkit, jadi tiap browser punya file sendiri. Yang dicek di tiap screenshot: garis hijau di kanan, badge di kiri, panah kembali menunjuk ke kanan, jam tetap menunjuk jam 2, harga dan tanggal terbaca utuh, gak ada teks yang saling menimpa.
⚠️ Jebakan yang sering ketemu
- Cuma memasang
lang.lang="ar-EG"tanpadir="rtl"gak membalik apa pun. - Meniru RTL dengan
row-reverseataufloat: right. Flex dan grid sudah ikut arah, jadi hasilnya terbalik dua kali. Pakaifloat: inline-start. - Mencerminkan semua ikon. Cuma ikon arah yang dibalik; jam, logo, dan centang tetap.
- Menaruh tanggal ISO atau teks tamu tanpa isolasi.
2026-12-12tampil12-12-2026. Bungkus dengan<bdi>, dan pakaidir="auto"untuk paragraf yang ditulis tamu.
TL;DR: Pasang lang dan dir="rtl" di <html> dari locale (ar, he, fa, ur berarti RTL), lewat atribut, bukan CSS direction. Flex dan grid ikut arah sendiri, jadi jangan pakai row-reverse. Properti fisik (padding-left, border-left, right, margin-right, text-align: left) diganti logical properties (padding-inline-start, border-inline-start, inset-inline-end, margin-inline-end, text-align: start): satu CSS untuk kedua arah. Cerminkan ikon arah dengan :dir(rtl), jangan jam dan logo. Angka selalu kiri ke kanan, tapi tanggal ISO polos sesudah huruf Arab tampil 12-12-2026; bungkus dengan <bdi>, dan pakai dir="auto" untuk teks dari tamu. Cek hasilnya dengan screenshot di Chromium, Firefox, dan WebKit.