0

[Series 100 Bài Học HTML/CSS Thực Chiến - Bài 03]: Làm Chủ Flexbox Dàn Trang Top Navbar Shopee & Tuyệt Chiêu Vẽ Vạch Ngăn Cách Bằng ::after

Chào mừng anh em đã đi tới Bài 03 của chuỗi 100 Bài Học Làm Chủ HTML/CSS Qua Dự Án Clone Shopee!

Sau Bài 02, chúng ta đã có một phần nền màu cam rực rỡ trên Header và xóa sạch những khoảng trắng thừa thãi của trình duyệt. Tuy nhiên, nếu nhìn vào khu vực Top Navbar (thanh điều hướng trên cùng chứa Kênh Người Bán, Tải ứng dụng, Đăng Ký, Đăng Nhập), bạn sẽ thấy một vấn đề lớn: Tất cả các mục đang xếp chồng dọc lên nhau từ trên xuống dưới, trong khi giao diện thật của Shopee lại nằm trên cùng một hàng ngang (một cụm dạt sang trái, một cụm dạt sang phải).

Trong bài học hôm nay, chúng ta sẽ giải quyết triệt để bài toán này bằng vũ khí dàn trang quan trọng bậc nhất của CSS hiện đại: Flexbox (display: flex), đồng thời học một thủ thuật cực kỳ tinh tế của các kỹ sư Frontend: dùng Pseudo-element ::after để vẽ các vạch đứng ngăn cách giữa các mục menu mà không làm "bẩn" cấu trúc HTML!

1. Tại sao các thẻ <ul>, <li> lại tự động rớt dòng?

Trong HTML, các thẻ như <div>, <p>, <ul>, <li>, <header>, <section> mặc định mang kiểu hiển thị là display: block (phần tử khối). Đặc tính cố hữu của một thẻ block là luôn chiếm trọn 100% chiều ngang của khung chứa nó, dẫn đến việc phần tử tiếp theo bắt buộc phải bị đẩy xuống dòng mới.

Ngày xưa, các lập trình viên thường dùng float: left / right hoặc display: inline-block để ép chúng nằm ngang, nhưng cách đó cực kỳ dễ vỡ giao diện và rất khổ sở khi muốn căn giữa theo chiều dọc. Giờ đây, chúng ta có Flexbox.

2. Bản chất của Flexbox trong 2 phút: Hệ trục Main Axis & Cross Axis

Để làm chủ Flexbox, bạn chỉ cần nhớ quy tắc cốt lõi: Khi bạn gắn thuộc tính display: flex vào thẻ cha (Flex Container), lập tức tất cả các thẻ con trực tiếp bên trong nó (Flex Items) sẽ tự động xếp hàng ngang và xoay quanh 2 trục tọa độ:

image.png

Hãy nhìn vào sơ đồ trên: khi flex-direction ở trạng thái mặc định là row (hàng ngang), hai trục của Flexbox hoạt động như sau:

Tên trục / Thuộc tính Hướng mặc định (row) Công dụng thực chiến trên Top Navbar Shopee
Main Axis (Trục chính) Nằm ngang (Trái →\rightarrow Phải) Dùng thuộc tính justify-content để điều khiển khoảng cách ngang. Cụ thể: dùng space-between để đẩy cụm menu trái sang sát mép trái, cụm menu phải sang sát mép phải.
Cross Axis (Trục vuông góc) Nằm dọc (Trên →\rightarrow Dưới) Dùng thuộc tính align-items để căn chỉnh vị trí dọc. Cụ thể: dùng center để mọi chữ và icon tự động nằm chính giữa chiều cao 34px của thanh Navbar.

3. Tuyệt chiêu vẽ vạch ngăn cách (|) bằng Pseudo-element ::after

Nếu nhìn kỹ thanh Top Navbar của Shopee, giữa chữ "Kênh Người Bán" và "Trở thành Người bán Shopee" có một vạch kẻ dọc mảnh màu trắng mờ.

  • Cách làm của người mới: Gõ thẳng ký tự gạch đứng <li>Kênh Người Bán |</li> hoặc chèn thêm thẻ <span class="divider">|</span> vào HTML. Cách này làm rác HTML, trình đọc màn hình (Screen Reader) sẽ đọc luôn cả chữ "vertical bar", và rất khó căn chỉnh chiều cao của vạch kẻ.

  • Cách làm của chuyên gia: Sử dụng phần tử giả ::after kết hợp với định vị tuyệt đối (position: absolute). Chúng ta tạo ra một "bóng ma" có chiều rộng 1px, chiều cao 12px nằm ngay sát mép phải của từng thẻ <li>.

