EduFlow

الوحدة 3 · لوحة المفاتيح والتركيز وARIA

الوصول بلوحة المفاتيح والتركيز المرئي

18 دقراءة 4 دبيئة تجريب الكود

الدرس في لمحة

اجعل كل عنصر تحكّم في Ticketly قابلًا للوصول بلوحة المفاتيح بترتيب منطقي، وأظهر مؤشّر تركيز واضحًا، وأضِف رابط تخطٍّ (skip link)، وامنع الأشرطة الثابتة من إخفاء التركيز.

  1. 1المفاتيح التي يستخدمها الناس
  2. 2ترتيب التركيز يتبع الـ DOM
  3. 3اجعل التركيز مرئيًا
  4. 4روابط التخطّي والأشرطة الثابتة

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

ضع الفأرة في الدرج وحاول شراء تذكرة الجمعة في Ticketly. ستكتشف ثلاثة أشياء في نحو ثلاثين ثانية. مفتاح Tab يمرّ بك على أربعين رابطًا ونيّف في الترويسة والتذييل قبل أن يصل إلى المهرجان. ثم يقفز التركيز من زر السبت إلى زر الجمعة ويعود إلى الأعلى. ومعظم الوقت لا ترى أين التركيز أصلًا، لأن أحدهم كتب outline: none عام 2019 ليتخلّص من «ذلك المربع الأزرق القبيح».

الوصول بلوحة المفاتيح ليس حالة نادرة. كل من يستخدم قارئ شاشة على حاسوب مكتبي يعتمد على لوحة المفاتيح، وكذلك مستخدمو أجهزة التبديل، والمصابون بالرعاش، ومن تعطّلت لوحة اللمس لديهم، والمطوّرون الذين يفضّلونها ببساطة. يشترط معيار WCAG ‏2.1.1 Keyboard ‏(A) أن يعمل كل شيء من لوحة المفاتيح، ومعيار 2.1.2 ‏(A) ألا يُحبس التركيز أبدًا.

المفاتيح التي يستخدمها الناس

  • Tab / Shift+Tab: التنقّل بين العناصر التفاعلية (الروابط والأزرار وحقول النماذج).
  • Enter: اتّباع رابط، أو تفعيل زر، أو إرسال نموذج.
  • Space: تفعيل زر، أو تبديل مربّع اختيار، أو تمرير الصفحة حين لا يكون أي عنصر عليه التركيز.
  • مفاتيح الأسهم: التنقّل داخل widget، مثل أزرار radio أو قائمة select أو التبويبات أو القوائم.
  • Escape: إغلاق نافذة حوار أو قائمة أو نافذة منبثقة.

العناصر الأصلية تدعم كل هذا أصلًا. ومهمّتك في الغالب ألا تكسره، وأن تلتزم بهذه الأعراف حين تبني widgets مخصّصة في درس أنماط الـ Widgets.

ترتيب التركيز يتبع الـ DOM

ينتقل Tab بين العناصر القابلة للتركيز بترتيب المصدر. إن وضع التخطيط المرئي زر “Add Saturday” قبل “Add Friday” لكن الكود يضعهما بالعكس، فسيبدو التركيز كأنه يقفز إلى الخلف. والإصلاح أن تضع الكود بالترتيب الذي تريد أن يقابله الناس به. واحذر من CSS الذي يعيد ترتيب العناصر بصريًا، مثل order أو flex-direction: row-reverse أو التموضع الصريح في الـ grid، لأن التركيز سيظلّ يتبع الكود (2.4.3 Focus Order).

تضبط tabindex قابلية التركيز:

القيمة الأثر استخدمها لـ
0 قابل للتركيز، بترتيب الـ DOM عنصر تحكّم مخصّص مبني من عنصر غير تفاعلي
-1 قابل للتركيز من السكربت فقط، لا بمفتاح Tab عناوين أو حاويات تنقل التركيز إليها
1 أو أكثر يتقدّم على كل شيء آخر لا تستخدمها أبدًا

اجعل التركيز مرئيًا

