جزء من سلسلة Compliance & Regulation
اقرأ الدليل الكاملإمكانية الوصول إلى الويب: دليل الامتثال WCAG 2.1 AA
إن إمكانية الوصول ليست ميزة تضيفها بعد الإطلاق، بل هي سمة جودة أساسية، مثل الأداء أو الأمان. أصبح الامتثال لـ WCAG 2.1 AA مطلوبًا قانونيًا الآن في الاتحاد الأوروبي (قانون إمكانية الوصول الأوروبي، الذي تم تطبيقه في يونيو 2025)، والولايات المتحدة (السوابق القضائية لـ ADA Title III)، والعديد من الولايات القضائية الأخرى. وبعيدًا عن الامتثال، تعمل الواجهات التي يمكن الوصول إليها على التحويل بشكل أفضل، وتحتل مرتبة أعلى في البحث، وتخدم ما يقدر بنحو 1.3 مليار شخص من ذوي الإعاقة في جميع أنحاء العالم.
هذا الدليل هو دليل تنفيذ عملي، وليس قائمة مرجعية. سوف تتعلم مبادئ WCAG الأربعة، والتقنيات الأكثر تأثيرًا، وكيفية الاختبار بشكل منهجي، وكيفية دمج إمكانية الوصول في سير عمل تطوير React/Next.js الخاص بك حتى تظل ثابتة.
الوجبات الرئيسية
- يتطلب WCAG 2.1 AA جميع مبادئ POUR الأربعة: قابلة للإدراك، وقابلة للتشغيل، وقابلة للفهم، وقوية
- ابدأ باستخدام HTML الدلالي - فهو يوفر إمكانية الوصول بنسبة 70% مجانًا قبل إضافة أي ARIA
- الحد الأدنى لنسبة تباين الألوان: 4.5:1 للنص العادي، 3:1 للنص الكبير (18pt/14pt غامق)
- يجب أن يكون كل عنصر تفاعلي قابلاً للتركيز على لوحة المفاتيح باستخدام مؤشر تركيز مرئي
- تعلن برامج قراءة الشاشة بناءً على شجرة إمكانية الوصول - اختبرها باستخدام NVDA (Windows) وVoiceOver (Mac)
- ARIA هو الملاذ الأخير - فهو يغير فقط كيفية تفسير التقنيات المساعدة لـ DOM، وليس السلوك
- التشغيل الآلي باستخدام النواة المحورية في خط أنابيب CI الخاص بك؛ يكتشف الاختبار اليدوي ما تفتقده الأتمتة
- قم بتوثيق بيان إمكانية الوصول الخاص بك وتوفير آلية تعليقات للمستخدمين للإبلاغ عن المشكلات
مبادئ POUR الأربعة
يتم تنظيم WCAG 2.1 حول أربعة مبادئ. كل معيار للنجاح ينتمي إلى واحد منهم.
يمكن إدراكها: يجب أن تكون المعلومات قابلة للعرض بطرق يمكن للمستخدمين إدراكها. يغطي هذا بدائل النص للصور، والتسميات التوضيحية للفيديو، وتباين الألوان الكافي، والمحتوى الذي لا يعتمد على اللون وحده لنقل المعنى.
قابلة للتشغيل: يجب أن تكون جميع الوظائف قابلة للتشغيل عبر لوحة المفاتيح، مع توفير الوقت الكافي للتفاعل، وعدم وجود محتوى يثير النوبات، وبنية قابلة للتنقل (تخطي الروابط، وعناوين الصفحات، وترتيب التركيز).
مفهوم: يجب أن يكون المحتوى قابلاً للقراءة ويمكن التنبؤ به. يجب تحديد اللغة، ويجب أن تكون رسائل الخطأ وصفية، ويجب أن تحتوي النماذج على تسميات واضحة وملاحظات للتحقق من الصحة.
قوي: يجب أن يكون المحتوى قابلاً للتفسير من خلال التقنيات المساعدة الحالية والمستقبلية. وهذا يعني HTML صالحًا واستخدام ARIA مناسبًا ورسائل الحالة التي يتم الإعلان عنها دون الحاجة إلى التركيز.
HTML الدلالي أولاً
HTML الدلالي هو الاستثمار الفردي الأعلى فعالية في إمكانية الوصول. تأتي عناصر HTML الأصلية مع أدوار وحالات وسلوك لوحة المفاتيح المضمنة لإمكانية الوصول - ولا يلزم وجود ARIA.
// BAD: Generic divs with no semantics
<div class="button" onclick="submit()">Submit</div>
<div class="nav">
<div class="link" onclick="navigate('/home')">Home</div>
</div>
// GOOD: Native semantics, free keyboard and screen reader support
<button type="submit" onClick={submit}>Submit</button>
<nav aria-label="Main navigation">
<a href="/home">Home</a>
</nav>
تساعد المناطق المميزة مستخدمي قارئ الشاشة على التنقل بسرعة من خلال التنقل بين الأقسام:
// Every page should have these landmarks
<header> {/* banner landmark */}
<nav aria-label="Main">...</nav>
</header>
<main> {/* main landmark */}
<h1>Page Title</h1>
<article>...</article>
<aside aria-label="Related content">...</aside>
</main>
<footer> {/* contentinfo landmark */}
<nav aria-label="Footer">...</nav>
</footer>
يجب أن يكون التسلسل الهرمي للعناوين منطقيًا وغير منقطع:
// BAD: Skipped heading levels
<h1>Page Title</h1>
<h3>Section</h3> {/* Skipped h2! */}
// GOOD: Sequential hierarchy
<h1>Page Title</h1>
<h2>Section</h2>
<h3>Subsection</h3>
تباين الألوان
يتطلب WCAG 2.1 AA ما يلي:
- 4.5:1 نسبة التباين للنص العادي (أقل من 18pt / 14pt غامق)
- 3:1 نسبة التباين للنص الكبير (18pt+ / 14pt+ غامق)
- 3:1 لمكونات واجهة المستخدم والكائنات الرسومية (الأزرار والأيقونات وحدود الإدخال)
// Tailwind color contrast examples
// FAIL: gray-400 on white (#9ca3af on #fff = 2.8:1)
<p className="text-gray-400">This fails AA</p>
// PASS: gray-700 on white (#374151 on #fff = 10.7:1)
<p className="text-gray-700">This passes AA</p>
// For dark mode, test both themes separately
<p className="text-gray-700 dark:text-gray-300">
gray-700 on white (10.7:1) / gray-300 on gray-900 (9.2:1)
</p>
استخدم WebAIM Contrast Checker أو أداة التباين DevTools الخاصة بالمتصفح أثناء التطوير. أضف هذا إلى Storybook أو نظام الرموز المميزة للتصميم الخاص بك لرصد الانحدارات:
// contrast-checker.ts
import { getContrast } from 'polished';
function assertContrast(fg: string, bg: string, level: 'AA' | 'AAA' = 'AA') {
const ratio = getContrast(fg, bg);
const required = level === 'AA' ? 4.5 : 7;
if (ratio < required) {
throw new Error(
`Contrast ratio ${ratio.toFixed(2)}:1 fails WCAG ${level} (requires ${required}:1)`
);
}
}
التنقل بلوحة المفاتيح
يجب أن يكون كل عنصر تفاعلي - الروابط والأزرار وحقول النماذج والأدوات المخصصة - قابلاً للوصول والتشغيل عبر لوحة المفاتيح.
إدارة التركيز
// Skip link: first element on every page
// Allows keyboard users to jump past navigation
export function SkipLink() {
return (
<a
href="#main-content"
className="sr-only focus:not-sr-only focus:fixed focus:top-4 focus:left-4
focus:z-50 focus:px-4 focus:py-2 focus:bg-blue-600 focus:text-white
focus:rounded focus:ring-2 focus:ring-white"
>
Skip to main content
</a>
);
}
// Main content target
<main id="main-content" tabIndex={-1}>
{/* tabIndex={-1} allows programmatic focus without appearing in tab order */}
تعويض التركيز في الوسائط
عند فتح مربع حوار، يجب أن يكون التركيز داخله. عند الإغلاق، يعود التركيز إلى المشغل:
// focus-trap.tsx using @radix-ui/react-focus-trap (used internally by shadcn Dialog)
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog';
import { useRef } from 'react';
export function AccessibleModal({ trigger, children, title }: Props) {
const triggerRef = useRef<HTMLButtonElement>(null);
return (
<Dialog>
<DialogTrigger ref={triggerRef} asChild>
<button>Open</button>
</DialogTrigger>
<DialogContent
// shadcn Dialog handles focus trap and returns focus to trigger on close
aria-describedby="dialog-description"
>
<DialogTitle>{title}</DialogTitle>
<p id="dialog-description" className="sr-only">
{/* Screen reader description of dialog purpose */}
</p>
{children}
</DialogContent>
</Dialog>
);
}
مؤشرات التركيز المرئية
يتطلب WCAG 2.1 SC 2.4.11 (AA في WCAG 2.2) حدًا أدنى للتركيز يبلغ 2 بكسل. لا تقم أبدًا بقمع التركيز بدون بديل:
/* globals.css */
/* NEVER do this: */
:focus { outline: none; }
/* DO this: custom, visible focus ring */
:focus-visible {
outline: 2px solid hsl(var(--ring));
outline-offset: 2px;
border-radius: 4px;
}
/* Remove for mouse users (only show for keyboard) */
:focus:not(:focus-visible) {
outline: none;
}
ARIA: متى وكيف يتم استخدامها
تعمل سمات ARIA (تطبيقات الإنترنت الغنية التي يمكن الوصول إليها) على تعديل كيفية تفسير التقنيات المساعدة لـ DOM. القاعدة الأولى لـ ARIA: لا تستخدمه في حالة وجود عنصر HTML أصلي لحالة الاستخدام الخاصة بك.
تسميات ARIA
// Icon-only button — screen reader has nothing to announce without aria-label
<button aria-label="Close dialog">
<X className="h-4 w-4" aria-hidden="true" />
</button>
// Form field with visible label — use htmlFor, not aria-label
<label htmlFor="email">Email address</label>
<input id="email" type="email" />
// Input with visible description
<input
id="password"
type="password"
aria-describedby="password-requirements"
/>
<p id="password-requirements">Must be at least 12 characters.</p>
مناطق ARIA الحية
الإعلان عن تغييرات المحتوى الديناميكي دون نقل التركيز:
// Status messages (search results count, form submission status)
function SearchResults({ count, loading }: Props) {
return (
<>
{/* aria-live="polite" waits for user to finish current action */}
<div aria-live="polite" aria-atomic="true" className="sr-only">
{loading ? 'Loading results...' : `${count} results found`}
</div>
{/* Visual result count (not for screen readers — aria-hidden) */}
<span aria-hidden="true">{count} results</span>
</>
);
}
// Error messages (aria-live="assertive" interrupts immediately)
function FormError({ error }: { error: string | null }) {
return (
<div
role="alert"
aria-live="assertive"
className={cn('text-red-500 text-sm', !error && 'hidden')}
>
{error}
</div>
);
}
ARIA للأدوات المخصصة
عندما يتعين عليك إنشاء عنصر واجهة مستخدم مخصص (لوحة علامات التبويب، عرض الشجرة، مربع التحرير والسرد)، اتبع أنماط دليل ممارسات التأليف ARIA (APG) بالضبط:
// Accessible tabs (ARIA tab pattern)
export function Tabs({ items }: { items: Tab[] }) {
const [active, setActive] = useState(0);
return (
<div>
<div role="tablist" aria-label="Content tabs">
{items.map((item, i) => (
<button
key={item.id}
role="tab"
aria-selected={active === i}
aria-controls={`panel-${item.id}`}
id={`tab-${item.id}`}
tabIndex={active === i ? 0 : -1} // Roving tabindex
onClick={() => setActive(i)}
onKeyDown={(e) => {
if (e.key === 'ArrowRight') setActive((active + 1) % items.length);
if (e.key === 'ArrowLeft') setActive((active - 1 + items.length) % items.length);
}}
>
{item.label}
</button>
))}
</div>
{items.map((item, i) => (
<div
key={item.id}
role="tabpanel"
id={`panel-${item.id}`}
aria-labelledby={`tab-${item.id}`}
hidden={active !== i}
>
{item.content}
</div>
))}
</div>
);
}
النماذج ومعالجة الأخطاء
تعد النماذج التي يمكن الوصول إليها من بين التحسينات ذات التأثير الأعلى للمستخدمين ذوي الإعاقات المعرفية والحركية.
// Accessible form field with error state
function TextField({
id,
label,
error,
required,
hint,
...props
}: TextFieldProps) {
const hintId = hint ? `${id}-hint` : undefined;
const errorId = error ? `${id}-error` : undefined;
const describedBy = [hintId, errorId].filter(Boolean).join(' ') || undefined;
return (
<div>
<label htmlFor={id} className="font-medium">
{label}
{required && <span aria-hidden="true" className="text-red-500 ml-1">*</span>}
{required && <span className="sr-only">(required)</span>}
</label>
{hint && (
<p id={hintId} className="text-sm text-gray-500 mt-1">
{hint}
</p>
)}
<input
id={id}
aria-required={required}
aria-invalid={!!error}
aria-describedby={describedBy}
className={cn('input', error && 'border-red-500')}
{...props}
/>
{error && (
<p id={errorId} className="text-sm text-red-500 mt-1" role="alert">
{error}
</p>
)}
</div>
);
}
الصور والوسائط
// Informative image
<img src="/chart.png" alt="Bar chart showing 40% revenue growth in Q4 2025" />
// Decorative image — empty alt hides it from screen readers
<img src="/divider.png" alt="" role="presentation" />
// Complex image — use aria-describedby for long descriptions
<figure>
<img
src="/architecture.png"
alt="System architecture diagram"
aria-describedby="arch-desc"
/>
<figcaption id="arch-desc">
The diagram shows three tiers: frontend Next.js on port 3000,
NestJS API on port 3001, and PostgreSQL database on port 5433.
Redis sits between the API and database layers.
</figcaption>
</figure>
// SVG icons used as decoration
<svg aria-hidden="true" focusable="false">
<use href="#icon-search" />
</svg>
الاختبار الآلي باستخدام قلب الفأس
pnpm add -D @axe-core/playwright axe-core
// tests/a11y/homepage.spec.ts
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test.describe('Homepage accessibility', () => {
test('should have no WCAG 2.1 AA violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
.exclude('#third-party-widget') // Exclude known external violations
.analyze();
expect(results.violations).toEqual([]);
});
test('should be keyboard navigable', async ({ page }) => {
await page.goto('/');
// Tab through interactive elements and verify focus is visible
await page.keyboard.press('Tab');
const focusedElement = await page.evaluate(
() => document.activeElement?.getAttribute('href')
);
expect(focusedElement).toBe('#main-content'); // Skip link
});
});
أضف إلى خط أنابيب CI:
# .github/workflows/ci.yml
- name: Run accessibility tests
run: cd apps/web && npx playwright test tests/a11y --reporter=html
- uses: actions/upload-artifact@v4
with:
name: a11y-report
path: apps/web/playwright-report/
الأسئلة المتداولة
ما الفرق بين WCAG 2.1 A وAA وAAA؟
المستوى "أ" هو الحد الأدنى - ويعني الفشل في المستوى "أ" أن المحتوى لا يمكن الوصول إليه لبعض المستخدمين بطرق أساسية. المستوى AA هو المعيار القانوني في معظم الولايات القضائية ويستهدف أوسع احتياجات المستخدمين. يعتبر المستوى AAA طموحًا، حيث لا يمكن استيفاء بعض المعايير لجميع أنواع المحتوى. استهدف الامتثال لـ AA باعتباره خط الأساس الخاص بك واهدف إلى AAA حيثما كان ذلك عمليًا.
هل يؤدي استخدام مكتبة مكونات مثل shadcn/ui إلى جعل تطبيقي قابلاً للوصول؟
تم بناء shadcn/ui على أساسيات Radix UI، والتي يمكن الوصول إليها عن طريق التصميم - وهي تتضمن أدوار ARIA الصحيحة، والتنقل عبر لوحة المفاتيح، وإدارة التركيز. ومع ذلك، لا تزال بحاجة إلى إضافة تسميات ذات معنى، والتعامل مع حالات الخطأ بشكل يسهل الوصول إليه، والتأكد من تباين الألوان الكافي مع السمة المخصصة الخاصة بك، والاختبار باستخدام تقنيات مساعدة حقيقية. تعمل مكتبات المكونات على تقليل العبء ولكنها لا تلغي الحاجة إلى اختبار إمكانية الوصول.
كيف يمكنني الاختبار باستخدام قارئ الشاشة؟
على نظام التشغيل Windows، استخدم NVDA (المجاني) مع Firefox أو Chrome. على نظام التشغيل macOS، استخدم VoiceOver (المدمج، Cmd+F5) مع Safari. على الهاتف المحمول، استخدم TalkBack (Android) أو VoiceOver (iOS). اختبار رحلات المستخدم الرئيسية: إكمال النموذج، والتفاعلات النموذجية، والتنقل عبر المعالم، وقراءة المحتوى الديناميكي. يلتقط اختبار قارئ الشاشة الإعلانات وترتيب القراءة وسلوك التركيز الذي تفتقده الأدوات الآلية.
ما هو نمط tabindex المتجول؟
إن شريط علامات التبويب المتنقل هو نمط لوحة المفاتيح لعناصر واجهة المستخدم المركبة (قوائم علامات التبويب، وأشرطة الأدوات، ومجموعات الاختيار، وطرق عرض الشجرة). يحتوي عنصر واحد فقط في المجموعة على tabIndex={0} في المرة الواحدة — العنصر النشط. يحصل جميع الآخرين على tabIndex={-1}. تعمل مفاتيح الأسهم على نقل التركيز داخل المجموعة وتحديث العنصر الذي يحتوي على tabIndex 0. وهذا يمنع المستخدم من التنقل عبر كل عنصر في المجموعة - حيث يدخل إلى المجموعة باستخدام علامة التبويب، ويتنقل باستخدام مفاتيح الأسهم، ويغادر باستخدام علامة التبويب.
كيف أتعامل مع إمكانية الوصول للمحتوى الديناميكي الذي تم تحميله عبر AJAX؟
استخدم مناطق aria-live لتحديثات الحالة (عدد نتائج البحث، وحفظ التأكيدات). بالنسبة لاستبدال قسم الصفحة بالكامل، انقل التركيز إلى عنوان المحتوى الجديد أو الحاوية بعد التحميل. بالنسبة لحالات التحميل، استخدم aria-busy="true" في المنطقة التي يتم تحديثها ومنطقة aria-live="polite" للإعلان عن الاكتمال. اختبر دائمًا باستخدام قارئ الشاشة للتأكد من أن الإعلانات واضحة وفي الوقت المناسب.
الخطوات التالية
تعد إمكانية الوصول إلى الويب ممارسة مستمرة، وليست عملية تدقيق لمرة واحدة. ابدأ بإصلاح HTML الدلالي وتباين الألوان، ثم قم بوضع طبقة في التنقل عبر لوحة المفاتيح وARIA للأدوات المعقدة، وقم بأتمتة التحقق من صحة WCAG في خط أنابيب CI الخاص بك لاكتشاف الانحدارات.
تقوم ECOSIRE ببناء تطبيقات الويب المتوافقة مع WCAG 2.1 AA كمعيار أساسي في كل مشروع. إذا كنت بحاجة إلى تدقيق إمكانية الوصول أو كنت ترغب في إنشاء نظام متوافق من الألف إلى الياء، استكشف خدماتنا الهندسية للواجهة الأمامية.
بقلم
ECOSIRE Research and Development Team
بناء منتجات رقمية بمستوى المؤسسات في ECOSIRE. مشاركة رؤى حول تكاملات Odoo وأتمتة التجارة الإلكترونية وحلول الأعمال المدعومة بالذكاء الاصطناعي.
مقالات ذات صلة
Audit Preparation Checklist: Getting Your Books Ready
Complete audit preparation checklist covering financial statement readiness, supporting documentation, internal controls documentation, auditor PBC lists, and common audit findings.
Australian GST Guide for eCommerce Businesses
Complete Australian GST guide for eCommerce businesses covering ATO registration, the $75,000 threshold, low value imports, BAS lodgement, and GST for digital services.
Canadian HST/GST Guide: Province-by-Province
Complete Canadian HST/GST guide covering registration requirements, province-by-province rates, input tax credits, QST, place of supply rules, and CRA compliance.
المزيد من Compliance & Regulation
Audit Preparation Checklist: Getting Your Books Ready
Complete audit preparation checklist covering financial statement readiness, supporting documentation, internal controls documentation, auditor PBC lists, and common audit findings.
Australian GST Guide for eCommerce Businesses
Complete Australian GST guide for eCommerce businesses covering ATO registration, the $75,000 threshold, low value imports, BAS lodgement, and GST for digital services.
Canadian HST/GST Guide: Province-by-Province
Complete Canadian HST/GST guide covering registration requirements, province-by-province rates, input tax credits, QST, place of supply rules, and CRA compliance.
Healthcare Accounting: Compliance and Financial Management
Complete guide to healthcare accounting covering HIPAA financial compliance, contractual adjustments, charity care, cost report preparation, and revenue cycle management.
India GST Compliance for Digital Businesses
Complete India GST compliance guide for digital businesses covering registration, GSTIN, rates, input tax credits, e-invoicing, GSTR returns, and TDS/TCS provisions.
Fund Accounting for Nonprofits: Best Practices
Master nonprofit fund accounting with net asset classifications, grant tracking, Form 990 preparation, functional expense allocation, and audit readiness best practices.