rtl-ui
Skill1 starsChanged 35 days agoMITView on GitHub
Audits and fixes web interfaces for right-to-left languages (Arabic, Hebrew, Persian, Urdu). Converts physical CSS and Tailwind classes to logical ones, sets dir and lang correctly, decides which icons to mirror, isolates mixed Arabic and English text, and checks forms, numerals, fonts, carousels and keyboard behaviour. Use when a user asks to add RTL or Arabic support, says a layout "looks broken in Arabic", mentions dir="rtl", logical properties, mirroring, bidi, or wants a bilingual (Arabic/English) UI reviewed.
In this repository13 other files it gives its agents.
- agent-ready-siteSkill
- arabic-copySkill
- arabic-localizationSkill
- arabic-seoSkill
- client-briefSkill
- design-tokens-extractorSkill
- freelance-quoteSkill
- product-sheetSkill
- shared-server-deploySkill
- ui-inventorySkill
- vat-worksheetSkill
- video-readerSkill
- zatca-invoiceSkill
The file
We split this skill into the rules it gives an agent
11 rules. About 0 are common, 1 is distinctive, and 10 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
10
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 24
Audit: Run the scanner from the skill folder against the source:
- Very specific: line 43
Verify in both directions: Run the checklist at the end with dir="rtl" and again with dir="ltr".
- Very specific: line 53
Never use CSS direction: rtl on the root as a substitute.
- Very specific: line 100
Do not add flex-direction: row-reverse for RTL.
- Very specific: line 119
Prefer swapping the icon (chevron-left ↔ chevron-right) over transforming it when the icon has a shadow or text inside, because scaleX(-1) …
- Very specific: line 154
Verify output in the browsers you support.
- Very specific: line 162
Never use letter-spacing on Arabic.
- Very specific: line 165
Avoid text-overflow: ellipsis on very short Arabic labels.
- Very specific: line 193
Tailwind: use logical utilities and rtl:/ltr: variants.
- Very specific: line 195
Flutter: use EdgeInsetsDirectional, AlignmentDirectional and PositionedDirectional.
- Very specific: line 199
Run with dir="rtl" on <html>, then again with dir="ltr":
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
RTL UI
Make a web UI correct in right-to-left languages without breaking left-to-right.
When to use
- Adding Arabic, Hebrew, Persian or Urdu to an existing site or app.
- A page "looks broken" or "is reversed wrong" in RTL.
- Reviewing a component library, design system or Tailwind config for RTL.
- Mixed Arabic and English text renders in the wrong order (phone numbers, emails, prices, code).
When not to use
- Translating or writing Arabic copy. Use the
arabic-copyskill. - Full i18n setup (string extraction, plurals, dates). Use
arabic-localization.
Workflow
Audit. Run the scanner from the skill folder against the source:
node scripts/rtl-audit.mjs src # or any folders/files node scripts/rtl-audit.mjs src --json # machine-readableIt lists every physical property and physical Tailwind class with
file:lineand the logical replacement. Exit code 1 means findings. It does not catch everything: also read the components that own layout (header, nav, sidebar, drawers, modals, tables, forms, carousels).- Set direction at the root. See "Direction and language" below. Do this before fixing CSS, so you can see the result.
- Fix CSS. Replace physical properties with logical ones. Full table: references/logical-properties.md. For Tailwind: references/tailwind.md.
- Fix icons and imagery. Mirror only icons that point in the reading direction. List: references/mirroring.md.
- Fix mixed-direction text. Isolate user content, numbers, emails, URLs and code. See references/bidi.md.
- Fix interaction. Carousels, sliders, swipe, keyboard arrows, progress, charts.
- Verify in both directions. Run the checklist at the end with
dir="rtl"and again withdir="ltr". A fix that breaks LTR is not a fix.
Direction and language
Set both on <html>. lang drives fonts, hyphenation, screen reader voice and quote marks. dir drives layout.
<html lang="ar" dir="rtl">- Never use CSS
direction: rtlon the root as a substitute. Use the attribute. CSSdirectiondoes not reach the document's base direction for things like form controls and scrollbars, and screen readers ignore it. - For a section in another language, set both on that element:
<blockquote lang="en" dir="ltr">. For text whose direction you don't know (user names, comments, search input, chat messages, titles from an API), use
dir="auto". The browser picks the direction from the first strong character.<p dir="auto">{comment.body}</p> <input type="search" dir="auto">- Read direction in JS from the document, not from the locale string:
getComputedStyle(el).directionordocument.dir. Do not hard-codelocale === 'ar'; Persian, Urdu and Hebrew are RTL too. If you must derive it from a locale,new Intl.Locale(code).getTextInfo()(older engines: thetextInfogetter) returns the direction in Chromium and Safari but not yet in Firefox, so keep a fallback list of RTL language codes:ar, ckb, dv, fa, he, ks, ps, sd, ug, ur, yi.
CSS rules
Replace physical with logical. The common ones:
| Physical | Logical |
margin-left / margin-right | margin-inline-start / margin-inline-end |
padding-left / padding-right | padding-inline-start / padding-inline-end |
left / right (positioned) | inset-inline-start / inset-inline-end |
border-left / border-right | border-inline-start / border-inline-end |
border-top-left-radius | border-start-start-radius |
border-top-right-radius | border-start-end-radius |
text-align: left / right | text-align: start / end |
float: left / right | float: inline-start / inline-end |
margin: 0 1rem 0 2rem (asymmetric) | margin-block: 0; margin-inline: 2rem 1rem; |
Notes:
- Symmetric values (
margin: 0 auto,padding: 1rem) need no change. margin-inline: a bis start then end. Watch the order when converting a 4-value shorthand:margin: T R B Lbecomesmargin-block: T B; margin-inline: L R;.float: inline-startis supported in current Chrome, Edge, Firefox and Safari (since 2023). If you support older browsers, keepfloatbehind[dir="rtl"]overrides or use flex instead.background-position: lefthas no logical keyword with wide support. Use[dir="rtl"]overrides or flip with a CSS variable.transform: translateX(...)is physical. Multiply by a direction variable::root { --dir: 1; } [dir="rtl"] { --dir: -1; } .drawer--closed { transform: translateX(calc(-100% * var(--dir))); }- Gradients:
linear-gradient(to right, ...)is physical, and there are no logical keywords (to inline-endis invalid CSS). Override the direction under[dir="rtl"]/:dir(rtl), or drive it with a custom property such as--dir-end: rightthat you flip for RTL. - Shadows with an x-offset (
box-shadow: 4px 0 ...) are physical. Flip them under[dir="rtl"]if the shadow implies direction.
Flex and grid
flex-direction: rowand grid columns already follow the writing direction. In RTL the first item sits on the right with no extra CSS.- Do not add
flex-direction: row-reversefor RTL. That double-flips and puts things back in LTR order. If you findrow-reverseused to "fix RTL", remove it. justify-content: flex-start/flex-endfollow direction too.start/endare the clearer modern form.ordervalues andgrid-template-areasfollow direction. Don't swap them per language.- Absolute positioning inside a flex row (badges, close buttons) must use
inset-inline-*, notleft/right.
Icons and images
Mirror an icon only if it shows motion or direction along the reading line. Full list in references/mirroring.md. Short version:
- Mirror: back/forward arrows, chevrons in breadcrumbs and "next", reply, undo/redo (they swap), send (paper plane), bulleted list, indent/outdent, text alignment icons, progress that fills along the line, sidebar toggle, logout door.
- Your call: external-link arrows (up-and-out). Follow your icon set and be consistent across the product.
- Do not mirror: logos, brand marks, media play/pause/fast-forward (they follow tape direction, not text), checkmarks, clocks and timers (clockwise is universal), circular refresh/reload, search magnifier (most systems keep it), slashes, numbers and anything with Latin text, charts' numeric axes, real-world objects (cameras, phones, keyboards), the right hand in hand icons.
Scope the flip to icons that need it, never to a whole container:
[dir="rtl"] .icon--directional { transform: scaleX(-1); }Prefer swapping the icon (chevron-left ↔ chevron-right) over transforming it when the icon has a shadow or text inside, because scaleX(-1) mirrors those too.
Photos and illustrations: do not mirror photos of people or places. Mirror illustrations only if they carry directional meaning (a person walking "forward" in onboarding).
Mixed Arabic and English (bidi)
The Unicode bidi algorithm reorders runs of text. Neutral characters (spaces, punctuation, +, -, @, /) take the direction of their neighbours, which is how +966 50 123 4567 turns into 4567 123 50 966+. Fix it by isolating the run.
Wrap embedded values whose direction differs from the paragraph in
<bdi>or an element withdir:<p>رقم الطلب: <bdi>#A-1042</bdi></p> <p>تواصل معنا على <span dir="ltr">+966 50 123 4567</span></p>- In CSS:
unicode-bidi: isolate;on the inline element (this is what<bdi>does). - For plain strings where you can't add markup (notifications,
<title>,<option>, SMS), use Unicode isolates: FSIU+2068+ text + PDIU+2069. LRMU+200E/ RLMU+200Ffix a single neutral at the edge. Details and code: references/bidi.md. Inputs that are always Latin get
dir="ltr"even in an RTL page: email, URL, password, phone, card number, IBAN, code, username. Align their text to the inline end only if the design wants it; never change the input's direction to RTL.<input type="email" dir="ltr" autocomplete="email">- Code blocks and terminals stay
dir="ltr"always.
Numerals
- Arabic UIs use either Western digits (0-9) or Arabic-Indic digits (٠-٩). Saudi, Gulf and most product UIs use Western digits; formal Egyptian and some government content uses Arabic-Indic. Make it a deliberate choice, not an accident.
Format with
Intl, and pin the numbering system so output is predictable across browsers:new Intl.NumberFormat('ar-SA', { numberingSystem: 'latn' }).format(1234.5) // "1,234.5" new Intl.NumberFormat('ar-EG').format(1234.5) // "١٬٢٣٤٫٥"Verify output in the browsers you support; CLDR defaults for
ar-*locales have changed between versions.- Digits are always laid out left to right, even inside RTL text. Don't reverse them.
- Phone numbers, card numbers and OTP inputs stay Western digits and
dir="ltr".
Arabic typography
- Use a font with real Arabic glyphs. Pair it explicitly; the fallback system font looks unrelated. Good free options: IBM Plex Sans Arabic, Noto Sans Arabic, Noto Kufi Arabic, Tajawal, Cairo, Readex Pro, Almarai. Load the Arabic subset, and put the Arabic face first under
:lang(ar). - Increase line-height. Arabic has tall ascenders and dots below;
1.6to1.8for body text is a common range where Latin uses1.4to1.5. Test with your font. - Never use
letter-spacingon Arabic. It breaks the joins between letters. Reset it::lang(ar) { letter-spacing: 0; }. - No
text-transform: uppercase(Arabic has no case; it just wastes the rule) and no italics (Arabic italic is a fake slant). Use weight or color for emphasis. - Arabic often needs a slightly larger size than Latin at the same nominal size. Adjust per font with
font-size-adjustor a:lang(ar)override. - Avoid
text-overflow: ellipsison very short Arabic labels; truncating a word mid-join reads badly. Allow wrapping.
:lang(ar) {
font-family: "IBM Plex Sans Arabic", "Noto Sans Arabic", system-ui, sans-serif;
line-height: 1.7;
letter-spacing: 0;
text-transform: none;
}Interaction
- Carousels and sliders: slides advance toward the inline end. In RTL the next slide sits to the left, so the user swipes right to advance and the "next" button sits on the left. Most libraries need an explicit flag: Swiper (
dir="rtl"on the container), Embla (direction: 'rtl'), Splide (direction: 'rtl'), Slick (rtl: true). Range inputs (<input type="range">) followdirnatively. Keyboard: in RTL, ArrowRight means "previous" and ArrowLeft means "next" for horizontal widgets (tabs, menubars, sliders). Read direction at runtime:
const rtl = getComputedStyle(el).direction === 'rtl'; const next = rtl ? 'ArrowLeft' : 'ArrowRight';- Progress and steppers: fill from the inline start.
- Charts: time-series x-axes are usually kept left-to-right (time flows left to right in most Arabic financial and analytics UIs), while legends, titles and tooltips are RTL. Decide per product and be consistent. Bar charts with category labels can mirror.
- Scroll containers: horizontal
scrollLeftis 0 at the start and negative toward the end in RTL in current Chrome, Firefox and Safari. Code that assumesscrollLeft >= 0breaks. UseMath.absorscrollBywith a direction factor. - Tooltips, popovers, dropdowns: placement libraries (Floating UI, Popper) accept
start/endplacements that follow direction. Use those, notleft/right.
Frameworks
- Tailwind: use logical utilities and
rtl:/ltr:variants. See references/tailwind.md. - React Native:
I18nManager.forceRTL(true)plus an app reload flips layout; usestart/endstyle keys (marginStart,paddingEnd) instead of left/right. - Flutter: use
EdgeInsetsDirectional,AlignmentDirectionalandPositionedDirectional; setDirectionalityor rely onMaterialApplocale.
Verification checklist
Run with dir="rtl" on <html>, then again with dir="ltr":
node scripts/rtl-audit.mjsreports nothing, or every remaining finding is intentional and commented.<html>has correctlanganddir; no rootdirection:CSS.- Header, nav, sidebar, drawer and modal close buttons sit on the correct side.
- No
row-reverseused as an RTL fix. - Directional icons mirror; logos, media controls, checkmarks and clocks do not.
- Phone numbers, emails, prices with currency, order IDs and code render in the right order.
- Email, URL, phone, password and code inputs are
dir="ltr". - Arabic font loads; no letter-spacing, uppercase or italics on Arabic.
- Carousel, slider, tabs and keyboard arrows move in the reading direction.
- Animations and transforms (drawers, toasts) enter from the correct side.
- Nothing changed visually in LTR except intended fixes.
Take a screenshot in each direction if you can run a browser, and compare.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

