إطار Tailwind CSS

إمكانية الوصول مع Tailwind CSS

20 دقيقة الدرس 32 من 35

إمكانية الوصول مع Tailwind CSS

بناء مواقع ويب يمكن الوصول إليها يضمن أن جميع المستخدمين، بما في ذلك ذوي الإعاقة، يمكنهم استخدام تطبيقاتك بفعالية. يوفر Tailwind CSS أدوات تجعل تطبيق ميزات إمكانية الوصول أمرًا مباشرًا. يغطي هذا الدرس تقنيات إمكانية الوصول الأساسية باستخدام Tailwind.

لماذا تهم إمكانية الوصول

فوائد إمكانية الوصول

  • التصميم الشامل: يجعل موقعك قابلاً للاستخدام من قبل الأشخاص ذوي الإعاقات البصرية أو السمعية أو الحركية أو الإدراكية
  • الامتثال القانوني: تتطلب العديد من البلدان إمكانية الوصول إلى الويب (WCAG, ADA, القسم 508)
  • تجربة مستخدم أفضل للجميع: تحسينات إمكانية الوصول تفيد جميع المستخدمين
  • فوائد SEO: HTML الدلالي والبنية الصحيحة تحسن ترتيب البحث
  • جمهور أوسع: حوالي 15٪ من سكان العالم لديهم شكل من أشكال الإعاقة

محتوى قارئ الشاشة فقط (sr-only)

تخفي الأداة sr-only المحتوى بصريًا مع الحفاظ على إمكانية الوصول إليه لقارئات الشاشة. استخدمها لتوفير سياق إضافي لا يحتاجه المستخدمون المرئيون.

الاستخدام الأساسي لـ sr-only

<!-- رابط تخطي التنقل لمستخدمي لوحة المفاتيح -->
<a href="#main-content" class="
  sr-only
  focus:not-sr-only
  focus:absolute focus:top-4 focus:left-4
  focus:z-50
  focus:px-4 focus:py-2
  focus:bg-blue-600 focus:text-white
  focus:rounded-md
">
  انتقل إلى المحتوى الرئيسي
</a>

<!-- زر أيقونة مع تسمية sr-only -->
<button class="p-2 hover:bg-gray-100 rounded-md">
  <svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
    <path d="M10 3.5a1.5 1.5 0 013 0V4a1 1 0 001 1h3a1 1 0 011 1v3a1 1 0 01-1 1h-.5a1.5 1.5 0 000 3h.5a1 1 0 011 1v3a1 1 0 01-1 1h-3a1 1 0 01-1-1v-.5a1.5 1.5 0 00-3 0v.5a1 1 0 01-1 1H6a1 1 0 01-1-1v-3a1 1 0 00-1-1h-.5a1.5 1.5 0 010-3H4a1 1 0 001-1V6a1 1 0 011-1h3a1 1 0 001-1v-.5z"/>
  </svg>
  <span class="sr-only">فتح الإعدادات</span>
</button>

<!-- زر إغلاق مع أيقونة -->
<button type="button" class="absolute top-4 right-4 p-1 hover:bg-gray-100 rounded">
  <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
  </svg>
  <span class="sr-only">إغلاق النافذة المنبثقة</span>
</button>

متى تستخدم sr-only

  • أزرار الأيقونات فقط بدون تسميات نصية مرئية
  • روابط تخطي التنقل
  • سياق إضافي لمدخلات النماذج
  • رسائل الحالة التي تتحدث ديناميكيًا
  • الصور الزخرفية التي تحتاج إلى وصف

التنقل باستخدام لوحة المفاتيح مع focus-visible

يعتمد مستخدمو لوحة المفاتيح على مؤشرات التركيز لمعرفة مكانهم على الصفحة. تساعد أدوات focus و focus-visible في Tailwind على إنشاء حالات تركيز واضحة.

أنماط التركيز للعناصر التفاعلية

<!-- حلقة تركيز أساسية -->
<button class="
  px-4 py-2
  bg-blue-600 text-white
  rounded-md
  focus:outline-none
  focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
">
  انقر هنا
</button>

<!-- focus-visible: يظهر حلقة التركيز فقط لمستخدمي لوحة المفاتيح -->
<a href="#" class="
  text-blue-600 underline
  focus-visible:outline-none
  focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:rounded
">
  اعرف المزيد
</a>

