How to test RTL across breakpoints
Learn which widths and states to test, how to catch RTL bugs at layout transitions, and how to verify fixes in both directions.
By Omran Khleifat, Founder of Ritla
Guide13 min read
Your Arabic dashboard is fine at desktop width. Then the viewport gets narrower, a status label jumps to the wrong side, and a heading loses half its room. The desktop screenshot was not lying. It just never exercised the mobile rule.
Testing RTL across breakpoints means checking the transitions, not collecting screenshots of a laptop, a tablet, and a phone and hoping the gaps between them are polite. A breakpoint can change the layout method, positioning context, navigation, and content available to the user. Direction-sensitive mistakes often live in those changed rules. This guide gives you a repeatable way to choose widths, inspect what changes, and prove a fix works in both directions.
Treat breakpoints as code paths, not devices#
A CSS breakpoint is a condition. A phone is a piece of hardware. They overlap, but they are not the same thing. The width media feature tests the viewport width. A rule written for max-width: 48rem applies whenever that condition is true, whether the browser is on a handset, a narrow desktop window, or a tablet in split view. Device names make convenient meeting notes; they are poor test specifications.
Begin by finding the page's actual layout-changing rules. Search its CSS for @media and, if the project uses them, @container. Note where navigation collapses, columns stack, buttons change position, and elements switch from normal flow to absolute or fixed positioning. Not every media query matters equally. A query that changes only a font weight is less likely to create an RTL placement bug than one that replaces a grid with a compact card.
For each relevant threshold, test just below it, at it, and just above it. If the rule is @media (max-width: 48rem), the at-threshold state belongs to the compact rule, assuming that value resolves to the threshold you expect in the test environment. The point is to exercise both branches and the exact handoff. A screenshot taken comfortably far from the boundary can miss a one-pixel collision or a selector that wins only at the boundary.
Then drag the responsive viewport slowly through the interval between thresholds. Content can fail at a width that has no media query at all: a translated label grows, a grid track reaches its minimum, or two controls can no longer share a line. If the first failure appears before the intended breakpoint, the answer may be to let the component wrap sooner, not to add an “Arabic phone” breakpoint. MDN's media-query guide describes the viewport conditions; your content decides whether the chosen thresholds are sensible.
Do not assume a viewport rule controls every compact component. A card can sit in a narrow sidebar on a wide page. If its layout changes through a container query, vary the container's inline size as well as the viewport. The page can be 1280 CSS pixels wide while the card has less room than it does on a 768-pixel page. Testing only viewport presets misses that state.
Build a test matrix small enough to use#
You need four dimensions: direction, width, content, and state. Testing every combination on every route would make the matrix a part-time job. Choose representative components and the combinations most likely to expose a changed layout rule.
| Dimension | Minimum useful cases | Why it changes the result |
|---|---|---|
| Direction | Same component in LTR and RTL | A physical-side rule can work in one and fail in the other. |
| Width | Each side of every layout breakpoint, plus a slow sweep between them | A branch can be wrong at its boundary, while content can fail between boundaries. |
| Content | Short label, longest realistic Arabic label, loaded production font | Text width and line breaks decide when the layout runs out of room. |
| State | Default, expanded menu or filter, error or loading state where applicable | Newly visible content can be the first thing to overflow or overlap. |
Write the actual CSS-pixel widths beside each component in the test plan. “Phone” is not enough to rerun a failure. Add the narrowest width your product supports and one roomy width that proves the desktop arrangement still works. Those are product decisions, not universal numbers to copy from a device catalogue.
Use the same underlying data where possible. English and Arabic strings will differ, but the card should represent the same inspection, booking, or account state. If English has a short title and Arabic has a long one, that is useful pressure on the layout; if the pages show entirely different records, you cannot tell whether direction or content caused the difference. You can also use a long English fixture to separate “translation made it longer” from “RTL moved the edge.” Both failures are real, but they need different repairs.
Record the browser and viewport settings. The browser behavior discussed here was checked in Chromium 151 on 2026-09-24; Firefox and Safari were not checked for the brief, so verify them in your own support matrix. Device emulation helps you sweep widths quickly, but Chrome's own guidance calls it an approximation, not a substitute for a real device. Save a real-device pass for interactions that depend on touch, the on-screen keyboard, browser chrome, and the particular mobile browser your users have.
Establish direction before comparing screenshots#
Make sure the Arabic route actually declares direction, rather than looking RTL because a few components were manually reversed:
<html lang="ar" dir="rtl">The HTML dir attribute supplies base direction; lang="ar" alone does not. concerns document direction that is missing, contradicted, or declared only in CSS. If the root is wrong, a breakpoint comparison will be noisy: flex and grid placement, text alignment, and local overrides may each be compensating for a different assumption. Correct the document direction first, then inspect the responsive rules. The HTML direction guide covers the markup decision in full.
At each width, check the component's computed direction in DevTools, not only the root attribute. A compact wrapper can accidentally set direction: ltr, or a mobile-only class can override the component's inherited direction. Select the element and inspect direction in the Computed panel. If it differs from the intended content direction, trace the winning rule and the nearest ancestor with a dir attribute. specifically concerns a forced-LTR container or bidi override over Arabic content; it is not a catch-all label for a misplaced box.
Do not demand that every child compute to RTL. A known LTR value may need its own direction. What you are checking is that the layout container and its text have the intended base direction before you judge their positions. An Arabic reader starts the main flow on the right; an element appearing on the right can be correct even when it differs from the English screenshot. “It moved” is not the same as “it broke.”
Sweep the page and mark the first failure#
Open the same route in LTR and RTL responsive viewports. Start wide and reduce the width in small steps. At each visible change, ask what rule became active: did the header collapse, did a two-column region stack, did a badge become positioned, or did a scroll container appear? When you see the first failure, stop and write down the width. Keep both the failing width and the nearest passing width. That pair often identifies the responsible rule faster than a screenshot alone.
Look for more than a document scrollbar. In an RTL document in the checked Chromium behavior, content projecting past the left edge can create horizontal scrolling; an equivalent right-side projection may not. concerns document-level horizontal overflow. A child can also be clipped inside a component without enlarging the document, so the absence of a page scrollbar proves only that the document did not overflow. The horizontal-overflow guide covers the longer search for an offending element.
Use the browser's element highlight to compare boxes, then its Computed and Styles panels to find the declaration that won. At the failing width, record the component's bounding rectangle, its parent's rectangle, and whether the content is inside the intended scroll container. The bounding-rectangle API reports the element's border box relative to the viewport; it does not explain why the box is there. A title that no longer fits its card calls for a different fix from a badge anchored to the wrong edge.
One quick document-level check in the browser console is:
({
documentWidth: document.documentElement.scrollWidth,
viewportWidth: document.documentElement.clientWidth
});Run it with the same open or expanded state that caused the failure. A wider document is evidence to investigate, not permission to add overflow-x: hidden and go home. That rule can remove the scrollbar while leaving the control inaccessible. For an intentionally scrollable rail, inspect the rail's own bounds and scroll behavior instead of treating every off-screen child as a document defect.
Follow a mobile-only rule through one component#
Consider a facilities-inspection dashboard. At wide widths, a card shows a status label next to the inspection title. At compact widths, the design puts the status in the card's start corner and reserves room for it beside the title. The Arabic record is long enough to expose a misplaced reserve:
<article class="inspection-card" lang="ar" dir="rtl">
<span class="inspection-card__status">بانتظار المعاينة</span>
<h2>فحص نظام التهوية في المبنى الشمالي</h2>
</article>The shared wide-screen layout keeps both items in flow:
.inspection-card {
display: flex;
align-items: start;
gap: 1rem;
padding: 1rem;
}With dir="rtl", the first flex item starts at the right in the measured Chromium behavior. No row-reverse is needed. The narrow rule, however, was written against physical left:
Problem
@media (max-width: 40rem) {
.inspection-card {
display: block;
position: relative;
padding: 1rem;
padding-left: 9rem;
}
.inspection-card__status {
position: absolute;
top: 1rem;
left: 1rem;
max-width: 7rem;
}
}The LTR card puts its badge at the left and reserves space to its right. At the breakpoint, the Arabic card also puts the badge at the physical left and reserves space there, even though the design's start corner is now the right. The title has a large gap on the wrong side while the badge remains visually at the end of the RTL line. A long title makes that mismatch obvious. The desktop rule looked fine because none of these mobile declarations had applied.
Better
@media (max-width: 40rem) {
.inspection-card {
display: block;
position: relative;
padding: 1rem;
padding-inline-start: 9rem;
}
.inspection-card__status {
position: absolute;
inset-block-start: 1rem;
inset-inline-start: 1rem;
max-inline-size: 7rem;
}
}The logical padding and logical inset map to the left side in horizontal LTR and the right side in horizontal RTL. Thus the LTR layout stays the same, while the RTL badge and reserved space move together. inset-block-start replaces top for consistency, though the vertical placement was not the bug. concerns direction-sensitive physical properties without an RTL override; the physical rules in the problem snippet are the ones worth inspecting here.
This fix has a limit. The badge may wrap into more lines than the absolute layout reserved, especially at increased text size or with a different status. Then the status can overlap the heading even though its side is correct. Test the longest real status and text scaling. If its height is variable, keep the badge in normal grid or flex flow instead of increasing the reserved pixels until this one fixture passes. A responsive test has done its job when it makes you question the layout method, not merely change left to right.
Inspect what each transition introduces#
A useful pass follows the elements that change their job at a breakpoint. A desktop navigation bar may become a disclosure control. A filter row may become a compact button. A side panel may move below the main content. Those transitions create fresh opportunities for physical-side assumptions and for hidden content to remain in the keyboard order.
At a wide layout, inspect the placement of the first grid or flex child, column gaps, headings, and the order in which a reader meets controls. In a dir="rtl" page, the first item of a flex row or grid sits on the right in the checked Chromium behavior. If a developer has added row-reverse solely to make it “look RTL,” the mobile layout can reverse it again when a different rule takes over. concerns row-reverse faking RTL when tab order fights visual order. Test Tab order rather than guessing from positions.
At the transition width, watch for a half-changed component: an old desktop control still visible beside its mobile replacement, a badge positioned according to the new rule while its parent still has the old padding, or a menu that opens against the wrong edge. The CSS cascade can combine declarations from different breakpoints. Read the winning rules at that exact width, including overrides from component styles and utility classes.
At a narrow width, inspect controls in their open state. Does the filter panel fit within the viewport? Can you reach its last option? Does the close button remain visible? A collapsed navigation that looks neat while closed may reveal the only serious overflow on the page when opened. If the component clips internally, document-width checks will not find the lost item. Open it and inspect its own scroll container.
Check overlapping text separately from overflow. concerns visible text-bearing elements that overlap. concerns text wider than a box with overflow: hidden and no ellipsis. concerns a fixed-pixel-width container whose Arabic text exceeds the space. All three can show up after a breakpoint, but they describe different failures. Inspect whether the whole box moved, the box became too small, or the content was simply hidden. The repair should match that finding.
Stress the layout with real content and font states#
Use the longest realistic Arabic title in the feature, not a repeated character string. Real content has spaces, punctuation, and words of different lengths. Test labels for the states the component actually supports: pending, failed, complete, unavailable, and whatever else your product displays. A layout that survives a short default label has not proved it survives the error state. For this inspection card, the long title and a wrapping status are more revealing than a generic “item” fixture.
Test once after webfonts finish loading. If the declared Arabic font fails, a fallback can change glyph widths and line breaks. concerns Arabic text falling back past the declared font family, including a webfont that failed to load. Do not approve the responsive layout solely from the first paint if the final font changes the card height. Also test at increased browser text size or zoom. A CSS breakpoint may still be unchanged while the content needs more space. The practical question is not whether the viewport has a fashionable device width; it is whether the words and controls still fit and remain usable.
Mixed-direction content deserves a spot-check where it occurs, but do not turn this page into a bidi tutorial. If a box stays in place while a phone number or Latin token changes apparent order, inspect the text run and its isolation rather than moving the box. The bidirectional-text guide treats that problem directly. concerns unisolated phones, emails, and Latin tokens inside Arabic text. Keep a layout failure and a text-order failure as separate test cases, even if one narrow screenshot shows both.
If your product has a table or data grid, test its narrow behavior intentionally. A contained horizontal scroller can be a reasonable design when the data cannot be compressed without losing meaning. In RTL, the checked Chromium behavior gives an RTL scroll container scrollLeft of zero at its start and negative values as you scroll toward its end. An automated interaction that assumes positive offsets can fail even though the CSS looks correct. Conversely, making the entire document scroll horizontally because a table escaped its container is not the same design. Test where the scrollbar belongs.
Test interaction after the boxes fit#
Responsive layout testing is not finished when rectangles stop overlapping. Tab through the compact header, open disclosures with the keyboard, and check that focus reaches visible controls in a reading order that makes sense. If the design visually moves a control without changing DOM order, confirm that the keyboard path remains understandable. Do not use row-reverse as a substitute for document direction and then rely on a screenshot to approve the result.
On a real narrow device, try the controls with touch and with the software keyboard open if the component contains inputs. Browser chrome and the keyboard reduce the visible area in ways a fixed desktop screenshot will not show. You do not need to test every device ever sold; select the browsers and devices your product supports, then use responsive emulation to cover the widths between them. The Chrome Device Mode documentation is explicit about the limits of emulation.
When the content becomes one column, check sequence rather than merely side. Does the primary action come after the information needed to decide? Does a status still label the correct inspection? A card can be physically mirrored and still be hard to understand if CSS order rearranges a meaningful sequence. Compare what the DOM says, what the screen displays, and what the keyboard visits. If all three agree, the layout is doing useful work instead of performing a mirror trick.
Turn the discovery into a repeatable release test#
Save the failing state as a test case with the route, content fixture, viewport width, direction, and open control. Add a passing width beside it. A useful regression for the inspection card checks both routes immediately below and above its compact breakpoint, then measures the badge and heading. The assertion should express the intended relationship: the badge sits at inline start, the heading has room beside it, neither box escapes the card, and the LTR placement has not changed. Avoid an assertion that the badge must be at one hard-coded screen coordinate. That is how tests become loyal to a bug.
You can make the test more resilient by asserting document overflow separately from component containment. On the Arabic page, compare document.documentElement.scrollWidth with clientWidth after opening the relevant control. Then inspect the component's own rectangle and scroll container. A page-wide width assertion alone misses clipped text inside a card; a component-only screenshot can miss a document scrollbar caused by something farther down the page.
Make breakpoint cases part of the Arabic release routine, not a one-time audit. When a new status, navigation item, or responsive rule lands, rerun the affected component in both directions. The Arabic release-testing guide covers how to keep those cases tied to the release process. The small habit is to test a changed rule at its boundary, with content that makes it earn its space. CSS is remarkably obedient to the rule you wrote, including the one you forgot was mobile-only.
A free Ritla scan can surface document-level horizontal overflow or visible text-bearing elements overlapping on the page being examined. Use those findings to choose where to inspect, then run your own width sweep and open-state checks around the breakpoint. The scan can identify a visible failure; the boundary test tells you which responsive rule introduced it.
Checks in this guide
- document direction missing, contradicted, or declared only in CSS
- Forced-LTR container, or a bidi override, over Arabic content
- Document-level horizontal overflow
- Direction-sensitive physical properties without an RTL override
- flex row-reverse faking RTL instead of dir (tab order fights visual order)
- Visible text-bearing elements overlapping
- Clipped text: content wider than its box with overflow hidden and no ellipsis
- Fixed-pixel-width container whose Arabic text exceeds the space
- Arabic text falling back past the declared font family (measured, declared, or a webfont that failed to load)
- Unisolated bidi hazards: phones, emails, Latin tokens inside Arabic text