Home > Blog > tech

Web Accessibility (a11y) คืออะไร? สอนทำเว็บให้ทุกคนเข้าถึงได้ สำหรับ Developer 2026

web accessibility a11y guide
Web Accessibility a11y Guide for Developers 2026
2026-04-10 | tech | 3500 words

เว็บไซต์ที่ดีไม่ใช่แค่สวยและเร็ว แต่ต้อง "เข้าถึงได้โดยทุกคน" ไม่ว่าจะเป็นผู้ที่มีความบกพร่องทางสายตา การได้ยิน การเคลื่อนไหว หรือการรับรู้ ในประเทศไทยมีผู้พิการประมาณ 2 ล้านคน และผู้สูงอายุที่มีปัญหาในการใช้งานเทคโนโลยีอีกหลายล้านคน การทำเว็บให้ Accessible ไม่ใช่แค่เรื่องของ "ความดี" แต่เป็นเรื่องของ "ความรับผิดชอบ" และในหลายประเทศเป็น "กฎหมาย" ที่ต้องปฏิบัติตาม

บทความนี้จะสอนคุณทุกอย่างเกี่ยวกับ Web Accessibility ตั้งแต่แนวคิดพื้นฐาน มาตรฐาน WCAG 2.2 หลักการ POUR การใช้ Semantic HTML และ ARIA การทดสอบด้วย Screen Reader และ Automated Tools ไปจนถึง Accessibility Patterns สำหรับ React, Vue และ Angular

Web Accessibility คืออะไร?

Web Accessibility (ย่อว่า a11y — ตัว "a" ตามด้วยตัวอักษร 11 ตัว แล้วจบด้วย "y") หมายถึงการออกแบบและพัฒนาเว็บไซต์ให้ผู้ใช้ทุกคนสามารถเข้าถึง เข้าใจ นำทาง และโต้ตอบกับเว็บไซต์ได้ ไม่ว่าจะมีความสามารถทางร่างกายหรือจิตใจแตกต่างกันอย่างไร

ผู้ใช้ที่ได้ประโยชน์จาก Web Accessibility ไม่ใช่แค่ผู้พิการเท่านั้น แต่รวมถึงผู้สูงอายุที่สายตาเริ่มมัว ผู้ใช้ที่อยู่ในสภาพแวดล้อมที่ไม่เอื้ออำนวย (เช่น แสงแดดจัดทำให้มองจอไม่ชัด) ผู้ที่มือบาดเจ็บชั่วคราวจนใช้เมาส์ไม่ได้ หรือแม้แต่ผู้ใช้ที่ Internet ช้า ซึ่งต้องการเว็บที่ทำงานได้แม้ไม่มี JavaScript

ทำไม Developer ต้องสนใจ Accessibility?

มาตรฐาน WCAG 2.2

WCAG (Web Content Accessibility Guidelines) คือมาตรฐานสากลที่กำหนดแนวทางการทำเว็บให้ Accessible พัฒนาโดย W3C (World Wide Web Consortium) เวอร์ชันล่าสุดคือ WCAG 2.2 ซึ่งเผยแพร่ในปี 2023 และเป็นมาตรฐานที่ใช้อ้างอิงทางกฎหมายในหลายประเทศ

ระดับความสอดคล้อง (Conformance Levels)

ระดับความหมายตัวอย่าง
Level Aขั้นต่ำสุด — ถ้าไม่ผ่าน ผู้ใช้บางกลุ่มจะใช้เว็บไม่ได้เลยAlt Text สำหรับรูปภาพ, Content ไม่ใช้สีเพียงอย่างเดียวในการสื่อความหมาย
Level AAมาตรฐานที่แนะนำ — เป้าหมายที่เว็บส่วนใหญ่ควรทำได้Color Contrast 4.5:1, Resize Text 200%, Consistent Navigation
Level AAAสูงสุด — ยากที่จะทำได้ทั้งเว็บ ใช้สำหรับ Content เฉพาะColor Contrast 7:1, Sign Language, Extended Audio Description
เป้าหมายที่ควรตั้ง: เว็บไซต์ส่วนใหญ่ควรตั้งเป้าที่ WCAG 2.2 Level AA ซึ่งเป็นระดับที่กฎหมายส่วนใหญ่กำหนด และเป็นจุดสมดุลระหว่างความเป็นไปได้ในการพัฒนากับประโยชน์ต่อผู้ใช้

