🔄 Tóm tắt bài học: Trong bài học trước, bạn đã nắm vững kiến trúc CSS hiện đại — Grid, Flexbox, Container Queries và design token. Giờ hãy cùng xây dựng các thành phần sử dụng những công cụ bố cục đó: thẻ (card), cửa sổ pop-up (modal), menu drop-down (dropdown) và biểu mẫu (form).
Các thành phần là nền tảng của mọi giao diện hiện đại. Các thành phần tốt phải có thể tái sử dụng, dễ truy cập và dễ bảo trì. AI tạo ra các thành phần trực quan chính xác một cách nhanh chóng — nhưng những tương tác bàn phím, thuộc tính trợ năng và kiến trúc gọn gàng cần đến chuyên môn của bạn.
Nguyên tắc thiết kế thành phần
Nguyên tắc
Ý nghĩa của nó
AI thường mắc sai lầm
Semantic HTML trước tiên
Sử dụng <dialog>, <details>, <button>
Sử dụng <div> cho mọi thứ
Cấu trúc phẳng
Tối đa 2-3 tầng lồng nhau
div soup lồng nhau sâu
Token thiết kế
var(--space-4) không phải 16px
Các giá trị được hardcode ở khắp mọi nơi
Có thể truy cập bằng bàn phím
Mọi tương tác đều được thực hiện thông qua bàn phím
Tương tác chỉ dành cho chuột
Tiêu điểm hiển thị
Chỉ báo tiêu điểm rõ ràng dành cho người dùng bàn phím
Không có kiểu tiêu điểm nào (hoặc đã xóa các kiểu mặc định)
Thành phần 1: Thẻ
Prompt AI cho thẻ sản phẩm:
Tạo một thành phần thẻ sản phẩm với: (1) Semantic HTML — sử dụng <article> làm thẻ gốc, (2) Hình ảnh với văn bản thay thế phù hợp và loading="lazy", (3) Tiêu đề là <h3> (giả sử thẻ nằm trong một phần có <h2>), (4) Giá với phần tử <data> cho giá trị có thể đọc được bằng máy, (5) 'Add to Cart' là một <button> thực sự với trạng thái tập trung hiển thị, (6) CSS sử dụng các token thiết kế (tôi sẽ cung cấp tên token), (7) Tối đa 2 cấp độ lồng nhau, 5 lớp CSS trở xuống, (8) Đáp ứng: ngang trên các container rộng, dọc trên những container hẹp (sử dụng Container Queries).
Thành phần 2: Modal với <dialog>
Prompt AI cho một modal dễ tiếp cận:
Tạo một thành phần modal sử dụng phần tử <dialog> gốc: (1) Mở bằng .showModal(), đóng bằng .close(), (2) Tạo kiểu cho ::backdrop để tạo hiệu ứng lớp phủ, (3) Đóng khi nhấn Escape (tích hợp sẵn) và đóng khi nhấp vào nền (thêm trình xử lý), (4) Đặt aria-labelledby trỏ đến tiêu đề của modal, (5) Tạo hiệu ứng động khi mở/đóng bằng chuyển đổi CSS (sử dụng @starting-style cho hiệu ứng động khi vào), (6) Tập trung vào phần tử tương tác đầu tiên khi mở. Không cần thêm JavaScript cho việc giữ tiêu điểm hoặc Escape — <dialog> xử lý những việc này một cách tự nhiên.
Thành phần 3: Menu drop-down
Prompt AI cho menu drop-down dễ truy cập:
Tạo một thành phần menu drop-down với đầy đủ hỗ trợ bàn phím: (1) Nút kích hoạt với aria-haspopup="true" và trạng thái aria-expanded, (2) Menu với role="menu", các mục với role="menuitem", (3) Bàn phím: Phím mũi tên xuống/lên điều hướng các mục, Enter chọn, Escape đóng và trả lại tiêu điểm cho nút kích hoạt, (4) Quản lý tiêu điểm: mục đầu tiên được chọn sẽ mở, tiêu điểm bị giữ lại bên trong menu, (5) Nhấp chuột bên ngoài sẽ đóng menu, (6) Định vị CSS: bên dưới nút kích hoạt, căn chỉnh với cạnh trái, lật lên nếu gần cuối viewpoint.
Các mẫu hành vi bàn phím cho mỗi thành phần
Mỗi thành phần tương tác đều có các hành vi bàn phím được mong đợi:
Thành phần
Mẫu hành vi bàn phím
Button
Nhấn Enter/Space để kích hoạt
Link
Nhấn Enter để kích hoạt
Dropdown
Các phím mũi tên để điều hướng, phím Enter để chọn, phím Escape để đóng
Modal
Phím Escape đóng, phím Tab chuyển đổi giữa các hộp thoại, tiêu điểm được khôi phục khi đóng
Tabs
Những phím mũi tên chuyển đổi giữa các tab, phím Tab di chuyển đến nội dung của tab đó
Accordion
Chuyển đổi phím Enter/Space, phím mũi tên chuyển đổi giữa các tiêu đề
Tooltip
Phím Escape đóng cửa sổ, thao tác chọn/di chuột sẽ kích hoạt cửa sổ mới
✅ Kiểm tra nhanh: AI tạo ra một thẻ có thể nhấp chuột, trong đó toàn bộ thẻ được đặt trong thẻ `<a>`. Đây có phải là một mẫu tốt không?
Câu trả lời: Nó hoạt động nhưng có một vấn đề: nếu thẻ chứa nhiều phần tử tương tác (liên kết, nút), việc lồng chúng bên trong thẻ `<a>` là HTML không hợp lệ và gây nhầm lẫn cho trình đọc màn hình. Mẫu tốt hơn: Đặt hành động chính của thẻ (ví dụ: liên kết tiêu đề) là thẻ `<a>`, sau đó sử dụng CSS `::after` trên liên kết đó với `position: absolute` và `inset: 0` để làm cho toàn bộ thẻ có thể nhấp chuột. Các phần tử tương tác khác bên trong thẻ (như nút "Add to Cart") vẫn có thể được chọn và nhấp chuột độc lập.
Danh sách kiểm tra chất lượng thành phần
Trước khi xuất bản bất kỳ thành phần nào:
Kiểm tra
Phương pháp
Semantic HTML
Xem nguồn — không có thẻ <div> không cần thiết
Điều hướng bằng bàn phím
Nhấn phím Tab để chọn thành phần, sử dụng phím Enter/Escape/Mũi tên
Tập trung vào khả năng hiển thị
Chỉ báo tiêu điểm bàn phím hiển thị rõ ràng
Trình đọc màn hình
Kiểm tra bằng VoiceOver (Mac) hoặc NVDA (Windows)
Dark mode
Bật/tắt dark mode của hệ thống — kiểm tra độ tương phản
Đáp ứng
Thay đổi kích thước trình duyệt từ 320px thành 1920px
Trạng thái load
Giao diện của thành phần sẽ trông như thế nào trong quá trình load dữ liệu?
Trạng thái lỗi
Điều gì xảy ra khi một thứ gì đó bị hỏng?
Trạng thái trống
Nếu không có dữ liệu nào để hiển thị thì sao?
Thử ngay: Tạo thành phần dễ tiếp cận của riêng bạn
Mở Claude, ChatGPT, Gemini hoặc Copilot:
📋 Cách sao chép prompt này: Nhấp vào bất kỳ đâu bên trong khối màu xám, nhấn Cmd+A rồi Cmd+C (Mac) hoặc Ctrl+A rồi Ctrl+C (Windows). Hoặc sử dụng biểu tượng sao chép xuất hiện.
Đóng vai trò là kiến trúc sư thành phần dễ tiếp cận của tôi. Tạo thành phần tương tác của RIÊNG TÔI với đầy đủ các mẫu bàn phím WAI-ARIA, sau đó kiểm tra kết quả đầu ra của CHÍNH BẠN và cung cấp code đã sửa đổi.
Thông tin về thành phần tôi cần:
- Loại thành phần (card / modal / drop-down / tab / accordion / tooltip / combobox / khác): []
- Framework (vanilla / React / Vue / Svelte / khác): []
- Cần phần tử gốc (dialog / details / select) hay phần tử tùy chỉnh: []
- Các token thiết kế tôi đã có (màu sắc / khoảng cách / tên kiểu chữ): []
- Container chứa nó (lưới thẻ / biểu mẫu / điều hướng / thanh bên): []
- Các trạng thái tôi cần (mặc định / di chuột / tập trung / hoạt động / bị vô hiệu hóa / đang load / lỗi / trống): []
- Cần dark mode (có / không / cả hai): []
- Nội dung văn bản cho nhãn hỗ trợ tiếp cận (aria-label cho biểu tượng, tiêu đề hộp thoại, v.v.): []
- Định dạng dữ liệu dự kiến: []
- Những lỗi tôi thấy AI mắc phải với loại thành phần này: []
Hãy cung cấp theo đúng thứ tự này:
1. LỰA CHỌN SEMANTIC HTML — sử dụng phần tử gốc nào (<dialog> / <details> / <button> / sự kết hợp của...)
2. CODE THÀNH PHẦN HOÀN CHỈNH với:
- Semantic HTML (không dùng thẻ div phức tạp)
- Thuộc tính ARIA (aria-expanded / aria-haspopup / aria-labelledby / role khi cần)
- Mẫu hành vi bàn phím theo WAI-ARIA Authoring Practices (Escape / mũi tên / Enter / Space / Tab / giữ tiêu điểm)
- CSS sử dụng design tokens, tối đa 2-3 cấp độ lồng nhau, 3-5 lớp
- Kiểu hiển thị khi có tiêu điểm
- Dark mode thông qua các thuộc tính tùy chỉnh CSS
3. TỰ KIỂM TRA dựa trên bảng mẫu hành vi bàn phím cho loại thành phần này
4. TỰ KIỂM TRA quản lý tiêu điểm (giữ khi mở / khôi phục khi đóng / mục tiêu có thể lấy tiêu điểm đầu tiên)
5. TỰ KIỂM TRA độ tương phản màu sắc, kích thước mục tiêu (tối thiểu 44×44 px CSS cho cảm ứng) và hỗ trợ chuyển động giảm
6. CODE HIỂN THỊ TRẠNG THÁI — code hiển thị trạng thái mặc định / di chuột / tiêu điểm / hoạt động / bị vô hiệu hóa / đang load / lỗi / trống
7. KỊCH BẢN KIỂM TRA — các bước chính xác để kiểm tra thành phần chỉ bằng bàn phím + Trình đọc màn hình
8. API THÀNH PHẦN — thuộc tính / khe cắm / sự kiện với kiểu TypeScript nếu có thể áp dụng
QUY TẮC BẮT BUỘC:
- Ưu tiên HTML gốc. <dialog> cho cửa sổ modal, <details> cho accordion, <button> cho mọi thứ kích hoạt hành động.
- Mọi thành phần tương tác phải hỗ trợ mẫu bàn phím WAI-ARIA của nó. Không thể thương lượng.
- Tiêu điểm phải hiển thị trên mọi phần tử tương tác. Không bao giờ xóa đường viền mặc định mà không có sự thay thế hiển thị.
- Cửa sổ Modal: Escape đóng, Tab chuyển vào bên trong, tiêu điểm được khôi phục về trình kích hoạt khi đóng.
- Menu drop-down: phím mũi tên điều hướng, Enter/Space kích hoạt, Escape trả lại tiêu điểm về trình kích hoạt.
- Tab: phím mũi tên chuyển đổi tab, Tab di chuyển đến nội dung bảng đang hoạt động.
- Vùng chạm: tối thiểu 44×44 px CSS. Các vùng chạm liền kề cần khoảng cách 8px.
- Độ tương phản: 4.5:1 cho văn bản chính, 3:1 cho văn bản lớn và đường viền giao diện người dùng.
- prefers-reduced-motion: tắt hoạt ảnh không cần thiết khi được thiết lập.
- Đừng bỏ qua bước tự kiểm tra. Bản nháp đầu tiên luôn bỏ sót các mẫu.
✏️ Cách điền thông tin chi tiết của bạn: Thay thế mỗi [] và trình giữ chỗ trong ngoặc bằng các chi tiết cụ thể từ tình huống thực tế của bạn. Thông tin đầu vào mơ hồ sẽ tạo ra kết quả mơ hồ — hãy cụ thể.
Những gì bạn sẽ thấy: Một thành phần HTML ưu tiên ngữ nghĩa + hỗ trợ bàn phím WAI-ARIA đầy đủ + tự kiểm tra + khung trạng thái + script kiểm thử.
📌 Nên làm gì với kết quả: Lưu phản hồi vào file Notes. Chọn đề xuất có tác động cao nhất và thực hiện nó trong tuần này — đừng thử mọi thứ cùng một lúc.
⚠️ Nếu kết quả không ổn: Nếu các đề xuất có vẻ chung chung, hãy dán nội dung này: "Hãy cụ thể hơn với ngữ cảnh thực tế của tôi. Bỏ qua lời khuyên chung chung." Nếu nó bỏ qua các chi tiết quan trọng bạn đã cung cấp, hãy hỏi: "Bạn đã bỏ sót [X] trong ngữ cảnh của tôi — hãy thực hiện lại với điều đó làm ràng buộc chính."
Những điểm chính cần ghi nhớ
Ưu tiên sử dụng các phần tử HTML gốc cho những thành phần tương tác: <dialog> cho cửa sổ modal, <details>/<summary> cho accordion, <select> cho menu drop-down — chúng cung cấp khả năng quản lý tiêu điểm, điều hướng bằng bàn phím và hỗ trợ trình đọc màn hình miễn phí mà các triển khai <div> tùy chỉnh phải tự sao chép (và thường bỏ sót)
Mỗi thành phần tương tác đều có các hành vi bàn phím dự kiến (mẫu ARIA): Menu drop-down cần phím mũi tên + Escape, cửa sổ modal cần giữ tiêu điểm + Escape, các tab cần phím mũi tên giữa những tiêu đề — AI tạo ra các tương tác chuột nhưng hiếm khi triển khai các mẫu bàn phím này
Giữ các thành phần phẳng (tối đa 2-3 cấp), sử dụng design token cho tất cả mọi giá trị và giới hạn ở 3-5 lớp CSS được đặt tên rõ ràng — lồng nhau quá sâu tạo ra xung đột về độ ưu tiên, các giá trị được hardcode chống lại việc tạo theme và số lượng lớp quá nhiều làm tăng gánh nặng nhận thức
Câu 1:
Bạn cần một thành phần modal (hộp thoại). AI tạo ra một thành phần như vậy bằng cách sử dụng thẻ `<div>` với thuộc tính `position: fixed`, một lớp phủ nền và JavaScript để bật/tắt hiển thị. Một đồng nghiệp đề xuất sử dụng phần tử `<dialog>` gốc thay thế. Liệu phần tử gốc có đủ tốt không?
GIẢI THÍCH:
Phần tử `<dialog>` hoàn toàn có thể tạo kiểu và cung cấp khả năng giữ tiêu điểm, xử lý phím Escape, lớp phủ nền và thông báo của trình đọc màn hình — tất cả đều miễn phí. Việc xây dựng một modal tùy chỉnh từ `<div>` yêu cầu phải tự triển khai từng hành vi này, và hầu hết các modal tùy chỉnh do AI tạo ra đều thiếu ít nhất một nửa trong số đó. Luôn ưu tiên các phần tử HTML gốc cung cấp khả năng truy cập tích hợp sẵn.
Câu 2:
Bạn đang xây dựng một thành phần thẻ hiển thị hình ảnh sản phẩm, tiêu đề, giá cả và nút 'Add to Cart'. AI tạo ra một thẻ với 15 lớp CSS, các thẻ div lồng nhau 4 cấp độ và 3 giá trị kích thước phông chữ khác nhau. Làm thế nào để cải thiện điều này?
GIẢI THÍCH:
Sự đơn giản của thành phần ảnh hưởng trực tiếp đến khả năng bảo trì. Lồng nhau sâu tạo ra các cuộc chiến về độ ưu tiên của CSS. Quá nhiều lớp làm tăng gánh nặng nhận thức. Các giá trị được hardcode chống lại việc tạo theme và dark mode. Một thành phần thẻ được cấu trúc tốt là phẳng (2 cấp), sử dụng các token thiết kế và có 3-5 lớp được đặt tên rõ ràng. Điều này giúp dễ dàng sửa đổi, tạo theme và bảo trì.
Theo Nghị định 147/2024/ND-CP, bạn cần xác thực tài khoản trước khi sử dụng tính năng này. Chúng tôi sẽ gửi mã xác thực qua SMS hoặc Zalo tới số điện thoại mà bạn nhập dưới đây: