ARIA
إثراء دلالات المكونات المعقدة لجعلها قابلة للوصول
ما هو ARIA؟
ARIA (تطبيقات الإنترنت الغنية القابلة للوصول) هي مواصفة W3C التي تسمح بتحسين إمكانية الوصول لتطبيقات الويب الغنية والمكونات الديناميكية.ARIA (Accessible Rich Internet Applications)
🏷️ الأدوار
تحدد ما هو العنصر (زر، تنقل، تنبيه...)
role="button"📊 الحالات
تصف الحالة الحالية للعنصر (محدد، موسع، معطل...)
aria-expanded="true"🔧 الخصائص
توفر معلومات إضافية (تسمية، describedby، controls...)
aria-label="القائمة الرئيسية"⚠️ القاعدة الذهبية ARIA
لا تستخدم ARIA إذا كان بإمكانك استخدام HTML الأصلي
النماذج مع سمات ARIA
مثال سيء
نموذج بدون ARIA
❌ نموذج غير قابل للوصول بدون سمات ARIA المناسبة:
<!-- سيء: لا توجد تسمية، لا يوجد وصف -->
<input type="text" placeholder="بريدك الإلكتروني">
<!-- سيء: الأيقونة غير قابلة للوصول -->
<button>
<span class="icon">🔍</span>
</button>
<!-- سيء: خطأ غير مرتبط، لا يوجد إكمال تلقائي -->
<input type="password">
<div class="error">كلمة المرور قصيرة جدًا</div>مثال جيد
نموذج مع ARIA
✅ نموذج يمكن الوصول إليه مع سمات ARIA المناسبة:
<!-- جيد: aria-label للحقل بدون تسمية مرئية -->
<input
type="text"
aria-label="عنوان البريد الإلكتروني (مطلوب)"
aria-describedby="email-desc"
aria-required="true">
<span id="email-desc">التنسيق: name@example.com</span>
<!-- جيد: زر مع aria-label -->
<button aria-label="إرسال النموذج">
<span aria-hidden="true">🔍</span>
</button>
<!-- جيد: خطأ مرتبط مع aria-describedby -->
<input
type="password"
aria-label="كلمة المرور (8 أحرف كحد أدنى)"
aria-invalid="true"
aria-describedby="pwd-error"
autocomplete="new-password">
<div id="pwd-error" role="alert">
يجب أن تحتوي كلمة المرور على 8 أحرف على الأقل
</div>سمات ARIA المستخدمة:
aria-label: يوفر تسمية يمكن الوصول إليهاaria-describedby: يربط وصفًا مفصلاًaria-required: يشير إلى حقل مطلوبaria-invalid: يشير إلى حقل غير صالحaria-labelledby: يشير إلى تسمية موجودةrole="alert": يعلن عن الأخطاء على الفورaria-hidden: يخفي العناصر الزخرفيةaria-live: يعلن عن التغييرات الديناميكية
الشرح: تعمل سمات ARIA على إثراء النماذج من خلال توفير التسميات والأوصاف والحالات للتقنيات المساعدة.
أكورديون يمكن الوصول إليه
مثال سيء
بدون سمات ARIA
<!-- سيء: لا توجد سمات ARIA، لا يوجد تنقل بلوحة المفاتيح -->
<div class="accordion-header" @click="toggle">
<span>Title</span>
<span>+</span>
</div>
<div v-if="open">
Content
</div>مثال جيد
مع ARIA المناسب
<!-- جيد: ARIA كامل مع التنقل بلوحة المفاتيح -->
<button
id="accordion-button-1"
aria-expanded="false"
aria-controls="panel-1"
@click="toggle"
@keydown.enter="toggle"
>
Title
</button>
<div
id="panel-1"
role="region"
aria-labelledby="accordion-button-1"
:hidden="!open"
>
Content
</div>الشرح: يجب أن يستخدم الأكورديون سمات ARIA المناسبة، ويدعم التنقل بلوحة المفاتيح ويعلن عن تغييرات الحالة.
Tabindex والتنقل بلوحة المفاتيح
تتحكم سمة tabindex في ما إذا كان العنصر يمكن أن يتلقى التركيز بلوحة المفاتيح وبأي ترتيب.
✅ tabindex="0"
العنصر قابل للتركيز في ترتيب DOM الطبيعي
tabindex="0"<!-- جيد: عنصر تفاعلي قابل للتركيز -->
<div role="button" aria-label="Custom action" tabindex="0">
Custom button
</div>⚠️ tabindex="-1"
قابل للتركيز بواسطة JavaScript ولكن ليس بواسطة Tab
tabindex="-1"<!-- جيد: للتركيز البرمجي -->
<div role="alert" aria-live="assertive" tabindex="-1">
Error message
</div>❌ tabindex > 0
يغير ترتيب التنقل (تجنب!)
tabindex="5"<!-- سيء: يكسر ترتيب Tab الطبيعي -->
<button tabindex="3">Third</button>
<button tabindex="1">First</button>
<button tabindex="2">Second</button>📋 أفضل ممارسات Tabindex
- استخدم
tabindex="0"لجعل العناصر التفاعلية المخصصة قابلة للتركيز - استخدم
tabindex="-1"للتركيز البرمجي (النوافذ المنبثقة، التنبيهات) - لا تستخدم أبدًا قيمًا موجبة - احترم ترتيب DOM
- العناصر التفاعلية الأصلية (
<button>،<a>،<input>) لا تحتاج إلى tabindex
🎯 أمثلة تفاعلية
اختبر التنقل بلوحة المفاتيح (Tab/Shift+Tab) مع هذه العناصر:
❌ عناصر غير قابلة للتركيز
فقرة
💡 نصائح الاختبار
- استخدم Tab للتنقل للأمام
- استخدم Shift + Tab للتنقل للخلف
- استخدم Space أو Enter لتنشيط العناصر
- لاحظ مؤشر التركيز (الخطوط الزرقاء)
- لاحظ العناصر التي يمكن أو لا يمكن أن تتلقى التركيز
قائمة منسدلة يمكن الوصول إليها
مثال سيء
قائمة بدون ARIA
<!-- سيء: لا يوجد دور قائمة، لا يوجد فخ تركيز -->
<div @click="open = !open">
Actions ▼
</div>
<div v-if="open">
<div @click="action">Edit</div>
<div @click="action">Delete</div>
</div>مثال جيد
قائمة مع ARIA
<!-- جيد: سمات ARIA وإدارة التركيز -->
<button
aria-haspopup="true"
aria-expanded="false"
aria-controls="menu"
@keydown.escape="close"
>
Actions
</button>
<div
id="menu"
role="menu"
v-if="open"
>
<button role="menuitem">Edit</button>
<button role="menuitem">Delete</button>
</div>الشرح: يجب أن تدعم القائمة المنسدلة التنقل بلوحة المفاتيح، والإغلاق بمفتاح Escape، والحفاظ على التركيز بشكل مناسب.
معرض صور يمكن الوصول إليه
مثال سيء
معرض صور يمكن الوصول إليه
<!-- ❌ سيء: المعرض غير قابل للوصول بلوحة المفاتيح -->
<div class="thumbnails">
<img v-for="image in images"
:src="image.thumb"
@click="selectImage(index)"
:class="{ active: selected === index }" />
</div>
<div class="main-image">
<img :src="images[selected].full" />
</div>مثال جيد
معرض صور يمكن الوصول إليه
منظر أمامي لجهاز MacBook Pro مقاس 16 بوصة مع الشاشة مغلقة، يظهر التصميم الألومنيوم الأنيق.
<!-- ✅ جيد: معرض يمكن الوصول إليه مع التنقل بلوحة المفاتيح -->
<div role="tablist" aria-label="صور المنتج">
<button v-for="(image, index) in images"
:id="`tab-${index}`"
role="tab"
:aria-selected="selected === index"
:aria-controls="`panel-${index}`"
:tabindex="selected === index ? 0 : -1"
@click="selectImage(index)"
@keydown="handleKeydown($event, index)">
<img :src="image.thumb" :alt="`${image.alt} - Miniature`" />
</button>
</div>
<div :id="`panel-${selected}`"
role="tabpanel"
:aria-labelledby="`tab-${selected}`">
<img :src="images[selected].full" :alt="images[selected].alt" />
<p>{{ images[selected].description }}</p>
</div>
<!-- Bonnes pratiques :
- Pattern tablist/tab/tabpanel ARIA
- Navigation avec flèches, Home, End
- Focus management avec tabindex
- Descriptions détaillées pour chaque image -->الشرح: يجب أن يسمح معرض الصور بالتنقل بلوحة المفاتيح وتوفير أوصاف سياقية لكل صورة. نمط ARIA tablist/tab/tabpanel مثالي لهذه الحالة.
جدول قابل للفرز يمكن الوصول إليه
مثال سيء
| الاسم ↑ | البريد الإلكتروني | الدور | الحالة |
|---|---|---|---|
| أليس مارتن | alice@example.com | مدير | نشط |
| بوب دوبونت | bob@example.com | مستخدم | غير نشط |
| تشارلي لوروي | charlie@example.com | مشرف | نشط |
| ديانا مورو | diana@example.com | مستخدم | معلق |
مثال جيد
| أليس مارتن | alice@example.com | مدير | نشط |
|---|---|---|---|
| بوب دوبونت | bob@example.com | مستخدم | غير نشط |
| تشارلي لوروي | charlie@example.com | مشرف | نشط |
| ديانا مورو | diana@example.com | مستخدم | معلق |
الشرح: يجب أن يشير الجدول القابل للفرز إلى حالة الفرز الحالية، والسماح بالتنقل بلوحة المفاتيح، والإعلان عن التغييرات لقارئات الشاشة.
مناطق aria-live الديناميكية
مثال سيء
بدون aria-live
لا يتم الإعلان عن التغييرات
<!-- Mauvais : pas d'attribut aria-live -->
<div class="status-banner">
<p></p>
</div>العداد: 0
المشاكل:
- لا يتم الإعلان عن التغييرات
- مستخدمو قارئ الشاشة لا يعرفون أن المحتوى تغير
- لا يوجد إشارة إلى أهمية الرسالة
مثال جيد
مع aria-live المناسب
يتم الإعلان عن التغييرات وفقًا لأولويتها
<!-- Bon : avec aria-live="polite" pour informations -->
<div
class="status-banner"
aria-live="polite"
aria-atomic="true"
role="status"
>
<p>Information : Données mises à jour</p>
</div>
<!-- Bon : avec aria-live="assertive" pour alertes -->
<div
class="alert-banner"
aria-live="assertive"
aria-atomic="true"
role="alert"
>
<p>Attention : Action requise immédiatement</p>
</div>منطقة Polite (معلومات)
منطقة Assertive (تنبيهات)
المزايا:
- الرسائل المهمة تقاطع مع aria-live="assertive"
- الرسائل الإعلامية تنتظر مع aria-live="polite"
- aria-atomic="true" يعلن عن محتوى المنطقة بالكامل
- الأدوار المناسبة (status، alert) توفر المزيد من السياق
الشرح: تسمح مناطق Aria-live بالإعلان عن تغييرات المحتوى لقارئات الشاشة. 'polite' ينتظر انتهاء الإعلان الحالي، 'assertive' يقاطع على الفور.
أزرار الأيقونات القابلة للوصول
مثال سيء
مثال جيد
الشرح: يجب أن تحتوي الأزرار التي تحتوي على أيقونات فقط على aria-label أو نص مخفي بصريًا ليتم فهمها بواسطة قارئات الشاشة.
Combobox / بحث مع اقتراحات
مثال سيء
بدون ARIA أو التنقل بلوحة المفاتيح
مثال جيد
مع ARIA والتنقل بلوحة المفاتيح
الشرح: يجب أن يكون البحث مع الاقتراحات (combobox) قابلاً للتنقل بلوحة المفاتيح، وإعلان النتائج والسماح بالاختيار.
إشعارات Toast (المناطق الحية)
مثال سيء
بدون aria-live
❌ لا يتم الإعلان عن الإشعارات لقارئات الشاشة
<!-- سيء: لم يتم الإعلان عنه لقارئات الشاشة -->
<div v-if="showToast" class="toast">
Votre demande a été enregistrée
</div>مثال جيد
مع aria-live المناسب
✅ يتم الإعلان عن الإشعارات بشكل صحيح
<!-- جيد: إشعار معلومات (polite) -->
<div
v-if="showToast"
role="status"
aria-live="polite"
aria-atomic="true"
class="toast"
>
<p>Votre demande a été enregistrée</p>
<button @click="dismiss" aria-label="Fermer notification">✕</button>
</div>
<!-- جيد: تنبيه خطأ (assertive) -->
<div
v-if="showError"
role="alert"
aria-live="assertive"
aria-atomic="true"
class="toast error"
>
<p>Erreur : action impossible à exécuter</p>
<button @click="dismiss" aria-label="Fermer alerte">✕</button>
</div>الفروقات:
role="status"+aria-live="polite": للإشعارات الإعلامية، لا يقاطع القراءة الحاليةrole="alert"+aria-live="assertive": للأخطاء الحرجة، يقاطع على الفورaria-atomic="true": يعلن عن محتوى الإشعار بالكامل
الشرح: يجب الإعلان عن الإشعارات الديناميكية لقارئات الشاشة دون سرقة التركيز. استخدم role='status' للمعلومات و role='alert' للأخطاء.