หลักการ POUR

WCAG ตั้งอยู่บนหลักการ 4 ข้อที่เรียกว่า POUR ซึ่งเป็นรากฐานของ Web Accessibility ทั้งหมด

1. Perceivable (รับรู้ได้)

ข้อมูลและส่วนประกอบของ UI ต้องนำเสนอในรูปแบบที่ผู้ใช้สามารถรับรู้ได้ ไม่ว่าจะใช้ประสาทสัมผัสใด

2. Operable (ใช้งานได้)

ส่วนประกอบ UI และ Navigation ต้องใช้งานได้ ไม่ว่าจะใช้อุปกรณ์ Input ใด

3. Understandable (เข้าใจได้)

ข้อมูลและการทำงานของ UI ต้องเข้าใจได้ง่าย

4. Robust (แข็งแกร่ง)

Content ต้องทำงานได้กับ User Agent หลากหลาย รวมถึง Assistive Technologies

Semantic HTML — รากฐานของ Accessibility

Semantic HTML คือการใช้ HTML Elements ตามความหมายที่ถูกต้อง ไม่ใช่แค่ตามหน้าตา นี่คือสิ่งสำคัญที่สุดที่คุณทำได้เพื่อ Accessibility เพราะ Screen Reader และ Assistive Technologies ใช้ Semantic Structure ในการนำทางและอธิบาย Content

<!-- ผิด: ใช้ div สำหรับทุกอย่าง -->
<div class="header">
  <div class="nav">
    <div class="nav-item" onclick="navigate()">Home</div>
  </div>
</div>
<div class="main">
  <div class="title">หัวข้อบทความ</div>
  <div class="content">เนื้อหา...</div>
</div>
<div class="footer">Copyright 2026</div>

<!-- ถูก: ใช้ Semantic HTML -->
<header>
  <nav aria-label="Main navigation">
    <a href="/">Home</a>
  </nav>
</header>
<main>
  <h1>หัวข้อบทความ</h1>
  <p>เนื้อหา...</p>
</main>
<footer>Copyright 2026</footer>

Heading Structure ที่ถูกต้อง

Heading ต้องเรียงลำดับตามลำดับชั้น (h1 → h2 → h3) ไม่ข้ามระดับ เพราะ Screen Reader ใช้ Heading Structure ในการสร้าง Document Outline ให้ผู้ใช้นำทาง

<!-- ผิด: ข้าม h2 ไปใช้ h3 เลย -->
<h1>Web Accessibility Guide</h1>
<h3>WCAG Guidelines</h3>
<h5>Level A</h5>

<!-- ถูก: เรียงตามลำดับ -->
<h1>Web Accessibility Guide</h1>
<h2>WCAG Guidelines</h2>
<h3>Level A</h3>

ARIA Roles และ Attributes

ARIA (Accessible Rich Internet Applications) คือชุดของ Attributes ที่เพิ่มความหมายให้กับ HTML Elements เพื่อให้ Assistive Technologies เข้าใจ Component ที่ซับซ้อนได้ แต่กฎสำคัญที่สุดคือ "No ARIA is better than Bad ARIA" — ถ้าใช้ Semantic HTML ได้ ให้ใช้ Semantic HTML ก่อน ใช้ ARIA เฉพาะเมื่อ HTML ธรรมดาไม่เพียงพอ

ARIA Roles ที่ใช้บ่อย

<!-- role="alert" - ข้อความสำคัญที่ต้องอ่านทันที -->
<div role="alert">การชำระเงินล้มเหลว กรุณาลองใหม่อีกครั้ง</div>

<!-- role="dialog" - Modal Dialog -->
<div role="dialog" aria-labelledby="dialog-title" aria-modal="true">
  <h2 id="dialog-title">ยืนยันการลบ</h2>
  <p>คุณต้องการลบรายการนี้หรือไม่?</p>
  <button>ยืนยัน</button>
  <button>ยกเลิก</button>
