RTL layout in Vue + Tailwind: what actually trips you up
Building a right-to-left UI in Tailwind CSS with Vue 3 is mostly logical direction props. The hard parts are the edge cases: mixed LTR/RTL content, icon mirroring, and date formatting.
The obvious part
Tailwind's RTL support is opt-in: add dir="rtl" to the root element and use logical properties — ms-, me-, ps-, pe- instead of ml-, mr-, pl-, pr-. The framework flips margins, padding, and flexbox direction automatically.
For a Vue 3 app, the pattern is: set dir on the <html> element via a head composable, and configure @tailwindcss/plugins with { future: { hoverOnlyWhenSupported: true } }. Most layout just works.
The hard parts
Mixed-direction content. An Arabic message thread still contains English words, timestamps, URLs, and emoji. A naive RTL layout breaks these — timestamps appear on the wrong side, URLs render backwards. The fix is unicode-bidi: isolate on mixed-content containers, and explicit dir="ltr" on elements that are always LTR (email addresses, phone numbers, tracking codes).
Icon mirroring. Directional icons (arrows, chevrons, send button) need to be mirrored for RTL. Lucide and most icon libraries do not do this automatically. We wrap them in a component that applies scale-x-[-1] when dir === 'rtl' and the icon is in the "directional" set.
Input placeholders. text-align: start handles the placeholder direction, but some browsers ignore it if the input does not have an explicit dir attribute. Set dir="auto" on text inputs to let the browser infer direction from the first character.
Date and number formatting. Arabic uses Eastern Arabic numerals (٠١٢٣٤٥٦٧٨٩) in some locales. Use Intl.DateTimeFormat with { locale: 'ar-AE', numberingSystem: 'arab' } rather than hardcoding Latin digits.
What we ship
Our widget supports RTL via a dir prop on the outermost container. Tenant configuration sets the locale, which drives both the dir attribute and the number/date formatting in one step. The result: the same widget renders correctly in Arabic or English without a separate codebase.
The biggest lesson: RTL is not a one-and-done change. Mixed-content edge cases keep surfacing. Build the toggle early and test with real Arabic text from the start.