Right-to-Left (RTL) Support
Build native bidirectional and Right-to-Left (RTL) applications for Arabic, Hebrew, Persian, Urdu, Sindhi, and Pashto. Boodoo provides first-class logical property primitives, bidirectional helper classes, an automated RTL JavaScript API, and seamless integration across Web, React, Vue, Angular, Electron, and React Native.
Bidirectional Primitives
CSS logical spacing (ms-*, me-*, ps-*, pe-*) and automated component alignment for carets, dropdowns, popovers, and dialogs.
Dynamic Direction API
Runtime direction switching and localStorage persistence with boodoo.rtl, [data-boodoo-toggle="direction"], and React/Vue hooks.
Universal Multi-Platform
Native support across vanilla web, React DOM, Vue 3 / Nuxt composables, Angular directives, Electron desktop windows, and React Native mobile.
1. HTML Markup & Quick Start
Enable Right-to-Left layout globally on your page by setting the standard dir="rtl" attribute and language code on the root <html> element:
<!doctype html>
<html lang="ar" dir="rtl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>تطبيق بودو</title>
<!-- Boodoo Combined CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/boodoo@1.3.1/dist/css/boodoo.min.css">
</head>
<body>
<div class="container py-4">
<h1><span>مرحباً بك في بودو</span></h1>
<p class="lead"><span>إطار عمل تصميم حديث ومتوافق تماماً مع الاتجاه من اليمين إلى اليسار.</span></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/boodoo@1.3.1/dist/js/boodoo.min.js" defer></script>
</body>
</html>
For scoped bidirectional sections inside an otherwise LTR document, you can apply dir="rtl" or the .dir-rtl helper class to any container or widget:
<!-- Scoped Arabic/Hebrew Widget -->
<section class="card p-3 dir-rtl" dir="rtl">
<h4><span>لوحة التحكم</span></h4>
<p><span>هذا القسم يعمل بنظام RTL بشكل مستقل عن باقي الصفحة.</span></p>
</section>
2. JavaScript Direction API (boodoo.rtl)
Boodoo includes a built-in direction manager accessible via boodoo.rtl. It handles instant direction updates, localStorage persistence, and custom change notifications without page reloads:
import boodoo, { rtl } from 'boodoo';
// Get active direction ('ltr' or 'rtl')
const currentDir = boodoo.rtl.get();
// Check if document is currently in RTL mode
if (boodoo.rtl.isRtl()) {
console.log('Document is currently RTL');
}
// Set explicit direction
boodoo.rtl.set('rtl'); // Switch to Right-to-Left
boodoo.rtl.set('ltr'); // Switch to Left-to-Right
boodoo.rtl.set('auto'); // Auto-detect from browser navigator.language
// Toggle direction dynamically
boodoo.rtl.toggle();
// Listen to direction change events
document.addEventListener('boodoo.direction.change', (e) => {
console.log('Direction changed to:', e.detail.resolved);
});
Declarative HTML Direction Toggler
You can trigger direction toggling directly from buttons without writing custom JavaScript by using the data-boodoo-toggle="direction" data attribute:
<button type="button" class="btn btn-outline-primary" data-boodoo-toggle="direction">
<span>Toggle RTL / LTR</span>
</button>
<button type="button" class="btn btn-secondary" data-boodoo-toggle="direction" data-boodoo-direction-value="rtl">
<span>Force RTL</span>
</button>
3. Component & Element Behavior in RTL
When dir="rtl" or .dir-rtl is active, Boodoo automatically mirrors directional components:
| Component | LTR Default | RTL Behavior (Automatic) |
|---|---|---|
.dropdown-toggle |
Caret on right (margin-left: 0.5em) |
Caret mirrors to left (margin-right: 0.5em; margin-left: 0) |
.accordion-button |
Chevron on right (margin-left: auto) |
Chevron aligns to left (margin-right: auto; margin-left: 0) |
.offcanvas-start |
Slides in from left edge | Slides in from right edge (right: 0; left: auto) |
.offcanvas-end |
Slides in from right edge | Slides in from left edge (left: 0; right: auto) |
.alert-dismissible |
Close button on top-right | Close button mirrors to top-left (left: 0; right: auto) |
.modal-header .btn-close |
Close button aligned far right | Close button aligned far left (margin-right: auto) |
.input-group |
First addon rounded on left, right flat | Addon rounded on right, inner borders seamlessly inverted |
.pagination |
Previous page item on left | First item rounded on right; subsequent items overlap on right |
.form-check-input |
Checkbox floated left | Checkbox floated right (float: right; margin-right: -1.5em) |
.form-floating label |
Label transform origin at (0, 0) | Label transform origin pinned at top-right (100% 0) |
4. React Integration (boodoo/react)
In React applications, wrap your tree in BoodooProvider and pass the direction or dir prop. Use the useDirection() hook to consume the active layout direction anywhere in your component tree:
import React, { useState } from 'react';
import { BoodooProvider, useDirection, Button, Dialog } from 'boodoo/react';
function Header() {
const direction = useDirection();
return (
<header className="d-flex justify-content-between align-items-center mb-4">
<h3 className="mb-0"><span>{direction === 'rtl' ? 'لوحة التحكم' : 'Dashboard'}</span></h3>
<span className="badge bg-primary"><span>Direction: {direction}</span></span>
</header>
);
}
export default function App() {
const [direction, setDirection] = useState('rtl');
return (
<BoodooProvider direction={direction}>
<div className="container py-4">
<Header />
<Button variant="secondary" onClick={() => setDirection(d => d === 'rtl' ? 'ltr' : 'rtl')}>
<span>تبديل الاتجاه (Switch Direction)</span>
</Button>
</div>
</BoodooProvider>
);
}
5. Vue 3 & Nuxt Integration (boodoo/vue)
Vue 3 applications can leverage the reactive useDirection() composable from boodoo/vue to manage bidirectional state, synchronize with <html dir="...">, and bind direction-aware classes:
<script setup>
import { useDirection, useDisclosure } from 'boodoo/vue';
const { direction, isRtl, setDirection, toggleDirection } = useDirection('rtl');
const modal = useDisclosure();
</script>
<template>
<div :dir="direction" class="container py-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<h2><span>{{ isRtl ? 'المستخدمين' : 'Users' }}</span></h2>
<button type="button" class="btn btn-outline-secondary" @click="toggleDirection">
<span>{{ isRtl ? 'English (LTR)' : 'عربي (RTL)' }}</span>
</button>
</div>
</div>
</template>
6. Angular Integration
In Angular applications, configure directionality using Angular CDK's BidiModule or an inline Boodoo direction directive that observes document.documentElement.dir:
import { Component, Inject, Renderer2 } from '@angular/core';
import { DOCUMENT } from '@angular/common';
@Component({
selector: 'app-root',
standalone: true,
template: `
<div class="container py-4" [attr.dir]="direction">
<div class="d-flex justify-content-between align-items-center mb-3">
<h2><span>{{ direction === 'rtl' ? 'لوحة القيادة' : 'Control Panel' }}</span></h2>
<button type="button" class="btn btn-primary" (click)="toggleDirection()">
<span>Switch to {{ direction === 'rtl' ? 'LTR' : 'RTL' }}</span>
</button>
</div>
</div>
`
})
export class AppComponent {
direction: 'ltr' | 'rtl' = 'rtl';
constructor(
private renderer: Renderer2,
@Inject(DOCUMENT) private document: Document
) {
this.updateDocumentDir();
}
toggleDirection(): void {
this.direction = this.direction === 'rtl' ? 'ltr' : 'rtl';
this.updateDocumentDir();
}
private updateDocumentDir(): void {
this.renderer.setAttribute(this.document.documentElement, 'dir', this.direction);
}
}
7. Electron Desktop Applications
When building cross-platform desktop applications with Electron and Boodoo, synchronize window layout direction with OS system locale or user settings in your preload and renderer scripts:
// renderer.js (Electron Renderer Process)
import boodoo from 'boodoo';
// Check system locale passed from main process via IPC
window.electronAPI.getSystemLocale().then((locale) => {
const isRtlLocale = /^(ar|he|fa|ur|ps|sd|ug|yi)/i.test(locale);
boodoo.rtl.set(isRtlLocale ? 'rtl' : 'ltr');
});
// Allow user to toggle direction at runtime with persistence
document.getElementById('toggleDirBtn')?.addEventListener('click', () => {
const newDir = boodoo.rtl.toggle();
window.electronAPI.saveSetting('app-direction', newDir);
});
8. React Native Mobile Applications
For React Native mobile apps (iOS & Android), use React Native's native I18nManager together with Boodoo's layout primitives. Boodoo stacks (HStack, XStack) and grid systems respect writing flow and allow explicit reversal:
import React from 'react';
import { I18nManager } from 'react-native';
import { Box, HStack, VStack, Text, Button, BoodooProvider } from 'boodoo/react-native';
export default function MobileRtlScreen() {
const isRTL = I18nManager.isRTL;
return (
<BoodooProvider>
<Box style={{ padding: 16 }}>
<Text variant="h2">
<Text>{isRTL ? 'إعدادات الحساب' : 'Account Settings'}</Text>
</Text>
{/* HStack naturally arranges items according to I18nManager.isRTL */}
<HStack space={12} style={{ marginTop: 16, alignItems: 'center' }}>
<Button variant="primary">
<Text>{isRTL ? 'حفظ' : 'Save'}</Text>
</Button>
<Button variant="secondary">
<Text>{isRTL ? 'إلغاء' : 'Cancel'}</Text>
</Button>
</HStack>
</Box>
</BoodooProvider>
);
}
9. Bidirectional Best Practices Checklist
- Prefer Logical Spacing Classes: Always use
ms-*andme-*(start/end) instead of physicalml-*ormr-*so margins automatically flip in RTL. - Prefer Logical Padding Classes: Always use
ps-*andpe-*instead of physicalpl-*orpr-*for text indentations and button spacing. - Icon Flipping: Directional icons (such as forward/backward arrows
→, next buttons, search magnifiers) must be mirrored in RTL using.rtl-flip(e.g.transform: scaleX(-1)), while non-directional media icons (play, pause, camera) remain un-mirrored. - Input Field Telephones & URLs: Phone numbers, code snippets, email addresses, and URLs should always remain Left-to-Right even in RTL documents by setting
dir="ltr"on those specific elements.