The unicode-bidi property works together with direction to control how browsers display bidirectional (bidi) text — content that mixes left-to-right (LTR) and right-to-left (RTL) writing systems, such as English, Arabic, Persian, or Hebrew.
.bilingual-excerpt {
direction: rtl;
unicode-bidi: embed;
}
Most of the time, you don’t need to think about bidirectional text at all. Browsers implement the Unicode Bidirectional Algorithm, which automatically determines the correct reading order based on the characters in the text. That means mixed-language content often displays correctly without any additional HTML or CSS.
unicode-bidi comes into play when you need more control over that behavior.
Note: Although unicode-bidi is available in CSS, HTML authors are generally encouraged to use the dir attribute, <bdi>, and <bdo> whenever possible. Those work even when CSS isn’t applied.
Syntax
unicode-bidi = normal | embed | isolate | bidi-override | isolate-override | plaintext
- Initial: normal
- Applies to: all elements, however some values have no effect on non-inline elements
- Inherited: no
- Computed value: as specified
- Animation type: not animatable
Values
unicode-bidi: normal;
unicode-bidi: embed;
unicode-bidi: isolate;
unicode-bidi: bidi-override;
unicode-bidi: isolate-override;
unicode-bidi: plaintext;
/* Global values */
unicode-bidi: inherit;
unicode-bidi: initial;
unicode-bidi: revert;
unicode-bidi: revert-layer;
unicode-bidi: unset;
normal
The default value. The element doesn’t create a new bidirectional context, so the browser handles its contents using the Unicode Bidirectional Algorithm.
In most cases, this is exactly what you want. The browser already knows how to display mixed LTR and RTL text, so a single word or phrase in another language usually doesn’t need any special handling.
Below is an English sentence containing a Persian word.
<p>My favorite language is <span>فارسی</span>, what is yours?</p>
The browser recognizes that فارسی is an RTL word and places it correctly within the LTR sentence.
embed
Creates a new embedding level for an inline element. The direction of that embedded content is determined by the direction property.
Unlike normal, this gives the element its own directional context. The browser still uses the Unicode Bidirectional Algorithm to reorder the text correctly, but it now does so using the direction you’ve specified.
For example, consider an English sentence containing an Arabic word followed by an exclamation mark.
<p>
The word
<span>مرحبا!</span>
means welcome.
</p>
By default, the exclamation mark appears on the wrong side of the Arabic word because the surrounding paragraph is left-to-right. You might expect direction: rtl on the <span> to fix it, but it doesn’t. That’s because the direction property doesn’t affect inline elements unless they also establish a bidirectional context.
That’s where embed comes in.
span {
unicode-bidi: embed;
direction: rtl;
}
The <span> element now creates its own embedding level which allows direction: rtl to take effect. The browser still applies the Unicode Bidirectional Algorithm inside the element, but it now treats the element’s contents as a right-to-left embedding.
isolate
Creates a new isolated directional context for an element. The direction of that isolated context is determined by the direction property.
Like embed, the element gets its own directional context. The difference is that isolate prevents the element and the surrounding text from affecting each other’s bidirectional layout.
Let’s see why that matters.
<p>
Languages:
<span>فارسی</span>,
<span>عربی</span>
are my favorites.
</p>
Suppose we use embed on both <span> elements:
span {
unicode-bidi: embed;
direction: rtl;
}
At first glance, this seems like the right solution. Each <span> now has its own RTL context. However, embed doesn’t isolate those contexts from one another. If you try this code, you’ll notice that the second <span> is displayed before the first one, even though the source order is فارسی followed by عربی.
The problem is the comma.
In bidirectional text, letters have their own direction, but punctuation—such as commas and periods—doesn’t. These are called neutral characters, and they take their direction from the text around them.
Because the two RTL words are separated only by a comma, the browser can treat them as one continuous RTL run. As a result, فارسی and عربی appear in the opposite order from how they were written in the source.
This is exactly the problem isolate was introduced to solve.
span {
unicode-bidi: isolate;
direction: rtl;
}
Now each <span> is sealed off from its neighbors. The browser keeps فارسی and عربی in the order they appear in the source because each isolated element acts as its own separate unit.
bidi-override
Creates a directional override for an element. Instead of letting the browser determine the visual order of the text, all characters are displayed strictly according to the direction property.
Normally, the Unicode Bidirectional Algorithm reorders characters so text is displayed naturally, even when it contains a mix of LTR and RTL scripts. bidi-override disables that automatic reordering and forces every character to appear in the exact sequence they were written.
Let’s add this value to our first example. This time, we’ll set direction: ltr on the Persian word.
<p>My favorite language is <span>فارسی</span>, what is yours?</p>
span {
unicode-bidi: bidi-override;
direction: ltr;
}
The word now appears backwards because the browser no longer reorders the RTL characters into their natural reading order.
isolate-override
Combines the behavior of isolate and bidi-override. The element is isolated from surrounding text, while its own contents are displayed strictly according to the direction property.
Like isolate, the element doesn’t affect neighboring text or get affected by it. Like bidi-override, the browser skips its normal bidirectional reordering inside the element and displays the characters exactly as they appear in the source.
.element {
unicode-bidi: isolate-override;
direction: ltr;
}
plaintext
Creates an isolated bidirectional context whose direction is determined by the element’s contents instead of the direction property.
Like isolate, the element is isolated from the surrounding text. However, instead of using the direction property, the browser examines the content and determines whether it should be displayed left-to-right or right-to-left based on the first strongly directional character.
Note: unicode-bidi doesn’t inherit, so applying bidi-override or plaintext to a block element won’t affect any nested block elements. These values work best on inline elements or on block elements whose content doesn’t contain other block-level structures.
Browser support
The unicode-bidi property is defined in the CSS Writing Modes Module Level 4 specification and is supported across all modern browsers.
This is really cool! Just learned something from this!
Same!
I knew about
U+202DandU+202Ebut I didn’t know there was css to do the same thing more legibly!What confuses me is when Unicode and HTML do the same thing (e.g. character tabulation
U+0009and HTML tables; I thought HTML just displayed text on the table, not formatted the table around the text?)btw rtl is Right to Left
U+202Eand ltr is Left to Right
U+202D