{"id":"l-a11y-4-3","courseId":"web-accessibility","lang":"ar","kind":"lesson","summary":"توقّع الاسم المتاح والوصف اللذين يحسبهما المتصفّح لأي عنصر تحكّم، وأعطِ أزرار الأيقونات والأزرار المكرّرة أسماء فريدة تطابق تسميتها المرئية.","html":"<p>في صفحة الفعالية في Ticketly ثلاثة أزرار كلها تقول “Book now”، وزر إغلاق يُظهر “×”، وزر بحث لا يحتوي إلا أيقونة SVG لعدسة مكبّرة، وزر للنشرة البريدية ضبط أحد المطوّرين اسمه إلى “newsletter-cta” لأغراض التحليلات. في قائمة الأزرار لدى قارئ الشاشة، تُقرأ الصفحة هكذا: “Book now, Book now, Book now, times, button, newsletter-cta”. كل واحدة من هذه مشكلة تسمية، وتُصلحها كلها بفهم خوارزمية واحدة.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">كيف يحسب المتصفّح الاسم</h2>\n<p>تحدّد مواصفة Accessible Name and Description Computation الصادرة عن W3C بدقة كيف يختار المتصفّح الاسم المتاح (accessible name). وبتبسيط، يجرّب هذه المصادر بالترتيب ويأخذ <strong>أول مصدر يُنتج نصًا</strong>:</p>\n<ol>\n<li><strong><code dir=\"ltr\">aria-labelledby</code></strong>: نص العناصر المُشار إليها، مضمومًا بمسافات، بترتيب الـ ids.</li>\n<li><strong><code dir=\"ltr\">aria-label</code></strong>: النص المكتوب في الخاصية.</li>\n<li><strong>التسمية الأصلية</strong>: عنصر <code dir=\"ltr\">&lt;label&gt;</code> لحقول النماذج، و<code dir=\"ltr\">alt</code> للصور، و<code dir=\"ltr\">&lt;legend&gt;</code> للـ fieldset، و<code dir=\"ltr\">&lt;caption&gt;</code> للجدول، و<code dir=\"ltr\">&lt;title&gt;</code> للـ SVG.</li>\n<li><strong>المحتوى</strong>، للأدوار التي تسمح بذلك: الأزرار، والروابط، والعناوين، والتبويبات، وخلايا الجداول، وعناصر القوائم، والخيارات. النص الداخلي، مضافًا إليه <code dir=\"ltr\">alt</code> الصور الداخلية، ومستبعدًا منه كل ما عليه <code dir=\"ltr\">aria-hidden=&quot;true&quot;</code>.</li>\n<li><strong><code dir=\"ltr\">title</code></strong> (و<code dir=\"ltr\">placeholder</code> لحقول الإدخال) كملاذ أخير.</li>\n</ol>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 230\" role=\"img\" aria-labelledby=\"l-a11y-4-3-t1\"><defs><marker id=\"arrow-l-a11y-4-3-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-a11y-4-3-t1\">سلّم من خمس درجات من الأعلى أولوية إلى الأدنى: aria-labelledby، ثم aria-label، ثم التسمية الأصلية مثل label أو alt أو legend، ثم المحتوى للأزرار والروابط والعناوين، وأخيرًا title أو placeholder. أول درجة تُنتج نصًا تصبح الاسم.</title>\n  <rect class=\"d-box-primary\" x=\"20\" y=\"20\" width=\"300\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-code\" x=\"35\" y=\"42\">1 aria-labelledby</text>\n  <rect class=\"d-box-primary\" x=\"60\" y=\"62\" width=\"300\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-code\" x=\"75\" y=\"84\">2 aria-label</text>\n  <rect class=\"d-box-accent\" x=\"100\" y=\"104\" width=\"300\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-label\" x=\"115\" y=\"126\">3 label، alt، legend، caption</text>\n  <rect class=\"d-box-accent\" x=\"140\" y=\"146\" width=\"300\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-label\" x=\"155\" y=\"168\">4 المحتوى (زر، رابط، عنوان)</text>\n  <rect class=\"d-box\" x=\"180\" y=\"188\" width=\"300\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-label\" x=\"195\" y=\"210\">5 title، placeholder</text>\n  <path class=\"d-arrow\" d=\"M560 30 L560 200\" marker-end=\"url(#arrow-l-a11y-4-3-1)\"/>\n  <text class=\"d-label-muted\" x=\"575\" y=\"110\">أولوية</text>\n  <text class=\"d-label-muted\" x=\"575\" y=\"130\">أدنى</text>\n</svg><figcaption>من أين يأتي الاسم، الأعلى أولوية أولًا</figcaption></figure>\n<p>نتيجتان تهمّان كل يوم. أولًا، <strong>تسمية ARIA تتجاوز النص المرئي</strong>: ما إن تضيف <code dir=\"ltr\">aria-label</code> حتى يُتجاهَل محتوى الزر نفسه. ثانيًا، <strong>الأسماء لا تندمج</strong>: إن وُجدت <code dir=\"ltr\">aria-labelledby</code>، تُتخطّى <code dir=\"ltr\">aria-label</code> الخاصة بالعنصر. مع استثناء واحد: إن ضمّت <code dir=\"ltr\">aria-labelledby</code> معرّف العنصر نفسه، فإن هذه الإشارة الذاتية تُحسب بقيمة <code dir=\"ltr\">aria-label</code> إن وُجدت، لا بمحتوى العنصر. ويمكنك التحقّق من أي نتيجة في المتصفّح: لوحة Accessibility في Chrome وAccessibility Inspector في Firefox كلتاهما تعرضان الاسم المحسوب.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">أزرار الأيقونات فقط</h2>\n<p>الزر الذي لا يحتوي إلا أيقونة ليس له محتوى نصي، فليس له اسم، إلا إن أعطيته واحدًا. ثلاثة خيارات، بترتيب الأفضلية:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">ticketly/header.html</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">&#x3C;!-- 1. Visible text next to the icon: best for everyone --></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">button</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> type</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"button\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> class</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"search\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">svg</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> aria-hidden</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"true\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> viewBox</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"0 0 24 24\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>…&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">svg</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  Search</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">button</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">&#x3C;!-- 2. Visually hidden text: the design stays icon-only --></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">button</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> type</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"button\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> class</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"close\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">span</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> aria-hidden</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"true\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>×&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">span</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">span</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> class</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"visually-hidden\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Close banner&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">span</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">button</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">&#x3C;!-- 3. aria-label: fine, but invisible to translation tools that skip attributes --></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">button</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> type</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"button\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> class</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"close\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> aria-label</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Close banner\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">span</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> aria-hidden</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"true\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>×&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">span</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">button</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<p>لاحظ <code dir=\"ltr\">aria-hidden=&quot;true&quot;</code> على الرمز “×”. من دونها، سيكون اسم الخيار 2 هو “× Close banner”، وتقرؤه بعض قارئات الشاشة “times Close banner”.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">خطأ شائع</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>تسمية ما لا يُسمّى</span></p>\n<p>وضع <code dir=\"ltr\">aria-label</code> على <code dir=\"ltr\">div</code> أو <code dir=\"ltr\">span</code> بلا دور لا يفعل شيئًا يمكن الاعتماد عليه: ARIA تمنع الأسماء على العناصر العامة، ومعظم قارئات الشاشة تتجاهلها. إن وجدت نفسك تسمّي div، فإما أنه يجب أن يكون عنصر تحكّم حقيقيًا (زرًّا أو رابطًا)، وإما أن النص يجب أن يكون مرئيًا في الصفحة.</p>\n</aside>\n<h2 id=\"sec-3\" tabindex=\"-1\">الأزرار المكرّرة ومعيار Label in Name</h2>\n<p>ثلاثة أزرار “Book now” مقبولة بصريًا، لأن كل واحد منها يقع في بطاقته. لكنها في قائمة الأزرار لا يمكن التمييز بينها. تريد أسماء مثل “Book now, Friday pass” دون تغيير التصميم المرئي. يمكن لـ <code dir=\"ltr\">aria-labelledby</code> أن تشير إلى الزر نفسه إضافةً إلى عنوان البطاقة:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">HTML</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">article</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h3</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> id</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"fri-title\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Friday pass&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">button</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> type</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"button\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> id</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"fri-book\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> aria-labelledby</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"fri-book fri-title\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Book now&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">button</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">article</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<p>يصبح الاسم “Book now Friday pass”. تأتي الكلمات المرئية <strong>أولًا</strong>، وهذا مهم لمعيار <strong>2.5.3 Label in Name ‏(A)</strong>: يجب أن يحتوي الاسم المتاح التسمية المرئية، وأفضل ممارسة أن يبدأ بها. تقول دينا، التي تستخدم التحكّم الصوتي، “click Book now”؛ فيجد برنامجها التطابقات الثلاثة ويرقّمها لتختار منها. لو كان الاسم “Reserve Friday” لما كان أمامها أي طريقة لنقره بنطق ما تراه. وهذا بالضبط ما كان خاطئًا في زر “newsletter-cta”: الكلمة المرئية “Subscribe” ليست في الاسم إطلاقًا. احذف الـ <code dir=\"ltr\">aria-label</code> فيعود المحتوى ليمنح الاسم الصحيح.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">الأوصاف</h2>\n<p><strong>الوصف (description)</strong> معلومات ثانوية تُعلَن بعد الاسم والدور، عادةً بعد توقّف قصير. تعمل <code dir=\"ltr\">aria-describedby</code> مثل <code dir=\"ltr\">aria-labelledby</code> لكنها تغذّي الوصف بدلًا من الاسم:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">HTML</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">label</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> for</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"promo\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Promo code&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">label</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">input</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> id</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"promo\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> aria-describedby</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"promo-hint\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> id</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"promo-hint\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Codes are 8 letters, for example HARBOUR8.&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<p>يقرأ NVDA: “Promo code, edit text, Codes are 8 letters, for example HARBOUR8.” استخدم الأوصاف للتلميحات والصيغ ورسائل الخطأ، كما فعلت في درس النماذج. اجعل الاسم قصيرًا والوصف اختياريًا: يستطيع المستخدمون تخطّي الوصف، لكنهم لا يستطيعون تخطّي الاسم.</p>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">نصيحة</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>تجنّب title لأي شيء مهم</span></p>\n<p>خاصية <code dir=\"ltr\">title</code> تُظهر تلميحًا عند التمرير بالفأرة فقط. لا يراه مستخدمو لوحة المفاتيح واللمس أبدًا، وتتعامل معه قارئات الشاشة بشكل غير متّسق. إنه آخر ملاذ في الخوارزمية لسبب وجيه. لا تعتمد عليه في الأسماء أو التعليمات.</p>\n</aside>\n<p>صار لكل عنصر تحكّم في Ticketly اسم يستطيع الإنسان نطقه. وينظر الدرس الأخير في هذا القسم في الكلمات نفسها: اللغة البسيطة، وخصائص اللغة التي تجعل محتوى Ticketly العربي يُنطق بشكل صحيح.</p>\n","toc":[{"id":"sec-1","text":"كيف يحسب المتصفّح الاسم","level":2},{"id":"sec-2","text":"أزرار الأيقونات فقط","level":2},{"id":"sec-3","text":"الأزرار المكرّرة ومعيار Label in Name","level":2},{"id":"sec-4","text":"الأوصاف","level":2}],"takeaways":["يحسب المتصفّح اسم عنصر التحكّم بترتيب ثابت: aria-labelledby، ثم aria-label، ثم التسمية الأصلية (label وalt وlegend وcaption)، ثم محتوى العنصر، ثم title.","aria-labelledby وaria-label تتجاوزان كل ما سواهما، بما في ذلك النص المرئي، فاستخدمهما بحذر.","يجب أن تظهر التسمية المرئية داخل الاسم المتاح (2.5.3 Label in Name)، ويُفضَّل أن تكون في بدايته، ليستطيع مستخدمو التحكّم الصوتي نطق ما يرونه.","aria-describedby تضيف معلومات ثانوية تُقرأ بعد الاسم والدور؛ وهي للتلميحات والأخطاء، لا للاسم نفسه."],"further":[{"title":"W3C: Accessible Name and Description Computation 1.2","url":"https://www.w3.org/TR/accname-1.2/"},{"title":"W3C APG: Providing Accessible Names and Descriptions","url":"https://www.w3.org/WAI/ARIA/apg/practices/names-and-descriptions/"},{"title":"W3C: Understanding 2.5.3 Label in Name","url":"https://www.w3.org/WAI/WCAG22/Understanding/label-in-name.html"}],"quiz":[{"q":"<p>ما الاسم المتاح لهذا الزر؟</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">HTML</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h3</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> id</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"fri\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Friday pass&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">button</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> id</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"b1\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> aria-label</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Buy\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> aria-labelledby</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"b1 fri\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Book now&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">button</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n","options":[{"text":"“Buy”","why":"aria-labelledby أعلى أولوية من aria-label على الزر، فيضمّ الاسم أيضًا العنوانَ المُشار إليه."},{"text":"“Book now”","why":"المحتوى هنا هو المصدر الأدنى أولوية. وخاصيتا ARIA كلتاهما أعلى منه."},{"text":"“Buy Friday pass”","why":"صحيح. تفوز aria-labelledby وتضمّ نصوص العناصر المُشار إليها بالترتيب. وحين تصل الخوارزمية إلى الزر نفسه عبر تلك القائمة، تتخطّى aria-labelledby (تجنّبًا للدوران في حلقة) وتأخذ المصدر التالي، أي aria-label بقيمة “Buy”. فلا يدخل النص المرئي “Book now” في الاسم إطلاقًا، وهذا يخالف أيضًا معيار Label in Name."},{"text":"“Book now Friday pass”","why":"هذا ما ستحصل عليه لو لم تكن هناك aria-label. أما معها، فالإشارة إلى الزر نفسه تُحسب “Buy” لا محتواه."}],"answer":2},{"q":"<p>يقول مستخدم تحكّم صوتي “click Get tickets”. فلا يحدث شيء. لماذا؟</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">HTML</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> href</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/tickets\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> aria-label</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Purchase festival passes\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Get tickets&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n","options":[{"text":"لا يمكن تفعيل الروابط بالتحكّم الصوتي","why":"التحكّم الصوتي يفعّل الروابط طوال الوقت. يكفي أن تطابق الكلمات الاسم."},{"text":"الـ aria-label يحلّ محلّ النص المرئي، فلا يحتوي الاسم “Get tickets”","why":"صحيح. هذا يخالف معيار 2.5.3 Label in Name. احذف الـ aria-label، أو ابدأه بالكلمات المرئية، مثل “Get tickets for Harbour Lights”."},{"text":"يحتاج الرابط إلى role=“button” ليمكن التحكّم فيه صوتيًا","why":"إنه ينقل المستخدم، فهو رابط عن حق. والدور لا علاقة له بالمطابقة الصوتية."}],"answer":1},{"q":"<p>أيّ عنصر يأتي اسمه المتاح من محتواه؟</p>","options":[{"text":"<code dir=\"ltr\">&lt;button&gt;Search&lt;/button&gt;</code>","why":"صحيح. الأزرار والروابط والعناوين والتبويبات وأدوار قليلة أخرى تأخذ اسمها من محتواها، بما في ذلك نص alt للصور التي بداخلها."},{"text":"<code dir=\"ltr\">&lt;input type=&quot;text&quot;&gt;</code> وبجانبه كلمة Search مكتوبة في span","why":"حقول النص لا تأخذ اسمها أبدًا من النص المجاور. إنها تحتاج إلى label أو aria-labelledby أو aria-label."},{"text":"<code dir=\"ltr\">&lt;div aria-label=&quot;Search area&quot;&gt;…&lt;/div&gt;</code>","why":"للـ div العادي دور عامّ (generic)، وARIA تمنع تسمية العناصر العامة. وقارئات الشاشة تتجاهل هذه التسمية عادةً."},{"text":"<code dir=\"ltr\">&lt;nav&gt;</code> يحتوي رابط بحث","why":"الـ landmarks لا تأخذ اسمها من محتواها؛ وإلا لقُرئت القائمة كلها كاسم. سمِّ الـ nav بـ aria-label أو aria-labelledby."}],"answer":0}],"exercise":{"kind":"web","mode":"dom","starter":{"html":"<div class=\"banner\">\n  <p>Early-bird prices end Friday.</p>\n  <button type=\"button\" class=\"close\">×</button>\n</div>\n\n<header class=\"bar\">\n  <strong>Ticketly</strong>\n  <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>\n</header>\n\n<div class=\"cards\">\n  <article class=\"card\">\n    <h3>Friday pass</h3>\n    <button type=\"button\" class=\"book\">Book now</button>\n  </article>\n  <article class=\"card\">\n    <h3>Saturday pass</h3>\n    <button type=\"button\" class=\"book\">Book now</button>\n  </article>\n  <article class=\"card\">\n    <h3>Weekend pass</h3>\n    <button type=\"button\" class=\"book\">Book now</button>\n  </article>\n</div>\n\n<footer>\n  <label for=\"news-email\">Email</label>\n  <input id=\"news-email\" type=\"email\" autocomplete=\"email\">\n  <button type=\"button\" class=\"subscribe\" aria-label=\"newsletter-cta\">Subscribe</button>\n</footer>","css":".banner { display: flex; justify-content: space-between; align-items: center; background: #fff4d6; padding: 4px 12px; border-radius: 8px; }\n.banner p { margin: 0; }\n.bar { display: flex; justify-content: space-between; align-items: center; margin-block: 12px; }\n.cards { display: grid; gap: 8px; }\n.card { border: 1px solid #c9c4dc; border-radius: 12px; padding: 8px 12px; }\n.card h3 { margin: 0 0 6px; font-size: 1rem; }\nbutton { font: inherit; min-block-size: 32px; min-inline-size: 32px; border-radius: 8px; border: 1px solid #5b3cc4; background: #fff; color: #5b3cc4; }\nfooter { margin-block-start: 12px; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }\n.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }\n","js":""},"solution":{"html":"<div class=\"banner\">\n  <p>Early-bird prices end Friday.</p>\n  <button type=\"button\" class=\"close\"><span aria-hidden=\"true\">×</span><span class=\"visually-hidden\">Close banner</span></button>\n</div>\n\n<header class=\"bar\">\n  <strong>Ticketly</strong>\n  <button type=\"button\" class=\"search\" aria-label=\"Search\"><svg aria-hidden=\"true\" 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>\n</header>\n\n<div class=\"cards\">\n  <article class=\"card\">\n    <h3 id=\"fri-title\">Friday pass</h3>\n    <button type=\"button\" class=\"book\" id=\"fri-book\" aria-labelledby=\"fri-book fri-title\">Book now</button>\n  </article>\n  <article class=\"card\">\n    <h3 id=\"sat-title\">Saturday pass</h3>\n    <button type=\"button\" class=\"book\" id=\"sat-book\" aria-labelledby=\"sat-book sat-title\">Book now</button>\n  </article>\n  <article class=\"card\">\n    <h3 id=\"wkd-title\">Weekend pass</h3>\n    <button type=\"button\" class=\"book\" id=\"wkd-book\" aria-labelledby=\"wkd-book wkd-title\">Book now</button>\n  </article>\n</div>\n\n<footer>\n  <label for=\"news-email\">Email</label>\n  <input id=\"news-email\" type=\"email\" autocomplete=\"email\">\n  <button type=\"button\" class=\"subscribe\">Subscribe</button>\n</footer>","css":".banner { display: flex; justify-content: space-between; align-items: center; background: #fff4d6; padding: 4px 12px; border-radius: 8px; }\n.banner p { margin: 0; }\n.bar { display: flex; justify-content: space-between; align-items: center; margin-block: 12px; }\n.cards { display: grid; gap: 8px; }\n.card { border: 1px solid #c9c4dc; border-radius: 12px; padding: 8px 12px; }\n.card h3 { margin: 0 0 6px; font-size: 1rem; }\nbutton { font: inherit; min-block-size: 32px; min-inline-size: 32px; border-radius: 8px; border: 1px solid #5b3cc4; background: #fff; color: #5b3cc4; }\nfooter { margin-block-start: 12px; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }\n.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }\n","js":""},"checks":[{"id":"close-named","test":"const textOf = (node) => { let out = ''; for (const n of node.childNodes) { if (n.nodeType === 3) out += n.textContent; else if (n.nodeType === 1) { if (n.getAttribute('aria-hidden') === 'true') continue; const cs = getComputedStyle(n); if (cs.display === 'none' || cs.visibility === 'hidden') continue; const tag = n.tagName.toLowerCase(); if (tag === 'img') out += ' ' + (n.getAttribute('alt') || '') + ' '; else if (tag === 'svg') { const t = n.querySelector('title'); out += ' ' + (n.getAttribute('aria-label') || (t ? t.textContent : '')) + ' '; } else out += ' ' + (n.getAttribute('aria-label') || textOf(n)) + ' '; } } return out.replace(/\\s+/g, ' ').trim(); }; const nameOf = (el) => { const lb = (el.getAttribute('aria-labelledby') || '').trim(); if (lb) { const t = lb.split(/\\s+/).map((id) => { const r = document.getElementById(id); return r ? (r.getAttribute('aria-label') || textOf(r)) : ''; }).join(' ').replace(/\\s+/g, ' ').trim(); if (t) return t; } const al = (el.getAttribute('aria-label') || '').trim(); if (al) return al; const c = textOf(el); return c || (el.getAttribute('title') || '').trim(); }; const b = $('.close'); return !!b && /close|dismiss/i.test(nameOf(b)) && !/×/.test(nameOf(b));"},{"id":"search-named","test":"const textOf = (node) => { let out = ''; for (const n of node.childNodes) { if (n.nodeType === 3) out += n.textContent; else if (n.nodeType === 1) { if (n.getAttribute('aria-hidden') === 'true') continue; const cs = getComputedStyle(n); if (cs.display === 'none' || cs.visibility === 'hidden') continue; const tag = n.tagName.toLowerCase(); if (tag === 'img') out += ' ' + (n.getAttribute('alt') || '') + ' '; else if (tag === 'svg') { const t = n.querySelector('title'); out += ' ' + (n.getAttribute('aria-label') || (t ? t.textContent : '')) + ' '; } else out += ' ' + (n.getAttribute('aria-label') || textOf(n)) + ' '; } } return out.replace(/\\s+/g, ' ').trim(); }; const nameOf = (el) => { const lb = (el.getAttribute('aria-labelledby') || '').trim(); if (lb) { const t = lb.split(/\\s+/).map((id) => { const r = document.getElementById(id); return r ? (r.getAttribute('aria-label') || textOf(r)) : ''; }).join(' ').replace(/\\s+/g, ' ').trim(); if (t) return t; } const al = (el.getAttribute('aria-label') || '').trim(); if (al) return al; const c = textOf(el); return c || (el.getAttribute('title') || '').trim(); }; const b = $('.search'); return !!b && /search/i.test(nameOf(b));"},{"id":"book-unique","test":"const textOf = (node) => { let out = ''; for (const n of node.childNodes) { if (n.nodeType === 3) out += n.textContent; else if (n.nodeType === 1) { if (n.getAttribute('aria-hidden') === 'true') continue; const cs = getComputedStyle(n); if (cs.display === 'none' || cs.visibility === 'hidden') continue; const tag = n.tagName.toLowerCase(); if (tag === 'img') out += ' ' + (n.getAttribute('alt') || '') + ' '; else if (tag === 'svg') { const t = n.querySelector('title'); out += ' ' + (n.getAttribute('aria-label') || (t ? t.textContent : '')) + ' '; } else out += ' ' + (n.getAttribute('aria-label') || textOf(n)) + ' '; } } return out.replace(/\\s+/g, ' ').trim(); }; const nameOf = (el) => { const lb = (el.getAttribute('aria-labelledby') || '').trim(); if (lb) { const t = lb.split(/\\s+/).map((id) => { const r = document.getElementById(id); return r ? (r.getAttribute('aria-label') || textOf(r)) : ''; }).join(' ').replace(/\\s+/g, ' ').trim(); if (t) return t; } const al = (el.getAttribute('aria-label') || '').trim(); if (al) return al; const c = textOf(el); return c || (el.getAttribute('title') || '').trim(); }; const bs = $$('.book'); if (bs.length !== 3) return false; const names = bs.map(nameOf); const passes = ['Friday', 'Saturday', 'Weekend']; return new Set(names).size === 3 && bs.every((b, i) => names[i].includes(passes[i]));"},{"id":"label-in-name","test":"const textOf = (node) => { let out = ''; for (const n of node.childNodes) { if (n.nodeType === 3) out += n.textContent; else if (n.nodeType === 1) { if (n.getAttribute('aria-hidden') === 'true') continue; const cs = getComputedStyle(n); if (cs.display === 'none' || cs.visibility === 'hidden') continue; const tag = n.tagName.toLowerCase(); if (tag === 'img') out += ' ' + (n.getAttribute('alt') || '') + ' '; else if (tag === 'svg') { const t = n.querySelector('title'); out += ' ' + (n.getAttribute('aria-label') || (t ? t.textContent : '')) + ' '; } else out += ' ' + (n.getAttribute('aria-label') || textOf(n)) + ' '; } } return out.replace(/\\s+/g, ' ').trim(); }; const nameOf = (el) => { const lb = (el.getAttribute('aria-labelledby') || '').trim(); if (lb) { const t = lb.split(/\\s+/).map((id) => { const r = document.getElementById(id); return r ? (r.getAttribute('aria-label') || textOf(r)) : ''; }).join(' ').replace(/\\s+/g, ' ').trim(); if (t) return t; } const al = (el.getAttribute('aria-label') || '').trim(); if (al) return al; const c = textOf(el); return c || (el.getAttribute('title') || '').trim(); }; const bs = [...$$('.book'), $('.subscribe')]; return bs.every((b) => { const visible = b.innerText.replace(/\\s+/g, ' ').trim().toLowerCase(); const name = nameOf(b).toLowerCase(); return name.includes(visible); }) && $$('.book').every((b) => /^book now/i.test(nameOf(b)));"},{"id":"subscribe","test":"const textOf = (node) => { let out = ''; for (const n of node.childNodes) { if (n.nodeType === 3) out += n.textContent; else if (n.nodeType === 1) { if (n.getAttribute('aria-hidden') === 'true') continue; const cs = getComputedStyle(n); if (cs.display === 'none' || cs.visibility === 'hidden') continue; const tag = n.tagName.toLowerCase(); if (tag === 'img') out += ' ' + (n.getAttribute('alt') || '') + ' '; else if (tag === 'svg') { const t = n.querySelector('title'); out += ' ' + (n.getAttribute('aria-label') || (t ? t.textContent : '')) + ' '; } else out += ' ' + (n.getAttribute('aria-label') || textOf(n)) + ' '; } } return out.replace(/\\s+/g, ' ').trim(); }; const nameOf = (el) => { const lb = (el.getAttribute('aria-labelledby') || '').trim(); if (lb) { const t = lb.split(/\\s+/).map((id) => { const r = document.getElementById(id); return r ? (r.getAttribute('aria-label') || textOf(r)) : ''; }).join(' ').replace(/\\s+/g, ' ').trim(); if (t) return t; } const al = (el.getAttribute('aria-label') || '').trim(); if (al) return al; const c = textOf(el); return c || (el.getAttribute('title') || '').trim(); }; const b = $('.subscribe'); return !!b && /subscribe/i.test(nameOf(b)) && !/cta/i.test(nameOf(b));"}],"title":"سمِّ كل زر في صفحة الفعالية","prompt":"<p>يعرض قارئ الشاشة أزرار Ticketly هكذا: “times, button, Book now, Book now, Book now, newsletter-cta”. أصلح الأسماء دون تغيير ما يراه المبصرون:</p>\n<ol>\n<li>زر الإغلاق <code dir=\"ltr\">.close</code> ‏&quot;×&quot; يحتاج إلى اسم يحتوي “Close” (أو “Dismiss”)؛ ولا ينبغي أن يُقرأ الرمز “×” نفسه.</li>\n<li>زر البحث <code dir=\"ltr\">.search</code> الذي لا يحتوي إلا أيقونة يحتاج إلى الاسم “Search”.</li>\n<li>كل زر <code dir=\"ltr\">.book</code> يحتاج إلى اسم فريد <strong>يبدأ بـ “Book now”</strong> ويتضمّن تذكرته (Friday أو Saturday أو Weekend).</li>\n<li>اسم زر <code dir=\"ltr\">.subscribe</code> يجب أن يحتوي كلمته المرئية “Subscribe”.</li>\n</ol>\n","hints":["أخفِ الرمز × بـ <span aria-hidden=\"true\">، ثم أضِف نصًا مخفيًا بصريًا أو aria-label على الزر.","أعطِ كل عنوان وكل زر Book now ‏id، ثم ضع aria-labelledby=“button-id heading-id” على الزر.","لزر Subscribe، أبسط إصلاح هو حذف الـ aria-label ليصبح محتوى الزر اسمه."],"explanation":"<p>صار اسم زر الإغلاق يأتي من نص مخفي بصريًا (أو من aria-label)، والرمز × مخفي فلا يُقرأ “times”. ويحصل زر البحث على اسمه من aria-label لأنه بلا محتوى نصي، وأيقونة الـ SVG الزخرفية فيه مخفية. ويستخدم كل زر Book now خاصية aria-labelledby ليشير إلى نفسه وإلى عنوان بطاقته، فينتج “Book now Friday pass”: اسم فريد في قائمة الأزرار، وما زال يبدأ بالكلمات المرئية، فيستطيع مستخدمو التحكّم الصوتي قول “click Book now” (2.5.3 Label in Name). وكان زر Subscribe الأسهل: حذف aria-label خاطئة سمح للنص المرئي بأن يعود ليكون الاسم.</p>\n","checkLabels":{"close-named":"زر الإغلاق اسمه Close (أو Dismiss) والرمز × لا يُقرأ","search-named":"زر البحث اسمه Search","book-unique":"لأزرار Book now الثلاثة أسماء فريدة تتضمّن تذكرة كلٍّ منها","label-in-name":"كل اسم يحتوي نصه المرئي، وأسماء Book now تبدأ به","subscribe":"اسم زر Subscribe يحتوي “Subscribe”"}},"words":615,"readMinutes":4}