UIDex

الشريط الدوّار

Carousel

aria-roledescription="carousel"

اسمه كمانكاروسيل، سلايدر، شريط الشرائح، معرض منزلق، عارض دوّار، شرائح متحرّكة، slider، image slider، slideshow، content rotator، hero slider، banner rotator

الشريط الدوّار حاوية تعرض عدّة شرائح في مساحة واحدة، فتُظهر شريحة أو شريحة ونصفاً في كل لحظة، ولكل شريحة موضع معلوم يصل إليه المستخدم بالأسهم أو بالنقاط أو بالسحب. والموضع الحالي مملوك للمستخدم ومُعلَن ومرقّم: «٣ من ٦». وهو ليس الشريط الزاحف (marquee)، فذاك يزحف بلا توقّف ولا يعرف شريحة حالية ولا يملك المستخدم فيه العودة إلى ما فات، فهو زخرفة لا وسيلة تنقّل. وهو ليس قائمة أفقية قابلة للتمرير، فالقائمة تُدفع بالإصبع أو بعجلة التمرير وكفى، بلا أزرار وبلا دلالة «شريحة» في شجرة الوصول، وهي في صفوف المنتجات الخيار الأصحّ لأن الحافّة المقصوصة تكشف وحدها أن هناك بقيّة. ويفارق التبويبات (tabs) في نقطة واحدة: عنوان التبويب يصف اللوحة التي يفتحها، أمّا النقطة فلا تصف إلا رقم موضعها. وللعنصر تكلفة تستحقّ أن تُقال في كل مراجعة: ما بعد الشريحة الأولى لا يراه إلا قلّة من الزوّار، فلا تضع فيه سعراً ولا زرّ تسجيل ولا عرضاً تحتاج فعلاً أن يصل.

لو قلت عليه…

«الصور اللي بتتبدّل لوحدها فوق في الصفحة»«السهمين اللي بيقلّبوا الإعلانات»«النقط الصغيّرة اللي تحت الصورة»«الشريط اللي بتزحّطه بصوابعك وبيقف عند كل كارت»«سلايد شو بتاع أول الصفحة»

العيّنة الحيّة

تفاعل مع الديمو. كل الأجزاء حقيقية ومرقّمة.

١ من ٥شحن مجانيعلى كل الطلبات٢ من ٥خصم ٣٠٪المجموعة الشتوية٣ من ٥استرجاع ١٤ يوماًبلا رسوم٤ من ٥تقسيط ٤ دفعاتبدون فوائد٥ من ٥نقاط مضاعفةلأعضاء البرنامج
الشريحة ١ من ٥
RTLالشريحة الأولى عند اليمين، والتالي يمرّر نحو اليسار

تشريح العنصر: كل جزء واسمه

مرّر على أي سطر ليتحدّد مكانه في الديمو فوق.

برومبت البناء

ابنِ شريطاً دوّاراً على CSS scroll-snap بلا مكتبة. اجعل الغلاف الخارجي role="group" مع aria-roledescription="carousel" واسم واضح في aria-label. وبداخله حاوية التمرير هي المنفذ: overflow-x: auto مع scroll-snap-type: inline mandatory و scroll-padding-inline-start للحشو و tabindex="0" واسم خاص بها، لأن كروم لا يجعل حاويات التمرير قابلة للتركيز تلقائياً إلا حين تخلو من عناصر قابلة للتركيز. المسار داخلها صفّ flex عرضه max-content، وكل شريحة عليها scroll-snap-align: start و scroll-snap-stop: always و role="group" و aria-roledescription="slide" و aria-label يقول «٣ من ٦». أضف زرّي سابق وتالٍ باسمين مقروءين ومساحة لمس ٤٤ بكسل، ثبّتهما بـ inset-inline-start و inset-inline-end، ونفّذ الحركة بـ el.scrollBy({ left: step * dir }) حيث dir تساوي ‎-1 عندما تكون getComputedStyle(el).direction هي rtl. لا تحسب الشريحة الحالية من scrollLeft، بل قارن getBoundingClientRect لكل شريحة بحافّة المنفذ المنطقية. أضف صفّ نقاط: كل نقطة زرّ باسم يذكر رقم شريحتها، والحالية عليها aria-current="true"، والقفز بـ slide.scrollIntoView({ inline: "start", block: "nearest" }). وضع في الصفحة منذ البداية منطقة aria-live="polite" تعلن الموضع الجديد بعد كل انتقال. وإن أضفت دوراناً تلقائياً فاجعل معه زرّ إيقاف مرئياً، وأوقفه عند hover وعند focus وعند prefers-reduced-motion، واضبط منطقة الإعلان على off ما دام يدور. استخدم الخصائص المنطقية في كل مكان: inset-inline و margin-inline و padding-inline و scroll-padding-inline-start، ولا تكتب left ولا right ولا margin-left. وللحالتين اللتين لا خاصية منطقية لهما أصلاً، وهما التدرّج فوق الصورة و background-position، اكتب تجاوزاً صريحاً داخل [dir="rtl"]. ولا تضع محتوى يجب أن يصل في الشرائح بعد الأولى.

سلوكه في الاتجاه من اليمين لليسار

الجزء ده حصري عندنا.

المحور المضمّن ينعكس وحده

ينعكس

الشريحة الأولى تبدأ من اليمين في العربية والتقدّم يمضي نحو اليسار، وهذا يقع تلقائياً في حاوية التمرير وفي صفّ flex وفي صفّ النقاط، لأن المحور المضمّن يتبع direction. لا تعكسه مرّة ثانية: قاعدة flex-direction: row-reverse داخل [dir="rtl"]، أو عكس المصفوفة في جافاسكربت، تعيد الترتيب البصري إلى حالته الإنجليزية وتجعل ترتيب Tab يخالف ما تراه العين. الاستثناء مسار مبني على transform: translateX(calc(-100% * var(--i)))، فالتحويلات لا تعرف الاتجاه، فاضرب الإزاحة في var(--dir) واضبط ‎--dir: -1 داخل [dir="rtl"].

السهمان: الموضع والرسم والوظيفة

ينعكس

زرّ «السابق» يجلس عند البداية المنطقية، أي على اليمين في العربية، فثبّت الزرّين بـ inset-inline-start و inset-inline-end لا بـ left و right. ورسم السهم ينقلب معه: ضع scaleX(-1) على صنف تستعمله للأسهم وحدها، أو بدّل الأيقونتين. والوظيفة تنقلب كذلك، فـ«التالي» يمرّر نحو اليسار: اكتب el.scrollBy({ left: step * dir }) مع dir تساوي ‎-1 في RTL، واقرأ ArrowLeft على أنه «التالي» في معالج لوحة المفاتيح.

إشارة scrollLeft

ينعكس

المحرّكات الحالية تبدأ من صفر عند الشريحة الأولى ثم تعطي قيماً سالبة كلّما تقدّمت، أمّا WebKit القديم فكان يبدأ من أكبر قيمة موجبة وينزل، و IE كان يعدّ صعوداً من الصفر. لذلك يعطي حساب مثل Math.round(scrollLeft / slideWidth) رقماً سالباً أو خاطئاً في العربية. ابتعد عن الإشارة كلّها: اقفز بـ slide.scrollIntoView({ inline: "start", block: "nearest" }) فهي قيمة منطقية أصلاً، واستخرج الشريحة الحالية بمقارنة getBoundingClientRect لكل شريحة بحافّة المنفذ المنطقية.

ما لا تقلبه CSS نيابةً عنك

ينعكس

قيمة scroll-snap-align: start منطقية فتصير الحافّة اليمنى في العربية بلا تدخّل، و scroll-snap-type: inline تقول المقصود أدقّ من x. أمّا scroll-padding-left فيُبقي الحشو على اليسار بعد الانعكاس، فاكتب scroll-padding-inline-start. والأسوأ طبقة التظليل فوق صورة الشريحة، فلا التدرّجات ولا background-position تقبل قيماً منطقية أصلاً: تبقى linear-gradient(to right, …) و background-position: left center في مكانهما بعد الانعكاس، فلا حلّ إلا تجاوز صريح داخل [dir="rtl"].

ما داخل الشريحة يبقى كما رُسم

لا ينعكس

صورة الشريحة، والشعار داخلها، وزرّ التشغيل والإيقاف في الشريط الذي يدور تلقائياً: كلّها تبقى كما هي في العربية. والسبب المعتاد في انقلابها قاعدة عامة من نوع [dir="rtl"] svg, [dir="rtl"] img { transform: scaleX(-1) }، فاقصر القلب على صنف تضعه على السهمين وحدهما. وأزرار التشغيل تشير إلى مسار الزمن لا إلى اتجاه القراءة، ولهذا يبقى المثلّث متّجهاً كما هو في اللغتين.

أسماؤه في الكود

كل سطر هو كلمة مكتبة واحدة عن نفس الشيء. خد السطر اللي بيكلّم مشروعك.

ARIAaria-roledescription="carousel"توضع على عنصر له role="group" أو role="region" واسم مقروء، وإلا فلا شيء يُنطق.
ARIArole="group" + aria-roledescription="slide"على كل شريحة، مع اسم يذكر الموضع مثل «٣ من ٦».
CSSscroll-snap-type: inline mandatoryالمحور المنطقي، فلا يحتاج أي تعديل في RTL، ويعطيك سلوك الترقيم بلا مكتبة.
CSSscroll-snap-stop: alwaysتمنع السحبة القويّة من تخطّي عدّة شرائح، وموضعها الشريحة نفسها لا الحاوية.
CSS::scroll-marker + ::scroll-button()نقاط وأسهم يولّدها المتصفّح من حاوية التمرير نفسها؛ شحنها كروم في الإصدار ١٣٥، والدعم في غيره ما زال يتشكّل، فاحتفظ ببديل.
shadcn/ui<Carousel opts={{ direction: 'rtl' }}>الغلاف هو Embla، وتحتاج المكتبة أن تُخبَر بالاتجاه صراحةً؛ وأصناف موضع الأسهم فيه فيزيائية فراجعها.
Ant Design<Carousel autoplay dots />مبني على react-slick، فيتوقّف عند مرور المؤشّر بحكم pauseOnHover الافتراضية، لكن لا زرّ إيقاف مرئياً فأضفه بنفسك.

شوف كمان

آخر تحديث · 2026-08-19