Skip to main content

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-* and me-* (start/end) instead of physical ml-* or mr-* so margins automatically flip in RTL.
  • Prefer Logical Padding Classes: Always use ps-* and pe-* instead of physical pl-* or pr-* 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.