UI Components Easy
RTL Mixed Text
Demonstrates proper handling of bidirectional text mixing Arabic and English content using Unicode BiDi algorithm and CSS properties.
Open in Lab
MCP
css
Targets: JS HTML
Code
/* ── Reset & Base ── */
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-primary: #0a0a0a;
--bg-card: #111111;
--bg-tertiary: #1a1a1a;
--bg-bubble-sent: #1a2744;
--bg-bubble-received: #1a1a1a;
--border: #222222;
--border-light: #333333;
--text-primary: #f0f0f0;
--text-secondary: #999999;
--text-muted: #666666;
--accent-blue: #3b82f6;
--accent-green: #22c55e;
--accent-red: #ef4444;
--radius: 10px;
}
html {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
line-height: 1.6;
}
body {
min-height: 100vh;
padding-block: 40px;
padding-inline: 24px;
}
button {
font: inherit;
cursor: pointer;
border: none;
background: none;
color: inherit;
}
code {
font-family: "SF Mono", "Fira Code", "Cascadia Code", monospace;
font-size: 0.82em;
background: var(--bg-tertiary);
padding-block: 2px;
padding-inline: 6px;
border-radius: 4px;
color: var(--accent-blue);
}
/* ── Page ── */
.page {
max-inline-size: 800px;
margin-inline: auto;
}
/* ── Toolbar ── */
.toolbar {
display: flex;
align-items: center;
justify-content: space-between;
margin-block-end: 40px;
flex-wrap: wrap;
gap: 16px;
}
.page-title {
font-size: 1.6rem;
font-weight: 700;
}
.dir-toggle {
display: flex;
align-items: center;
gap: 8px;
padding-block: 8px;
padding-inline: 16px;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 8px;
font-size: 0.85rem;
font-weight: 500;
color: var(--accent-blue);
transition: background 0.2s;
}
.dir-toggle:hover {
background: var(--bg-tertiary);
}
/* ── Demo sections ── */
.demo-section {
margin-block-end: 48px;
}
.section-title {
font-size: 1.15rem;
font-weight: 600;
margin-block-end: 8px;
}
.section-desc {
font-size: 0.88rem;
color: var(--text-secondary);
margin-block-end: 20px;
}
/* ── Text cards ── */
.text-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius);
padding-block: 20px;
padding-inline: 24px;
margin-block-end: 12px;
}
.mixed-text {
font-size: 1rem;
line-height: 1.8;
color: var(--text-primary);
}
.embedded-ltr {
color: var(--accent-blue);
font-weight: 500;
}
.embedded-rtl {
color: var(--accent-blue);
font-weight: 500;
font-size: 1.05em;
}
/* ── Mixed list ── */
.mixed-list {
list-style: none;
display: flex;
flex-direction: column;
gap: 8px;
}
.list-item {
display: flex;
align-items: center;
gap: 14px;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 8px;
padding-block: 14px;
padding-inline: 20px;
}
.list-number {
inline-size: 28px;
block-size: 28px;
border-radius: 50%;
background: var(--bg-tertiary);
display: flex;
align-items: center;
justify-content: center;
font-size: 0.78rem;
font-weight: 600;
color: var(--accent-blue);
flex-shrink: 0;
}
.list-text {
font-size: 0.92rem;
line-height: 1.5;
}
/* ── Chat ── */
.chat-container {
display: flex;
flex-direction: column;
gap: 12px;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius);
padding-block: 24px;
padding-inline: 24px;
}
.chat-bubble {
max-inline-size: 80%;
padding-block: 12px;
padding-inline: 16px;
border-radius: 12px;
position: relative;
}
.chat-bubble.sent {
align-self: flex-end;
background: var(--bg-bubble-sent);
border-end-end-radius: 4px;
}
.chat-bubble.received {
align-self: flex-start;
background: var(--bg-bubble-received);
border: 1px solid var(--border);
border-end-start-radius: 4px;
}
.bubble-sender {
font-size: 0.75rem;
font-weight: 600;
color: var(--accent-blue);
margin-block-end: 4px;
}
.bubble-text {
font-size: 0.9rem;
line-height: 1.6;
color: var(--text-primary);
}
.bubble-text .embedded-ltr {
color: var(--accent-green);
}
.bubble-time {
font-size: 0.7rem;
color: var(--text-muted);
margin-block-start: 6px;
text-align: end;
}
/* ── Comparison ── */
.comparison-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.comparison-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
}
.comparison-card.correct {
border-color: rgba(34, 197, 94, 0.3);
}
.comparison-card.incorrect {
border-color: rgba(239, 68, 68, 0.3);
}
.comparison-header {
display: flex;
align-items: center;
justify-content: space-between;
padding-block: 10px;
padding-inline: 16px;
border-block-end: 1px solid var(--border);
background: var(--bg-primary);
}
.comp-badge {
font-size: 0.76rem;
font-weight: 600;
padding-block: 2px;
padding-inline: 8px;
border-radius: 4px;
}
.comp-badge.good {
background: rgba(34, 197, 94, 0.12);
color: var(--accent-green);
}
.comp-badge.bad {
background: rgba(239, 68, 68, 0.12);
color: var(--accent-red);
}
.comp-code {
font-size: 0.72rem;
}
.comparison-body {
padding-block: 16px;
padding-inline: 16px;
}
.comp-text {
font-size: 0.92rem;
line-height: 1.7;
margin-block-end: 12px;
padding-block: 10px;
padding-inline: 14px;
background: var(--bg-tertiary);
border-radius: 6px;
}
.comp-note {
font-size: 0.78rem;
color: var(--text-muted);
line-height: 1.5;
}
/* ── Responsive ── */
@media (max-width: 640px) {
body {
padding-inline: 16px;
}
.comparison-grid {
grid-template-columns: 1fr;
}
.chat-bubble {
max-inline-size: 90%;
}
}(() => {
const html = document.documentElement;
const dirToggle = document.getElementById("dir-toggle");
const dirLabel = document.getElementById("dir-label");
dirToggle.addEventListener("click", () => {
const isRtl = html.getAttribute("dir") === "rtl";
const newDir = isRtl ? "ltr" : "rtl";
html.setAttribute("dir", newDir);
html.setAttribute("lang", isRtl ? "en" : "ar");
dirLabel.textContent = newDir.toUpperCase();
});
})();<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>RTL Mixed Text</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="page">
<!-- Toolbar -->
<div class="toolbar">
<h1 class="page-title">Bidirectional Text Demo</h1>
<button class="dir-toggle" id="dir-toggle" type="button" aria-label="Toggle text direction">
<span id="dir-label">LTR</span>
<span>⇄</span>
</button>
</div>
<!-- ── Section 1: Arabic paragraphs with English words ── -->
<section class="demo-section">
<h2 class="section-title">Arabic Text with English Terms</h2>
<p class="section-desc">Embedded English words within Arabic paragraphs render correctly using the Unicode BiDi algorithm.</p>
<div class="text-card">
<p class="mixed-text" dir="rtl" lang="ar">
نظام التشغيل <bdi class="embedded-ltr">Windows 11</bdi> يتضمن ميزات جديدة مثل
<bdi class="embedded-ltr">Snap Layouts</bdi> و<bdi class="embedded-ltr">Virtual Desktops</bdi>
لتحسين تجربة المستخدم.
</p>
</div>
<div class="text-card">
<p class="mixed-text" dir="rtl" lang="ar">
يمكنك استخدام لغة <bdi class="embedded-ltr">JavaScript</bdi> مع مكتبة
<bdi class="embedded-ltr">React</bdi> لبناء واجهات مستخدم تفاعلية وسريعة.
كما يمكنك استخدام <bdi class="embedded-ltr">TypeScript</bdi> لإضافة أنواع ثابتة.
</p>
</div>
</section>
<!-- ── Section 2: English paragraphs with Arabic words ── -->
<section class="demo-section">
<h2 class="section-title">English Text with Arabic Words</h2>
<p class="section-desc">Arabic words embedded in English text flow correctly when marked with proper directionality.</p>
<div class="text-card">
<p class="mixed-text" dir="ltr" lang="en">
The Arabic word <bdi class="embedded-rtl" dir="rtl" lang="ar">مرحبا</bdi> (marhaba) means "hello" and is commonly used as a greeting across the Arab world, from Morocco to Iraq.
</p>
</div>
<div class="text-card">
<p class="mixed-text" dir="ltr" lang="en">
In calligraphy, the <bdi class="embedded-rtl" dir="rtl" lang="ar">بسملة</bdi> (basmala) is one of the most frequently rendered phrases. It translates to "In the name of God, the Most Gracious, the Most Merciful."
</p>
</div>
</section>
<!-- ── Section 3: Mixed-direction list ── -->
<section class="demo-section">
<h2 class="section-title">Mixed-Direction List</h2>
<p class="section-desc">Lists with mixed content automatically handle directionality per item.</p>
<ul class="mixed-list">
<li class="list-item" dir="rtl" lang="ar">
<span class="list-number">1</span>
<span class="list-text">السلام عليكم — Peace be upon you</span>
</li>
<li class="list-item" dir="ltr" lang="en">
<span class="list-number">2</span>
<span class="list-text">Hello World — <bdi dir="rtl" lang="ar">مرحبا بالعالم</bdi></span>
</li>
<li class="list-item" dir="rtl" lang="ar">
<span class="list-number">3</span>
<span class="list-text">شكرا جزيلا — Thank you very much</span>
</li>
<li class="list-item" dir="ltr" lang="en">
<span class="list-number">4</span>
<span class="list-text">Good morning — <bdi dir="rtl" lang="ar">صباح الخير</bdi></span>
</li>
<li class="list-item" dir="rtl" lang="ar">
<span class="list-number">5</span>
<span class="list-text">مع السلامة — Goodbye</span>
</li>
</ul>
</section>
<!-- ── Section 4: Chat-style UI ── -->
<section class="demo-section">
<h2 class="section-title">Chat UI with Mixed Messages</h2>
<p class="section-desc">A chat interface where each message independently sets its own text direction.</p>
<div class="chat-container">
<div class="chat-bubble sent" dir="ltr" lang="en">
<div class="bubble-sender">You</div>
<div class="bubble-text">Hey! Can you help me with the Arabic translation for the new interface?</div>
<div class="bubble-time">10:30 AM</div>
</div>
<div class="chat-bubble received" dir="rtl" lang="ar">
<div class="bubble-sender">أحمد</div>
<div class="bubble-text">طبعا! أرسل لي النصوص وسأقوم بالترجمة.</div>
<div class="bubble-time">10:32 AM</div>
</div>
<div class="chat-bubble sent" dir="ltr" lang="en">
<div class="bubble-sender">You</div>
<div class="bubble-text">How do you say "Submit Form" in Arabic?</div>
<div class="bubble-time">10:33 AM</div>
</div>
<div class="chat-bubble received" dir="rtl" lang="ar">
<div class="bubble-sender">أحمد</div>
<div class="bubble-text">تقول «<bdi class="embedded-ltr">Submit Form</bdi>» = «إرسال النموذج»</div>
<div class="bubble-time">10:34 AM</div>
</div>
<div class="chat-bubble sent" dir="ltr" lang="en">
<div class="bubble-sender">You</div>
<div class="bubble-text">Perfect, <bdi dir="rtl" lang="ar">شكرا</bdi>! I'll update the UI now.</div>
<div class="bubble-time">10:35 AM</div>
</div>
<div class="chat-bubble received" dir="rtl" lang="ar">
<div class="bubble-sender">أحمد</div>
<div class="bubble-text">عفوا! إذا احتجت مساعدة أخرى أنا موجود.</div>
<div class="bubble-time">10:36 AM</div>
</div>
</div>
</section>
<!-- ── Section 5: Correct vs Incorrect approaches ── -->
<section class="demo-section">
<h2 class="section-title">Correct vs Incorrect Approaches</h2>
<p class="section-desc">Side-by-side comparison showing the impact of proper BiDi markup.</p>
<div class="comparison-grid">
<div class="comparison-card correct">
<div class="comparison-header">
<span class="comp-badge good">✓ Correct</span>
<code class="comp-code"><bdi> + dir attribute</code>
</div>
<div class="comparison-body">
<p class="comp-text" dir="ltr" lang="en">
The user <bdi dir="rtl" lang="ar">فاطمة العلي</bdi> scored 95 points.
</p>
<p class="comp-note">The name is correctly isolated and does not affect surrounding punctuation or numbers.</p>
</div>
</div>
<div class="comparison-card incorrect">
<div class="comparison-header">
<span class="comp-badge bad">✗ Incorrect</span>
<code class="comp-code">No BiDi isolation</code>
</div>
<div class="comparison-body">
<p class="comp-text" dir="ltr" lang="en">
The user فاطمة العلي scored 95 points.
</p>
<p class="comp-note">Without <code><bdi></code>, the BiDi algorithm may reorder surrounding characters incorrectly.</p>
</div>
</div>
<div class="comparison-card correct">
<div class="comparison-header">
<span class="comp-badge good">✓ Correct</span>
<code class="comp-code">unicode-bidi: isolate</code>
</div>
<div class="comparison-body">
<p class="comp-text" dir="rtl" lang="ar" style="unicode-bidi: isolate;">
رقم الطلب: <span style="unicode-bidi: embed; direction: ltr;">#ORDER-2024-1587</span>
</p>
<p class="comp-note">The order number is correctly embedded as LTR within the RTL context.</p>
</div>
</div>
<div class="comparison-card incorrect">
<div class="comparison-header">
<span class="comp-badge bad">✗ Incorrect</span>
<code class="comp-code">No direction override</code>
</div>
<div class="comparison-body">
<p class="comp-text" dir="rtl" lang="ar">
رقم الطلب: #ORDER-2024-1587
</p>
<p class="comp-note">Without explicit LTR embedding, the order number may be rendered in an unexpected sequence.</p>
</div>
</div>
</div>
</section>
</div>
<script src="script.js"></script>
</body>
</html>A showcase of bidirectional text handling techniques, demonstrating how to correctly mix Arabic and English content using the Unicode BiDi algorithm, CSS direction properties, and proper HTML markup.