<!-- أنماط تركيز مخصصة للروابط -->
<nav class="space-x-6">
  <a href="#" class="
    text-gray-700 hover:text-gray-900
    focus:outline-none
    focus:underline focus:decoration-2 focus:decoration-blue-500
    focus:underline-offset-4
  ">
    الرئيسية
  </a>
  <a href="#" class="
    text-gray-700 hover:text-gray-900
    focus:outline-none
    focus:underline focus:decoration-2 focus:decoration-blue-500
    focus:underline-offset-4
  ">
    حول
  </a>
</nav>

<!-- التركيز داخل: تصميم الأب عندما يكون الطفل مركّزًا -->
<div class="
  p-4 border-2 border-gray-200 rounded-lg
  focus-within:border-blue-500
  focus-within:ring-2 focus-within:ring-blue-200
">
  <label for="email" class="block text-sm font-medium text-gray-700 mb-2">
    البريد الإلكتروني
  </label>
  <input
    type="email"
    id="email"
    class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none"
  >
</div>

Focus مقابل Focus-Visible

  • focus: يعرض أنماط التركيز لكل من نقرات الماوس والتنقل بلوحة المفاتيح
  • focus-visible: يعرض أنماط التركيز فقط للتنقل بلوحة المفاتيح (موصى به لتجربة مستخدم أفضل)
  • focus-within: يطبق الأنماط على العنصر الأب عندما يكون أي طفل مركّزًا

خصائص ARIA مع Tailwind

توفر خصائص ARIA (تطبيقات الإنترنت الغنية الممكن الوصول إليها) معنى دلاليًا لتقنيات المساعدة. يدعم Tailwind التصميم بناءً على حالات ARIA.

التصميم القائم على ARIA

<!-- زر مع aria-pressed -->
<button
  type="button"
  aria-pressed="false"
  class="
    px-4 py-2
    bg-gray-200 text-gray-700
    rounded-md
    aria-pressed:bg-blue-600 aria-pressed:text-white
    hover:bg-gray-300
    aria-pressed:hover:bg-blue-700
  "
  onclick="this.setAttribute('aria-pressed', this.getAttribute('aria-pressed') === 'true' ? 'false' : 'true')"
>
  تبديل
</button>

<!-- علامات التبويب مع ARIA -->
<div role="tablist" class="flex border-b border-gray-200">
  <button
    role="tab"
    aria-selected="true"
    aria-controls="panel-1"
    class="
      px-4 py-2
      border-b-2 border-transparent
      text-gray-600
      aria-selected:border-blue-600 aria-selected:text-blue-600
      hover:text-gray-900
      focus:outline-none focus:ring-2 focus:ring-blue-500
    "
  >
    علامة التبويب 1
  </button>
  <button
    role="tab"
    aria-selected="false"
    aria-controls="panel-2"
    class="
      px-4 py-2
      border-b-2 border-transparent
      text-gray-600
      aria-selected:border-blue-600 aria-selected:text-blue-600
      hover:text-gray-900
      focus:outline-none focus:ring-2 focus:ring-blue-500
    "
  >
    علامة التبويب 2
  </button>
</div>

<!-- الكشف مع aria-expanded -->
<button
  type="button"
  aria-expanded="false"
  aria-controls="content"
  class="
    flex items-center justify-between
    w-full px-4 py-3
    text-left
    bg-gray-100
    hover:bg-gray-200
    rounded-lg
  "
>
  <span>انقر للتوسيع</span>
  <svg
    class="
      w-5 h-5
      transition-transform
      aria-expanded:rotate-180
    "
    fill="none"
    stroke="currentColor"
    viewBox="0 0 24 24"
  >
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
  </svg>
</button>

أنماط ARIA الشائعة

<!-- مربع حوار نموذجي -->
<div
  role="dialog"
  aria-modal="true"
  aria-labelledby="modal-title"
  class="
    fixed inset-0 z-50
    flex items-center justify-center
    bg-black/50
  "
>
  <div class="bg-white rounded-lg shadow-xl max-w-md w-full p-6">
    <h2 id="modal-title" class="text-xl font-bold mb-4">
      عنوان النافذة المنبثقة
    </h2>
    <p class="text-gray-600 mb-6">
      محتوى النافذة المنبثقة يذهب هنا...
    </p>
    <div class="flex gap-3 justify-end">
      <button class="px-4 py-2 bg-gray-200 rounded-md">
        إلغاء
      </button>
      <button class="px-4 py-2 bg-blue-600 text-white rounded-md">
        تأكيد
      </button>
    </div>
  </div>