مستخدم لوحة المفاتيح بلا مؤشّر تركيز مرئي كمستخدم الفأرة بمؤشّر غير مرئي. يشترط معيار WCAG ‏2.4.7 Focus Visible ‏(AA) وجود مؤشّر؛ ويعني معيار 1.4.11 Non-text Contrast أن تباينه يجب ألا يقلّ عن 3:1 مع ما يحيط به.

الأداة الحديثة هي :focus-visible. تطبّقها المتصفّحات حين يجب أن يظهر التركيز: دائمًا عند التنقّل بلوحة المفاتيح وفي الحقول النصية، لكن عادةً لا حين يُنقر زر بالفأرة. هذا يجيب عن شكوى المصمّمة الحقيقية دون أن يؤذي أحدًا:

ticketly/styles.css
:focus-visible {
  outline: 3px solid #5b3cc4;
  outline-offset: 2px;
}

استخدم outline لا box-shadow للحلقة. سمات التباين في Windows (وضع forced colors) تحذف الظلال لكنها تُبقي الـ outline، فالمؤشّر المبني على الظلّ وحده يختفي عند أكثر الناس حاجة إليه.

روابط التخطّي والأشرطة الثابتة

في ترويسة Ticketly شعار، وعشرة روابط قائمة، وحقل بحث، وقائمة للحساب: ثلاث عشرة محطة لمفتاح Tab. على مستخدم لوحة المفاتيح أن يمرّ عليها كلها بمفتاح Tab في كل صفحة. يطلب معيار WCAG ‏2.4.1 Bypass Blocks ‏(A) طريقة لتجاوز المحتوى المتكرّر، والحلّ الكلاسيكي هو رابط التخطّي (skip link): أول عنصر قابل للتركيز في الصفحة، مخفي حتى يتلقّى التركيز.

HTML
<body>
  <a class="skip-link" href="#main">Skip to main content</a>
  <header>…</header>
  <main id="main">…</main>
</body>
CSS
.skip-link {
  position: absolute;
  inset-block-start: 8px;
  inset-inline-start: 8px;
  padding: 8px 12px;
  background: #fff;
  transform: translateY(-150%);
}
.skip-link:focus {
  transform: none;
}

لا تُخفِه بـ display: none، فهذا يُخرجه من ترتيب Tab. نقله خارج الشاشة وإعادته عند التركيز يُبقيه قابلًا للوصول ويُظهره تمامًا حين يحتاجه مستخدم لوحة المفاتيح.

من دون رابط تخطٍّ، تصل أول ضغطة Tab إلى الشعار وتعبر اثنتا عشرة ضغطة أخرى الترويسة قبل الوصول إلى المحتوى الرئيسي. ومع رابط التخطّي، تُظهر أول ضغطة Tab الرابط Skip to main content، وينقل Enter مباشرة إلى main. من دون رابط تخطٍّ … 13 محطة main مع رابط تخطٍّ Skip to main content Enter main
أول ضغطة Tab في Ticketly

الترويسات الثابتة وأشرطة ملفات تعريف الارتباط تجلب المشكلة المعاكسة: ينتقل التركيز إلى عنصر مخفي تحتها، وهذا يخالف معيار 2.4.11 Focus Not Obscured ‏(AA). أخبر المتصفّح بمقدار ما تغطّيه الأجزاء الثابتة من الـ viewport، فيمرّر العناصر التي عليها التركيز بعيدًا عنها:

CSS
html {
  scroll-padding-block-start: 96px; /* sticky header height plus a little air */
}

أما شريط ملفات تعريف الارتباط، فالإصلاح الحقيقي ألا تتركه يطفو فوق المحتوى إلى ما لا نهاية: ضعه في تدفّق الصفحة، أو تأكّد أنه يمكن إغلاقه قبل الوصول إلى أي شيء آخر.

صار بالإمكان عبور Ticketly من أعلاه إلى أسفله بلوحة المفاتيح. يتناول الدرس التالي الحالات الأصعب، حيث يجب على كودك أن ينقل التركيز بنفسه: نوافذ الحوار والتنقّل في التطبيقات أحادية الصفحة.