</div>

<!-- role="tablist" / role="tab" / role="tabpanel" -->
<div role="tablist" aria-label="Account settings">
  <button role="tab" aria-selected="true" aria-controls="panel-1" id="tab-1">
    โปรไฟล์
  </button>
  <button role="tab" aria-selected="false" aria-controls="panel-2" id="tab-2">
    การแจ้งเตือน
  </button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
  <!-- เนื้อหาแท็บโปรไฟล์ -->
</div>

<!-- role="status" - Live Region ที่ไม่ด่วนเท่า alert -->
<div role="status" aria-live="polite">
  บันทึกเรียบร้อยแล้ว
</div>

ARIA Attributes ที่ใช้บ่อย

Attributeใช้เมื่อตัวอย่าง
aria-labelให้ชื่อที่ Screen Reader อ่านได้ เมื่อไม่มี Visible Label<button aria-label="ปิด">X</button>
aria-labelledbyชี้ไปยัง Element ที่เป็น Label<div aria-labelledby="section-title">
aria-describedbyชี้ไปยัง Element ที่อธิบายเพิ่มเติม<input aria-describedby="password-hint">
aria-hiddenซ่อน Element จาก Screen Reader<span aria-hidden="true">*</span>
aria-expandedบอกสถานะ Expand/Collapse<button aria-expanded="false">Menu</button>
aria-liveบอก Screen Reader ว่าพื้นที่นี้มีการอัปเดตแบบ Dynamic<div aria-live="polite">
aria-requiredระบุว่า Field บังคับกรอก<input aria-required="true">
aria-invalidระบุว่าค่าที่กรอกไม่ถูกต้อง<input aria-invalid="true">

Keyboard Navigation

ผู้ใช้จำนวนมากไม่สามารถใช้เมาส์ได้ ไม่ว่าจะเป็นผู้พิการทางการเคลื่อนไหว ผู้ใช้ Screen Reader หรือ Power Users ที่ชอบใช้คีย์บอร์ด ดังนั้นทุก Interactive Element ต้องใช้งานได้ด้วยคีย์บอร์ด

ปุ่มคีย์บอร์ดที่สำคัญ

<!-- ผิด: div ที่ไม่สามารถ Focus ด้วยคีย์บอร์ดได้ -->
<div class="button" onclick="doSomething()">Click Me</div>

<!-- ถูก: ใช้ button element ที่มี Keyboard Support ในตัว -->
<button type="button" onclick="doSomething()">Click Me</button>

<!-- ถ้าต้องใช้ div จริงๆ (ไม่แนะนำ) -->
<div role="button" tabindex="0"
     onclick="doSomething()"
     onkeydown="if(event.key==='Enter'||event.key===' ')doSomething()">
  Click Me
</div>

Focus Management

Focus Management คือการควบคุมว่า Element ใดได้รับ Focus เมื่อไหร่ ซึ่งสำคัญมากสำหรับ Dynamic Content เช่น Modal, Dropdown, SPA Navigation

// Focus Trap สำหรับ Modal Dialog
function trapFocus(modalElement) {
    const focusableElements = modalElement.querySelectorAll(
        'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
    );
    const firstFocusable = focusableElements[0];
    const lastFocusable = focusableElements[focusableElements.length - 1];

    // Focus Element แรกเมื่อเปิด Modal
    firstFocusable.focus();

    modalElement.addEventListener('keydown', (e) => {
        if (e.key !== 'Tab') return;

        if (e.shiftKey) {
            // Shift + Tab: ถ้าอยู่ที่ Element แรก ย้อนกลับไป Element สุดท้าย
            if (document.activeElement === firstFocusable) {
                e.preventDefault();
                lastFocusable.focus();
            }
        } else {
            // Tab: ถ้าอยู่ที่ Element สุดท้าย วนกลับไป Element แรก
            if (document.activeElement === lastFocusable) {
                e.preventDefault();
                firstFocusable.focus();
            }
        }
    });
}