</div>

<!-- رسالة تنبيه -->
<div
  role="alert"
  aria-live="assertive"
  class="
    p-4
    bg-red-100 border border-red-400
    text-red-700
    rounded-md
  "
>
  <strong class="font-semibold">خطأ!</strong>
  <span class="block sm:inline">حدث خطأ ما.</span>
</div>

<!-- مؤشر التحميل -->
<div
  role="status"
  aria-live="polite"
  aria-label="التحميل"
  class="flex items-center gap-3"
>
  <svg class="animate-spin h-5 w-5 text-blue-600" viewBox="0 0 24 24">
    <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"/>
    <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/>
  </svg>
  <span class="sr-only">جارٍ التحميل...</span>
</div>

اعتبارات تباين الألوان

التباين الصحيح للألوان ضروري للمستخدمين ذوي الإعاقات البصرية. يتطلب WCAG 2.1 نسب تباين دنيا للنص.

متطلبات تباين WCAG

  • المستوى AA (الحد الأدنى): 4.5:1 للنص العادي، 3:1 للنص الكبير (18pt+ أو 14pt+ غامق)
  • المستوى AAA (محسّن): 7:1 للنص العادي، 4.5:1 للنص الكبير
  • غير النصي: 3:1 لمكونات واجهة المستخدم والكائنات الرسومية

أمثلة على التباين الجيد والسيئ

<!-- سيء: تباين منخفض (2.5:1) -->
<p class="text-gray-400 bg-white">
  هذا النص صعب القراءة للعديد من المستخدمين
</p>

<!-- جيد: تباين عالٍ (10.4:1) -->
<p class="text-gray-900 bg-white">
  هذا النص يلبي معايير WCAG AAA
</p>

<!-- سيء: زر بتباين ضعيف -->
<button class="bg-yellow-300 text-yellow-100 px-4 py-2 rounded">
  إرسال
</button>

<!-- جيد: زر بتباين جيد -->
<button class="bg-blue-600 text-white px-4 py-2 rounded">
  إرسال
</button>

<!-- تركيبات الألوان الآمنة في Tailwind -->
<div class="space-y-4">
  <!-- نص داكن على خلفيات فاتحة -->
  <div class="bg-gray-100 text-gray-900 p-4 rounded">
    رمادي 900 على رمادي 100 (13.6:1) ✓
  </div>

  <!-- نص فاتح على خلفيات داكنة -->
  <div class="bg-gray-800 text-white p-4 rounded">
    أبيض على رمادي 800 (10.8:1) ✓
  </div>

  <!-- خلفيات ملونة -->
  <div class="bg-blue-600 text-white p-4 rounded">
    أبيض على أزرق 600 (4.6:1) ✓
  </div>

  <div class="bg-green-700 text-white p-4 rounded">
    أبيض على أخضر 700 (5.2:1) ✓
  </div>
</div>

أدوات لفحص التباين

  • WebAIM Contrast Checker: https://webaim.org/resources/contrastchecker/
  • أدوات مطور المتصفح: لدى Chrome/Edge فاحصو تباين مدمجين
  • مولد ألوان Tailwind: أدوات تعرض أزواج الألوان المتوافقة مع WCAG
  • إضافات Figma: Stark, Contrast, A11y - Color Contrast Checker

تفضيلات الحركة والرسوم المتحركة

يعاني بعض المستخدمين من دوار الحركة أو اضطرابات الدهليز التي تسببها الرسوم المتحركة. يوفر Tailwind أدوات لاحترام تفضيلات حركة المستخدم.

motion-reduce و motion-safe

<!-- تعطيل الرسوم المتحركة للمستخدمين الذين يفضلون الحركة المخفضة -->
<button class="
  px-4 py-2
  bg-blue-600 text-white
  rounded-md
  transform
  hover:scale-105
  motion-reduce:transform-none
  motion-reduce:hover:scale-100
  transition-transform
  motion-reduce:transition-none
">
  مرر عليّ
</button>

<!-- رسوم متحركة فقط للمستخدمين الذين يسمحون بالحركة -->
<div class="
  motion-safe:animate-spin
  motion-reduce:animate-none
">
  <svg class="w-8 h-8" viewBox="0 0 24 24">
    <!-- أيقونة الدوران -->
  </svg>
</div>

<!-- رسوم متحركة للظهور التدريجي مع تفضيل الحركة -->
<div class="
  opacity-0
  motion-safe:animate-fade-in
  motion-reduce:opacity-100