أهم النقاط

  • ترتيب التركيز يتبع ترتيب الـ DOM، فضع الكود بالترتيب الذي يجب أن يقابله الناس به، ولا تعاكسه بإعادة الترتيب عبر CSS.
  • استخدم tabindex=“0” لإضافة عنصر تحكّم مخصّص إلى ترتيب Tab، وtabindex=“-1” للعناصر التي تنقل إليها التركيز من السكربت؛ ولا تستخدم أبدًا tabindex موجبًا.
  • لا تحذف إطار التركيز أبدًا دون بديل؛ نسّق :focus-visible بمؤشّر لا يقلّ تباينه عن 3:1.
  • رابط التخطّي كأول عنصر قابل للتركيز يتيح لمستخدمي لوحة المفاتيح تجاوز التنقّل المتكرّر، وscroll-padding يمنع الأشرطة الثابتة من إخفاء التركيز.

قراءات إضافية

تحقّق من فهمك

4 أسئلة

السؤال 1 من 4

بأيّ ترتيب يزور مفتاح Tab هذه الأزرار؟

HTML
<button>Friday</button>
<button tabindex="2">Saturday</button>
<button tabindex="1">Sunday</button>
<button>Weekend</button>
السؤال 2 من 4

تجد مصمّمة أن حلقة التركيز الافتراضية قبيحة. أيّ CSS هو أفضل ردّ؟

السؤال 3 من 4

متى يجب أن يحصل عنصر على tabindex="-1"؟

السؤال 4 من 4

ارتفاع الترويسة الثابتة في Ticketly ‏80px. حين يتنقّل مستخدم لوحة المفاتيح إلى الخلف صعودًا في الصفحة، تنزلق الروابط التي عليها التركيز تحتها. أيّ إصلاح يعالج معيار 2.4.11 Focus Not Obscured؟

تدريب

بيئة تجريب الكود

اجعل Ticketly صديقًا للوحة المفاتيح

ثلاث مشكلات في لوحة المفاتيح على صفحة صغيرة واحدة:

  1. أزرار التذاكر تستخدم قيم tabindex موجبة، فيقفز ترتيب التركيز هنا وهناك. احذفها؛ فترتيب الـ DOM صحيح أصلًا.
  2. قاعدة من عام 2019 تُخفي كل مؤشّرات التركيز. استبدلها بـ outline مرئي عبر :focus-visible لا يقلّ عرضه عن 2px.
  3. لا توجد طريقة لتجاوز الترويسة. أضِف رابط تخطٍّ يكون أول عنصر قابل للتركيز، نصه يحتوي “Skip” وhref="#main". يمكن أن يكون مخفيًا خارج الشاشة، لكن يجب أن يكون مرئيًا حين يكون عليه التركيز (لا تستخدم display: none).
<header class="site-header">
  <a href="#" class="logo">Ticketly</a>
  <nav aria-label="Site">
    <a href="#events">Events</a>
    <a href="#faq">FAQ</a>
    <a href="#account">Account</a>
  </nav>
</header>
<main id="main">
  <h1>Harbour Lights 2026</h1>
  <div class="passes">
    <button tabindex="3">Add Friday pass</button>
    <button tabindex="1">Add Saturday pass</button>
    <button>Add weekend pass</button>
  </div>
</main>جارٍ تحميل المحرّر…

Ctrl/⌘ + Enter للتشغيل · يعمل في بيئة معزولة داخل متصفّحك. لا شيء يغادر جهازك.

المعاينة

الـ console

(لا مخرجات)

الفحوص

  • لا يوجد عنصر له tabindex موجب
  • أول عنصر قابل للتركيز رابط تخطٍّ يشير إلى ‎#main
  • رابط التخطّي مرئي على الشاشة حين يكون عليه التركيز
  • الروابط والأزرار تُظهر outline لا يقلّ عرضه عن 2px عند التركيز