// Skip Link สำหรับข้ามไปเนื้อหาหลัก
// <a href="#main-content" class="skip-link">ข้ามไปเนื้อหาหลัก</a>
// CSS:
// .skip-link { position: absolute; left: -9999px; }
// .skip-link:focus { left: 10px; top: 10px; z-index: 9999; }
กฎสำคัญ: เมื่อเปิด Modal ต้อง Trap Focus ไว้ใน Modal ไม่ให้หลุดไปข้างนอก เมื่อปิด Modal ต้องคืน Focus กลับไปที่ Element ที่เปิด Modal (Trigger Element) และเมื่อ Navigate ใน SPA ต้อง Focus ไปที่หัวข้อของหน้าใหม่

Color Contrast

Color Contrast คืออัตราส่วนความสว่างระหว่างสีตัวอักษรกับสีพื้นหลัง WCAG กำหนดอัตราส่วนขั้นต่ำดังนี้

ประเภทLevel AALevel AAA
ข้อความปกติ (< 18pt)4.5:17:1
ข้อความใหญ่ (>= 18pt หรือ 14pt bold)3:14.5:1
UI Components (ปุ่ม ขอบ Input)3:1-
/* ผิด: Contrast ต่ำเกินไป (2.5:1) */
.low-contrast {
    color: #999999;
    background: #ffffff;
}

/* ถูก: Contrast เพียงพอ (7:1) */
.high-contrast {
    color: #333333;
    background: #ffffff;
}

/* ตรวจสอบ Contrast ด้วย CSS custom properties */
:root {
    --color-text-primary: #1e293b;     /* 13.5:1 on white */
    --color-text-secondary: #475569;   /* 7.2:1 on white */
    --color-text-muted: #64748b;       /* 4.6:1 on white — ผ่าน AA */
    --color-bg-primary: #ffffff;
}

/* อย่าใช้สีเพียงอย่างเดียวในการสื่อความหมาย */
.error-field {
    border-color: #ef4444;     /* สีแดง */
    border-width: 2px;          /* เพิ่มความหนาขอบ */
}
.error-message {
    color: #ef4444;
    /* เพิ่มไอคอนเพื่อไม่พึ่งสีอย่างเดียว */
}
.error-message::before {
    content: "⚠ ";             /* ไอคอนเตือน */
}

Alt Text สำหรับรูปภาพ

ทุกรูปภาพที่สื่อความหมายต้องมี Alt Text ที่อธิบายเนื้อหาของรูป ส่วนรูปที่เป็น Decorative ให้ใช้ alt="" (ค่าว่าง) เพื่อให้ Screen Reader ข้ามไป

<!-- รูปที่มีความหมาย — ต้องมี alt text -->
<img src="chart.png" alt="กราฟแสดงยอดขายเพิ่มขึ้น 25% ในไตรมาส 3">

<!-- รูป Decorative — ใช้ alt ว่าง -->
<img src="divider.png" alt="">

<!-- รูปที่ซับซ้อน — ใช้ aria-describedby สำหรับคำอธิบายยาว -->
<img src="architecture.png" alt="สถาปัตยกรรมระบบ" aria-describedby="arch-desc">
<div id="arch-desc" class="sr-only">
  ระบบประกอบด้วย Load Balancer ที่กระจาย Traffic ไปยัง Web Server 3 ตัว
  เชื่อมต่อกับ Database Cluster แบบ Primary-Replica...
</div>

<!-- ไอคอนปุ่ม — ต้องมี aria-label -->
<button aria-label="ค้นหา">
  <svg aria-hidden="true">...</svg>
</button>
เทคนิค Alt Text ที่ดี: อย่าเริ่มด้วย "รูปภาพของ..." (Screen Reader บอกอยู่แล้วว่าเป็นรูป) ให้อธิบายสิ่งที่รูปสื่อ ไม่ใช่สิ่งที่รูปเป็น เช่น แทนที่จะเขียน "กราฟแท่ง" ให้เขียน "ยอดขายเพิ่มขึ้น 25% ในไตรมาส 3"

Form Accessibility

ฟอร์มเป็น Component ที่พบได้บ่อยที่สุดบนเว็บและเป็นจุดที่ Accessibility มักมีปัญหามากที่สุด ต่อไปนี้คือแนวทางปฏิบัติ

<!-- ผิด: Input ไม่มี Label -->
<input type="email" placeholder="Email">

<!-- ถูก: ใช้ label element ที่เชื่อมกับ input -->
<label for="email">อีเมล</label>
<input type="email" id="email" name="email"
       aria-required="true"
       aria-describedby="email-hint">
<span id="email-hint">เช่น user@example.com</span>

<!-- Error Message ที่ Screen Reader อ่านได้ -->
<label for="password">รหัสผ่าน</label>
<input type="password" id="password"
       aria-required="true"
       aria-invalid="true"
       aria-describedby="password-error">
<div id="password-error" role="alert">
  รหัสผ่านต้องมีอย่างน้อย 8 ตัวอักษร
</div>

<!-- Fieldset + Legend สำหรับ Radio Group -->
<fieldset>
  <legend>วิธีการชำระเงิน</legend>
  <label>
    <input type="radio" name="payment" value="credit"> บัตรเครดิต
  </label>
  <label>
    <input type="radio" name="payment" value="bank"> โอนเงิน
  </label>
  <label>
    <input type="radio" name="payment" value="promptpay"> PromptPay
  </label>
</fieldset>

<!-- Autocomplete สำหรับฟอร์มที่กรอกบ่อย -->
<input type="text" autocomplete="given-name" name="firstname">
<input type="text" autocomplete="family-name" name="lastname">
<input type="tel" autocomplete="tel" name="phone">

Screen Reader Testing

การทดสอบด้วย Screen Reader จริงเป็นขั้นตอนที่ขาดไม่ได้ เพราะ Automated Tools ตรวจจับปัญหาได้แค่ประมาณ 30-50% ของปัญหา Accessibility ทั้งหมด

NVDA (Windows — ฟรี)

NVDA (NonVisual Desktop Access) เป็น Screen Reader ฟรีที่นิยมที่สุดบน Windows ดาวน์โหลดจาก nvaccess.org

# NVDA Keyboard Shortcuts ที่ควรรู้
Insert + Space      # สลับ Focus Mode / Browse Mode
Tab                  # เลื่อนไปยัง Interactive Element ถัดไป
H                    # กระโดดไป Heading ถัดไป
1-6                  # กระโดดไป Heading ระดับ 1-6
F                    # กระโดดไป Form Field ถัดไป
T                    # กระโดดไป Table ถัดไป
Insert + F7          # แสดง Elements List (Links, Headings, etc.)
Ctrl                 # หยุดอ่าน
Insert + Down Arrow  # อ่านทั้งหน้า

VoiceOver (macOS / iOS — มาพร้อมเครื่อง)

VoiceOver เปิดใช้ได้เลยบน Mac ด้วย Cmd + F5 บน iPhone ที่ Settings → Accessibility → VoiceOver

# VoiceOver Shortcuts (macOS)
Cmd + F5             # เปิด/ปิด VoiceOver
VO + Right Arrow     # ไปยัง Element ถัดไป (VO = Control + Option)
VO + Left Arrow      # ย้อนกลับ
VO + Space           # กด/เลือก
VO + U               # เปิด Rotor (ดู Headings, Links, Forms)
VO + Cmd + H         # กระโดดไป Heading ถัดไป

Automated Testing Tools

Automated Tools ช่วยตรวจจับปัญหา Accessibility ที่เป็นรูปแบบชัดเจนได้อย่างรวดเร็ว ใช้ร่วมกับ Manual Testing เพื่อให้ครอบคลุมมากที่สุด

axe-core

axe-core เป็น Accessibility Testing Engine ที่ใช้กันแพร่หลายที่สุด พัฒนาโดย Deque Systems สามารถใช้ได้ทั้งใน Browser Extension, CI/CD Pipeline และ Unit Tests

# ติดตั้ง axe-core สำหรับ Testing
npm install @axe-core/cli axe-core

# รัน CLI
npx axe https://your-site.com

# ใช้กับ Playwright (E2E Testing)
npm install @axe-core/playwright

// playwright.test.ts
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