">
  المحتوى
</div>

<!-- رسوم متحركة للانزلاق مع بديل -->
<div class="
  transform -translate-x-full
  motion-safe:transition-transform motion-safe:translate-x-0
  motion-reduce:translate-x-0
">
  محتوى منزلق
</div>

رسوم متحركة مخصصة مع prefers-reduced-motion

<!-- في CSS أو tailwind.config.js -->
@keyframes fade-in {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.animate-fade-in {
  animation: fade-in 0.5s ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .animate-fade-in {
    animation: none;
    opacity: 1;
    transform: translateY(0);
  }
}

<!-- في HTML -->
<div class="animate-fade-in">
  المحتوى يظهر بسلاسة (أو يظهر فورًا إذا تم تقليل الحركة)
</div>

أهمية HTML الدلالي

استخدام عناصر HTML الدلالية هو أساس إمكانية الوصول. يمكن تطبيق أنماط Tailwind على أي عنصر، لذا اختر العنصر المناسب.

دلالي مقابل غير دلالي

<!-- سيء: استخدام divs لكل شيء -->
<div class="cursor-pointer text-blue-600 underline" onclick="navigate()">
  اذهب إلى الصفحة
</div>

<!-- جيد: استخدام عناصر دلالية -->
<a href="/page" class="text-blue-600 underline hover:text-blue-800">
  اذهب إلى الصفحة
</a>

<!-- سيء: بنية غير دلالية -->
<div class="text-2xl font-bold mb-4">عنوان الصفحة</div>
<div class="mb-8">
  <div class="mb-2">محتوى المقالة...</div>
</div>

<!-- جيد: بنية دلالية -->
<article>
  <h1 class="text-2xl font-bold mb-4">عنوان الصفحة</h1>
  <p class="mb-2">محتوى المقالة...</p>
</article>

<!-- استخدم العناصر الدلالية المناسبة -->
<header class="bg-white shadow">...</header>
<nav class="flex space-x-4">...</nav>
<main class="container mx-auto">...</main>
<aside class="w-64 bg-gray-50">...</aside>
<footer class="bg-gray-900 text-white">...</footer>

نماذج يمكن الوصول إليها

النماذج ضرورية لتفاعل المستخدم ويجب أن تكون في متناول جميع المستخدمين.

مثال نموذج كامل يمكن الوصول إليه

<form class="space-y-6 max-w-md mx-auto">
  <!-- إدخال نصي مع تسمية -->
  <div>
    <label
      for="name"
      class="block text-sm font-medium text-gray-700 mb-2"
    >
      الاسم الكامل
      <span class="text-red-600" aria-label="مطلوب">*</span>
    </label>
    <input
      type="text"
      id="name"
      name="name"
      required
      aria-required="true"
      aria-describedby="name-error"
      class="
        w-full px-3 py-2
        border border-gray-300 rounded-md
        focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
        disabled:bg-gray-100 disabled:cursor-not-allowed
      "
    >
    <p id="name-error" class="text-sm text-red-600 mt-1 hidden">
      الرجاء إدخال اسمك
    </p>
  </div>

  <!-- البريد الإلكتروني مع نص مساعد -->
  <div>
    <label for="email" class="block text-sm font-medium text-gray-700 mb-2">
      البريد الإلكتروني
    </label>
    <input
      type="email"
      id="email"
      name="email"
      aria-describedby="email-help"
      class="
        w-full px-3 py-2
        border border-gray-300 rounded-md
        focus:outline-none focus:ring-2 focus:ring-blue-500
      "
    >
    <p id="email-help" class="text-sm text-gray-500 mt-1">
      لن نشارك بريدك الإلكتروني أبدًا
    </p>
  </div>

  <!-- مجموعة أزرار الراديو -->
  <fieldset>
    <legend class="block text-sm font-medium text-gray-700 mb-2">
      نوع الاشتراك
    </legend>
    <div class="space-y-2">
      <div class="flex items-center">
        <input
          type="radio"
          id="free"
          name="subscription"
          value="free"
          class="
            w-4 h-4
            text-blue-600
            focus:ring-2 focus:ring-blue-500
          "
        >
        <label for="free" class="mr-2 text-gray-700">
          مجاني
        </label>
      </div>
      <div class="flex items-center">
        <input
          type="radio"
          id="premium"
          name="subscription"
          value="premium"
          class="
            w-4 h-4
            text-blue-600
            focus:ring-2 focus:ring-blue-500
          "
        >
        <label for="premium" class="mr-2 text-gray-700">
          مميز
        </label>
      </div>
    </div>
  </fieldset>

  <!-- خانة اختيار -->
  <div class="flex items-start">
    <input
      type="checkbox"
      id="terms"
      name="terms"
      required
      aria-required="true"
      class="
        w-4 h-4 mt-1
        text-blue-600
        focus:ring-2 focus:ring-blue-500
      "
    >
    <label for="terms" class="mr-2 text-sm text-gray-700">
      أوافق على
      <a href="/terms" class="text-blue-600 hover:underline">
        الشروط والأحكام
      </a>
    </label>
  </div>

  <!-- زر الإرسال -->
  <button
    type="submit"
    class="
      w-full px-4 py-2
      bg-blue-600 text-white
      font-medium rounded-md
      hover:bg-blue-700
      focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
      disabled:opacity-50 disabled:cursor-not-allowed
    "
  >
    إرسال
  </button>
</form>

روابط تخطي التنقل

تسمح روابط التخطي لمستخدمي لوحة المفاتيح بتجاوز التنقل المتكرر والانتقال مباشرة إلى المحتوى الرئيسي.

تنفيذ روابط التخطي

<!-- ضعها في أعلى body جدًا -->
<a
  href="#main-content"
  class="
    sr-only
    focus:not-sr-only
    focus:absolute
    focus:top-4 focus:left-4
    focus:z-50
    focus:px-4 focus:py-2
    focus:bg-blue-600 focus:text-white
    focus:font-medium focus:rounded-md
    focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2
  "
>
  انتقل إلى المحتوى الرئيسي
</a>

<header>
  <!-- التنقل -->
</header>

<main id="main-content" tabindex="-1">
  <!-- يبدأ المحتوى الرئيسي هنا -->
</main>

تمرين عملي 1: تدقيق إمكانية الوصول

خذ مكون Tailwind موجود ودقق في إمكانية الوصول إليه:

  1. تحقق من نسب تباين الألوان (استخدم أدوات مطور المتصفح)
  2. تأكد من إمكانية الوصول إلى جميع العناصر التفاعلية بلوحة المفاتيح
  3. أضف خصائص ARIA المناسبة
  4. اختبر باستخدام قارئ شاشة (NVDA, JAWS, أو VoiceOver)
  5. تحقق من احترام الحركة لـ prefers-reduced-motion

تمرين عملي 2: بناء نافذة منبثقة يمكن الوصول إليها

أنشئ مربع حوار نموذجي مع إمكانية الوصول الكاملة:

  • أدوار وخصائص ARIA الصحيحة
  • فخ التركيز (يبقى التركيز داخل النافذة المنبثقة)
  • مفتاح ESC يغلق النافذة المنبثقة
  • عودة التركيز إلى عنصر التحفيز عند الإغلاق
  • التنقل بلوحة المفاتيح للأزرار

تمرين عملي 3: جدول بيانات يمكن الوصول إليه

بناء جدول بيانات مع:

  • بنية جدول صحيحة (thead, tbody, th, td)
  • رؤوس أعمدة مع scope="col"
  • تسمية توضيحية تصف الجدول
  • أعمدة قابلة للفرز مع خصائص ARIA
  • التنقل بلوحة المفاتيح بين الخلايا

الخلاصة

بناء واجهات يمكن الوصول إليها مع Tailwind CSS يتضمن:

  • sr-only: إخفاء المحتوى بصريًا مع الحفاظ على إمكانية الوصول
  • أنماط التركيز: استخدم focus-visible لمؤشرات التنقل بلوحة المفاتيح
  • خصائص ARIA: توفير معنى دلالي مع أدوار وحالات ARIA الصحيحة
  • تباين الألوان: تأكد من نسب تباين WCAG AA (4.5:1) أو AAA (7:1)
  • تفضيلات الحركة: احترام prefers-reduced-motion مع أدوات motion-reduce
  • HTML الدلالي: استخدم العناصر الصحيحة (nav, main, article, إلخ.)
  • نماذج يمكن الوصول إليها: وسّم المدخلات، قدم رسائل الخطأ، استخدم fieldset للمجموعات
  • روابط التخطي: اسمح لمستخدمي لوحة المفاتيح بتجاوز التنقل

في الدرس التالي، سنستكشف Tailwind CSS v4 وميزاته الحديثة بما في ذلك نظام التكوين CSS-first الجديد.