4. Thực hành: Code hoàn chỉnh thanh Top Navbar Shopee

Bước 1: Cập nhật cấu trúc HTML chuẩn chỉnh trong index.html

Hãy mở file index.html, tìm đến thẻ <header class="shopee-header"> và cập nhật lại khu vực .top-navbar.

(Lưu ý: Chúng ta bọc thêm thẻ <div class="grid-container"> vào trong <header> để nội dung Header luôn nằm gọn trong khung 1200px thẳng hàng với phần thân trang, và gắn class .nav-item--separate cho những mục cần có vạch ngăn cách bên phải).

HTML

<!-- 1. VÙNG ĐẦU TRANG (HEADER) -->
<header class="shopee-header">
    <div class="grid-container">
        
        <!-- Thanh điều hướng phía trên (Top Navbar) -->
        <nav class="top-navbar">
            <!-- Cụm menu bên trái -->
            <ul class="nav-list">
                <li class="nav-item nav-item--separate">
                    <a href="#" class="nav-link">Kênh Người Bán</a>
                </li>
                <li class="nav-item nav-item--separate">
                    <a href="#" class="nav-link">Trở thành Người bán Shopee</a>
                </li>
                <li class="nav-item nav-item--separate">
                    <a href="#" class="nav-link">Tải ứng dụng</a>
                </li>
                <li class="nav-item">
                    <span class="nav-title-no-pointer">Kết nối</span>
                </li>
            </ul>

            <!-- Cụm menu bên phải -->
            <ul class="nav-list">
                <li class="nav-item">
                    <a href="#" class="nav-link">Thông Báo</a>
                </li>
                <li class="nav-item">
                    <a href="#" class="nav-link">Hỗ Trợ</a>
                </li>
                <li class="nav-item nav-item--separate">
                    <a href="#" class="nav-link nav-link--strong">Đăng Ký</a>
                </li>
                <li class="nav-item">
                    <a href="#" class="nav-link nav-link--strong">Đăng Nhập</a>
                </li>
            </ul>
        </nav>

        <!-- Khu vực Logo, Tìm kiếm và Giỏ hàng (Sẽ làm ở Bài 05) -->
        <div class="header-with-search">
            <!-- Giữ nguyên code cũ từ Bài 01 -->
        </div>

    </div>
</header>

Bước 2: Viết CSS dàn trang Flexbox và vẽ vạch ::after trong main.css

Mở file ./assets/css/main.css và bổ sung đoạn code dưới đây ngay bên dưới class .shopee-header:

CSS

/* ./assets/css/main.css */

/* --- KHU VỰC TOP NAVBAR --- */
.top-navbar {
    height: var(--navbar-height); /* 34px đã khai báo ở base.css */
    display: flex;
    justify-content: space-between; /* Đẩy 2 thẻ <ul> ra 2 đầu trái - phải */
    align-items: center;            /* Căn giữa theo chiều dọc */
}

.nav-list {
    display: flex;                  /* Giúp các thẻ <li> bên trong xếp thành hàng ngang */
    align-items: center;
}

.nav-item {
    position: relative;             /* Làm gốc tọa độ cho vạch ngăn cách ::after */
    margin: 0 0.8rem;               /* Tạo khoảng cách 8px trái/phải giữa các mục */
}

/* Xóa khoảng cách thừa ở mục đầu tiên bên trái và mục cuối cùng bên phải */
.nav-list:first-child .nav-item:first-child {
    margin-left: 0;
}

.nav-list:last-child .nav-item:last-child {
    margin-right: 0;
}

.nav-link,
.nav-title-no-pointer {
    font-size: 1.3rem;              /* Cỡ chữ 13px chuẩn Topbar Shopee */
    color: var(--white-color);
    font-weight: 300;               /* Nét chữ mảnh, thanh thoát */
    transition: opacity 0.2s ease;  /* Tạo hiệu ứng mờ dần mượt mà khi di chuột */
}

