text-align: start vs text-align: right

Why text-align: start follows Arabic and English direction, when right should stay physical, and how to test nested content and inherited styles.

Guide16 min read

An Arabic page is right-aligned, so text-align: right seems sensible. It also seems sensible to fix a watch by moving the hands to the correct time. Both solutions work beautifully until anything changes.

For text that should align with its reading direction, use text-align: start. In a horizontal LTR block, start is the left edge. In a horizontal RTL block, it is the right edge. The same declaration serves both versions of a component, including nested blocks whose direction differs from the page.

text-align: right means the right side regardless of whether the element is LTR or RTL. That is useful when the right side itself is the requirement. It is a poor substitute for direction, and it is usually the wrong way to make an Arabic component adapt.

The practical rule is short:

  • Use start for normal headings, paragraphs, labels and other text that follows reading direction.
  • Use end when content belongs at the trailing edge of its own line.
  • Use left or right only when the physical side is intentional in both LTR and RTL.

The rest of this guide is about the word “intentional,” which has caused more CSS review comments than it probably expected.

The difference in one table#

For the horizontal writing mode used by ordinary English and Arabic interfaces:

ValueIn an LTR blockIn an RTL blockWhat it means
text-align: startLeftRightReading start
text-align: endRightLeftReading end
text-align: leftLeftLeftLeft side
text-align: rightRightRightRight side
text-align: centerCenterCenterCenter of the line box
text-align: justifyFills the lineFills the lineDistributes inline content across the line

This is not an RTL-specific convention invented by a framework. The CSS Text specification defines start as the start edge of the line box and end as its end edge. It defines left and right against the line-left and line-right edges. MDN's text-align reference gives the useful horizontal shorthand: start acts as left in LTR and right in RTL, while end does the reverse.

There are two details hidden inside that tidy table.

First, the relevant direction belongs to the block whose line is being aligned. It does not necessarily belong to the document. An English source note inside an Arabic article can be LTR and start-aligned on the left while the article around it is RTL and start-aligned on the right.

Second, text-align aligns inline content inside a block container. It does not move the block itself. A right-aligned paragraph still occupies the same box. Only the inline content within each line shifts. This distinction matters as soon as somebody tries to repair a toolbar with text-align and discovers that the buttons are unimpressed.

Start is alignment, not direction#

text-align: start needs a direction from somewhere. It does not inspect the language, detect Arabic, run the Unicode Bidirectional Algorithm and choose a side by intuition. It uses the inline base direction of the line box, which normally comes from the element's computed direction.

In HTML, set that base direction with the dir attribute:

html
<html lang="ar" dir="rtl">

lang="ar" declares that the document language is Arabic. It does not set direction. If dir is absent all the way up the ancestor chain, the default direction is LTR. The text may contain Arabic letters and look visibly Arabic, but text-align: start will still use the LTR start edge. The HTML dir reference documents both the default and the fact that lang does not imply direction.

That produces a common debugging surprise:

html
<html lang="ar">
  <body class="city-portal">
    <p class="service-update">اكتملت أعمال الصيانة في الحي.</p>
  </body>
</html>
css
.service-update {
  text-align: start;
}

The CSS is logical, but the document is still LTR. The declaration therefore aligns the line to the left. The fix is not text-align: right; it is to give the Arabic document the correct semantic direction:

html
<html lang="ar" dir="rtl">

Once the direction is correct, the initial alignment already follows it. This ordering matters: establish the content's direction first, then decide whether its alignment should be at start, end, center or a physical side. Using alignment to imitate direction treats one symptom and leaves punctuation, inline ordering, table flow and other directional behavior untouched. For the document-level part of that job, see the guide to dir="rtl".

Replace the English left rule, not the Arabic symptom#

Consider a city service update used on English and Arabic routes. The English component was built first and given an explicit left alignment.

Problem

html
<article class="service-update">
  <p class="service-update__label">Network notice</p>
  <h2 class="service-update__title">Water pressure has returned to normal</h2>
  <p class="service-update__body">Crews will continue monitoring the district.</p>
</article>
css
.service-update {
  text-align: left;
}

