Làm menu điều hướng responsive với hamburger bằng HTML, CSS và một chút JavaScript

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

Thanh menu là thứ gần như trang web nào cũng có, và cũng là thứ dễ làm sai nhất trên điện thoại. Bài này hướng dẫn bạn làm menu trải ngang trên máy tính, tự thu gọn thành nút hamburger trên màn hình nhỏ, đồng thời đảm bảo dùng được bằng bàn phím.

Phân tích yêu cầu

Menu của chúng ta cần: logo bên trái, các liên kết bên phải trên màn hình rộng; trên màn hình hẹp, các liên kết ẩn đi và hiện ra khi bấm nút ba gạch. Ta làm theo hướng mobile first: viết style cho điện thoại trước, rồi dùng media query để mở rộng cho màn hình lớn.

Bước 1: HTML có ngữ nghĩa

Dùng thẻ nav bao quanh menu, danh sách ul cho các liên kết, và button thật cho nút hamburger (không dùng div) để trình đọc màn hình và bàn phím hiểu được.

<header class="site-header">
  <a class="logo" href="/">MyBrand</a>

  <button class="nav-toggle" aria-expanded="false" aria-controls="main-nav" aria-label="Mở menu">
    <span class="bar"></span>
    <span class="bar"></span>
    <span class="bar"></span>
  </button>

  <nav id="main-nav" class="main-nav">
    <ul>
      <li><a href="/">Trang chủ</a></li>
      <li><a href="/khoa-hoc">Khóa học</a></li>
      <li><a href="/blog">Blog</a></li>
      <li><a href="/lien-he">Liên hệ</a></li>
    </ul>
  </nav>
</header>

Hai thuộc tính ARIA đáng chú ý: aria-expanded báo trạng thái đóng hay mở, còn aria-controls chỉ ra phần tử mà nút điều khiển. Tên nhãn aria-label cần thiết vì nút chỉ chứa các thanh trang trí, không có chữ.

Bước 2: CSS mobile first

.site-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 20px;
  background: #111827;
  position: relative;
}
.logo { color: #fff; font-weight: 700; text-decoration: none; }

.nav-toggle {
  background: none; border: 0; cursor: pointer; padding: 8px;
  display: flex; flex-direction: column; gap: 5px;
}
.bar { width: 24px; height: 3px; background: #fff; border-radius: 2px; }

.main-nav {
  display: none;
  position: absolute; top: 100%; left: 0; right: 0;
  background: #1f2937;
}
.main-nav.open { display: block; }
.main-nav ul { list-style: none; margin: 0; padding: 0; }
.main-nav a {
  display: block; padding: 14px 20px; color: #e5e7eb; text-decoration: none;
}
.main-nav a:hover, .main-nav a:focus-visible { background: #374151; }

@media (min-width: 768px) {
  .nav-toggle { display: none; }
  .main-nav { display: block; position: static; background: none; }
  .main-nav ul { display: flex; gap: 8px; }
  .main-nav a { padding: 8px 14px; border-radius: 6px; }
}

Trên màn hình từ 768px trở lên, nút hamburger bị ẩn và menu luôn hiển thị theo hàng ngang nhờ Flexbox. Đặt position: relative cho header để menu thả xuống định vị tương đối theo nó.

Bước 3: JavaScript bật tắt menu

const toggle = document.querySelector('.nav-toggle');
const nav = document.getElementById('main-nav');

function setMenu(open) {
  nav.classList.toggle('open', open);
  toggle.setAttribute('aria-expanded', String(open));
  toggle.setAttribute('aria-label', open ? 'Đóng menu' : 'Mở menu');
}

toggle.addEventListener('click', () => {
  setMenu(!nav.classList.contains('open'));
});

// Đóng menu khi nhấn Escape
document.addEventListener('keydown', (e) => {
  if (e.key === 'Escape' && nav.classList.contains('open')) {
    setMenu(false);
    toggle.focus();
  }
});

// Đóng menu khi bấm vào một liên kết
nav.addEventListener('click', (e) => {
  if (e.target.closest('a')) setMenu(false);
});

Kiểm tra khả năng truy cập

  • Nhấn phím Tab để di chuyển qua nút và liên kết, bạn phải luôn thấy vòng focus rõ ràng.
  • Nhấn Enter hoặc Space khi đang ở nút hamburger phải mở được menu (nhờ dùng thẻ button).
  • Nhấn Escape phải đóng menu và đưa focus về nút.
  • Vùng chạm nên đủ lớn, khoảng 44px chiều cao là mức thường được khuyến nghị cho ngón tay.
  • Màu chữ và màu nền cần đủ độ tương phản.

Những lỗi người mới hay gặp

  • Dùng :hover để mở menu con trên điện thoại, nơi không có chuột.
  • Dùng display: none cho menu rồi quên hiển thị lại ở màn hình lớn, khiến menu biến mất khi xoay ngang.
  • Quên thẻ viewport nên media query không hoạt động trên thiết bị thật.
  • Menu mở nhưng nội dung trang bên dưới vẫn cuộn, gây cảm giác rối. Bạn có thể xử lý nâng cao hơn nếu menu phủ toàn màn hình.

Kết luận

Một menu tốt không chỉ đẹp mà còn phải dùng được bằng cảm ứng, chuột và bàn phím. Với HTML ngữ nghĩa, vài dòng CSS Flexbox và đoạn JavaScript ngắn, bạn đã có thể tự làm thay vì phụ thuộc vào thư viện. Đây cũng là nền tảng để làm menu con, mega menu hay ngăn kéo bên cạnh.

Để học thiết kế giao diện web một cách bài bản, hãy tham khảo các khóa học tại codebem.com.

author_photo
Nguyễn Đình Cường

0 comment