ملاحظات
RTL في React + Tailwind: قرارات تقنية
كل مشروع سعودي نبنيه يبدأ بسؤال مكرّر: كيف ندعم RTL وLTR في قاعدة الكود نفسها دون أن يمتلئ الكود بالشروط؟ الإجابات الموجودة على الإنترنت قديمة، أو ناقصة، أو تتجاهل تفاصيل تظهر فقط في الإنتاج. هذا ما تعلّمناه.
القاعدة الأولى: الخصائص المنطقية، لا الفيزيائية
أول قرار، وأهمّها: لا نستخدم margin-left أو padding-right مطلقاً في الـCSS الذي يطلّ على المحتوى. كل ما يُشير إلى اتجاه يجب أن يكون منطقياً:
- margin-inline-start بدل margin-left
- padding-inline-end بدل padding-right
- border-inline-end بدل border-right
- inset-inline-start بدل left
- text-align: start بدل text-align: left
المتصفّح يقلب هذه القيم تلقائياً حسب dir السائد على العنصر. هذا يحذف ٩٠٪ من المنطق الشرطي الذي كان يتراكم في الكود. والأفضل: Tailwind v4 يدعم هذه الخصائص أصلياً عبر أدوات الاتجاه المنطقية (ms-* وme-* وstart-* وend-*).
ضبط dir على الجذر، لا على كل مكوّن
كثير من الشروحات تقترح إضافة خاصية dir لكل مكوّن. هذا خطأ. الاتجاه يُضبط مرّة واحدة على document.documentElement، والكل يرث منه:
document.documentElement.dir = lang === 'ar' ? 'rtl' : 'ltr'
في Whisper نضبط هذا داخل LangContext useEffect، ونعتمد على الخصائص المنطقية في CSS لقلب التخطيط. لا خصائص dir متناثرة، لا سمات بيانات متراكمة، ولا تمرير للسياق عبر الطبقات.
المشكلة الحقيقية: الأرقام والتواريخ
هذه النقطة لا تتحدث عنها معظم المقالات. حين يكون dir=rtl، قد يقلب المتصفّح اتجاه الأرقام في سياق معيّن، خاصة حين تختلط مع نصّ عربي. مثال: '٢٥,٠٠٠ ر.س' قد يُعرض بترتيب غريب في بعض المتصفحات لو لم نضبط السياق.
الحلّ عندنا قاعدة بسيطة: كل نص يحتوي على أرقام لاتينية أو رموز عملة + نص عربي نضعه في <span dir="ltr"> داخلياً، حتى داخل الجمل العربية. هذا يُجبر خوارزمية الاتجاه (bidi) على المعاملة الصحيحة.
للأرقام العربية (٠١٢٣٤٥٦٧٨٩) لا حاجة لذلك — خوارزمية Unicode bidi تتعامل معها صحيحاً.
Lenis + RTL: نقطة دقيقة
Lenis (مكتبة التمرير الناعم) تدعم RTL، لكن مع ملاحظة: التمرير في مستند RTL يحدث أفقياً بقيم سالبة في بعض المتصفحات. لو كانت عندك حركة GSAP ScrollTrigger مربوطة بتمرير أفقي، يجب أن تختبر على Safari + Firefox + Chrome، لأن النتيجة قد تختلف.
في Whisper، نتجنّب التمرير الأفقي كلياً ما لم نحتجه فعلاً (في قسم الخدمات على الحاسوب فقط). التمرير الرأسي يتصرّف بشكل متّسق عبر المتصفحات في RTL مثل LTR.
الأيقونات والأسهم
الأسهم في أزرار الدعوة (مثل '←' و '→') تحتاج قراراً واضحاً. لدينا قاعدتان:
- الأسهم الاتجاهية في النصوص (مثل 'اعرف أكثر ←') نكتبها يدوياً بالرمز المناسب لكل لغة. في العربية: '←' (يشير لجهة البداية الفعلية، وهي اليمين). في الإنجليزية: '→'. هذا قرار على مستوى المحتوى، لا الـCSS.
- الأسهم الوظيفية في الواجهة (زر الرجوع، أسهم ترقيم الصفحات) نقلب أيقونة SVG عبر transform: scaleX(-1) داخل قاعدة CSS مثل [dir="rtl"] .icon-arrow. هذه عملية مرّة واحدة في الأنماط الأساسية، لا تتكرّر.
Tailwind v4: الفروقات المهمة
Tailwind v4 أحدث تغييرات جوهرية في معاملته للاتجاه. أدوات ms-* و me-* و ps-* و pe-* تعمل الآن دون أي إضافة للإعدادات، ودعم مواضع البداية/النهاية يشمل النصوص (text-start, text-end).
لكن انتبه: بعض إضافات Tailwind من الطرف الثالث (خاصة محوّلات headlessui أو radix-ui) لا تزال تستخدم margin-left/right مباشرة في مخرجاتها. نراجع كل إضافة قبل اعتمادها، ونتجاوزها عند الحاجة عبر قاعدة CSS موجّهة على المكوّن.
اختبار RTL: ليس مجرّد تبديل مرّة واحدة
خطأ شائع: المطوّر يبني الصفحة في LTR، ثم يضغط مفتاح التبديل لـRTL ويتفقّد بصرياً 'كل شيء يبدو سليم'. هذا غير كافٍ. كل ميزة يجب أن تُختبر بالعربية أولاً من البداية — يعني نكتب البيانات التجريبية بنصّ عربي، نختبر ترتيب التنقّل بالعربي، ونتفقّد أنماط التركيز على الحقول في RTL.
لماذا؟ لأن RTL ليس صورة معكوسة. هو تجربة متكاملة — ترتيب التركيز مختلف، التسلسل البصري مختلف، مسار القراءة مختلف. اللي يبدو 'سليماً' كصورة معكوسة غالباً يفشل في اختبار الاستخدام الفعلي.
في Whisper، العربية هي الافتراض. كل مكوّن نبنيه، نكتبه بالعربي أولاً، ثم نضيف النسخة الإنجليزية المقابلة. هذا قرار تصميم قبل أن يكون قراراً تقنياً.
خلاصة
بناء RTL/LTR صحيح في React + Tailwind ليس صعباً، لكنّه يحتاج انضباطاً. القواعد الخمس التي نشتغل بها:
- الخصائص المنطقية في كل CSS — لا margin-left، لا right-0.
- ضبط dir على document.documentElement، لا على المكوّنات.
- أرقام لاتينية في نص عربي تُلفّ بـ<span dir="ltr">.
- الأسهم الاتجاهية في المحتوى يدوياً، وأسهم الواجهة الوظيفية عبر transform.
- اختبار العربية أولاً من اليوم الأول، لا في النهاية.
اتّباع هذه القواعد يحذف ٩٠٪ من علل RTL في الإنتاج. الـ١٠٪ الباقية هي الحالات الحدّية الخاصة بمشروعك — تكتشفها مع الوقت، وتُضيفها لقائمتك الخاصة.
— سعيد، استوديو ويسبر
ويسبر · استوديو سعودي للعمل المدروس