test('homepage should have no a11y violations', async ({ page }) => {
    await page.goto('https://your-site.com');
    const results = await new AxeBuilder({ page }).analyze();
    expect(results.violations).toEqual([]);
});

// ทดสอบเฉพาะบางส่วนของหน้า
test('login form should be accessible', async ({ page }) => {
    await page.goto('/login');
    const results = await new AxeBuilder({ page })
        .include('#login-form')
        .withTags(['wcag2a', 'wcag2aa'])
        .analyze();
    expect(results.violations).toEqual([]);
});

Lighthouse Accessibility Audit

Lighthouse มาพร้อม Chrome DevTools และสามารถรันจาก CLI ได้ ให้ Accessibility Score 0-100

# รัน Lighthouse CI
npm install -g lighthouse
lighthouse https://your-site.com --only-categories=accessibility --output=json

# ใช้ใน CI/CD (GitHub Actions)
# .github/workflows/a11y.yml
name: Accessibility Check
on: [push, pull_request]
jobs:
  a11y:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Lighthouse CI
        uses: treosh/lighthouse-ci-action@v11
        with:
          urls: |
            https://staging.your-site.com
            https://staging.your-site.com/login
          budgetPath: ./lighthouse-budget.json
      - name: Check a11y score
        run: |
          # ถ้า Score ต่ำกว่า 90 ให้ Fail
          jq '.[] | select(.categories.accessibility.score < 0.9)' results.json

pa11y

pa11y เป็น CLI Tool ที่เน้น Accessibility Testing โดยเฉพาะ รองรับทั้ง WCAG2A, WCAG2AA, WCAG2AAA

# ติดตั้งและใช้งาน pa11y
npm install -g pa11y

# ตรวจสอบหน้าเดียว
pa11y https://your-site.com

# ตรวจสอบหลายหน้า ด้วย pa11y-ci
npm install -g pa11y-ci

# .pa11yci.json
{
    "defaults": {
        "standard": "WCAG2AA",
        "timeout": 30000,
        "wait": 2000
    },
    "urls": [
        "https://your-site.com",
        "https://your-site.com/login",
        "https://your-site.com/dashboard",
        {
            "url": "https://your-site.com/form",
            "actions": [
                "click element #submit-button",
                "wait for element #error-message to be visible"
            ]
        }
    ]
}

pa11y-ci

React / Vue / Angular Accessibility Patterns

React Accessibility

// React: Accessible Modal Component
import { useRef, useEffect } from 'react';

function AccessibleModal({ isOpen, onClose, title, children }) {
    const modalRef = useRef(null);
    const triggerRef = useRef(null);

    useEffect(() => {
        if (isOpen) {
            // บันทึก Element ที่เปิด Modal
            triggerRef.current = document.activeElement;
            // Focus ไปที่ Modal
            modalRef.current?.focus();
        } else {
            // คืน Focus กลับไปที่ Trigger
            triggerRef.current?.focus();
        }
    }, [isOpen]);

    // กด Escape เพื่อปิด
    const handleKeyDown = (e) => {
        if (e.key === 'Escape') onClose();
    };

    if (!isOpen) return null;

    return (
        <div className="modal-overlay" onClick={onClose}>
            <div
                ref={modalRef}
                role="dialog"
                aria-modal="true"
                aria-labelledby="modal-title"
                tabIndex={-1}
                onKeyDown={handleKeyDown}
                onClick={(e) => e.stopPropagation()}
            >
                <h2 id="modal-title">{title}</h2>
                {children}
                <button onClick={onClose}>ปิด</button>
            </div>
        </div>
    );
}

// React: Live Region สำหรับ Dynamic Content
function SearchResults({ results, loading }) {
    return (
        <div>
            <div aria-live="polite" aria-atomic="true" className="sr-only">
                {loading ? 'กำลังค้นหา...' :
                  `พบ ${results.length} ผลลัพธ์`}
            </div>
            {results.map(item => (
                <div key={item.id}>{item.title}</div>
            ))}
        </div>
    );
}

Vue Accessibility

