RTL Layout
When a visitor views a trainer’s site in Arabic or Persian, the platform sets dir="rtl" on the <html> element. This automatically mirrors many CSS properties, but you’ll need explicit overrides for some layouts.
What dir="rtl" does automatically
The browser automatically flips these when dir="rtl" is set:
- Text alignment — defaults to right-aligned
- Table layout — columns flow right-to-left
- CSS logical properties —
margin-inline-startadapts to direction
What it does not flip automatically:
- Flexbox
rowdirection —flex-direction: rowstill goes left-to-right - Absolute/fixed positioning —
left: 0stays on the left - Transforms —
translateX(10px)still moves right - Animations — slide-in-from-left still comes from the left
- Padding/margin physical properties —
padding-leftstays left
CSS Module RTL overrides
Use :global([dir="rtl"]) in your CSS Modules to target RTL mode:
/* Navigation flows left-to-right by default */
.nav {
display: flex;
flex-direction: row;
gap: 1.5rem;
}
/* Flip navigation for RTL */
:global([dir="rtl"]) .nav {
flex-direction: row-reverse;
}Common patterns
Horizontal flexbox:
.row {
display: flex;
gap: 1rem;
}
:global([dir="rtl"]) .row {
flex-direction: row-reverse;
}Absolute positioning:
.badge {
position: absolute;
right: 1rem;
top: 1rem;
}
:global([dir="rtl"]) .badge {
right: auto;
left: 1rem;
}Margin and padding:
.icon {
margin-right: 0.5rem;
}
:global([dir="rtl"]) .icon {
margin-right: 0;
margin-left: 0.5rem;
}Or use CSS logical properties (recommended):
/* Automatically adapts to text direction — no RTL override needed */
.icon {
margin-inline-end: 0.5rem;
}Prefer CSS logical properties where possible. Properties like margin-inline-start, padding-inline-end, inset-inline-start, and border-inline-end automatically adapt to the text direction without needing RTL overrides.
Tailwind RTL prefix
Tailwind includes an rtl: prefix for RTL-specific utilities:
<div className="flex flex-row rtl:flex-row-reverse gap-4">
<span className="ml-2 rtl:ml-0 rtl:mr-2">Text</span>
</div>Flipping animations
Slide animations need to reverse direction in RTL:
.slideIn {
animation: slideInLeft 0.3s ease-out;
}
@keyframes slideInLeft {
from { transform: translateX(-100%); }
to { transform: translateX(0); }
}
:global([dir="rtl"]) .slideIn {
animation: slideInRight 0.3s ease-out;
}
@keyframes slideInRight {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}Common RTL gotchas
Icons with directional meaning
Some icons imply direction (arrows, chevrons, progress indicators). These should flip in RTL:
.chevron {
display: inline-block;
}
:global([dir="rtl"]) .chevron {
transform: scaleX(-1);
}Icons that should not flip: play buttons, checkmarks, phone icons, search icons.
Progress bars
Progress bars that fill left-to-right should fill right-to-left in RTL:
.progressBar {
direction: ltr; /* Force LTR so percentages work correctly */
}
:global([dir="rtl"]) .progressBar {
direction: rtl;
}
.progressFill {
transform-origin: left;
}
:global([dir="rtl"]) .progressFill {
transform-origin: right;
}Mobile menu slide-in direction
The mobile menu should slide in from the right in LTR and from the left in RTL:
.mobileMenu {
position: fixed;
top: 0;
right: 0;
transform: translateX(100%);
transition: transform 0.3s ease;
}
.mobileMenu.open {
transform: translateX(0);
}
:global([dir="rtl"]) .mobileMenu {
right: auto;
left: 0;
transform: translateX(-100%);
}
:global([dir="rtl"]) .mobileMenu.open {
transform: translateX(0);
}Testing RTL
The local dev server lets you switch locales. Test with Arabic (ar) to verify your RTL layout:
npm run dev -- --template my-template --locale arTemplates that don’t properly support RTL will be rejected during review. Test every page and component in RTL mode before submitting.