{"id":"l-a11y-2-1","courseId":"web-accessibility","lang":"ar","kind":"lesson","summary":"حوّل صفحة من عناصر div مجهولة الهوية إلى landmarks ومخطّط عناوين منطقي يستطيع مستخدمو قارئ الشاشة مسحه والقفز خلاله في ثوانٍ.","html":"<p>وصف لي سمير ذات مرة صفحة سيئة البناء بأنها «دليل هاتف مُزّقت منه الأسماء». كل شيء موجود، لكن لا شيء يخبره أين ينتهي جزء ويبدأ التالي. والصفحة الرئيسية في Ticketly هي ذلك بالضبط: كل كتلة فيها <code dir=\"ltr\">div</code>، وأكبر نص فيها <code dir=\"ltr\">div</code> بخط كبير. يرى المستخدم المبصر ترويسة وقائمة وعنوان المهرجان وقائمة الفنانين وتذييلًا. أما قارئ الشاشة فيرى سيلًا واحدًا طويلًا من النص.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">الـ Landmarks: مناطق الصفحة</h2>\n<p>في HTML عناصر تصف دور منطقة كاملة من الصفحة. يربط المتصفّح كلًّا منها بـ <strong>landmark</strong> (معلم) في ARIA، وتتيح قارئات الشاشة للمستخدمين عرض قائمة بالـ landmarks أو القفز بينها (مفتاح D في NVDA، والـ rotor في VoiceOver).</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"جدول قابل للتمرير\" tabindex=\"0\"><table><thead>\n<tr>\n<th>العنصر</th>\n<th>دور الـ landmark</th>\n<th>ملاحظات</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code dir=\"ltr\">&lt;header&gt;</code></td>\n<td>banner</td>\n<td>فقط حين يكون جزءًا مباشرًا من الصفحة، لا داخل article أو aside أو main أو nav أو section</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">&lt;nav&gt;</code></td>\n<td>navigation</td>\n<td>سمِّه حين يوجد أكثر من واحد</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">&lt;main&gt;</code></td>\n<td>main</td>\n<td>واحد مرئي بالضبط في كل صفحة</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">&lt;aside&gt;</code></td>\n<td>complementary</td>\n<td>محتوى ذو صلة مثل الشريط الجانبي</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">&lt;footer&gt;</code></td>\n<td>contentinfo</td>\n<td>قاعدة النطاق نفسها التي تنطبق على header</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">&lt;form&gt;</code>، <code dir=\"ltr\">&lt;section&gt;</code></td>\n<td>form، region</td>\n<td>فقط حين يكون لها اسم متاح</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">&lt;search&gt;</code></td>\n<td>search</td>\n<td>يلفّ نموذج البحث في الموقع</td>\n</tr>\n</tbody>\n</table></div><p>هذا هيكل Ticketly قبل الإصلاح وبعده:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">before.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\">div</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\">\"top\"</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\">div</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\">\"logo\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Ticketly&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">div</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\">div</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\">\"links\"</span><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\">\"/events\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Events&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><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\">\"/faq\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>FAQ&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">div</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\">div</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\">div</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\">\"content\"</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\">div</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\">\"title\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Harbour Lights 2026&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">div</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><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\">div</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\">div</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\">\"bottom\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>© 2026 Ticketly · &#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\">\"/terms\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Terms&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">div</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">after.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\">header</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\">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\">\"/\"</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\">\"logo\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Ticketly&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</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\">nav</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\">\"Site\"</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\">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\">\"/events\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Events&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><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\">\"/faq\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>FAQ&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</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\">nav</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\">header</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\">main</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\">h1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Harbour Lights 2026&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><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\">main</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\">footer</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>© 2026 Ticketly · &#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\">\"/terms\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Terms&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">footer</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<p>لا يتغيّر التصميم المرئي إطلاقًا؛ فهذه العناصر ليس لها تنسيق خاص سوى <code dir=\"ltr\">display: block</code>. الذي يتغيّر هو شجرة إمكانية الوصول: يستطيع سمير الآن أن يضغط D ليصل إلى “main landmark” ويبدأ قراءة محتوى المهرجان، متخطّيًا الترويسة بالكامل.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 300\" role=\"img\" aria-labelledby=\"l-a11y-2-1-t1\"><defs><marker id=\"arrow-l-a11y-2-1-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-2-1-t1\">الصفحة مقسّمة إلى banner يحتوي تنقّل الموقع، ومنطقة main تضمّ العنوان h1 ‏Harbour Lights 2026 مع قسمين h2 هما Line-up وTickets، ويحتوي Tickets عنصرين h3، ثم تذييل contentinfo.</title>\n  <rect class=\"d-box-accent\" x=\"20\" y=\"15\" width=\"300\" height=\"50\" rx=\"8\"/>\n  <text class=\"d-label\" x=\"35\" y=\"45\">header (banner)</text>\n  <rect class=\"d-box\" x=\"200\" y=\"23\" width=\"110\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-label-muted\" x=\"255\" y=\"45\" text-anchor=\"middle\">nav \"Site\"</text>\n  <rect class=\"d-box-primary\" x=\"20\" y=\"80\" width=\"300\" height=\"150\" rx=\"8\"/>\n  <text class=\"d-label\" x=\"35\" y=\"105\">main</text>\n  <rect class=\"d-box-accent\" x=\"20\" y=\"245\" width=\"300\" height=\"40\" rx=\"8\"/>\n  <text class=\"d-label\" x=\"35\" y=\"270\">footer (contentinfo)</text>\n  <text class=\"d-code\" x=\"380\" y=\"105\">h1 Harbour Lights 2026</text>\n  <text class=\"d-code\" x=\"410\" y=\"140\">h2 Line-up</text>\n  <text class=\"d-code\" x=\"410\" y=\"175\">h2 Tickets</text>\n  <text class=\"d-code\" x=\"440\" y=\"205\">h3 Friday pass</text>\n  <text class=\"d-code\" x=\"440\" y=\"232\">h3 Weekend pass</text>\n  <path class=\"d-arrow\" d=\"M320 110 L370 105\" marker-end=\"url(#arrow-l-a11y-2-1-1)\"/>\n  <path class=\"d-line\" d=\"M395 112 L395 175 L405 175\"/>\n  <path class=\"d-line\" d=\"M395 140 L405 140\"/>\n  <path class=\"d-line\" d=\"M425 182 L425 232 L435 232\"/>\n  <path class=\"d-line\" d=\"M425 205 L435 205\"/>\n</svg><figcaption>الـ landmarks ومخطّط العناوين في Ticketly</figcaption></figure>\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>لفّ كل شيء بأدوار ARIA</span></p>\n<p>قد ترى <code dir=\"ltr\">&lt;div role=&quot;main&quot;&gt;</code> أو <code dir=\"ltr\">&lt;nav role=&quot;navigation&quot;&gt;</code> في كود قديم. الأول يعمل لكنه طريقة أطول لكتابة <code dir=\"ltr\">&lt;main&gt;</code>. والثاني حشو مكرّر. استخدم عنصر HTML؛ ولا تلجأ إلى الـ role إلا حين لا تستطيع فعلًا تغيير العنصر.</p>\n</aside>\n<h2 id=\"sec-2\" tabindex=\"-1\">تسمية المناطق، وكم منها تستخدم</h2>\n<p>عنصر <code dir=\"ltr\">&lt;section&gt;</code> وحده مجرد حاوية عامة. أعطِه اسمًا فيصبح landmark من نوع <strong>region</strong> يظهر في قائمة الـ landmarks. وأنظف طريقة هي أن تشير إلى عنوانه هو، فلا يمكن أن يفترق الاسم عن النص المرئي أبدًا:</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\">section</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\">\"tickets-heading\"</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\">h2</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\">\"tickets-heading\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Tickets&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><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\">section</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<p>لكن لا تحوّل كل section إلى region. الـ landmarks تعمل مثل قائمة فصول الكتاب: خمسة إلى ثمانية مداخل مفيدة، أما ثلاثون فلا. القاعدة العملية أن يعيش كل جزء من المحتوى داخل landmark ما (تنبّه أدوات الفحص الآلي إلى النص الذي لا يقع داخل أي landmark)، وألا تحصل على landmark خاص بها إلا المناطق الكبيرة المميّزة التي قد يرغب المستخدم في القفز إليها.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">العناوين: المخطّط الذي يتنقّل فيه الناس</h2>\n<p>الـ landmarks هي غرف الصفحة؛ والعناوين هي اللافتات على أبوابها. يجد معظم مستخدمي قارئ الشاشة المحتوى بالقفز من عنوان إلى عنوان (H في NVDA وJAWS، أو من 1 إلى 6 لمستوى محدّد)، أو بفتح قائمة بكل العناوين والكتابة لتصفيتها. وهذه القائمة لا تفيد إلا إذا شكّلت العناوين مخطّطًا حقيقيًا.</p>\n<p>ثلاث قواعد تغطّي الغالبية العظمى من الحالات:</p>\n<ol>\n<li><strong>عنوان <code dir=\"ltr\">h1</code> واحد يقول ما هي الصفحة.</strong> في Ticketly هو “Harbour Lights 2026”، لا الشعار.</li>\n<li><strong>المستويات تصف التداخل.</strong> الأقسام الفرعية لقسم ما تكون أعمق منه بمستوى واحد. الصعود مقبول (من h3 إلى h2)؛ أما تخطّي المستويات نزولًا (من h2 إلى h4) فيوحي بمستوى مفقود.</li>\n<li><strong>تحت كل عنوان محتوى</strong>، وكل جزء من المحتوى يستحق أن يُعثر عليه له عنوان.</li>\n</ol>\n<p>تستخدم منطقة التذاكر في Ticketly العنصرين <code dir=\"ltr\">h4</code> و<code dir=\"ltr\">h5</code> لأن مصمّمًا أراد نصًا أصغر. أصلح البنية أولًا، ثم نسّق:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"3\"><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\">h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Tickets&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</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\"> class</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"pass-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\">h3</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\">\"pass-title\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Weekend pass&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"css\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">CSS</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:#0550AE;--shiki-dark:#79C0FF\">.pass-title</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  font-size</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">rem</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  font-weight</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">600</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\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>افحص المخطّط في ثوانٍ</span></p>\n<p>شغّل قارئ شاشة واضغط H مرارًا، أو استخدم إضافة متصفّح تعرض قائمة بالعناوين. إن كانت القائمة تُقرأ كفهرس محتويات للصفحة، فقد انتهيت. وإن كانت تُقرأ “Ticketly, Menu, Newsletter, Follow us”، فالعناوين تصف الزخرفة لا المحتوى.</p>\n</aside>\n<p>ولا تنسَ عنصر <code dir=\"ltr\">&lt;title&gt;</code> للصفحة أيضًا. إنه أول ما يُعلنه قارئ الشاشة حين تُحمَّل الصفحة، وهو ما يظهر في التبويبات وسجلّ التصفّح (2.4.2 Page Titled). ضع الجزء المحدّد أولًا: <code dir=\"ltr\">&lt;title&gt;Harbour Lights 2026 tickets · Ticketly&lt;/title&gt;</code>.</p>\n<p>بعد أن صار الهيكل في مكانه، يتناول الدرس التالي الأجزاء التي ينقرها المستخدمون فعلًا: الأزرار والروابط، وعناصر الـ div القابلة للنقر التي تتظاهر بأنها هذا وذاك.</p>\n","toc":[{"id":"sec-1","text":"الـ Landmarks: مناطق الصفحة","level":2},{"id":"sec-2","text":"تسمية المناطق، وكم منها تستخدم","level":2},{"id":"sec-3","text":"العناوين: المخطّط الذي يتنقّل فيه الناس","level":2}],"takeaways":["العناصر header وnav وmain وaside وfooter تُنشئ landmarks يستطيع مستخدمو قارئ الشاشة عرض قائمة بها والقفز بينها.","للصفحة landmark واحد من نوع main؛ وحين توجد عدة مناطق nav أو aside، أعطِ كلًّا منها اسمًا متاحًا مميّزًا.","العناوين تشكّل مخطّط الصفحة؛ اختر المستوى بحسب البنية، لا بحسب حجم الخط، واضبط الحجم بـ CSS.","ابدأ بعنوان h1 واحد يسمّي الصفحة، ولا تتخطَّ أي مستوى وأنت تنزل في التسلسل."],"further":[{"title":"W3C WAI: Page Structure Tutorial","url":"https://www.w3.org/WAI/tutorials/page-structure/"},{"title":"MDN: The HTML section heading elements","url":"https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/Heading_Elements"},{"title":"W3C APG: Landmark Regions","url":"https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/"}],"quiz":[{"q":"<p>في Ticketly قائمتا تنقّل: قائمة الموقع في الأعلى وقائمة «أيام المهرجان» في الشريط الجانبي. يعرض قارئ الشاشة الـ landmarks هكذا: “navigation, navigation”. ما أفضل إصلاح؟</p>\n","options":[{"text":"أعطِ كل nav اسمًا متاحًا، مثلًا <code dir=\"ltr\">aria-label=&quot;Site&quot;</code> و<code dir=\"ltr\">aria-label=&quot;Festival days&quot;</code>","why":"صحيح. عندها تُعلن قارئات الشاشة “Site navigation” و&quot;Festival days navigation&quot;، فيستطيع المستخدمون التمييز بينهما."},{"text":"حوّل قائمة الشريط الجانبي إلى div حتى لا يبقى إلا navigation واحد","why":"هذا يحذف landmark مفيدًا بدلًا من تسميته. ويخسر المستخدمون طريقة سريعة للوصول إلى قائمة الأيام."},{"text":"أضف <code dir=\"ltr\">role=&quot;navigation&quot;</code> إلى عنصري nav كليهما","why":"لعنصر nav دور navigation أصلًا؛ وتكراره لا يغيّر شيئًا في الإعلان المكرّر."}],"answer":0},{"q":"<p>أرادت مصمّمة أن يبدو عنوان «تذكرة الجمعة» صغيرًا، فاستخدم المطوّر h5 مباشرة بعد h2. ما الخطأ، وما الإصلاح؟</p>","options":[{"text":"لا خطأ؛ أي مستوى عنوان مقبول ما دام النص وصفيًا","why":"النص الوصفي مهم، لكن المستويات تنقل التسلسل الهرمي. القفز من h2 إلى h5 يوحي بأقسام مفقودة ويربك التنقّل عبر المخطّط."},{"text":"استخدم فقرة بخط عريض بدلًا منه، لأن العناوين لا ينبغي أن تكون صغيرة","why":"الفقرة العريضة ليست عنوانًا أصلًا، فتختفي من التنقّل بين العناوين. ويمكن أن تكون العناوين بأي حجم مرئي."},{"text":"استخدم h3 للبنية واجعله صغيرًا بـ CSS","why":"صحيح. المستوى يصف موقع المحتوى في المخطّط؛ وحجم الخط قرار تنسيقي مكانه CSS."}],"answer":2},{"q":"<p>أيّ عنصر لا يُنشئ landmark إلا حين يكون له اسم متاح؟</p>","options":[{"text":"<code dir=\"ltr\">&lt;main&gt;</code>","why":"عنصر main هو دائمًا الـ landmark الرئيسي؛ ولا يحتاج إلى اسم."},{"text":"<code dir=\"ltr\">&lt;nav&gt;</code>","why":"عنصر nav هو دائمًا landmark من نوع navigation. والاسم يساعد فقط على التمييز بين عدة عناصر nav."},{"text":"<code dir=\"ltr\">&lt;footer&gt;</code> مباشرة داخل body","why":"الـ footer غير المتداخل داخل article أو aside أو main أو nav أو section هو دائمًا landmark من نوع contentinfo."},{"text":"<code dir=\"ltr\">&lt;section&gt;</code>","why":"صحيح. لا يصبح section منطقةً من نوع region إلا حين يُسمّى، مثلًا بـ aria-labelledby تشير إلى عنوانه."}],"answer":3},{"q":"<p>في صفحة طويلة من Ticketly، ما أول ما يفعله مستخدم قارئ الشاشة في الغالب ليجد قسم «سياسة الاسترداد»؟</p>","options":[{"text":"يستمع إلى الصفحة كلها من الأعلى","why":"هذه أبطأ استراتيجية، وقليل من المستخدمين المتمرّسين يتّبعونها في الصفحات الطويلة."},{"text":"يضغط Tab مرارًا حتى يصل إليه","why":"مفتاح Tab يزور العناصر التفاعلية فقط مثل الروابط والحقول، فيتخطّى العناوين والفقرات تمامًا."},{"text":"يقفز بين العناوين، مثلًا بمفتاح H في NVDA","why":"صحيح. في استطلاع WebAIM لعام 2026، يتنقّل 67.8% من مستخدمي قارئ الشاشة في الصفحات الطويلة عبر العناوين أولًا."}],"answer":2}],"exercise":{"kind":"web","mode":"dom","starter":{"html":"<div class=\"top\">\n  <div class=\"logo\">Ticketly</div>\n  <div class=\"links\">\n    <a href=\"#events\">Events</a>\n    <a href=\"#faq\">FAQ</a>\n    <a href=\"#account\">Account</a>\n  </div>\n</div>\n<div class=\"content\">\n  <div class=\"title\">Harbour Lights 2026</div>\n  <p>Two nights of music on the old harbour wall, 12 and 13 June.</p>\n  <h4>Line-up</h4>\n  <ul>\n    <li>Nour Quartet</li>\n    <li>The Saltmarsh Band</li>\n  </ul>\n  <h4>Tickets</h4>\n  <h5>Friday pass</h5>\n  <p>SAR 180, standing.</p>\n  <h5>Weekend pass</h5>\n  <p>SAR 320, both nights.</p>\n</div>\n<div class=\"bottom\">© 2026 Ticketly · <a href=\"#terms\">Terms</a></div>","css":".top { display: flex; justify-content: space-between; align-items: center; padding-block-end: 8px; border-block-end: 1px solid #ddd; }\n.logo { font-weight: 700; }\n.links { display: flex; gap: 12px; }\n.title { font-size: 1.75rem; font-weight: 700; margin: 16px 0 8px; }\nh2, h4 { font-size: 1.2rem; margin: 16px 0 4px; }\nh3, h5 { font-size: 1rem; margin: 8px 0 2px; }\n.bottom { margin-block-start: 24px; font-size: 0.875rem; color: #555; }\n","js":""},"solution":{"html":"<header class=\"top\">\n  <div class=\"logo\">Ticketly</div>\n  <nav class=\"links\" aria-label=\"Site\">\n    <a href=\"#events\">Events</a>\n    <a href=\"#faq\">FAQ</a>\n    <a href=\"#account\">Account</a>\n  </nav>\n</header>\n<main class=\"content\">\n  <h1 class=\"title\">Harbour Lights 2026</h1>\n  <p>Two nights of music on the old harbour wall, 12 and 13 June.</p>\n  <h2>Line-up</h2>\n  <ul>\n    <li>Nour Quartet</li>\n    <li>The Saltmarsh Band</li>\n  </ul>\n  <h2>Tickets</h2>\n  <h3>Friday pass</h3>\n  <p>SAR 180, standing.</p>\n  <h3>Weekend pass</h3>\n  <p>SAR 320, both nights.</p>\n</main>\n<footer class=\"bottom\">© 2026 Ticketly · <a href=\"#terms\">Terms</a></footer>","css":".top { display: flex; justify-content: space-between; align-items: center; padding-block-end: 8px; border-block-end: 1px solid #ddd; }\n.logo { font-weight: 700; }\n.links { display: flex; gap: 12px; }\n.title { font-size: 1.75rem; font-weight: 700; margin: 16px 0 8px; }\nh2, h4 { font-size: 1.2rem; margin: 16px 0 4px; }\nh3, h5 { font-size: 1rem; margin: 8px 0 2px; }\n.bottom { margin-block-start: 24px; font-size: 0.875rem; color: #555; }\n","js":""},"checks":[{"id":"one-main","test":"const m = $$('main, [role=\"main\"]'); return m.length === 1 && m[0].textContent.includes('Harbour Lights 2026') && m[0].textContent.includes('Weekend pass');"},{"id":"banner-nav","test":"const h = $('header'); if (!h || h.closest('main, article, aside, nav, section')) return false; const nav = $('nav'); return !!nav && h.contains(nav) && nav.textContent.includes('Events') && nav.textContent.includes('Account');"},{"id":"footer","test":"const f = $('footer'); return !!f && !f.closest('main, article, aside, nav, section') && f.textContent.includes('Terms');"},{"id":"one-h1","test":"const h1 = $$('h1'); return h1.length === 1 && h1[0].textContent.trim() === 'Harbour Lights 2026';"},{"id":"heading-order","test":"const hs = $$('h1, h2, h3, h4, h5, h6'); const names = hs.map((h) => h.textContent.trim()); if (!['Line-up', 'Tickets', 'Friday pass', 'Weekend pass'].every((n) => names.includes(n))) return false; const lv = hs.map((h) => Number(h.tagName[1])); if (lv[0] !== 1) return false; for (let i = 1; i < lv.length; i++) if (lv[i] - lv[i - 1] > 1) return false; const lvl = (n) => lv[names.indexOf(n)]; return lvl('Friday pass') === lvl('Tickets') + 1 && lvl('Line-up') === lvl('Tickets');"}],"title":"امنح Ticketly هيكلًا","prompt":"<p>الصفحة الرئيسية في Ticketly مبنية من عناصر div مجهولة الهوية، وعناوينها اختيرت بحسب حجم خطّها. أعد هيكلة الكود دون تغيير النص:</p>\n<ul>\n<li>لُفّ الشعار والقائمة بعنصر <code dir=\"ltr\">&lt;header&gt;</code>، واجعل القائمة عنصر <code dir=\"ltr\">&lt;nav&gt;</code> (أعطِها اسمًا مثل <code dir=\"ltr\">aria-label=&quot;Site&quot;</code>).</li>\n<li>ضع محتوى المهرجان في عنصر <code dir=\"ltr\">&lt;main&gt;</code> واحد.</li>\n<li>اجعل سطر حقوق النشر عنصر <code dir=\"ltr\">&lt;footer&gt;</code> خارج <code dir=\"ltr\">main</code>.</li>\n<li>حوّل “Harbour Lights 2026” إلى عنصر <code dir=\"ltr\">&lt;h1&gt;</code> الوحيد، وأصلح بقية العناوين حتى لا يتخطّى المخطّط أي مستوى: Line-up وTickets قسمان؛ والتذكرتان تنتميان إلى Tickets.</li>\n</ul>\n","hints":["يمكنك الإبقاء على أسماء الكلاسات الحالية على العناصر الجديدة، فيظلّ الـ CSS مطبّقًا.","يقع Line-up وTickets مباشرة تحت h1، فهما h2. والتذكرتان تقعان تحت Tickets.","header &gt; nav، ثم main &gt; h1، h2، h2، h3، h3، ثم footer."],"explanation":"<p>عناصر الـ landmarks لا تكلّف شيئًا بصريًا لكنها تمنح مستخدمي قارئ الشاشة خريطة: header ‏(banner)، وnav ‏(navigation)، وmain، وfooter ‏(contentinfo). صارت مستويات العناوين تصف التداخل لا الحجم: h1 واحد للصفحة، وh2 لقسميها، وh3 للتذكرتين داخل Tickets. ويُبقي الـ CSS التذكرتين صغيرتين، لأن الحجم قرار تنسيقي. والتنقّل عبر العناوين صار يُقرأ كفهرس محتويات: Harbour Lights 2026، Line-up، Tickets، Friday pass، Weekend pass.</p>\n","checkLabels":{"one-main":"يوجد landmark واحد بالضبط من نوع main يضمّ محتوى المهرجان","banner-nav":"عنصر header في المستوى الأعلى يحتوي nav فيه روابط القائمة","footer":"عنصر footer في المستوى الأعلى يحتوي رابط Terms","one-h1":"“Harbour Lights 2026” هو عنصر h1 الوحيد في الصفحة","heading-order":"العناوين تبدأ من h1 ولا تتخطّى أي مستوى؛ والتذكرتان أعمق من Tickets بمستوى واحد"}},"words":624,"readMinutes":4}