EduFlow

الوحدة 4 · التصميم البصري والمحتوى

الأسماء والأوصاف المتاحة (Accessible Names)

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

الدرس في لمحة

توقّع الاسم المتاح والوصف اللذين يحسبهما المتصفّح لأي عنصر تحكّم، وأعطِ أزرار الأيقونات والأزرار المكرّرة أسماء فريدة تطابق تسميتها المرئية.

  1. 1كيف يحسب المتصفّح الاسم
  2. 2أزرار الأيقونات فقط
  3. 3الأزرار المكرّرة ومعيار Label in Name
  4. 4الأوصاف

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

في صفحة الفعالية في Ticketly ثلاثة أزرار كلها تقول “Book now”، وزر إغلاق يُظهر “×”، وزر بحث لا يحتوي إلا أيقونة SVG لعدسة مكبّرة، وزر للنشرة البريدية ضبط أحد المطوّرين اسمه إلى “newsletter-cta” لأغراض التحليلات. في قائمة الأزرار لدى قارئ الشاشة، تُقرأ الصفحة هكذا: “Book now, Book now, Book now, times, button, newsletter-cta”. كل واحدة من هذه مشكلة تسمية، وتُصلحها كلها بفهم خوارزمية واحدة.

كيف يحسب المتصفّح الاسم

تحدّد مواصفة Accessible Name and Description Computation الصادرة عن W3C بدقة كيف يختار المتصفّح الاسم المتاح (accessible name). وبتبسيط، يجرّب هذه المصادر بالترتيب ويأخذ أول مصدر يُنتج نصًا:

  1. aria-labelledby: نص العناصر المُشار إليها، مضمومًا بمسافات، بترتيب الـ ids.
  2. aria-label: النص المكتوب في الخاصية.
  3. التسمية الأصلية: عنصر <label> لحقول النماذج، وalt للصور، و<legend> للـ fieldset، و<caption> للجدول، و<title> للـ SVG.
  4. المحتوى، للأدوار التي تسمح بذلك: الأزرار، والروابط، والعناوين، والتبويبات، وخلايا الجداول، وعناصر القوائم، والخيارات. النص الداخلي، مضافًا إليه alt الصور الداخلية، ومستبعدًا منه كل ما عليه aria-hidden="true".
  5. title (وplaceholder لحقول الإدخال) كملاذ أخير.
سلّم من خمس درجات من الأعلى أولوية إلى الأدنى: aria-labelledby، ثم aria-label، ثم التسمية الأصلية مثل label أو alt أو legend، ثم المحتوى للأزرار والروابط والعناوين، وأخيرًا title أو placeholder. أول درجة تُنتج نصًا تصبح الاسم. 1 aria-labelledby 2 aria-label 3 label، alt، legend، caption 4 المحتوى (زر، رابط، عنوان) 5 title، placeholder أولوية أدنى
من أين يأتي الاسم، الأعلى أولوية أولًا

نتيجتان تهمّان كل يوم. أولًا، تسمية ARIA تتجاوز النص المرئي: ما إن تضيف aria-label حتى يُتجاهَل محتوى الزر نفسه. ثانيًا، الأسماء لا تندمج: إن وُجدت aria-labelledby، تُتخطّى aria-label الخاصة بالعنصر. مع استثناء واحد: إن ضمّت aria-labelledby معرّف العنصر نفسه، فإن هذه الإشارة الذاتية تُحسب بقيمة aria-label إن وُجدت، لا بمحتوى العنصر. ويمكنك التحقّق من أي نتيجة في المتصفّح: لوحة Accessibility في Chrome وAccessibility Inspector في Firefox كلتاهما تعرضان الاسم المحسوب.

أزرار الأيقونات فقط

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

ticketly/header.html
<!-- 1. Visible text next to the icon: best for everyone -->
<button type="button" class="search">
  <svg aria-hidden="true" viewBox="0 0 24 24">…</svg>
  Search
</button>

<!-- 2. Visually hidden text: the design stays icon-only -->
<button type="button" class="close">
  <span aria-hidden="true">×</span>
  <span class="visually-hidden">Close banner</span>
</button>

<!-- 3. aria-label: fine, but invisible to translation tools that skip attributes -->
<button type="button" class="close" aria-label="Close banner">
  <span aria-hidden="true">×</span>
</button>

لاحظ aria-hidden="true" على الرمز “×”. من دونها، سيكون اسم الخيار 2 هو “× Close banner”، وتقرؤه بعض قارئات الشاشة “times Close banner”.

الأزرار المكرّرة ومعيار Label in Name

ثلاثة أزرار “Book now” مقبولة بصريًا، لأن كل واحد منها يقع في بطاقته. لكنها في قائمة الأزرار لا يمكن التمييز بينها. تريد أسماء مثل “Book now, Friday pass” دون تغيير التصميم المرئي. يمكن لـ aria-labelledby أن تشير إلى الزر نفسه إضافةً إلى عنوان البطاقة:

HTML
<article>
  <h3 id="fri-title">Friday pass</h3>
  <button type="button" id="fri-book" aria-labelledby="fri-book fri-title">Book now</button>
</article>

