persian-rtl
Skill0 starsChanged 14 days agoMITView on GitHub
Build, review or fix Persian (Farsi) right-to-left web UIs — RTL layout with CSS logical properties and Tailwind, icon mirroring, bidi text, Persian/Arabic digits and characters (ی/ي, ک/ك), ZWNJ, Jalali (Shamsi) dates, Tehran time, number and price formatting, and Persian fonts. Use when creating or converting an RTL page, template, component or store front, fixing RTL bugs, or auditing a front end for Persian users; includes a lint script.
In this repository7 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- bug-huntSkill
- compaction-guardSkill
- framework-upgradeSkill
- iran-paymentsSkill
- record-skillSkill
- task-observerSkill
The file
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 0 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 13
Write every horizontal value as logical: margin-inline-start, padding-inline-end, inset-inline-start, text-align: start, …
- Very specific: line 52
Run the lint; convert physical → logical (mapping in references/css-tailwind.md).
≈ “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
Persian RTL UIs
Claude's usual RTL mistakes: flipping things twice (flex-row-reverse on top of dir="rtl"), using left/right everywhere, mirroring icons that must not mirror, parsing ۱۲۳ as NaN, storing ي and ك so search fails, and formatting dates with a Gregorian calendar. Fix the direction model first; details are in references/.
Direction model — get this right and most of RTL is free
- Set direction once, in markup:
<html lang="fa" dir="rtl">. Notdirection: rtlin CSS (it does not change:dir(), form controls or browser bidi in the same way). - Flexbox, grid and inline flow already follow
dir. Never addflex-row-reverse,row-reverseororderto "make it RTL" — that flips back to LTR. - Write every horizontal value as logical:
margin-inline-start,padding-inline-end,inset-inline-start,text-align: start,border-inline-start,border-start-end-radius,float: inline-start. Tailwind:ms-* me-* ps-* pe-* start-* end-* text-start text-end rounded-s-* rounded-e-* border-s border-e. - Things logical properties do not flip — handle by hand under
[dir="rtl"]/:dir(rtl)/ Tailwindrtl::transform: translateX(),box-shadowx-offset,background-position, gradientsto right, keyframe animations that slide,scrollLeft(negative in RTL in modern browsers), canvas/SVG drawing, JS position math (getBoundingClientRect().left). - Third-party widgets need their own RTL switch: sliders/carousels (Swiper:
dir="rtl"on the container; Slick:rtl: true), date pickers, charts, rich-text editors, maps. Check every one.
Mirror or not
| Mirror (direction-meaningful) | Never mirror |
| Back/forward arrows, chevrons in breadcrumbs, "next/previous", pagination, carousel arrows, progress/stepper direction, send button, undo/redo, list-bullet/indent icons, text-direction icons | Logos, brand marks, checkmarks, play/pause/media controls, clocks, search magnifier, cart, phone, shapes with no direction, charts' numeric axes, code |
Mirror with [dir="rtl"] .icon-dir { transform: scaleX(-1); } (or rtl:-scale-x-100), only on icons marked as directional. |
Text, digits and characters — the bugs that break data, not just looks
- Normalize on input and before search/storage:
ي(U+064A) →ی(U+06CC),ك(U+0643) →ک(U+06A9); Persian۰–۹(U+06F0–06F9) and Arabic-Indic٠–٩(U+0660–0669) → ASCII0–9before parsing or saving. Apply the same normalization to the search query and to stored data (or a normalized search column), or search misses half the results. - Display digits in Persian with
Intl.NumberFormat('fa-IR')/toLocaleString('fa-IR'). Never store Persian digits; never send them in JSON numbers. - ZWNJ (نیمفاصله, U+200C) is part of the word (
میخواهم,کتابها). Don't strip it in trim/sanitize, don't replace it with a space, keep it in slugs or convert it to-, and include it in "allowed characters" validators. - Mixed-direction text: wrap user content in
<bdi>or setdir="auto"; forcedir="ltr"on phone numbers, e-mail, URLs, IBAN/card numbers, codes, and their<input>s (keeptext-align: end/rightso they align with the RTL form). Useunicode-bidi: isolatefor inline LTR runs inside Persian sentences. - Inputs for numbers:
inputmode="numeric", accept both digit sets, normalize on change.
Dates and time
- Store and compute in UTC (
DateTimeOffset/ISO withZ); convert to Jalali only for display/input. - Iran time is Asia/Tehran, UTC+03:30, no DST since 2023. Always use the IANA zone, keep tzdata current — outdated systems still jump an hour in spring.
- JS display:
new Intl.DateTimeFormat('fa-IR-u-ca-persian', { dateStyle: 'long', timeZone: 'Asia/Tehran' }). Jalali math and pickers: seereferences/jalali.md. Week starts on Saturday; Friday is the weekend.
Numbers, money, typography
- Prices:
۱۲٬۵۰۰ تومان—Intl.NumberFormat('fa-IR')+ the unit as a word after the number. Keep the internal amount an integer in Rial (see theiran-paymentsskill). - Fonts and line height: see
references/typography.md. Neverletter-spacingortext-transformon Persian; no fake italic; line-height around 1.7–2.
References — load only what the task needs
| File | When |
references/css-tailwind.md | Writing or converting CSS/SCSS/Tailwind; physical → logical mapping table; RTL/LTR mixed pages |
references/jalali.md | Date display, pickers, Jalali math in JS and .NET, Tehran time zone |
references/typography.md | Persian fonts (licenses!), loading, digit variants, line height, justification |
references/text-data.md | Normalization code (JS, C#, SQL), search, validation, slugs, sorting |
Lint script
python3 <skill-dir>/scripts/rtl_lint.py <path> [--max N] — scans CSS/SCSS/LESS/HTML/JSX/TSX/Vue/Svelte/Razor/C#/TS files and prints one line per problem: physical CSS properties, physical Tailwind classes, row-reverse, Arabic ي/ك in source strings, text-align: left/right. Exit 1 when it finds something. Silence a deliberate line with a rtl-ok comment on it. Run it before and after a conversion and report the counts.
Review / conversion workflow
dir+langon<html>; remove CSSdirectionhacks and anyrow-reverseadded for RTL.- Run the lint; convert physical → logical (mapping in
references/css-tailwind.md); fix the non-flipping list (rule 4) by hand. - Mark directional icons; check every third-party widget.
- Input pipeline: digit + character normalization, ZWNJ preserved, LTR fields isolated.
- Dates: UTC storage, Jalali display, Asia/Tehran.
- Visual check in a browser at mobile and desktop width, with long Persian words and mixed English (brand names, numbers). Re-run the lint.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
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.