This looks correct in LTR because the design's reading start and the physical left edge happen to be the same edge. Put the component under dir="rtl", translate its content and the declaration remains left. CSS is doing exactly what it was asked to do, which is rarely comforting at this stage.

A direction-specific override can hide the problem:

css
.service-update {
  text-align: left;
}

[dir="rtl"] .service-update {
  text-align: right;
}

The override works for this component in these two routes. It is still the weaker model. Every new selector, nested direction and component variant has to remember both halves of the rule. A preview panel that places Arabic inside an English page also depends on whether the selector happens to match the right ancestor.

Better

html
<article class="service-update">
  <p class="service-update__label">Network notice</p>
  <h2 class="service-update__title">Water pressure has returned to normal</h2>
  <p class="service-update__body">Crews will continue monitoring the district.</p>
</article>
css
.service-update {
  text-align: start;
}

The LTR layout stays exactly as it was: start maps to left. The Arabic version maps start to right. The component now describes the relationship the design actually wanted instead of listing two current outcomes.

This is the safe migration when the original left meant “where reading begins.” Do not mechanically replace every right with start. If a trailing timestamp is right-aligned in the English design and should move to the left in Arabic, its logical replacement is end. If it must remain physically right in both versions, keep right.

Search and replace is fast. So is putting the wrong declaration everywhere.

Inheritance is where start earns its keep#

text-align is inherited. A declaration on a card, table row or section can affect the block containers below it unless they set their own value. Physical alignment turns that convenience into a trap when a descendant changes direction.

Here is an Arabic bulletin with a separate English source note:

html
<article class="bulletin" lang="ar" dir="rtl">
  <h2>تحديث حول أعمال الطرق</h2>
  <p>سيُفتح المسار الشرقي صباح الأحد.</p>

  <aside class="bulletin__source" lang="en" dir="ltr">
    Source: Municipal Works Office
  </aside>
</article>

With a physical rule on the parent, the LTR note inherits the physical result:

css
.bulletin {
  text-align: right;
}

The source note has the correct LTR base direction, so its words and punctuation are handled as LTR. Its line is still aligned to the right because right is the inherited value. Direction and alignment are related, but neither secretly rewrites the other.

Change the parent to a logical value:

css
.bulletin {
  text-align: start;
}

Now the Arabic blocks align to their right-hand start. The LTR source note inherits the keyword start, and that keyword is interpreted using the note's own LTR direction, so its line aligns left. One declaration supports both directions within the same component.

This follows from an easily missed standards detail. The computed value of text-align: start remains start; it is not permanently converted to right when computed on the RTL parent. Because the keyword is inherited, a descendant can apply it against a different direction. The CSS Text specification lists the computed value as the specified keyword, with a special exception only for match-parent.

Why an inline span does not get its own alignment#

Suppose only the words “Municipal Works Office” sit in a <span dir="ltr"> inside an Arabic paragraph. The span gets an LTR bidi context, but it does not create a separate block line to align. text-align belongs to the block container and positions all inline content in that line box together.

If a nested passage needs an independently aligned edge, give that passage its own block container, such as a paragraph, aside or block-level wrapper, and put the appropriate dir on that container. Do not add text-align: left to an inline span and wait for a small miracle.

The distinction is simple:

  • dir on an inline element can control and isolate that inline run.
  • text-align controls how the block's line content sits within the line box.
  • A separately aligned passage therefore needs its own block container and line boxes.

For inline punctuation and ordering problems, use the bidirectional text guide. Alignment is not a bidi isolation tool, even though the two subjects keep arriving at the same bug report.

start is already the initial value#

The initial value of text-align is start, and the property is inherited. In a clean stylesheet with correct document direction, normal block text should already align at its reading start. You do not need to add this declaration to every paragraph:

css
p,
li,
label,
h1,
h2,
h3 {
  text-align: start;
}

That rule mostly records the browser default in a much longer place. It can also become another selector to untangle later.