يصبح الاسم “Book now Friday pass”. تأتي الكلمات المرئية أولًا، وهذا مهم لمعيار 2.5.3 Label in Name ‏(A): يجب أن يحتوي الاسم المتاح التسمية المرئية، وأفضل ممارسة أن يبدأ بها. تقول دينا، التي تستخدم التحكّم الصوتي، “click Book now”؛ فيجد برنامجها التطابقات الثلاثة ويرقّمها لتختار منها. لو كان الاسم “Reserve Friday” لما كان أمامها أي طريقة لنقره بنطق ما تراه. وهذا بالضبط ما كان خاطئًا في زر “newsletter-cta”: الكلمة المرئية “Subscribe” ليست في الاسم إطلاقًا. احذف الـ aria-label فيعود المحتوى ليمنح الاسم الصحيح.

الأوصاف

الوصف (description) معلومات ثانوية تُعلَن بعد الاسم والدور، عادةً بعد توقّف قصير. تعمل aria-describedby مثل aria-labelledby لكنها تغذّي الوصف بدلًا من الاسم:

HTML
<label for="promo">Promo code</label>
<input id="promo" aria-describedby="promo-hint">
<p id="promo-hint">Codes are 8 letters, for example HARBOUR8.</p>

يقرأ NVDA: “Promo code, edit text, Codes are 8 letters, for example HARBOUR8.” استخدم الأوصاف للتلميحات والصيغ ورسائل الخطأ، كما فعلت في درس النماذج. اجعل الاسم قصيرًا والوصف اختياريًا: يستطيع المستخدمون تخطّي الوصف، لكنهم لا يستطيعون تخطّي الاسم.

صار لكل عنصر تحكّم في Ticketly اسم يستطيع الإنسان نطقه. وينظر الدرس الأخير في هذا القسم في الكلمات نفسها: اللغة البسيطة، وخصائص اللغة التي تجعل محتوى Ticketly العربي يُنطق بشكل صحيح.

أهم النقاط

  • يحسب المتصفّح اسم عنصر التحكّم بترتيب ثابت: aria-labelledby، ثم aria-label، ثم التسمية الأصلية (label وalt وlegend وcaption)، ثم محتوى العنصر، ثم title.
  • aria-labelledby وaria-label تتجاوزان كل ما سواهما، بما في ذلك النص المرئي، فاستخدمهما بحذر.
  • يجب أن تظهر التسمية المرئية داخل الاسم المتاح (2.5.3 Label in Name)، ويُفضَّل أن تكون في بدايته، ليستطيع مستخدمو التحكّم الصوتي نطق ما يرونه.
  • aria-describedby تضيف معلومات ثانوية تُقرأ بعد الاسم والدور؛ وهي للتلميحات والأخطاء، لا للاسم نفسه.

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

تحقّق من فهمك

3 أسئلة

السؤال 1 من 3

ما الاسم المتاح لهذا الزر؟

HTML
<h3 id="fri">Friday pass</h3>
<button id="b1" aria-label="Buy" aria-labelledby="b1 fri">Book now</button>
السؤال 2 من 3

يقول مستخدم تحكّم صوتي “click Get tickets”. فلا يحدث شيء. لماذا؟

HTML
<a href="/tickets" aria-label="Purchase festival passes">Get tickets</a>
السؤال 3 من 3

أيّ عنصر يأتي اسمه المتاح من محتواه؟

تدريب

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

سمِّ كل زر في صفحة الفعالية

يعرض قارئ الشاشة أزرار Ticketly هكذا: “times, button, Book now, Book now, Book now, newsletter-cta”. أصلح الأسماء دون تغيير ما يراه المبصرون:

  1. زر الإغلاق .close ‏"×" يحتاج إلى اسم يحتوي “Close” (أو “Dismiss”)؛ ولا ينبغي أن يُقرأ الرمز “×” نفسه.
  2. زر البحث .search الذي لا يحتوي إلا أيقونة يحتاج إلى الاسم “Search”.
  3. كل زر .book يحتاج إلى اسم فريد يبدأ بـ “Book now” ويتضمّن تذكرته (Friday أو Saturday أو Weekend).
  4. اسم زر .subscribe يجب أن يحتوي كلمته المرئية “Subscribe”.
<div class="banner">
  <p>Early-bird prices end Friday.</p>
  <button type="button" class="close">×</button>
</div>

<header class="bar">
  <strong>Ticketly</strong>
  <button type="button" class="search"><svg viewBox="0 0 24 24" width="18" height="18"><circle cx="10" cy="10" r="6" fill="none" stroke="currentColor" stroke-width="2"/><path d="M15 15 L21 21" stroke="currentColor" stroke-width="2"/></svg></button>
</header>

<div class="cards">
  <article class="card">
    <h3>Friday pass</h3>
    <button type="button" class="book">Book now</button>
  </article>
  <article class="card">
    <h3>Saturday pass</h3>
    <button type="button" class="book">Book now</button>
  </article>
  <article class="card">
    <h3>Weekend pass</h3>
    <button type="button" class="book">Book now</button>
  </article>
</div>

<footer>
  <label for="news-email">Email</label>
  <input id="news-email" type="email" autocomplete="email">
  <button type="button" class="subscribe" aria-label="newsletter-cta">Subscribe</button>
</footer>جارٍ تحميل المحرّر…

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

المعاينة

الـ console

(لا مخرجات)

الفحوص

  • زر الإغلاق اسمه Close (أو Dismiss) والرمز × لا يُقرأ
  • زر البحث اسمه Search
  • لأزرار Book now الثلاثة أسماء فريدة تتضمّن تذكرة كلٍّ منها
  • كل اسم يحتوي نصه المرئي، وأسماء Book now تبدأ به
  • اسم زر Subscribe يحتوي “Subscribe”