Esc

رابط کاربری راست‌به‌چپ RTL UI

ساخت رابط کاربری فارسی و راست‌به‌چپ: ویژگی‌های منطقی CSS، فونت فارسی، متن دوجهته، ارقام و آینه کردن آیکون‌ها

مهارتتوسعه نرم‌افزار

پیش از نصب بدانید

  • منبعساخت بازارچهنوشته و نگهداری‌شده در همین مخزن
  • کد منبعمتن‌باز، در همین مخزنآخرین بررسی: ۱۱ مهر ۱۴۰۵

نصب رابط کاربری راست‌به‌چپ

Claude Code با افزونه

یک بار بازارچه را اضافه کنید، بعد هر مهارت را جدا نصب کنید.

Claude Code
/plugin marketplace add mcp-farsi/mcp-farsi
/plugin install rtl-ui@mcp-farsi

نصب دستی در پوشه مهارت‌ها

برای یک پروژه خاص، به جای ~/.claude از .claude در ریشه پروژه استفاده کنید. در PowerShell به جای curl بنویسید curl.exe و به جای ~ بنویسید $HOME.

Terminalshell
curl -fsSL --create-dirs -o ~/.claude/skills/rtl-ui/SKILL.md {ORIGIN}/skills/rtl-ui/SKILL.md

Claude.ai و اپ دسکتاپ

فایل‌های مهارت را در پوشه‌ای به نام rtl-ui بگذارید، آن را zip کنید و در تنظیمات Claude، بخش Capabilities، بارگذاری کنید.

دریافت SKILL.md
سازنده
بازارچه MCP
مجوز
MIT
آخرین بررسی
۱۱ مهر ۱۴۰۵

درباره

بیشتر کدی که مدل‌ها برای رابط کاربری می‌نویسند چپ‌به‌راست فرض شده است: margin-left به جای margin-inline-start، فلش‌هایی که جهتشان برعکس است و شماره تلفنی که وسط متن فارسی به‌هم می‌ریزد. این مهارت چک‌لیست ساخت رابط فارسی درست را در اختیار Claude می‌گذارد.

کجا به کار می‌آید

  • ساخت صفحه یا کامپوننت تازه با HTML، CSS، React یا Tailwind
  • فارسی‌سازی یک رابط انگلیسی موجود
  • رفع مشکل متن‌های دوجهته، ارقام و ورودی‌های فرم

متن کامل مهارت

نمایش محتوای SKILL.md

رابط کاربری راست‌به‌چپ (RTL Persian UI)

1. Document setup

<html lang="fa" dir="rtl">
  • Put dir in markup on the root element, not only CSS direction. The attribute drives the bidi algorithm, form controls, :dir() and accessibility; lang="fa" lets screen readers pick a Persian voice.
  • Multilingual app: set both from the active locale (document.documentElement.lang = 'fa'; document.documentElement.dir = 'rtl').
  • Mark LTR islands locally with dir="ltr" (section 5).

2. Layout with logical properties

Write direction-agnostic CSS so one stylesheet serves RTL and LTR.

Physical (avoid) Logical (use)
margin-left / margin-right margin-inline-start / margin-inline-end
padding-left / padding-right padding-inline-start / padding-inline-end
left: 0 / right: 0 inset-inline-start: 0 / inset-inline-end: 0
text-align: left / right text-align: start / end
border-left border-inline-start
border-top-left-radius border-start-start-radius
  • Flexbox (flex-direction: row) and Grid columns already follow dir: in RTL the first item is on the right. Do not add row-reverse for RTL; that flips it back.
  • justify-content: flex-start / start follow the direction; the keywords left / right do not.
  • Things that do not flip automatically: transform: translateX(), background-position, box-shadow offsets, gradients with to left/right, absolutely positioned SVG, canvas drawing, animation keyframes. Override them with :dir(rtl) (Baseline widely available since December 2023):
.drawer { transform: translateX(-100%); }
.drawer:dir(rtl) { transform: translateX(100%); }
  • Horizontal scrollers: in RTL, scrollLeft is 0 at the start (rightmost position) and becomes increasingly negative as the user scrolls toward the end. Carousel code that assumes positive values breaks.