An explicit text-align: start is useful when it communicates or restores a component contract. Typical cases include:

  • a legacy global rule sets left or right;
  • a utility class applies physical alignment higher in the tree;
  • a reusable component must reset an inherited center, end or physical value;
  • a component library documents that ordinary text follows its own direction;
  • user-generated blocks can independently be LTR or RTL.

Otherwise, let the initial behavior do its job. Browsers are quite capable of aligning text without a committee meeting.

Be careful with unset. Because text-align is inherited, text-align: unset behaves like inherit, not like a guaranteed reset to start. If the parent is physically right-aligned, the child remains physically right-aligned. Use text-align: start when you need to restore logical start explicitly.

inherit and match-parent are not the same promise#

Most product CSS never needs text-align: match-parent, but its behavior reveals why start works well for mixed-direction components.

Imagine an RTL parent with text-align: start and an LTR child:

Child declarationChild's resulting alignmentReason
No declarationLeftIt inherits start, then uses its own LTR direction
text-align: inheritLeftIt explicitly inherits the parent's computed keyword, start
text-align: startLeftIt uses its own LTR start edge
text-align: match-parentRightThe parent's start is resolved using the parent's RTL direction
text-align: rightRightThe physical right side was requested

The CSS Text definition of match-parent says it behaves like inheritance except that an inherited start or end is resolved against the parent's direction and becomes left or right. That makes it useful when a child must visually match the parent's aligned edge even though the child has another direction.

It also makes it the wrong value when every block should align with its own reading direction. The name sounds friendly. The behavior is specific.

Use end for content that belongs at the trailing edge#

end is not a polite synonym for right. It is the other logical edge.

Suppose the service update has a small status line that the design places opposite the title's reading start. In English it belongs on the right. In Arabic it belongs on the left.

html
<article class="service-update">
  <p class="service-update__label">Network notice</p>
  <h2 class="service-update__title">Water pressure has returned to normal</h2>
  <p class="service-update__status">Monitoring continues</p>
</article>
css
.service-update {
  text-align: start;
}

.service-update__status {
  text-align: end;
}

The status remains aligned to the component's trailing text edge in either direction. The LTR version is unchanged from a physical right alignment, while the RTL version moves to the left.

Use end when the role is trailing: metadata opposite a heading, a short secondary note at the conclusion edge, or a table cell whose content follows the row's logical end. Do not use it merely because a mockup happens to show something on the right. Ask what should happen when direction changes. The answer chooses the value.

If the design puts several sibling components at opposite sides, text-align may not be the property you need at all. It aligns inline content inside each block. Flexbox or grid alignment places the sibling boxes. A heading and a status label in one flex row may need justify-content: space-between; their text can still use text-align: start inside their own boxes.

When right should remain right#

Logical values are not a ban on physical values. They are a way to express directional meaning. Sometimes the meaning really is physical.

Keep text-align: right when the requirement is “align this inline content to the right side in every writing direction.” Plausible cases include:

  • labels attached to the right edge of a chart whose axis remains physically fixed;
  • a comparison canvas where left and right correspond to fixed spatial regions, not reading order;
  • a numeric report column that the product specification deliberately keeps on one physical edge across locales;
  • a diagnostic preview meant to show an exact physical alignment choice.

Those cases need an actual product requirement. “Arabic is right-to-left” is not one. Normal Arabic paragraphs, headings and labels should follow the content direction, not acquire a blanket physical alignment rule because somebody remembered which side Arabic begins on.

Physical values can also be appropriate inside a deliberately LTR technical surface, such as an editor whose coordinate system remains left to right. In that case, set the surface's direction explicitly and document why its alignment is physical. Otherwise the next engineer will “fix” it back to logical CSS with perfectly reasonable intentions.

For horizontal Arabic interfaces, it is convenient to describe left and right as physical sides. The full CSS model is slightly broader. In vertical writing modes, the specification's line-left and line-right edges can map to top or bottom. start and end follow the inline axis and its direction there too. If your product supports vertical writing, test that writing mode rather than extending the horizontal table by optimism.

dir="auto" can make each block choose its own start#

User-generated text may arrive in Arabic, English or another script without a reliable language field. dir="auto" lets the browser determine an element's base direction from its first strongly directional character. Pairing it with start alignment gives each block a reading-appropriate edge.

