إتاحة الويب تعني أن يتمكن أشخاص يستخدمون لوحة المفاتيح أو قارئ الشاشة أو التكبير أو وسائل إدخال مختلفة من إكمال المهمة نفسها. وجود نسخة عربية يضيف اختبارات خاصة باللغة واتجاه النص، لكنه لا يغير الأساس: بنية HTML واضحة، عناصر تحكم قابلة للتشغيل، وحالات يمكن فهمها دون الاعتماد على اللون وحده.
هذا المقال يستخدم WCAG 2.2 مستوى AA كمرجع. اجتياز قائمة قصيرة أو أداة آلية لا يثبت المطابقة الكاملة. الاختبار اليدوي جزء أساسي من المراجعة.
أعلن اللغة والاتجاه
ضع لغة الصفحة واتجاهها على عنصر <html>:
<html lang="ar" dir="rtl"></html>
يساعد lang="ar" التقنيات المساعدة على اختيار النطق المناسب. أما dir="rtl" فيحدد اتجاه التخطيط الأساسي وعلامات الترقيم والنص ثنائي الاتجاه.
إذا احتوت الصفحة على فقرة إنجليزية، عرّف لغتها محلياً:
<p lang="en" dir="ltr">Your account is ready.</p>
للمحتوى الذي يكتبه المستخدم وقد يكون عربياً أو إنجليزياً، استخدم dir="auto". يعين المتصفح الاتجاه من أول حرف قوي:
<p class="comment" dir="auto">{comment}</p>
يمكن لعنصر <bdi> عزل اسم أو قيمة قصيرة داخل جملة مختلفة الاتجاه:
<p>كتب <bdi>{username}</bdi> تعليقاً جديداً.</p>
لا تستخدم direction في CSS بديلاً عن سمة dir على المستند. توصي W3C باستخدام dir للاتجاه الأساسي، مع خصائص CSS المنطقية للمسافات والمحاذاة.
استخدم HTML الدلالي قبل ARIA
العنصر الأصلي يقدم اسماً ودوراً وسلوك لوحة مفاتيح جاهزاً. استخدم <button> للعمل الذي يحدث داخل الصفحة، و<a href> للانتقال. لا تحول <div> إلى زر إلا إذا كان هناك سبب لا يمكن حله بعنصر أصلي.
<header>...</header>
<nav aria-label="التنقل الرئيسي">...</nav>
<main id="main-content">...</main>
<footer>...</footer>
رتب العناوين بحسب بنية المحتوى. وجود عنوان رئيسي واضح ثم أقسام بعناوين من المستوى الثاني أسهل للتنقل من عناوين اختير مستواها بسبب حجم الخط فقط.
أضف رابط تخط إلى المحتوى في بداية الصفحة، واجعله ظاهراً عند التركيز:
<a class="skip-link" href="#main-content">تخط إلى المحتوى الرئيسي</a>
اختبر بلوحة المفاتيح
استخدم Tab وShift + Tab وEnter وSpace والأسهم حيث ينطبق نمط المكون. يجب أن يكون ترتيب التركيز قريباً من ترتيب القراءة، وأن يظهر مؤشر التركيز بوضوح، وألا تحجبه نافذة ثابتة أو شريط علوي.
لا تحذف outline إلا إذا قدمت بديلاً أوضح:
:focus-visible {
outline: 3px solid #22c55e;
outline-offset: 3px;
}
في نافذة حوار، انقل التركيز إلى داخلها، امنع وصوله إلى الصفحة خلفها، وأعده إلى الزر الذي فتحها بعد الإغلاق. هذه التفاصيل تحتاج اختباراً حقيقياً، لأن الشكل البصري لا يكشفها.
راجع التباين دون الاعتماد على الانطباع
يتطلب معيار WCAG 2.2 AA نسبة تباين لا تقل عن 4.5:1 للنص العادي، و3:1 للنص الكبير. كما تحتاج حدود عناصر التحكم وحالات التركيز المهمة إلى تباين يمكن تمييزه.
الخط العربي قد يبدو أصغر أو أكثر كثافة من خط لاتيني بالحجم الرقمي نفسه. لا يوجد حد عام في WCAG يفرض 16 أو 18 بكسلاً لكل نص عربي، لذلك اختبر الخط الفعلي وارتفاع السطر والوزن على الهاتف وسطح المكتب. لا تعالج ضعف الوضوح بمجرد زيادة الوزن، لأن بعض الأوزان الثقيلة تغلق المساحات الداخلية للحروف.
تأكد أيضاً من بقاء المحتوى قابلاً للاستخدام عند تكبير النص وإعادة تدفق الصفحة. لا تثبت ارتفاع بطاقة تحتوي نصاً مترجماً، ولا تقص النص بـ overflow: hidden إذا كان ضرورياً للمهمة.
اكتب نصوصاً بديلة حسب وظيفة الصورة
الصورة المعلوماتية تحتاج وصفاً موجزاً لما تضيفه في هذا السياق:
<img
src="chart.png"
alt="ارتفاع الطلبات المكتملة من 120 إلى 175 خلال ثلاثة أشهر"
/>
الصورة الزخرفية تحتاج قيمة فارغة، لا وصفاً عاماً:
<img src="divider.svg" alt="" />
غياب سمة alt ليس مساوياً لـ alt="". عند غياب السمة قد يعلن قارئ الشاشة اسم الملف. أما الصورة الموجودة داخل رابط أو زر فتحتاج بديلاً يصف وظيفة الرابط، إلا إذا كان هناك نص ظاهر يؤدي الغرض نفسه.
لا تبدأ كل وصف بكلمة “صورة”؛ قارئ الشاشة يعلن نوع العنصر. وإذا احتاج الرسم البياني إلى شرح طويل، ضع البيانات أو الملخص في النص القريب بدلاً من حشره في alt.
اجعل النماذج مفهومة قبل الخطأ وبعده
كل حقل يحتاج <label> مرتبطاً به. placeholder مثال مؤقت وليس اسماً للحقل:
<form id="contact-form" novalidate>
<label for="email">البريد الإلكتروني</label>
<input
id="email"
name="email"
type="email"
required
autocomplete="email"
aria-describedby="email-help"
/>
<p id="email-help">سنستخدمه للرد على رسالتك.</p>
<p id="email-error" role="alert" hidden></p>
<button type="submit">إرسال</button>
</form>
في الحالة الأولى لا يعلن قارئ الشاشة رسالة خطأ، لأن الحقل لا يشير إليها وهي مخفية وفارغة. بعد محاولة إرسال قيمة غير صالحة، أظهر الرسالة وأضفها إلى الوصف واضبط aria-invalid="true". عندما يصحح المستخدم القيمة، أزل الحالات الثلاث معاً:
export function attachEmailValidation(form: HTMLFormElement) {
const email = form.elements.namedItem("email");
const error = form.querySelector("#email-error");
if (!(email instanceof HTMLInputElement)) {
throw new Error("The email input is missing");
}
if (!(error instanceof HTMLParagraphElement)) {
throw new Error("The email error element is missing");
}
let validationAttempted = false;
function clearEmailError() {
email.removeAttribute("aria-invalid");
email.setAttribute("aria-describedby", "email-help");
error.textContent = "";
error.hidden = true;
}
function showEmailError() {
const message = email.validity.valueMissing
? "أدخل بريدك الإلكتروني."
: "أدخل عنوان بريد بصيغة صحيحة.";
email.setAttribute("aria-invalid", "true");
email.setAttribute("aria-describedby", "email-help email-error");
error.hidden = false;
error.textContent = message;
}
function validateEmail() {
if (email.validity.valid) {
clearEmailError();
return true;
}
showEmailError();
return false;
}
form.addEventListener("submit", (event) => {
event.preventDefault();
validationAttempted = true;
if (!validateEmail()) {
email.focus();
return;
}
// صل هذا الفرع بطلب الإرسال الحقيقي في التطبيق.
});
email.addEventListener("input", () => {
if (validationAttempted) validateEmail();
});
}
const form = document.querySelector("#contact-form");
if (!(form instanceof HTMLFormElement)) {
throw new Error("The contact form is missing");
}
attachEmailValidation(form);
يمكن لاختبار DOM أن يثبت الوصف الذي سيحسبه المتصفح في الحالات الثلاث. يستخدم المثال التالي Jest في بيئة jsdom مع dom-accessibility-api:
import { fireEvent } from "@testing-library/dom";
import { computeAccessibleDescription } from "dom-accessibility-api";
import { attachEmailValidation } from "./emailValidation";
it("announces an error only after validation and clears it after correction", () => {
document.body.innerHTML = `
<form id="contact-form" novalidate>
<label for="email">البريد الإلكتروني</label>
<input id="email" name="email" type="email" required
aria-describedby="email-help">
<p id="email-help">سنستخدمه للرد على رسالتك.</p>
<p id="email-error" role="alert" hidden></p>
<button type="submit">إرسال</button>
</form>
`;
const form = document.querySelector("#contact-form");
const email = document.querySelector("#email");
const error = document.querySelector("#email-error");
if (!(form instanceof HTMLFormElement)) throw new Error("Missing form");
if (!(email instanceof HTMLInputElement)) throw new Error("Missing input");
if (!(error instanceof HTMLParagraphElement))
throw new Error("Missing error");
attachEmailValidation(form);
expect(email.hasAttribute("aria-invalid")).toBe(false);
expect(error.hidden).toBe(true);
expect(computeAccessibleDescription(email)).toBe("سنستخدمه للرد على رسالتك.");
fireEvent.submit(form);
expect(email.getAttribute("aria-invalid")).toBe("true");
expect(error.hidden).toBe(false);
expect(computeAccessibleDescription(email)).toContain(
"أدخل بريدك الإلكتروني.",
);
email.value = "reader@example.com";
fireEvent.input(email);
expect(email.hasAttribute("aria-invalid")).toBe(false);
expect(error.hidden).toBe(true);
expect(computeAccessibleDescription(email)).toBe("سنستخدمه للرد على رسالتك.");
});
لا تستخدم إطاراً أحمر وحده. اكتب الرسالة بالعربية المباشرة وحدد طريقة التصحيح.
إذا نجح الإرسال دون انتقال إلى صفحة جديدة، أعلن النتيجة في منطقة حالة مناسبة:
<p role="status" aria-live="polite">تم إرسال الرسالة.</p>
لا تجعل كل تحديث aria-live. كثرة الإعلانات تقاطع المستخدم وتخفي المهم.
راقب الأسماء المختلطة والأرقام
البريد الإلكتروني ورقم الهاتف وكود الطلب قد يظهران داخل جملة عربية. اعزل القيمة عند الحاجة، ولا تغير ترتيب أحرفها بصرياً باستخدام مسافات أو أحرف غير مرئية عشوائية:
<p>رقم الطلب: <bdi dir="ltr">QA-2048</bdi></p>
استخدم نص رابط يوضح الوجهة. عبارة “اضغط هنا” لا تكفي عندما يتنقل مستخدم قارئ الشاشة بين قائمة الروابط وحدها.
خطة اختبار قصيرة وقابلة للتكرار
- شغل الصفحة بالعربية ثم الإنجليزية، وتحقق من
langوdirلكل جزء. - أكمل كل مهمة باستخدام لوحة المفاتيح فقط.
- راجع التركيز عند فتح القوائم والحوارات وإغلاقها.
- اختبر قارئ شاشة واحداً على الأقل على النظام المستهدف، مثل VoiceOver على macOS أو iOS و NVDA على Windows.
- كبر النص والصفحة وتأكد من عدم اختفاء الإجراءات أو تداخلها.
- افحص التباين في الحالات العادية وحالات hover و focus و disabled والخطأ.
- استخدم فاحصاً آلياً لاكتشاف المشكلات السريعة، ثم أكمل المراجعة يدوياً.
- اختبر رسائل الخطأ والتحميل والفراغ والنجاح، لا الحالة المثالية فقط.
الإتاحة لا تضمن ترتيباً أعلى في نتائج البحث، ولا تصف Google وجود “عقوبة” عامة للمواقع غير المتاحة. توجد فوائد مشتركة بين الإتاحة وجودة الصفحة، مثل البنية الواضحة والنص البديل المفيد، لكن الهدف المباشر هو أن يستطيع المستخدم إكمال عمله.