3. Icons: what to mirror

Mirror (they express direction, motion or reading order):

  • Back / forward arrows, next / previous chevrons, breadcrumb separators, “open submenu” carets. In RTL, a back button points right.
  • Progress bars, sliders, steppers (and the order of the numbers along them).
  • Icons that represent lines of text or list alignment.
  • Icons showing forward motion, for example a speaker with sound waves.

Do not mirror:

  • Media playback controls (play, pause, fast-forward, rewind).
  • Checkmarks, close (X), and other universal or direction-neutral symbols.
  • Clocks and other real-world objects; logos (never flip a logo); code icons such as </>.
  • The slash in “disabled / prohibited” variants stays the same.

Flip only icons you mark as directional; never flip all icons globally:

.icon-directional:dir(rtl) { transform: scaleX(-1); }

4. Bidi: mixed Persian, English and numbers

Typical bugs: a trailing ! or . jumps to the wrong side, React 19 or an English user name reorders the words around it, a phone number shows up reversed in groups.

  • Text injected at run time whose direction you cannot predict (user names, product titles, search terms): wrap it in <bdi>. It isolates the text and picks a direction from its first strong character.
<p>کاربر <bdi>{{ username }}</bdi> ۳ دیدگاه نوشت.</p>
  • Blocks of user-generated content (comments, posts, chat messages) and free-text inputs and textareas: dir="auto".
  • Any element with a dir attribute is isolated from surrounding text. In CSS, use unicode-bidi: isolate on inline badges or chips, and unicode-bidi: plaintext on blocks where each paragraph should pick its own direction.
  • Prefer markup over Unicode control characters. Use the isolate characters FSI U+2068 … PDI U+2069 (or LRM U+200E / RLM U+200F) only where markup is impossible: <title>, attribute values (title, placeholder, aria-label), <option> text, notifications, SMS. In code, write them as escapes (\u2068, \u2069), never as invisible literals.

5. LTR islands

These always stay LTR, and the digit order inside a number never reverses:

  • Phone and card numbers, IBAN (شبا), tracking codes, OTP codes.
  • Emails, URLs, file paths, code snippets, version strings, keyboard shortcuts.
<span dir="ltr">0912 345 6789</span>
<input type="tel" dir="ltr" inputmode="tel" autocomplete="tel">
<input type="email" dir="ltr">
<pre dir="ltr"><code>npm install vazirmatn</code></pre>

A Persian placeholder in an LTR input aligns left. Optional fix:

input[dir="ltr"]:placeholder-shown { direction: rtl; }

6. Persian fonts

Font License Notes
Vazirmatn SIL OFL 1.1 First choice. npm vazirmatn, or Fontsource @fontsource-variable/vazirmatn. Variable font; extra builds in the repo (UI, Farsi-Digits, Non-Latin)
Sahel SIL OFL 1.1 rastikerdar/sahel-font
Shabnam SIL OFL 1.1 Repository archived (discontinued); prefer Vazirmatn for new work
  • Self-host the font files. fonts.googleapis.com has been slow or disrupted from inside Iran (Iranian hosts reported ISP disruption starting 1401) and a failed font request falls back silently. jsDelivr and other foreign CDNs carry the same risk; serve woff2 from your own origin or a domestic CDN.
@font-face {
  font-family: Vazirmatn;
  src: url('/fonts/Vazirmatn[wght].woff2') format('woff2');
  font-weight: 100 900;
  font-display: swap;
}
:root { font-family: Vazirmatn, Tahoma, sans-serif; }
  • Preload the main weight on the critical path (<link rel="preload" as="font" type="font/woff2" crossorigin>).
  • “Farsi-Digits” font builds draw Latin 0-9 with Persian shapes, but copy-paste still yields Latin digits. Prefer real Persian digits in the text (section 8) and a standard build.