/* Hiệu ứng mờ đi khi di chuột vào link (trừ chữ "Kết nối") */
.nav-link:hover {
    color: rgba(255, 255, 255, 0.7);
}

/* In đậm cho nút Đăng Ký / Đăng Nhập */
.nav-link--strong {
    font-weight: 500;
}

/* Chữ "Kết nối" chỉ là nhãn văn bản, không cho phép bấm vào */
.nav-title-no-pointer {
    cursor: text;
}

/* TUYỆT CHIÊU: Vẽ vạch ngăn cách dọc bằng Pseudo-element ::after */
.nav-item--separate::after {
    content: "";                    /* Bắt buộc phải có thuộc tính content thì ::after mới hiện */
    display: block;
    position: absolute;
    width: 1px;                     /* Độ dày vạch 1px */
    height: 1.2rem;                 /* Chiều cao vạch 12px */
    background-color: hsla(0, 0%, 100%, 0.22); /* Màu trắng trong suốt 22% chuẩn Shopee */
    right: -0.9rem;                 /* Đẩy vạch ra giữa khe hở của 2 thẻ <li> */
    top: 50%;                       /* Đưa đỉnh của vạch xuống chính giữa (50%) chiều cao thẻ <li> */
    transform: translateY(-50%);    /* Kéo ngược vạch lên 50% chiều cao của chính nó để căn tâm tuyệt đối */
}

image.png

5. Giải mã dòng code: 3 Chi tiết "ăn tiền" của dân chuyên nghiệp

  1. Flexbox lồng nhau (Nested Flexbox):

    • Chúng ta đặt display: flex lần 1 ở .top-navbar để tách 2 cụm <ul class="nav-list"> sang 2 đầu màn hình.

    • Sau đó, chúng ta đặt display: flex lần 2 ở chính .nav-list để ép các thẻ <li class="nav-item"> bên trong từng cụm xếp thành hàng ngang. Đây là mô hình Flexbox trong Flexbox xuất hiện ở 90% các thanh điều hướng thực tế.

  2. Công thức căn giữa tuyệt đối top: 50% + transform: translateY(-50%):

    • Khi bạn dùng position: absolute cho vạch ngăn cách ::after, nếu chỉ viết top: 50%, trình duyệt sẽ đặt mép trên cùng của vạch kẻ tại điểm giữa của dòng chữ (làm vạch kẻ bị lệch xuống dưới).

    • Kết hợp thêm transform: translateY(-50%) sẽ kéo vạch kẻ dịch ngược lên trên đúng bằng một nửa chiều cao của chính nó (6px), giúp vạch kẻ nằm chính tâm 100% bất kể chữ cao hay thấp!

  3. Bộ chọn :first-child và :last-child:

    • Khi cho .nav-item có margin: 0 0.8rem, chữ "Kênh Người Bán" ở ngoài cùng bên trái cũng bị thụt vào 8px, làm nó lệch trục dọc so với Logo Shopee bên dưới. Dùng :first-child để xóa margin-left: 0 giúp mép trái của chữ thẳng tắp tuyệt đối với khung 1200px.

6. Bài tập thử thách & Hẹn gặp ở Bài 04!

Hãy lưu lại toàn bộ file và nhấn F5 trên trình duyệt! Bạn sẽ thấy thanh Top Navbar của Shopee hiện lên sắc nét đến từng pixel: chữ mảnh tinh tế, di chuột vào có hiệu ứng mờ nhẹ, và các vạch ngăn cách đứng thẳng hàng hoàn hảo.

Thử thách nhỏ trước khi sang Bài 04:

  1. Mở Chrome DevTools (F12), chọn thẻ <nav class="top-navbar">, thử đổi giá trị justify-content: space-between lần lượt thành center, flex-start, space-around, space-evenly để tận mắt quan sát cách các cụm menu di chuyển.

  2. Thử tắt dòng content: ""; trong .nav-item--separate::after xem điều gì xảy ra với các vạch ngăn cách nhé!

Ở Bài 04, chúng ta sẽ học cách nhúng thư viện Icon (FontAwesome) và Font chữ chuẩn (Roboto) vào dự án để thêm biểu tượng Facebook, Instagram cạnh chữ "Kết nối", thêm quả chuông "Thông báo", và đặc biệt là dựng Hộp thoại mã QR Tải ứng dụng tự động hiện ra khi di chuột vào!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí