> For AI agents: the complete documentation index is available at https://nhanbe.id.vn/llms.txt, the full documentation bundle is available at https://nhanbe.id.vn/llms-full.txt.

# EqualWeb

## EqualWeb là gì

EqualWeb là giải pháp Accessibility Widget (Client-side Overlay) hỗ trợ người dùng cuối tùy biến giao diện và bổ sung các thuộc tính hỗ trợ tiếp cận (a11y) theo chuẩn WCAG 2.1/2.2 AA, ADA Title III và Section 508.

<div align="center">
  <img src="/imgs/equalweb-widget.png" alt="Giao diện EqualWeb Accessibility Widget" width="360" />
</div>

Đối với developer, đây là giải pháp tích hợp nhanh qua script nhúng bên thứ ba mà không cần can thiệp sâu vào codebase hiện tại.

Độ phức tạp: 1/10

## Cơ chế hoạt động

EqualWeb hoạt động hoàn toàn ở phía client (browser runtime):

1. **Loader Script**: Một file JS nhỏ (\~5KB) được tải bất đồng bộ (`async`/`defer`) để khởi tạo namespace `window.interdeal`.
2. **DOM Injection**: Script tải bundle chính từ EqualWeb CDN, sau đó inject floating icon và modal điều khiển vào `document.body`.
3. **DOM Mutation & CSS Overrides**: Khi người dùng bật tính năng (độ tương phản, cỡ chữ, phông chữ Dyslexic, dừng animation), script sẽ:
   - Chèn các class và thẻ `<style>` inline để override CSS hiện tại.
   - Bổ sung động các thuộc tính `aria-label`, `role`, `tabindex` vào các element thiếu sót thông qua cơ chế quét DOM tự động.

## Cài đặt và tích hợp

### 1. Nhúng trực tiếp vào HTML

Đặt snippet sau trước thẻ đóng `</body>` hoặc trong `<head>` của trang:

```html title="index.html"
<script type="text/javascript">
  window.interdeal = {
    sitekey: "YOUR_SITE_KEY",
    Position: "Left",
    Menulang: "EN",
    domains: {
      js: "https://cdn.equalweb.com/",
      acc: "https://access.equalweb.com/"
    }
  };

  (function(doc, head, body) {
    var coreCall = doc.createElement('script');
    coreCall.src = 'https://cdn.equalweb.com/core/4.5.2/accessibility.js';
    coreCall.defer = true;
    coreCall.setAttribute('data-core', 'equalweb');
    (doc.querySelector(head) || doc.querySelector(body)).appendChild(coreCall);
  })(document, 'head', 'body');
</script>
```

### 2. Tích hợp trong SPA (React / Next.js)

Với các framework SPA, cần đảm bảo script chỉ được nạp một lần duy nhất tại root layout:

```tsx title="app/layout.tsx (Next.js App Router)"
import Script from 'next/script';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script id="equalweb-config" strategy="afterInteractive">
          {`
            window.interdeal = {
              sitekey: "${process.env.NEXT_PUBLIC_EQUALWEB_KEY}",
              Position: "Right",
              Menulang: "EN",
              domains: {
                js: "https://cdn.equalweb.com/",
                acc: "https://access.equalweb.com/"
              }
            };
          `}
        </Script>
        <Script
          src="https://cdn.equalweb.com/core/4.5.2/accessibility.js"
          strategy="lazyOnload"
          data-core="equalweb"
        />
      </body>
    </html>
  );
}
```

## Bảng tham số cấu hình (`window.interdeal`)

| Thuộc tính       | Kiểu dữ liệu | Mặc định                         | Mô tả                                                                    |
| :--------------- | :----------- | :------------------------------- | :----------------------------------------------------------------------- |
| `sitekey`        | `string`     | _(Bắt buộc)_                     | Key định danh tài khoản được cấp từ EqualWeb Portal.                     |
| `Position`       | `string`     | `"Left"`                         | Vị trí neo nút widget trên viewport: `"Left"` hoặc `"Right"`.            |
| `Menulang`       | `string`     | `"EN"`                           | Mã ngôn ngữ hiển thị của menu widget: `"VI"`, `"EN"`, `"ES"`, `"FR"`,... |
| `btn_horizontal` | `number`     | `10`                             | Khoảng cách từ icon đến mép màn hình ngang (pixel).                      |
| `btn_vertical`   | `number`     | `10`                             | Khoảng cách từ icon đến mép màn hình dọc (pixel).                        |
| `btn_size`       | `string`     | `"medium"`                       | Kích thước icon: `"small"`, `"medium"`, `"large"`.                       |
| `btn_shape`      | `string`     | `"circle"`                       | Hình dạng nút: `"circle"`, `"square"`.                                   |
| `btn_color`      | `string`     | `"#0066cc"`                      | Mã màu HEX cho icon mở widget.                                           |
| `domains.js`     | `string`     | `"https://cdn.equalweb.com/"`    | CDN host tải script thực thi.                                            |
| `domains.acc`    | `string`     | `"https://access.equalweb.com/"` | API endpoint ghi nhận log và kiểm tra license.                           |