7. Typography

  • line-height: Persian has dots and marks above and below the letters; it needs more leading than Latin. Start around 1.8 for body text and tune by eye.
  • letter-spacing: normal for Persian. CSS Text 3 forbids letter-spacing from breaking the joins of cursive scripts, but browsers differ: recent Chrome and Firefox skip spacing between joined letters, while WebKit (Safari) has been reported to space every glyph, and the words fall apart. Scope tracking to Latin only, for example :lang(en).
  • No text-transform: uppercase/capitalize and no small-caps. Persian has no letter case, so these do nothing to Persian and only distort embedded Latin (brand names, acronyms). Remove them from RTL styles.
  • Persian next to uppercase Latin at the same size can look small; a slightly larger Persian size can balance it.
  • Vazirmatn has no italic; browsers fake one by slanting the glyphs. Use bold for emphasis.

8. Numbers

  • Display: (1234567.5).toLocaleString('fa-IR') returns ۱٬۲۳۴٬۵۶۷٫۵ (Persian digits, ٬ thousands, ٫ decimal). (0.5).toLocaleString('fa-IR', {style: 'percent'}) returns ۵۰٪.
  • Toman is not an ISO 4217 currency (IRR, Rial, is). Most shops show Toman: format the number and append the unit yourself, ${n.toLocaleString('fa-IR')} تومان.
  • Keep Latin digits for values the user copies into another system (card numbers, IBAN, tracking codes), and in code, URLs and data sent to APIs.

9. Input handling

Users type Persian digits (Persian keyboard) or Arabic-Indic digits (Arabic keyboards, some Android keyboards). Normalize to ASCII before validation and storage.

  • JS: /\d/ matches only ASCII digits and Number('۱۲۳') is NaN, so unnormalized input fails validation.
  • Python: \d matches Persian digits and int('۱۲۳') returns 123, so unnormalized input passes validation and leaks into storage. Normalize anyway, or use re.ASCII.
// Persian (U+06F0-06F9) and Arabic-Indic (U+0660-0669) digits -> ASCII
export const toEnDigits = (s) =>
  s.replace(/[\u06F0-\u06F9\u0660-\u0669]/g, (d) => String(d.charCodeAt(0) & 0xf));

const mobile = toEnDigits(input.value.trim());
/^(?:\+98|0098|0)?9\d{9}$/.test(mobile); // Iranian mobile: 09xxxxxxxxx, +989xxxxxxxxx
  • Use type="text" with inputmode="numeric" (or "tel") for numeric fields, so you can normalize before validating.
  • Search: normalize Arabic ي/ك to Persian ی/ک (see the persian-writing skill), and treat ZWNJ, space and nothing as equivalent when matching (می‌شود, می شود, میشود).
  • Never strip ZWNJ (U+200C) from names or text fields; it is part of correct spelling.

10. Tailwind CSS

  • Prefer logical utilities: ms-* / me-*, ps-* / pe-*, border-s / border-e, rounded-s-* / rounded-e-*, text-start / text-end. For inset, current docs use inset-s-* / inset-e-*; Tailwind v3 uses start-* / end-*.
  • Use gap-* instead of space-x-* for horizontal spacing; gap does not depend on direction.
  • Use the rtl: / ltr: variants only for what logical utilities cannot express, for example flipping a directional icon: rtl:-scale-x-100.
  • Replace ml-*, mr-*, pl-*, pr-*, left-*, right-*, text-left, text-right in RTL-capable code.

11. RTL test checklist

  • <html lang="fa" dir="rtl"> is set; switching the root dir mirrors every page (nav, forms, tables, modals, toasts, drawers, carousels, breadcrumbs, pagination).
  • No physical properties left. Search, for example: rg -n "(margin|padding|border)-(left|right)|text-align:\s*(left|right)|\b(left|right):" src/
  • Directional icons are mirrored; media controls, checkmarks and logos are not.
  • Mixed strings render correctly: نسخهٔ React 19 منتشر شد!, an English user name inside a Persian sentence, ۵ GB, a URL at the end of a sentence followed by a period.
  • Phone numbers, codes and emails display LTR; their inputs are dir="ltr".
  • Inputs accept Persian and Arabic-Indic digits and store ASCII; ZWNJ survives saving.
  • Fonts still load with fonts.googleapis.com blocked (DevTools request blocking).
  • No letter-spacing or text-transform on Persian text; checked in Safari as well.
  • Screen reader reads Persian content with a Persian voice (lang="fa").