Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn cầm một cuốn sách dày 500 trang nhưng ai đó đã xé bỏ toàn bộ mục lục, đồng thời tất cả các tiêu đề chương đều bị in cùng một cỡ chữ giống hệt phần nội dung. Để tìm được "Chương 7 — Chính sách hoàn tiền", bạn buộc phải đọc lướt từng dòng, từng trang một. Đó chính xác là trải nghiệm mà một người dùng screen reader phải chịu đựng khi trang web của bạn không có cấu trúc heading (tiêu đề) đúng đắn.
Với người sáng mắt, bộ não chúng ta "quét" một trang web trong vòng chưa đầy hai giây: cỡ chữ to, chữ đậm, khoảng trắng — tất cả tạo nên một bản đồ trực quan giúp mắt nhảy thẳng đến phần cần đọc. Nhưng người dùng screen reader không có "cái nhìn tổng thể" đó. Với họ, trang web là một dòng chảy tuyến tính từ trên xuống dưới. Heading chính là công cụ điều hướng số một giúp họ nhảy cóc qua các phần, dựng lại bản đồ trang trong đầu, và tìm đúng thứ họ cần mà không phải nghe toàn bộ.
Điều làm nhiều designer và developer bất ngờ: heading không phải chuyện "làm cho chữ to lên cho đẹp". Nó là một cấu trúc ngữ nghĩa (semantic structure) — một bộ khung xương thông tin. Khi bạn dùng đúng <h1> đến <h6> theo thứ tự hợp lý, bạn đang tặng cho hàng triệu người dùng khiếm thị một tấm bản đồ. Khi bạn dùng sai, bạn đang xé bỏ mục lục của họ. Bài này sẽ chỉ cho bạn cách xây dựng bộ khung xương ấy thật vững — một kỹ năng nền tảng mà mọi bài sau về form, table, landmark đều phải dựa vào.
Khái niệm cốt lõi
Heading là gì và vì sao screen reader "yêu" nó
Trong HTML, heading là các thẻ từ <h1> (quan trọng nhất) đến <h6> (chi tiết nhất). Chúng biểu diễn cấp bậc của thông tin, không phải kích cỡ của chữ. Đây là điểm mấu chốt: <h2> không có nghĩa "chữ cỡ 24px", nó có nghĩa "đây là một mục con của chủ đề chính".
Screen reader — như NVDA (phổ biến, miễn phí trên Windows), VoiceOver (có sẵn trên máy Mac và iPhone), hay JAWS — đọc heading khác hẳn văn bản thường. Khi con trỏ chạm vào một heading, screen reader không chỉ đọc nội dung mà còn thông báo cấp bậc: "Chính sách hoàn tiền, heading cấp 2". Nhờ đó người dùng biết ngay mình đang ở đâu trong cấu trúc trang.
Điều hướng bằng phím H và rotor
Đây là lý do heading quan trọng đến mức gần như là "linh hồn" của khả năng điều hướng:
- Trên NVDA và JAWS (Windows): người dùng nhấn phím
Hđể nhảy đến heading tiếp theo,Shift + Hđể lùi lại. Họ còn có thể nhấn số1,2,3... để nhảy trực tiếp đến heading cấp tương ứng — ví dụ2sẽ nhảy qua các<h2>để lướt nhanh các mục chính. - Trên VoiceOver (Mac/iOS): người dùng mở "rotor" (xoay hai ngón trên iPhone, hoặc
VO + Utrên Mac), chọn "Headings", rồi vuốt lên/xuống để duyệt qua danh sách toàn bộ heading của trang.
Ba nguyên tắc vàng của cấu trúc heading
1. Mỗi trang nên có đúng một <h1>. Đó là tiêu đề chính, trả lời câu hỏi "Trang này nói về cái gì?". Trên trang chi tiết sản phẩm, <h1> là tên sản phẩm. Trên bài blog, đó là tiêu đề bài viết. Có nhiều <h1> sẽ khiến người dùng bối rối vì không rõ đâu mới là chủ đề thật sự của trang.
2. Không được nhảy cấp. Sau <h1> phải là <h2>, không được nhảy thẳng xuống <h4>. Cấp bậc phải liền mạch: h1 → h2 → h3, rồi mới quay lại h2 cho mục lớn tiếp theo. Nhảy cấp giống như một mục lục ghi "Chương 1, rồi tiểu mục 1.1.1.1" mà thiếu 1.1 và 1.1.1 ở giữa — người nghe mất phương hướng, tưởng mình vừa bỏ lỡ nội dung.
3. Heading phải phản ánh đúng cấu trúc nội dung, không phải để trang trí. Đừng bao giờ chọn <h3> chỉ vì "cỡ chữ h3 trông vừa mắt". Nếu về mặt logic đó là một mục con của một <h2>, thì nó phải là <h3> — sau đó bạn dùng CSS để chỉnh cỡ chữ theo ý muốn. Ngữ nghĩa và hình thức là hai chuyện tách rời.
Heading trực quan vs heading ngữ nghĩa
Đây là cạm bẫy lớn nhất trong thực tế. Một designer làm chữ to, in đậm, màu đậm — nhìn giống heading. Nhưng nếu developer code nó bằng <div class="big-title"> hoặc <p style="font-size:28px; font-weight:bold">, thì với screen reader nó vẫn chỉ là một đoạn văn thường. Nó không xuất hiện trong danh sách heading, người dùng không thể nhảy đến nó bằng phím H. Trực quan thì có heading, ngữ nghĩa thì không có — và screen reader chỉ đọc được ngữ nghĩa.
Ngược lại cũng đúng: một <h2> thật sự trong code nhưng bị CSS làm cho nhỏ xíu vẫn là heading hợp lệ với screen reader. Bài học rút ra: hình thức phục vụ đôi mắt, thẻ HTML phục vụ đôi tai. Bạn phải làm đúng cả hai.
Tình huống thực tế
Ví dụ 1 — Trang tin tức VnExpress và "biển chữ đậm"
Giả sử đội frontend của một trang báo điện tử lớn tại Việt Nam (tạm gọi mô hình như VnExpress) xây trang chủ. Để trang chủ trông "gọn", họ quyết định code toàn bộ tiêu đề các chuyên mục — "Thời sự", "Kinh doanh", "Thể thao" — bằng <span class="section-label"> với CSS làm chữ to đậm. Nhìn trên màn hình rất đẹp và cân đối.
Một bạn kiểm thử khiếm thị dùng NVDA mở trang. Bạn ấy nhấn H để duyệt heading, mong nhảy thẳng đến mục "Thể thao". Nhưng NVDA im lặng — vì với nó, cả trang chủ này gần như không có heading nào. Toàn bộ chuyên mục chỉ là các span vô danh. Bạn ấy buộc phải nhấn mũi tên xuống hàng trăm lần, nghe qua từng dòng tin để dò ra mục thể thao.
Diễn giải: đội frontend đã tối ưu cho mắt mà quên mất tai. Chữ đậm không tạo ra heading ngữ nghĩa. Bản sửa đơn giản đến bất ngờ: đổi <span class="section-label"> thành <h2 class="section-label">, giữ nguyên toàn bộ CSS. Sau khi sửa, danh sách heading của NVDA hiện ra đầy đủ tám chuyên mục, người dùng nhảy đến "Thể thao" bằng một phím bấm.
Bài học: trước khi tô đậm bất cứ dòng chữ nào trông giống tiêu đề, hãy tự hỏi "về mặt nội dung, đây có phải một tiêu đề không?". Nếu có, nó phải là thẻ <h>.
Ví dụ 2 — Trang chi tiết sản phẩm của một sàn e-commerce
Một sàn thương mại điện tử Đông Nam Á (mô hình tương tự Tiki hay Shopee) có trang chi tiết sản phẩm. Về mặt hình ảnh nó rất chỉn chu, nhưng thứ tự heading trong code lại lộn xộn do nhiều team ghép các "block" vào trang qua nhiều năm:
- Logo trang được đặt là
<h1>(sai — logo không phải chủ đề của trang). - Tên sản phẩm "Tai nghe không dây XZ" lại chỉ là
<h3>. - Mục "Đánh giá từ khách hàng" là
<h2>, nhưng "Thông số kỹ thuật" — cùng cấp — lại là<h4>.
1 để nhảy đến chủ đề chính của trang, họ nghe thấy... "Logo sàn thương mại điện tử". Họ tưởng mình vẫn ở trang chủ. Đến khi lần mò xuống, tên sản phẩm nằm ở cấp h3 khiến nó nghe như một chi tiết phụ, không phải nhân vật chính.Diễn giải: cấu trúc heading ở đây phản ánh lịch sử ghép code chứ không phản ánh ý nghĩa nội dung. Bản sửa: <h1> phải là tên sản phẩm; logo chuyển về một liên kết bình thường không dùng heading; các mục ngang hàng như "Đánh giá", "Thông số kỹ thuật", "Sản phẩm liên quan" đều phải là <h2>; các mục con bên trong (ví dụ "Đánh giá 5 sao") là <h3>.
Bài học: khi nhiều team cùng đóng góp vào một trang, cấu trúc heading dễ bị "trôi" theo thời gian. Cần một người sở hữu bức tranh tổng thể của outline trang, thay vì mỗi block tự quyết cấp heading của mình.
Ví dụ 3 — Trang khóa học có tab và accordion
Một nền tảng học trực tuyến (bối cảnh giống chính khóa học bạn đang học) có trang giới thiệu khóa học với nhiều phần: "Giới thiệu", "Nội dung khóa học", "Giảng viên", "Câu hỏi thường gặp". Team dùng <h2> cho từng phần — rất tốt. Nhưng trong phần "Nội dung khóa học", họ hiển thị 60 bài học, mỗi tên bài lại được code là <h4>, bỏ trống hoàn toàn cấp <h3>.
Người dùng VoiceOver mở rotor xem danh sách heading. Họ thấy một cú nhảy khó hiểu: từ <h2> "Nội dung khóa học" tụt thẳng xuống một loạt <h4>. Cảm giác như có một tầng thông tin bị mất ở giữa. Tệ hơn, 60 tên bài học đều là heading khiến danh sách heading dài lê thê, làm loãng mất các mục thật sự quan trọng.
Diễn giải: hai lỗi cùng lúc — nhảy cấp (h2 xuống h4) và lạm dụng heading (biến từng dòng danh sách thành heading). Bản sửa: nếu 60 bài học là một danh sách, hãy dùng <ul><li> cho chúng thay vì mỗi cái một heading; còn nếu muốn giữ vài mốc chương lớn thì các mốc đó là <h3> nằm đúng dưới <h2>.
Bài học: không phải cứ thứ gì "nổi bật" cũng phải là heading. Heading dành cho tiêu đề của một khối nội dung, còn các mục trong danh sách thì dùng list. Dùng heading đúng liều lượng cũng quan trọng như dùng đúng cấp.
Hướng dẫn từng bước
Đây là quy trình thực chiến để xây và kiểm tra cấu trúc heading cho bất kỳ trang nào.
Bước 1 — Vẽ outline trước khi code. Trước khi động vào HTML, hãy viết ra dàn ý của trang như thể viết mục lục cho một tài liệu: chủ đề chính là gì (đó sẽ là <h1>), các phần lớn là gì (<h2>), mỗi phần lớn chia thành mục con nào (<h3>). Nếu bạn không thể viết outline mạch lạc, đó là dấu hiệu cấu trúc nội dung của trang đang có vấn đề — sửa nội dung trước, sửa code sau.
Bước 2 — Đặt một và chỉ một <h1>. Xác định câu trả lời cho "Trang này nói về cái gì?" và gán nó cho <h1>. Với trang blog là tiêu đề bài; trang sản phẩm là tên sản phẩm; trang liên hệ là "Liên hệ". Đừng để logo hay tên thương hiệu chiếm chỗ <h1>.
Bước 3 — Gán cấp bậc liền mạch. Đi từ trên xuống, mỗi khi mở một mục mới hãy tự hỏi "mục này là con của mục nào?". Là con của <h1> thì dùng <h2>; là con của một <h2> thì dùng <h3>. Tuyệt đối không nhảy cấp khi đi xuống. Lưu ý: khi lùi lên (kết thúc một <h3> để mở một <h2> mới) thì được phép nhảy nhiều cấp — quay từ h3 về h2 hoàn toàn hợp lệ.
Bước 4 — Tách hình thức khỏi ngữ nghĩa. Chọn thẻ heading dựa hoàn toàn vào logic nội dung. Nếu cỡ chữ mặc định không đẹp, dùng CSS (class) để chỉnh — ví dụ một <h3> có thể được style to như h2 nếu thiết kế yêu cầu, mà vẫn giữ đúng ngữ nghĩa h3.
Bước 5 — Kiểm tra bằng công cụ. Cài extension trình duyệt như "HeadingsMap" (Chrome/Firefox) hoặc dùng phần "Structure" của thanh công cụ WAVE. Chúng hiển thị cây heading của trang dưới dạng mục lục. Nhìn vào đó, bạn phát hiện ngay các cú nhảy cấp hoặc các <h1> bị trùng.
Bước 6 — Kiểm tra bằng chính screen reader. Bật NVDA (Windows) hoặc VoiceOver (Mac), rồi nhấn H liên tục để "đi tour" qua các heading. Hãy tự hỏi: chỉ nghe riêng danh sách heading, bạn có hiểu trang này nói về gì và có gì trong đó không? Nếu có, cấu trúc của bạn đã tốt.
Lỗi thường gặp & mẹo
Lỗi: chọn heading theo cỡ chữ. "Tôi muốn dòng này nhỏ nên tôi dùng <h5>." Sai hoàn toàn. Chọn cấp theo vị trí trong outline, rồi dùng CSS để chỉnh cỡ. Mẹo: khi làm việc với dev, hãy annotate rõ trong file thiết kế "đây là h2, hiển thị 20px" để tránh dev đoán mò.
Lỗi: nhiều <h1> trên một trang. Thường xảy ra khi mỗi component (banner, sidebar, nội dung chính) tự đặt <h1> của riêng nó. Mẹo: quy ước trong design system rằng chỉ template trang mới được sinh <h1>, còn mọi component tái sử dụng đều bắt đầu từ <h2> trở xuống, hoặc nhận cấp heading qua tham số.
Lỗi: heading rỗng hoặc chứa icon không có nhãn. Một <h2> chỉ chứa một icon SVG không có text sẽ được screen reader đọc là "heading cấp 2" rồi... im lặng, khiến người dùng hoang mang. Mẹo: mọi heading phải có nội dung text thật (dùng text ẩn trực quan bằng kỹ thuật visually-hidden nếu cần giữ giao diện gọn).
Lỗi: nhảy cấp khi đi xuống. h2 → h4 là lỗi kinh điển, thường do copy-paste một block từ trang khác. Mẹo: luôn chạy HeadingsMap trước khi ship — nó tô đỏ mọi cú nhảy cấp.
Lỗi: nhồi từ khóa SEO vào heading một cách gượng ép. Nhiều người dồn từ khóa vào <h1>/<h2> chỉ để lên top Google, khiến heading dài dòng, khó hiểu khi đọc bằng tai. Mẹo: viết heading như một mục lục cho người, rõ ràng và ngắn gọn — điều này vừa tốt cho a11y vừa tốt cho SEO hiện đại.
Mẹo tổng quát — "bài kiểm tra mục lục": che toàn bộ nội dung, chỉ để lại các heading. Nếu chuỗi heading đọc lên vẫn thành một mục lục mạch lạc, có nghĩa, đủ để hiểu trang — bạn đã làm đúng. Nếu nó rời rạc hoặc thiếu logic, cấu trúc cần sửa.
Bài tập thực hành
Bài 1 — Dựng outline heading. Chọn một trang bất kỳ bạn đang làm hoặc một trang web quen thuộc (ví dụ trang chủ một ngân hàng Việt Nam). Không nhìn code, hãy tự viết ra dàn ý heading mà bạn nghĩ trang nên có: đâu là <h1>, các <h2>, <h3>. Sau đó cài HeadingsMap và so sánh với thực tế. Ghi lại mọi điểm khác biệt.
Bài 2 — Săn lỗi nhảy cấp. Mở ba trang web bất kỳ bằng HeadingsMap. Tìm ít nhất một trang có lỗi (thiếu <h1>, có nhiều <h1>, hoặc nhảy cấp h2→h4). Viết ra bản sửa: cấu trúc heading đúng nên như thế nào.
Bài 3 — Nghe bằng tai. Bật VoiceOver (Mac/iPhone) hoặc NVDA (Windows). Mở rotor/danh sách heading của một trang tin tức. Chỉ dùng phím H (hoặc rotor), thử tìm đến một mục cụ thể mà không đọc nội dung khác. Ghi lại: bạn mất bao nhiêu lần bấm phím? Trải nghiệm đó nói lên điều gì về chất lượng heading của trang?
Bài 4 — Sửa một component. Lấy một card hoặc block trong dự án của bạn đang dùng chữ đậm giả làm heading (<div>/<span>). Chuyển nó sang thẻ heading đúng cấp, giữ nguyên giao diện bằng CSS. Kiểm tra lại bằng screen reader để xác nhận nó đã xuất hiện trong danh sách heading.
Tóm tắt
Heading không phải là chuyện cỡ chữ — nó là bộ khung xương ngữ nghĩa của trang, và là công cụ điều hướng số một của người dùng screen reader. Đa số người dùng khiếm thị dựa vào phím H (NVDA/JAWS) hoặc rotor (VoiceOver) để duyệt danh sách heading như một mục lục tự động, nên một cấu trúc heading tốt gần như quyết định trang của bạn có dùng được hay không.
Ba nguyên tắc cần khắc cốt: một <h1> duy nhất cho chủ đề chính; không nhảy cấp khi đi xuống (h1→h2→h3 liền mạch); và chọn cấp theo logic nội dung, không theo hình thức — cỡ chữ để CSS lo. Cạm bẫy phổ biến nhất là "heading giả": chữ đậm bằng <div>/<span> trông giống heading với mắt nhưng vô hình với tai.
Quy trình an toàn: vẽ outline trước khi code, gán cấp bậc mạch lạc, tách hình thức khỏi ngữ nghĩa, rồi kiểm tra kép bằng công cụ (HeadingsMap, WAVE) và bằng chính screen reader. Và hãy luôn nhớ "bài kiểm tra mục lục": nếu chỉ đọc riêng các heading mà vẫn hiểu được trang, bạn đã trao cho hàng triệu người dùng một tấm bản đồ thay vì một cuốn sách bị xé mất mục lục. Cấu trúc heading vững chính là nền móng để các bài tiếp theo — landmark, form, table — có chỗ đứng vững chắc.