## Cấu hình Content Security Policy (CSP)

Nếu web server hoặc CDN có header CSP chặt chẽ, cần whitelist các domain của EqualWeb:

```http title="Nginx / Cloudflare / Apache CSP Header"
Content-Security-Policy:
  script-src 'self' https://cdn.equalweb.com;
  connect-src 'self' https://access.equalweb.com https://cdn.equalweb.com;
  style-src 'self' 'unsafe-inline' https://cdn.equalweb.com;
  font-src 'self' https://cdn.equalweb.com data:;
  img-src 'self' https://cdn.equalweb.com data:;
  frame-src 'self' https://access.equalweb.com;
```

:::warning\[Lưu ý CSP]
EqualWeb inject inline styles vào DOM khi người dùng kích hoạt các tùy chọn màu sắc hoặc độ tương phản. Do đó, `style-src` bắt buộc cần `'unsafe-inline'` trừ khi dùng nonce hỗ trợ runtime injection.
:::

## Tác động hiệu năng (Performance & CWV)

Khi tích hợp EqualWeb, developer cần lưu ý các chỉ số Web Vitals sau:

1. **Largest Contentful Paint (LCP)**:
   - Sử dụng thuộc tính `defer` hoặc `strategy="lazyOnload"` để script không chặn tải tài nguyên chính của trang.
2. **Total Blocking Time (TBT) / INP**:
   - Bundle của widget thực thi sau khi DOM ready. Trên các trang có DOM tree lớn (>1500 nodes), việc quét và gắn ARIA thuộc tính có thể chiếm thread chính trong khoảng 50–150ms.
3. **Layout Shift (CLS)**:
   - Cố định icon bằng `position: fixed` với tọa độ cố định (`btn_horizontal`, `btn_vertical`) để không đẩy các element khác trong layout.
   - Chú ý tránh trùng tọa độ với các widget thứ ba khác (Zendesk chat, Intercom, back-to-top button).

## Đánh giá kỹ thuật: Overlay vs Native Remediation

| Tiêu chí                 | EqualWeb Overlay                                                     | Sửa trực tiếp từ source code (Native)                          |
| :----------------------- | :------------------------------------------------------------------- | :------------------------------------------------------------- |
| **Thời gian triển khai** | Vài phút (chèn script snippet).                                      | Hàng tuần hoặc hàng tháng tuỳ quy mô dự án.                    |
| **Công cụ cho end-user** | Có sẵn (Dyslexia font, contrast toggle, ruler, cursor resize).       | Phải tự xây dựng UI hoặc dựa vào OS/Browser accessibility.     |
| **Xử lý Semantic HTML**  | Không sửa được mã nguồn gốc; chỉ inject tạm thời vào DOM runtime.    | Sửa triệt để từ mã nguồn (`<button>` thay vì `<div onClick>`). |
| **Hỗ trợ Screen Reader** | Tương đối; một số screen reader độc lập (NVDA, JAWS) bỏ qua overlay. | Tối ưu 100% cho mọi thiết bị trợ năng tiêu chuẩn.              |
| **Phụ thuộc bên thứ ba** | Phụ thuộc CDN và uptime của EqualWeb.                                | Độc lập, không có rủi ro bên thứ ba.                           |

## Tóm tắt

- Dùng EqualWeb khi cần đáp ứng gấp yêu cầu tuân thủ giao diện người dùng và cung cấp bộ công cụ trợ năng nhanh chóng.
- Luôn tải script với `defer`/`lazyOnload` và khai báo đầy đủ CSP directives để tránh lỗi bảo mật.
- Overlay không thay thế được việc viết Semantic HTML chuẩn, quản lý keyboard focus hợp lý và cung cấp thẻ `alt` đúng ngữ cảnh trong source code.