<!-- Vue: Accessible Dropdown -->
<template>
  <div class="dropdown" @keydown.escape="close">
    <button
      :aria-expanded="isOpen"
      aria-haspopup="listbox"
      @click="toggle"
      @keydown.down.prevent="openAndFocusFirst"
      ref="trigger"
    >
      { selected || 'เลือกตัวเลือก' }
    </button>
    <ul v-if="isOpen" role="listbox" ref="listbox">
      <li v-for="(option, index) in options"
          :key="option.value"
          role="option"
          :aria-selected="option.value === modelValue"
          :tabindex="index === focusedIndex ? 0 : -1"
          @click="select(option)"
          @keydown.down.prevent="focusNext"
          @keydown.up.prevent="focusPrev"
          @keydown.enter.prevent="select(option)"
      >
        { option.label }
      </li>
    </ul>
  </div>
</template>

Angular Accessibility

// Angular: CDK A11y Module
import { A11yModule, LiveAnnouncer, FocusTrapFactory } from '@angular/cdk/a11y';

@Component({
    selector: 'app-notification',
    template: `<button (click)="announce()">แจ้งเตือน</button>`
})
export class NotificationComponent {
    constructor(private liveAnnouncer: LiveAnnouncer) {}

    announce() {
        // Screen Reader จะอ่านข้อความนี้
        this.liveAnnouncer.announce('มีการแจ้งเตือนใหม่ 3 รายการ', 'polite');
    }
}

// Angular CDK Focus Trap สำหรับ Dialog
@Component({
    selector: 'app-dialog',
    template: `
        <div cdkTrapFocus cdkTrapFocusAutoCapture>
            <h2>Dialog Title</h2>
            <input placeholder="Name">
            <button (click)="close()">Close</button>
        </div>
    `
})
export class DialogComponent {}

Accessible Component Libraries

ถ้าคุณไม่ต้องการสร้าง Accessible Components เอง มี Libraries ที่ออกแบบมาให้ Accessible ตั้งแต่แรก

LibraryFrameworkจุดเด่น
Radix UIReactUnstyled, Fully Accessible Primitives ยอดนิยมมาก
Headless UIReact, Vueโดย Tailwind Labs, Unstyled Accessible Components
React AriaReactโดย Adobe, Hooks สำหรับ Accessible Components
Ark UIReact, Vue, Solidโดย Chakra UI Team, Headless Components
Angular CDKAngularA11y Module พร้อม FocusTrap, LiveAnnouncer
VuetifyVueMaterial Design Components ที่ Accessible

Legal Requirements

ADA (Americans with Disabilities Act) — สหรัฐอเมริกา

ศาลสหรัฐฯ ตีความว่าเว็บไซต์เป็น "สถานที่สาธารณะ" ภายใต้ ADA Title III ธุรกิจที่มีเว็บไซต์ที่ไม่ Accessible อาจถูกฟ้องร้อง ในปี 2025 มีคดี ADA เกี่ยวกับ Web Accessibility มากกว่า 4,000 คดี โดยส่วนใหญ่อ้างอิง WCAG 2.1 Level AA

European Accessibility Act (EAA) — สหภาพยุโรป

EAA มีผลบังคับใช้เต็มรูปแบบตั้งแต่ 28 มิถุนายน 2025 ครอบคลุมเว็บไซต์ Mobile Apps และ E-commerce ของธุรกิจที่ให้บริการใน EU อ้างอิงมาตรฐาน EN 301 549 ซึ่งสอดคล้องกับ WCAG 2.1 Level AA

ประเทศไทย

ประเทศไทยมี พ.ร.บ.ส่งเสริมและพัฒนาคุณภาพชีวิตคนพิการ พ.ศ. 2550 และ พ.ร.บ.การเข้าถึงเทคโนโลยีดิจิทัลสำหรับคนพิการ แม้จะยังไม่มีการบังคับใช้อย่างเข้มงวดเท่าสหรัฐฯ หรือ EU แต่หน่วยงานภาครัฐเริ่มกำหนดให้เว็บไซต์ต้องเป็นไปตามมาตรฐาน WCAG

Accessibility ใน CI/CD