html
<section class="resident-messages">
  <article class="resident-message" dir="auto">
    الطريق مفتوح الآن أمام المركبات.
  </article>

  <article class="resident-message" dir="auto">
    The eastern entrance is open again.
  </article>
</section>
css
.resident-message {
  text-align: start;
}

The Arabic message gets an RTL base direction and aligns to its start on the right. The English message gets an LTR base direction and aligns left. This is one of the few cases where automatic direction and inherited logical alignment work together with very little ceremony.

There are limits. dir="auto" is a first-strong-character heuristic, not language detection. A message that begins with a Latin organization name and continues in Arabic can resolve LTR. In Chromium 151, checked on 2026-09-24, a block containing only digits, spaces and punctuation resolved LTR. Firefox and Safari were not measured for that fixture. If your data model knows the direction, explicit dir="rtl" or dir="ltr" is less ambiguous.

Also put dir="auto" on each independently directed message, not only on the list around them. One container has one base direction. It cannot give each child a private decision by administrative decree.

What text alignment does not fix#

Several bugs can sit next to a wrong edge and look like alignment problems. text-align handles only the position of inline content within line boxes.

It does not:

  • set the document or element's base direction;
  • reorder mixed Arabic, Latin text and punctuation;
  • isolate an embedded phone number, code or foreign-language phrase;
  • mirror icons, padding, borders or absolute positioning;
  • change DOM order;
  • reverse a flex or grid layout;
  • move a block to one side of its parent;
  • change the direction in which a user types into a field.

If Arabic punctuation appears on an unexpected side of an English phrase, changing the whole paragraph to text-align: right may move the visible problem without repairing the inline bidi context. If a toolbar remains on the left, text-align: start will not relocate its flex items. If an input accepts Arabic while its caret and typed text behave LTR, the field direction needs attention.

This boundary is worth keeping because CSS problems become expensive when one property is asked to impersonate four others. Use dir for semantic direction. Use bidi isolation for independent inline runs. Use logical box properties and layout alignment for component geometry. Use text-align for the lines themselves. The RTL CSS guide covers those layout responsibilities without making text-align carry all the groceries.

Find the rule that is actually winning#

A visible left alignment does not prove that the element declares text-align: left. The value might be inherited from an ancestor, introduced by a utility class, or applied by a selector that only matches one locale.

Use browser DevTools on the misaligned block and inspect these in order:

  1. Check the element's computed direction. For a known Arabic block, expect rtl; for a known English block, expect ltr.
  2. Check the computed text-align. A modern browser can report the keyword start rather than a resolved physical side, because start is the computed value defined by the standard.
  3. Open the matched rules for text-align and identify the winning declaration. Also look for an inherited value and the ancestor that supplies it.
  4. Inspect the nearest meaningful dir attribute in the ancestor chain. Do not assume lang="ar" did this work.
  5. Disable the winning physical rule temporarily. If the element returns to the correct start edge, you have found the alignment override rather than a bidi problem.

Common cascade traps include:

css
.text-left {
  text-align: left !important;
}

.card-copy {
  text-align: start;
}

The logical component rule cannot beat an important utility. Another trap is order:

css
.service-update {
  text-align: start;
}

.legacy-content article {
  text-align: left;
}

If the second selector wins in the cascade, the presence of start earlier in the stylesheet changes nothing except the mood of the person debugging it.

Do not add a more specific RTL override until you know why the physical rule exists. If it is obsolete, remove or replace it at the source. A selector arms race gives the component correct alignment today and an archaeology project next quarter.

Migrate physical alignment without changing LTR#

A safe migration classifies each declaration by meaning before changing it.

1. Find explicit physical values#

Search authored styles, component props and alignment utilities for text-align: left and text-align: right. Include CSS-in-JS objects and class-generation maps, not only .css files. Browser defaults are not the target. Authored physical assumptions are.

2. Name the intended edge#

For each rule, ask what the element's content is supposed to follow:

Existing LTR ruleIntended meaningReplacement
text-align: leftReading starttext-align: start
text-align: rightReading endtext-align: end
text-align: rightPhysical right in every localeKeep right
text-align: leftPhysical left in every localeKeep left
Either physical valueNo deliberate alignmentRemove it and use the initial behavior

The first two replacements preserve the existing LTR result. That is the migration invariant. If changing to a logical value moves the English component, you chose the wrong logical edge or uncovered an earlier layout assumption.

3. Remove duplicate RTL patches#

After moving the base rule to start or end, an old selector such as [dir="rtl"] .component { text-align: right; } may be redundant. Remove it only after checking that it does not cover another declaration or component state. CSS selectors sometimes carry unrelated luggage.

4. Test direction boundaries, not only whole pages#

An English page and an Arabic page are necessary fixtures, but they do not exercise inheritance fully. Include an LTR block inside an RTL component and an RTL block inside an LTR preview. These nested cases reveal whether the stylesheet expresses logical alignment or merely patches the root route.

5. Keep deliberate physical rules visible#

A short comment can protect a physical exception when its purpose is not obvious:

css
.comparison-axis__label {
  /* The axis stays on the physical right in both locale views. */
  text-align: right;
}

Comments should explain the product invariant, not translate the declaration into English. /* Align right */ is technically documentation, in the way that writing “door” on a door is technically signage.

Test the result with real direction changes#

Do not verify this fix by swapping English strings for Arabic strings while leaving the DOM direction unchanged. That tests translation inside an LTR layout. It is useful for finding one class of mistake and useless for proving the component adapts.

Use a small matrix:

FixtureDirectionExpected start alignment
English service updateltrLeft
Arabic service updatertlRight
English source note inside Arabic bulletinltrLeft
Arabic bulletin inside an English previewrtlRight
English resident message with dir="auto"Auto resolves LTRLeft
Arabic resident message with dir="auto"Auto resolves RTLRight

For each fixture:

  1. Inspect the dir attribute or the inherited direction source.
  2. Confirm computed direction on the block that owns the line box.
  3. Confirm the winning alignment value is start, unless the case intentionally uses end or a physical side.
  4. Check the first and wrapped lines visually at more than one width.
  5. Check nested blocks separately. A correct parent does not excuse a physical rule on the child.
  6. Compare the same component in LTR and RTL without changing its CSS bundle.

Add a screenshot or visual regression case at a width where the heading wraps. A single short line can make left and right alignment obvious, but wrapping also exposes child spans, badges and inline controls that inherited unexpected styles. Keep the fixture text realistic. Very short placeholders have an impressive record of approving broken typography.

Automated style assertions can confirm the contract:

js
const update = document.querySelector("[data-testid='service-update']");
const styles = getComputedStyle(update);

console.assert(styles.direction === "rtl");
console.assert(styles.textAlign === "start");

This checks the semantic values, not the pixels. Pair it with a rendered comparison or screenshot so a later layout rule cannot pass by keeping start in computed styles while changing the line's available box. If your browser serializes the computed value differently, inspect what it returns and test the rendered edge rather than hard-coding an assumption from this snippet.

Catch the Arabic failure before release#

An Arabic page can have correct translations, correct dir="rtl" and one inherited text-align: left that pins an entire content region to the wrong edge. The page is directional; the text is simply being overruled.

Ritla check R022 reports text-align: left explicitly set on majority-Arabic text. That is the precise failure it covers. A deliberate physical right, a wrong end, a missing direction attribute or a nested LTR block needs separate reasoning and, where no check names it, a manual test.

A free Ritla scan can surface the explicit left alignment while you use the direction-boundary matrix above to verify the intended fix. If the page still needs its base direction repaired, continue with the dir="rtl" guide; if the text edge is correct but the surrounding component still assumes left and right, use the RTL CSS guide.

Choose the value that describes the edge's job. start for reading start, end for reading end, and right only when you genuinely mean right. CSS becomes pleasantly boring after that, which is one of its finest moods.

Checks in this guide

See what your Arabic pages are hiding

Paste a URL. Ritla renders the page on desktop and mobile, runs every check, and shows the top issues with screenshot evidence.