Arabic QA checklist for frontend engineers
Ship Arabic interfaces with a code-level QA checklist for document direction, logical CSS, bidi isolation, fonts, forms, formats and focus.
Guide13 min read
An Arabic build can compile cleanly, pass the English end-to-end suite and still be wrong at the browser boundary. The root can declare Arabic without setting RTL direction. A ticket reference can have the correct DOM string but appear with its groups reversed. A component can look mirrored while keyboard focus moves against the visual sequence.
Frontend ownership starts where translation files stop: document metadata, inherited direction, logical layout, bidi boundaries, field behavior, fonts, formatters, DOM order and rendered states. This checklist turns those responsibilities into code and test conditions.
Use it against the real Arabic route with product-shaped data. An English story containing one Arabic label is not an RTL implementation test.
Establish the document contract#
Set language and direction on the root element rendered for the Arabic route.
<html lang="ar" dir="rtl">The two attributes solve different problems. lang identifies the content language to browsers and assistive technology. dir establishes the base direction inherited by layout and text. One does not substitute for the other.
Check that your implementation:
- renders both attributes in the initial HTML for server-rendered Arabic routes;
- updates both when a client-side locale switch changes the document;
- preserves the Arabic locale through authentication redirects and refreshes;
- does not rely on
direction: rtlin CSS as the only direction signal; - sets page titles and metadata from the Arabic route;
- removes stale direction when switching back to an LTR locale.
R001 reports document direction that is missing, contradicted or declared only in CSS. R002 reports a missing, non-Arabic or contradictory html[lang].
Test the lifecycle, not just the first render:
// page and expect are supplied by the browser test runner.
await page.goto("/ar/events");
const root = page.locator("html");
await expect(root).toHaveAttribute("lang", /^ar(?:-|$)/);
await expect(root).toHaveAttribute("dir", "rtl");
await page.getByRole("link", { name: "تذاكري" }).click();
await expect(root).toHaveAttribute("lang", /^ar(?:-|$)/);
await expect(root).toHaveAttribute("dir", "rtl");Add the product's redirect, error-boundary and sign-out paths. A layout that is RTL only after hydration can flash the wrong direction and give crawlers or no-script clients different metadata. The HTML dir="rtl" guide covers the root implementation in depth.
Let direction inherit through the component tree#
Do not pass an isRtl prop through every component when the browser already provides inherited direction. It creates two sources of truth: document direction and application state.
Use dir at a boundary only when a subtree has a different direction from its parent. Examples include an LTR code editor, a machine identifier or user content whose direction must be determined independently. Do not stamp dir="rtl" on every wrapper. Redundant attributes make local overrides harder to reason about and can hide the element that introduced a contradiction.
For components that render in portals, confirm the portal inherits or receives the correct direction. A dialog mounted under body is outside the direction boundary of a nested application root. Either put the language and direction on html, or set the portal container's direction deliberately.
In component tests, render inside a production-like Arabic frame:
// ArabicTestFrame supplies the document context for browser component tests.
export function ArabicTestFrame({ children }) {
return <main lang="ar" dir="rtl">{children}</main>;
}Keep route-level tests as well. A wrapper proves the component behaves when it receives RTL context; it does not prove the application creates that context.
Replace physical layout assumptions#
Physical properties encode a side. Logical properties encode a relationship to the writing direction. Use the logical form when the design intent is start, end, before or after.
Suppose an event card places its availability badge at the inline end. The English card puts it on the right; the Arabic card should put it on the left.
Problem
.event-card__availability {
position: absolute;
top: 1rem;
right: 1rem;
margin-left: 0.5rem;
}Better
.event-card__availability {
position: absolute;
inset-block-start: 1rem;
inset-inline-end: 1rem;
margin-inline-start: 0.5rem;
}The better version preserves the LTR geometry because inline end is still the right side in English. Under RTL it follows the intended relationship without a duplicate override. R030 reports direction-sensitive physical properties without an RTL override.
Audit:
left,right,margin-left,margin-right,padding-leftandpadding-right;- physical border radii and border sides;
- absolute and fixed positioning;
- floats in content layout;
- directional box shadows;
- background positions and gradient directions;
- transforms, keyframes and SVG coordinates.
Do not replace every physical token mechanically. A venue map coordinate can be physically left. A decorative light source can cast a fixed shadow. The question is whether the value represents a writing-direction relationship. The RTL CSS guide covers logical properties and the cases that still need explicit variants.
Keep DOM order semantic#
Setting flex-direction: row-reverse can make a row look RTL without changing its underlying document order. That is a visual patch, not a direction model.
Place elements in the DOM in the task's logical sequence. Let dir="rtl" change the inline layout of ordinary flex, grid and table rows. Then test keyboard focus, screen-reader order and visual order together.
This matters in a ticket transfer flow. If recipient, ticket choice and confirmation actions are rearranged visually with CSS while DOM order stays tied to the English composition, Tab can jump across the screen. Positive tabindex values do not repair the model; they add a third order to maintain.
R081 reports flex row-reverse used to fake RTL instead of document direction, where tab order fights visual order.
Checklist:
- DOM order represents reading and task order;
- CSS does not reorder focusable controls for presentation;
- mobile and desktop variants preserve a sensible sequence;
- dialogs focus their heading or first required control as designed;
- closing an overlay returns focus to its trigger;
- live content is announced after the action that caused it.
Inspect both LTR and RTL. A DOM rewrite created only for Arabic can fix one locale and break the shared accessibility order.
Isolate mixed-direction values#
Arabic interfaces routinely place Latin text, digits and punctuation inside RTL sentences. The Unicode Bidirectional Algorithm resolves their visual order from surrounding characters. A value can be correct in source and still appear wrong.
Use an event booking reference with fixed group order:
Problem
<p dir="rtl">مرجع الحجز <span>73105-28-604</span></p>A plain span creates no isolation. In Chromium 151, measured on 2026-09-24, the digit groups display as 604-28-73105 after the Arabic label, even though a DOM snapshot and copied source retain 73105-28-604.
Better
<p dir="rtl">مرجع الحجز <bdi dir="ltr">73105-28-604</bdi></p>The isolated value keeps its identifier order while the paragraph remains RTL. R021 reports unisolated bidi hazards such as phones, emails and Latin tokens inside Arabic text.
Do not force every mixed value to LTR. Arabic user content may need dir="auto", and a numeric range can have a different reading-order requirement from a machine identifier. Define the value contract first. The bidirectional text developer guide explains isolation choices and how to verify them.
Test display, editing and copy separately. Move the caret through punctuation, select a group, delete at boundaries, paste the value and compare the submitted string with the visible value. A source assertion alone cannot catch visual reordering.
Set field direction by the value contract#
The form's direction does not mean every control should be RTL. Arabic prose fields should accept and align Arabic naturally. Email, URL and phone values remain LTR. Booking codes and voucher tokens need an explicit contract based on their syntax.
For an event support form:
<label for="issue">وصف المشكلة</label>
<textarea id="issue" name="issue" dir="auto"></textarea>
<label for="ticket-code">رمز التذكرة</label>
<input id="ticket-code" name="ticketCode" dir="ltr" inputmode="text">
<label for="contact-email">البريد الإلكتروني</label>
<input id="contact-email" name="email" type="email" dir="ltr" autocomplete="email">dir="auto" is useful for user-generated text whose direction is unknown. It is not ideal for every required Arabic field: an empty auto-direction field resolves LTR, so an Arabic placeholder can begin at the wrong edge until the user types Arabic. For known Arabic content, inherit RTL or set it explicitly.
Check:
- Arabic text fields and textareas render RTL where Arabic input is expected;
- email, URL and phone controls render LTR;
- code fields preserve the specified group order;
- select controls render their Arabic options in RTL;
- labels, help and validation are associated with the right field;
- identity and contact fields have correct autocomplete tokens;
- server errors return focus and do not reset valid fields.
R023 reports text inputs rendered LTR where Arabic input is expected. R070 reports tel, email or url inputs rendered RTL. R072 reports a select rendered LTR with Arabic options. The field's expanded menu and keyboard behavior still need hands-on testing because that is outside the check description.
Own validation messages#
An Arabic page does not guarantee Arabic native validation messages on every user's device. Browser UI language can determine the built-in message independently from the page language.
If consistent Arabic validation is a product requirement, render product-owned messages and associate them programmatically with the controls. Keep native constraints where they help semantics, but do not assume the browser's visible sentence has been localized by your app.
Test each form in these states:
- empty required submission;
- invalid syntax;
- cross-field conflict;
- server rejection;
- expired session;
- corrected value and successful resubmission.
R071 reports untranslated form labels, validation or help text. It does not prove error association, focus placement or recovery, so include those assertions separately.
Do not test validation only by querying for an error node. Confirm that the message is visible at supported widths, exposes an accessible relationship, names the problem and leaves the correction action reachable.
Make number and date rules explicit#
Locale identifiers are not complete product requirements. Numbering systems, calendars, currencies, time zones and fraction digits need explicit decisions.
For a Cairo event listing that requires Arabic-Indic digits and the Gregorian calendar:
const eventDateFormat = new Intl.DateTimeFormat(
"ar-EG-u-ca-gregory-nu-arab",
{
dateStyle: "medium",
timeStyle: "short",
timeZone: "Africa/Cairo",
}
);This example pins the choices in the locale tag and options. Do not copy them into a different market without confirming the product contract.
Maintain fixtures for:
- event dates around day and month boundaries;
- venue-local time versus viewer-local time;
- ticket price, fees, refunds and zero amounts;
- integer and fractional quantities;
- percentages and units;
- plural branches used by ticket counts;
- identifiers that must not be localized as numbers.
Assert resolvedOptions() where it helps prove the configured calendar, numbering system, currency and time zone. Also approve rendered output in the supported runtime, since locale data can change after dependency or browser updates.
R060 reports English month or day names, or MM/DD/YYYY dates, inside Arabic text. R061 reports Arabic-Indic and Western digits mixed in the same context. Do not silence mixed-digit findings globally: an intentional LTR identifier is different from inconsistent price formatting.
Load and verify Arabic fonts#
Declaring a font family does not prove that its Arabic glyphs loaded. A Latin-first family can fall through for Arabic while DevTools still shows the original declaration in the CSS rule.
Build the font stack deliberately:
@font-face {
font-family: "Event Arabic";
src: url("/fonts/event-arabic.woff2") format("woff2");
font-style: normal;
font-weight: 400 700;
font-display: swap;
}
:lang(ar) {
font-family: "Event Arabic", "Noto Sans Arabic", sans-serif;
}Verify the actual rendered font after document.fonts.ready, and make font requests visible in CI. Test intended weights, not just regular body text. A bold heading can fall back when the regular face succeeds.
R050 reports Arabic text falling back past the declared font family, whether measured, declared or caused by a webfont load failure. R051 reports letter spacing applied to Arabic text, which breaks the connected script.
Audit Latin display tokens applied through shared typography classes. Uppercase transforms, italic styling and tracking do not transfer safely just because the class name is “label.” Use language-aware typography tokens and inspect the rendered result.
Wait for fonts before visual capture and geometry measurement. Otherwise the suite can approve fallback line breaks or compare two different font states.
Mirror assets by meaning#
CSS direction does not flip SVG paths, bitmap images or transform coordinates. Decide asset behavior explicitly.
Mirror controls that communicate progression through an RTL sequence, such as previous and next in a step flow. Preserve icons whose meaning is not directional, including play, pause, clocks and brand marks. Venue maps and physical directions need a product-specific decision rather than automatic reversal.
For each directional asset:
- record whether it mirrors and why;
- expose an RTL variant or a controlled transform;
- keep the accessible name about the action, not “left arrow” or “right arrow”;
- test the asset inside its actual sequence;
- verify animation start and end positions in both directions.
R025 reports a directional icon pointing against the sequence its control advances. R082 reports UI text that references physical arrow directions. The guide to what should be mirrored in RTL provides a decision framework for ambiguous assets.
Avoid flipping an entire component to fix one icon. It can mirror text and non-directional content, create nested transforms and make pointer coordinates difficult to debug.
Test geometry after content settles#
Arabic layout defects often appear only after fonts, images, translations and asynchronous states have loaded. Measure the state the user actually sees.
Check every representative route at the supported width extremes and at layout breakpoints:
- the document does not scroll horizontally;
- text-bearing elements do not overlap;
- required text is not clipped;
- fixed-width containers survive Arabic expansion;
- sticky and fixed controls do not cover content;
- dialogs, menus and tooltips remain inside the viewport;
- intentional inner scrollers expose content at both ends;
- zoom and text enlargement do not remove required actions.
Automate the document measurement:
// page and expect are supplied by the browser test runner.
await page.goto("/ar/events/venue-selection");
await page.evaluate(() => document.fonts.ready);
const overflow = await page.evaluate(() => {
const root = document.documentElement;
return Math.max(0, root.scrollWidth - root.clientWidth);
});
expect(overflow).toBeLessThanOrEqual(1);The one-pixel tolerance is a suite policy, not a standard. Document it. Measure intended component scrollers separately from the document root.
R040 reports document-level horizontal overflow. R041 reports visible text-bearing elements that overlap. R042 reports clipped text when content exceeds a box with hidden overflow and no ellipsis. R043 reports a fixed-pixel-width container whose Arabic text exceeds its space.
When overflow fails, inspect bounding boxes and computed styles from the widest node outward. Hiding overflow-x on the root removes the symptom and can make the inaccessible content impossible to reach. Use the RTL overflow debugging guide for a focused diagnosis.
Exercise state transitions#
A static success screen does not cover the component. RTL regressions can enter during loading, validation, a live update or the transition between responsive layouts.
For each critical event journey, exercise:
- loading to populated;
- empty to first item;
- valid to invalid and back;
- request to server failure and retry;
- available to sold out;
- signed in to expired session;
- overlay closed, open and closed again;
- Arabic to English and back where runtime switching is supported;
- narrow to wide viewport if the product responds without reload.
Use controlled API fixtures and stable data. A live event feed changes availability, timestamps and seat counts, which makes a poor visual baseline. Test the real integration separately, but make the UI state reproducible in CI.
Watch focus and announcements during transitions. A dialog can be visually correct after it opens while focus remains behind it. A translated status can render without being announced. These are interaction assertions, not screenshot assertions.
Build an Arabic fixture pack#
Keep fixtures close to the component or journey they protect, with the expected behavior documented.
For the ticketing product, include:
- the fixed-order reference
73105-28-604next to an Arabic label; - the Latin-leading seat block
BLK-J3-26; - a long event title and a multi-line venue policy;
- Arabic attendee notes and a foreign-language artist name;
- a sold-out state, waitlist state and partial refund notice;
- a ticket image that fails to load;
- a missing optional translation and a missing required translation;
- ordinary and maximum supported ticket quantities;
- regular, bold and disabled Arabic typography.
The fixture name should state its purpose, such as sold-out-with-long-arabic-policy, not arabic-data-2. That makes failures readable and prevents a single fixture from becoming a vague claim of full RTL coverage.
Keep a control fixture alongside the stress case. The Latin-leading seat block should remain intact after Arabic under the measured browser behavior, while the digit-leading reference exposes the isolation risk. Both values are useful because they prevent an overbroad fix.
Add browser assertions to the test pyramid#
Unit tests can prove locale selection, formatter options and translation-key lookup. Browser component tests can prove inherited direction and rendered geometry. End-to-end tests can prove locale persistence and complete journeys. Manual review still owns language quality, semantic mirroring and interaction judgments that do not have stable machine oracles.
At minimum, automate:
- root
langanddiron direct Arabic routes; - locale persistence through navigation and authentication;
- required Arabic control names and validation states;
- computed direction of Arabic and inherently LTR fields;
- approved formatter fixtures and resolved options;
- loaded Arabic fonts and weights;
- document overflow at named states and widths;
- bidi boundaries for known mixed values;
- focus placement after overlays and failed submissions;
- visual baselines for high-risk component states.
Do not rely on DOM snapshots for bidi display or geometry. Do not rely on screenshots for focus order, language metadata or accessible names. Each layer needs assertions for facts it can observe.
Put the checklist in pull requests#
Make Arabic impact a required part of shared frontend changes. A useful pull-request prompt asks the author to identify affected surfaces and attach evidence.
Review when the change touches:
- shared markup, portals or locale routing;
- spacing, positioning, breakpoints or transforms;
- typography tokens or font assets;
- icons, illustrations or animations;
- form controls and validation;
- formatters, locale data or time zones;
- tables, charts or dense dashboards;
- translation keys and fallback logic;
- keyboard order or focus management.
“No Arabic impact” should include a reason. A shared margin token or overlay primitive has direction risk even when the ticket was filed against English.
Require LTR and RTL evidence from the same state. That can be a browser assertion, focused screenshot or short recording depending on the change. The goal is not to collect media; it is to make the claim reproducible.
Run the final engineering pass#
Before handing the candidate to language and product reviewers:
- Open Arabic routes directly in a clean browser context.
- Verify root language and direction through redirects and client navigation.
- Run product-shaped fixtures across supported viewports.
- Check mixed-direction display, editing and copy behavior.
- Exercise every field type and validation path.
- Confirm formatters use the approved market options.
- Wait for Arabic fonts and inspect rendered faces and weights.
- Measure overflow, overlap and clipping after content settles.
- Test keyboard focus, overlays and live state updates.
- Review directional assets inside their real sequences.
- Re-run affected cases after every fix.
- Record known gaps with route, state, fixture, impact and owner.
The handoff should name the build and include failures that still require human judgment. Engineers can prove that the approved Arabic string renders and the action is reachable. A fluent reviewer must decide whether the sentence is accurate and understandable.
Keep Arabic behavior inside the code contract#
Arabic QA becomes maintainable when direction, value types, market formats and rendered states are explicit inputs rather than late exceptions. Each production defect should improve the nearest contract: a fixture, component assertion, route test or review rule.
A Ritla scan can expose rendered Arabic findings that the English test path does not observe, giving frontend teams specific failures to reproduce and guard in code. Use the common RTL bugs guide when a finding crosses component boundaries, and the Arabic website testing guide when the engineering checklist needs to expand into a full release plan.
Checks in this guide
- R001document direction missing, contradicted, or declared only in CSS
- R002html[lang] missing, non-Arabic, or contradicting detected content
- R030Direction-sensitive physical properties without an RTL override
- R081flex row-reverse faking RTL instead of dir (tab order fights visual order)
- R021Unisolated bidi hazards: phones, emails, Latin tokens inside Arabic text
- R023Text inputs rendered LTR where Arabic input is expected
Show every check in this guideShow fewer
- R070tel/email/url inputs rendered RTL (these must stay LTR)
- R072<select> rendered LTR with Arabic options
- R071Untranslated form labels / validation / help text
- R060English month/day names or MM/DD/YYYY dates in Arabic text
- R061Arabic-Indic and Western digits mixed in the same context
- R050Arabic text falling back past the declared font family (measured, declared, or a webfont that failed to load)
- R051letter-spacing applied to Arabic text (breaks the connected script)
- R025Directional icon pointing against the sequence its control advances
- R082UI text referencing physical arrow directions
- R040Document-level horizontal overflow
- R041Visible text-bearing elements overlapping
- R042Clipped text: content wider than its box with overflow hidden and no ellipsis
- R043Fixed-pixel-width container whose Arabic text exceeds the space