Fix
The store that renders its own language back to front
Right-to-left is not a styling choice; it is how Arabic, Hebrew, Persian and Urdu are written. Leave the direction undeclared and the browser assumes the opposite — then quietly rearranges punctuation, prices and part numbers on every line of the shop.
Who this locks out
An RTL store served without dir="rtl" renders its own language back to front.
- Shoppers who read Arabic, Hebrew, Persian or Urdu, served punctuation and brackets at the wrong end of every line
- Shoppers with low vision or dyslexia, for whom misplaced marks add decoding work to every sentence
- Anyone reading mixed lines — prices, SKUs, phone numbers — where the wrong base direction shuffles the order
The transform
Before
<html lang="ar"> After
<html lang="ar" dir="rtl"> Detection
How this is tested here
Every scan runs three independent engines and 19 custom checks against your rendered pages, and records which engine flagged what. These are the real rules behind this fix family.
| Criterion | Level | Detected by | What we do |
|---|
What Klarvo Access does
Klarvo reads the text direction WordPress itself declares for your locale, sets the matching dir attribute on the html element server-side, and independently re-validates the served page — where WordPress declares no direction, it reports rather than guesses.
Auto-applied
Generated, then applied server-side in the HTML your store actually serves — and independently re-validated in the served page before it counts as resolved.
An automated finding is evidence, not a certificate. Conformance is certified only by the professional audit — how that works.
Do it yourself
Fixing this by hand
What a missing direction declaration does to your shop
Open an Arabic storefront and, unless the page says otherwise, the browser assumes the text runs left to right. That assumption is wired into HTML itself: the default direction is ltr, and every page that never declares a direction inherits it. For an English shop the default happens to be true. For a shop written in Arabic, Hebrew, Persian or Urdu it is wrong on every line, and the browser’s bidirectional algorithm starts making small, confident mistakes with your words.
The letters themselves survive — Unicode knows an Arabic character runs right to left whatever the page declares. What breaks is everything that has no direction of its own. Full stops, question marks, brackets and currency symbols are neutral: they take their position from the base direction of the text around them. Declare the wrong base and the punctuation migrates to the wrong end of the sentence — the exclamation mark that should close a headline turns up stranded at its start. Brackets open the wrong way. A price can render with the amount and the currency symbol swapped around each other.
Mixed lines are where a store really feels it. Product copy in a right-to-left language is full of left-to-right fragments — Latin brand names, SKUs, model numbers, sizes like “42 EU” — and the order of those fragments within the line is decided by the base direction. With the wrong base, a sentence whose pieces are each correct comes out with the pieces in the wrong sequence: the model number reads before the phrase that introduces it, the quantity detaches from its unit. Alignment goes with it — the whole page ranges left while the reading eye starts on the right. None of this is subtle to the person it happens to. It is the shop looking broken in their own language.
Two honest framings before anything else. First, no single WCAG A or AA success criterion mandates the dir attribute, and this page will not pretend one does. It is best practice — the other half of a declaration WCAG does mandate. Criterion 3.1.1, Language of Page, asks the page to name its language; direction is how that language is physically read, and the two attributes sit side by side on the same tag. Klarvo treats them as the pair they are: the document-language fix handles lang, this family handles dir, and the report files this one honestly as an advisory, not a failure. Second, the damage here is mostly visual. A screen reader follows the order of the markup, not the rendered order, so it largely shrugs this off — which means the people hit hardest are sighted readers of right-to-left scripts, and that is exactly why an English-language testing toolchain can miss it forever.
One more thing worth saying plainly: direction belongs in markup, not in a stylesheet. The W3C’s internationalisation guidance is unambiguous — direction: rtl in CSS is lost when styles fail, get overridden or are stripped by a reader mode, and direction is a property of the content rather than of its presentation. The attribute travels with the words.
How it is detected here
None of the three engines in the scan — axe-core, IBM Equal Access, HTML_CodeSniffer — carries a named rule for a missing dir attribute, for the honest reason above: no A or AA criterion requires it, so there is nothing for an engine to map it to. A store can pass every automated language rule and still render its own words back to front.
So the finding comes from Klarvo’s own deterministic check, and the check is deliberately narrow. It reads the dir attribute on the html element of the page your server actually serves and asks a single question: is the value exactly ltr or exactly rtl? No attribute at all, or a value a browser cannot act on — an empty string, a typo, a stray auto — is the finding. Any valid value passes.
Your locale is not consulted at this stage, and two honest consequences follow. An English shop serving no dir attribute is flagged even though it renders correctly; the report files that as the advisory it is, and the fix below writes the ltr WordPress already declares. And a theme that hard-codes dir="rtl" on an English store is not flagged, because rtl is a valid value — that particular mismatch is the case on this page automated detection does not reach, and a person has to spot it. The comparison against what WordPress declares happens at the fix stage, described next, on pages this check has already raised.
Because no criterion owns it, the finding is filed as an advisory: a best-practice note supporting correct bidirectional rendering, sitting alongside the language declaration it completes. The report says exactly that, rather than dressing it up as a WCAG failure.
What Klarvo does about it
This family is auto tier, behind an authority gate. The engine applies the fix only when WordPress can answer for the direction; it never derives one itself.
When your site’s locale declares a direction — Arabic, Hebrew, Persian and Urdu locales declare right-to-left; the rest declare left-to-right — that answer is authoritative, and the fix writes it onto the html element server-side, in the HTML your server sends. The transform is the one at the top of this page: <html lang="ar"> becomes <html lang="ar" dir="rtl">. It is applied the same way as the language fix — by rewriting just the opening tag in the response, never by round-tripping the whole document, and never by a script patching the page in the visitor’s browser. A tag already carrying the right value is left alone, and the operation is idempotent: it never stacks a second attribute onto a page that has one.
When no authority exists — the locale is missing, custom or ambiguous, and WordPress cannot vouch for a direction — the engine routes the finding to report and applies nothing. This is deliberate. A guessed direction is worse than a missing one: dir="rtl" stamped on a left-to-right shop would manufacture, at a stroke, every rendering fault this page describes. The engine states what it found and leaves the call with a person.
Then the result is checked rather than assumed. The page is fetched again, independently, as an ordinary visitor, and the served opening tag has to carry the exact declared value — not merely some value — before the fix is recorded. The tag before and after is kept as evidence, because an automated finding is evidence, not a certificate.
How to fix it yourself
Set the locale first. Settings → General → Site Language. WordPress ships the text direction for every locale it ships, so choosing the real language of your shop is most of the work — is_rtl(), the admin mirroring and everything below all read from it.
Classic themes. The opening tag in header.php should delegate to WordPress:
<!doctype html>
<html <?php language_attributes(); ?>>
language_attributes() prints the lang attribute and, on a right-to-left locale, adds dir="rtl" for you. The bug, when there is one, is nearly always a hard-coded tag — <html lang="en" dir="ltr"> pasted in by a theme that was only ever tested in English. Fix it in a child theme, so the next update does not undo you.
Block themes. Core generates the opening tag itself and already calls language_attributes(), so a missing direction on a block theme usually means something downstream — an HTML minifier, a cache layer, an optimisation plugin — is rewriting the tag. Find that plugin before adding markup on top of it.
If you cannot edit the template, filter the output from a small site-specific plugin or a child theme’s functions.php:
add_filter( 'language_attributes', function ( $output ) {
if ( false === strpos( $output, 'dir=' ) ) {
$output .= is_rtl() ? ' dir="rtl"' : ' dir="ltr"';
}
return $output;
} );
Let the theme mirror. The attribute puts your text right; your layout mirrors through CSS. WordPress loads a theme’s RTL stylesheet on right-to-left locales where the theme provides one, and themes written with CSS logical properties — margin-inline-start rather than margin-left — mirror on their own. If your theme has neither, the text will be correct and the design will look odd. That is a theme shortcoming to raise with its author, not a reason to remove the attribute.
Multilingual shops. With WPML, Polylang or TranslatePress serving both directions, each URL declares its own pair: the Arabic URLs want lang="ar" dir="rtl" while the English URLs stay left to right. The declaration is per page, not per site — test one URL per language.
Phrases against the grain get their direction inline, on the element that wraps them, exactly as with language:
<p dir="rtl">متوفر الآن: <span dir="ltr">Model X-200</span></p>
For text you do not control — search terms, review author names — dir="auto" lets the browser take the direction from the first strong character, and <bdi> isolates a name of unknown direction so it cannot drag the punctuation around it out of place. Those inline cases are judgement calls inside your own content, so automation reports them; it does not rewrite your copy.
Then clear your caches, or the old opening tag will outlive the fix by hours.
How you know it worked
View source on the front end — the served HTML, not the editor — and read the first line: the html tag should carry both lang and dir, and the direction should match how the declared language is written. From a terminal, the same proof, more strictly:
curl -s YOUR_STORE_URL | head -3
Then trust your eyes, because this fix is visible. On a right-to-left store: sentence-ending punctuation sits where the sentence actually ends, brackets open the correct way, text ranges from the right edge, and a mixed line — an Arabic sentence holding a Latin model number — reads in its intended order. Check the pages where mixed runs cluster: a product page with SKUs and sizes, the basket with its quantities and totals, the checkout with its prices and postcode field.
Check more than one template, and one URL per language if you are multilingual — different templates and translation plugins can emit their own opening tags. Then re-scan: the direction advisory should clear alongside the language checks it complements, because the pair is one declaration in two attributes, and the page is only honest when both agree with the words on it.
A screen reader, for once, will not tell you much here — direction is the rare fix you verify by reading your own shop the way your customers do. If you have a native reader of the language on the team, five minutes of their eyes on the product page and the checkout is worth more than any automated pass.
Sources: W3C WAI — Understanding SC 3.1.1: Language of Page · W3C Internationalization — Structural markup and right-to-left text in HTML · W3C Internationalization — Inline markup and bidirectional text in HTML · W3C Internationalization — CSS vs. markup for bidi support · MDN — dir (global attribute)
FAQ
Questions this raises
My shop is in English. Do I need to add dir="ltr"?
Not for rendering — left-to-right is the HTML default, so an English shop reads correctly without the attribute. The check is narrower than that, and it does not read your locale: it fires when the served html element carries no dir attribute or an invalid one, whatever the language, so an English store with no declaration is flagged and the report files it as the advisory it is. Where the engine acts it writes the direction WordPress declares, and on an English locale that is ltr. Worth knowing what it does not catch: a dir="rtl" hard-coded by a theme built and tested in the other direction is a valid value, so it passes this check — that one still needs a person. The case that costs money is a right-to-left store with no declaration.
Will dir="rtl" break my theme layout?
It corrects the text straight away: punctuation, brackets and mixed runs render in reading order. Mirroring the layout is the job of the theme, and WordPress helps — it loads a theme RTL stylesheet on right-to-left locales, and themes built with CSS logical properties mirror on their own. A theme with neither will show its seams; that is a real gap worth raising with the theme author, and leaving the whole shop misrendered to hide it is the worse trade.
If no WCAG criterion requires dir, why fix it at all?
Because the criterion that does exist — 3.1.1, declaring the language of the page — is only half of what a right-to-left language needs to render readably, and the other half costs one attribute. Klarvo files this as an advisory rather than a failure, and the report keeps that distinction. The reason to apply it is not a checkbox: your Arabic-reading customers meet the result on every line of the shop.
Find out whether your store has this one.
The free scan checks your real pages — home, product, populated basket, hydrated checkout — with all three engines and all 19 Klarvo checks, and tells you exactly what it found and where.