วิธีที่ดีที่สุดในการรักษา Accessibility คือทำให้เป็นส่วนหนึ่งของ Development Pipeline ตรวจสอบอัตโนมัติทุกครั้งที่มีการเปลี่ยนแปลง Code

# GitHub Actions: Accessibility Testing Pipeline
name: Accessibility CI
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  a11y-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build

      - name: Start preview server
        run: npm run preview &
        env:
          PORT: 3000

      - name: Wait for server
        run: npx wait-on http://localhost:3000

      - name: Run axe-core tests
        run: npx @axe-core/cli http://localhost:3000 --exit

      - name: Run pa11y-ci
        run: npx pa11y-ci

      - name: Run Lighthouse
        uses: treosh/lighthouse-ci-action@v11
        with:
          urls: http://localhost:3000
          configPath: ./lighthouserc.json
// ESLint Plugin สำหรับ React (ตรวจ Accessibility ตอนเขียน Code)
// .eslintrc.json
{
    "extends": [
        "plugin:jsx-a11y/recommended"
    ],
    "plugins": ["jsx-a11y"],
    "rules": {
        "jsx-a11y/alt-text": "error",
        "jsx-a11y/anchor-has-content": "error",
        "jsx-a11y/aria-props": "error",
        "jsx-a11y/click-events-have-key-events": "error",
        "jsx-a11y/no-noninteractive-element-interactions": "warn",
        "jsx-a11y/label-has-associated-control": "error"
    }
}

Common Mistakes — ข้อผิดพลาดที่พบบ่อย

จากการ Survey เว็บไซต์กว่า 1 ล้านหน้าโดย WebAIM ในปี 2025 พบข้อผิดพลาดที่เกิดบ่อยที่สุดดังนี้

ข้อผิดพลาด% เว็บที่มีปัญหาวิธีแก้
Low Contrast Text83%ใช้ Contrast Ratio อย่างน้อย 4.5:1
Missing Alt Text55%เพิ่ม alt ให้ img ทุกตัวที่มีความหมาย
Missing Form Labels46%ใช้ label element เชื่อมกับ input
Empty Links44%ทุก Link ต้องมีข้อความหรือ aria-label
Empty Buttons27%ทุกปุ่มต้องมีข้อความหรือ aria-label
Missing Document Language18%เพิ่ม lang attribute ใน html tag
Quick Win: แค่แก้ 6 ข้อข้างบนก็จะแก้ปัญหา Accessibility ได้มากกว่าครึ่งของปัญหาทั้งหมด เริ่มจากสิ่งง่ายๆ: ใส่ alt text, ใช้ label, ตรวจ contrast

Building Inclusive Design Culture

Accessibility ไม่ใช่แค่ Checklist ที่ทำตอนท้ายโปรเจกต์ แต่ต้องเป็นส่วนหนึ่งของ Culture ตั้งแต่เริ่มต้น

แนวทางสร้าง Accessibility Culture

Accessibility Checklist สำหรับ Code Review

สรุป

Web Accessibility ไม่ใช่ฟีเจอร์เสริมที่ทำเมื่อมีเวลา แต่เป็นพื้นฐานของการทำเว็บที่ดี เว็บที่ Accessible คือเว็บที่ทำงานได้สำหรับทุกคน ไม่ว่าจะมีความสามารถทางร่างกายแตกต่างกันอย่างไร ใช้อุปกรณ์อะไร หรืออยู่ในสภาพแวดล้อมแบบไหน

เริ่มต้นจากสิ่งง่ายๆ วันนี้ ใส่ Alt Text ให้รูปภาพ ใช้ Label สำหรับ Form ตรวจ Color Contrast ลองใช้ Tab ท่องเว็บของคุณ ลองเปิด Screen Reader ทีนึง แค่นี้ก็จะเข้าใจว่าทำไม Accessibility ถึงสำคัญ และเว็บของคุณจะดีขึ้นสำหรับทุกคน อ่านบทความเกี่ยวกับ Web Development และ Frontend Engineering เพิ่มเติมได้ที่ SiamCafe Blog ครับ


Back to Blog | iCafe Forex | SiamLanCard | Siam2R