[Series 100 Bài Học HTML/CSS Thực Chiến - Bài 04]: Nhúng Google Fonts (Roboto), Thư Viện Icon FontAwesome & Tuyệt Chiêu Làm Popover QR Code Với "Cầu Nối Vô Hình"
Chào mừng anh em đã bước sang Bài 04 trong chuỗi 100 Bài Học Làm Chủ HTML/CSS Qua Dự Án Clone Shopee!
Ở Bài 03, chúng ta đã dùng Flexbox để dàn ngang thanh Top Navbar và vẽ các vạch ngăn cách sắc nét bằng ::after. Tuy nhiên, nếu đặt giao diện của chúng ta cạnh trang chủ Shopee thật, bạn sẽ nhận ra ngay 3 điểm còn thiếu sót:
-
Nét chữ chưa "ra chất" hiện đại: Font mặc định
Arial / Helveticatrên một số máy tính nhìn hơi cứng, chưa có độ tròn trịa, đồng nhất trên mọi hệ điều hành như font Roboto. -
Thiếu hệ thống biểu tượng (Icons): Chữ "Kết nối" chưa có icon Facebook, Instagram đi kèm; mục "Thông Báo" và "Hỗ Trợ" chưa có hình quả chuông hay dấu chấm hỏi.
-
Thiếu tính tương tác (Interactive UI): Trên Shopee thật, khi bạn rê chuột (Hover) vào dòng chữ "Tải ứng dụng", một hộp thoại trắng chứa Mã QR Code và link tải App Store / Google Play sẽ lập tức hiện ra mượt mà!
Trong Bài 04 hôm nay, chúng ta sẽ giải quyết trọn vẹn cả 3 bài toán này, đồng thời học một kỹ thuật cực kỳ nổi tiếng trong giới Frontend khi làm menu thả xuống: Kỹ thuật làm "Cầu nối vô hình" (::before) để hộp thoại không bị tắt đột ngột khi rê chuột!
1. Tại sao phải nhúng Google Fonts (Roboto) và FontAwesome CDN?
Vấn đề của Font chữ hệ thống (System Fonts)
Ở Bài 02, chúng ta khai báo font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;. Vấn đề là máy Mac thì có Helvetica Neue rất đẹp, nhưng máy Windows lại không có sẵn font này nên trình duyệt tự động rơi về Arial — làm giao diện hiển thị trên máy mỗi người một kiểu!
Khi nhúng Google Fonts (Font Roboto) qua đường dẫn CDN (Content Delivery Network), trình duyệt của người dùng sẽ tải trực tiếp bộ font chuẩn từ máy chủ của Google về. Dù người dùng mở web trên Windows, macOS, Linux hay Android, nét chữ đều giống nhau 100% đến từng điểm ảnh.
Tại sao dùng Icon Font (FontAwesome) thay vì cắt ảnh .png?
Ngày xưa, để chèn icon Facebook hay quả chuông, lập trình viên phải cắt từng tấm ảnh .png nhỏ xíu. Cách đó có 2 nhược điểm chí mạng:
-
Không đổi được màu bằng CSS: Khi rê chuột vào muốn icon mờ đi hoặc đổi từ màu trắng sang màu cam Shopee, ảnh
.pnghoàn toàn bó tay. -
Bị vỡ hạt (Pixelated): Khi xem trên màn hình Retina độ phân giải cao hoặc phóng to trang web, ảnh
.pngsẽ bị nhòe.
Với FontAwesome, mỗi chiếc icon thực chất là một ký tự văn bản dạng Vector. Bạn có thể phóng to nó lên 100px bằng font-size, hoặc đổi màu nó bằng color: #ee4d2d mượt mà y hệt như đang chỉnh một chữ cái!
2. Cơ chế hoạt động của Popover QR Code & Bẫy "Khe hở tử thần"
Khi bạn rê chuột vào mục <li class="nav-item">Tải ứng dụng</li>, chúng ta muốn hộp thoại <div class="header-qr"> hiện ra ngay bên dưới nó. Để làm được điều này mà không làm xô lệch chiều cao 34px của thanh Top Navbar, chúng ta sử dụng cặp bài trùng định vị kinh điển:
-
Thẻ cha (
.nav-item): Đặtposition: relative(Làm gốc tọa độ). -
Thẻ con (
.header-qr): Đặtposition: absolutevàtop: 120%,left: 0(Tách khỏi luồng trang và nằm lơ lửng ngay dưới đáy thẻ cha).
Tuy nhiên, đây chính là lúc 99% người mới học CSS gặp phải một cái bẫy kinh điển mang tên "Khe hở tử thần" (The Hover Gap Trap):
Plaintext
[ Tải ứng dụng (Thẻ cha <li>) ] <-- Đang đặt chuột ở đây: QR hiện ra
↕ Khe hở trống 6px (top: 120%) <-- Kéo chuột qua khe này: MẤT HOVER -> QR tự tắt!
+---------------------------------------+
| |
| [ MÃ QR SHOPEE ] |
| |
| [App Store] [Google Play] |
+---------------------------------------+
Giải thích nguyên nhân: Để hộp QR nhìn thoáng đãng và không dính sát vào chữ "Tải ứng dụng", chúng ta phải đẩy nó cách chữ một khoảng tầm
6pxđến8px. Nhưng ngay khoảnh khắc người dùng kéo con trỏ chuột từ chữ "Tải ứng dụng" đi xuống hộp QR để bấm vào nút App Store, con chuột phải đi ngang qua khe hở 6px đó. Khi chuột lọt vào khe hở, trình duyệt hiểu là bạn đã rời khỏi thẻ<li>Hộp QR biến mất ngay trước mũi người dùng!
Giải pháp của chuyên gia — "Cây cầu vô hình" (::before):
Chúng ta dùng phần tử giả .header-qr::before tạo ra một dải băng trong suốt (height: 15px, top: -10px) bắc ngang qua khe hở để nối liền chữ "Tải ứng dụng" với hộp QR. Nhờ cây cầu tàng hình này, chuột đi từ trên xuống dưới vẫn luôn nằm trong phạm vi của thẻ <li>, giúp hộp QR đứng vững 100%!
3. Thực hành: Nâng cấp HTML & CSS cho Top Navbar Shopee
Bước 1: Nhúng Google Fonts Roboto & FontAwesome vào thẻ <head> (index.html)
Mở file index.html, tìm đến khu vực <head> và chèn thêm đường dẫn Google Fonts (với các độ đậm 300, 400, 500, 700) cùng thư viện FontAwesome 6 miễn phí:
HTML
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shopee Việt Nam | Mua và Bán Trên Ứng Dụng Di Động Hoặc Website</title>
<!-- 1. Nhúng Google Fonts: Roboto (Độ đậm 300, 400, 500, 700) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
<!-- 2. Nhúng thư viện Icon FontAwesome 6 CDN -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<!-- 3. Các file CSS nội bộ của dự án -->
<link rel="stylesheet" href="./assets/css/reset.css">
<link rel="stylesheet" href="./assets/css/base.css">
<link rel="stylesheet" href="./assets/css/main.css">
</head>
Đồng thời, mở nhanh file ./assets/css/base.css và cập nhật lại dòng font-family trong thẻ html để kích hoạt font Roboto cho toàn trang:
CSS
/* Trong ./assets/css/base.css */
html {
font-size: 62.5%;
line-height: 1.6rem;
font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif;
scroll-behavior: smooth;
}
Bước 2: Bổ sung Icon và Khối HTML QR Code vào index.html
Hãy cập nhật lại thẻ <nav class="top-navbar"> trong index.html như sau (Chú ý class .nav-item--has-qr ở mục Tải ứng dụng và các thẻ <i class="fa-..."> dùng để hiển thị icon):
HTML
<!-- 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>
<!-- Mục Tải ứng dụng có chứa hộp thoại QR Code ẩn bên trong -->
<li class="nav-item nav-item--separate nav-item--has-qr">
<a href="#" class="nav-link">Tải ứng dụng</a>
<!-- HỘP THOẠI QR CODE (Mặc định ẩn, hiện khi Hover) -->
<div class="header-qr">
<!-- Sử dụng API tạo ảnh QR mẫu để bạn chạy thử có hình ngay lập tức -->
<img src="https://api.qrserver.com/v1/create-qr-code/?size=180x180&data=https://shopee.vn" alt="Shopee QR Code" class="header-qr-img">
<div class="header-qr-apps">
<a href="#" class="header-qr-link">
<i class="fa-brands fa-apple"></i> App Store
</a>
<a href="#" class="header-qr-link">
<i class="fa-brands fa-google-play"></i> Google Play
</a>
<a href="#" class="header-qr-link">
<i class="fa-solid fa-bag-shopping"></i> AppGallery
</a>
</div>
</div>
</li>
<li class="nav-item">
<span class="nav-title-no-pointer">Kết nối</span>
<a href="#" class="nav-icon-link" title="Kết nối Facebook">
<i class="nav-icon fa-brands fa-facebook"></i>
</a>
<a href="#" class="nav-icon-link" title="Kết nối Instagram">
<i class="nav-icon fa-brands fa-instagram"></i>
</a>
</li>
</ul>
<!-- Cụm menu bên phải -->
<ul class="nav-list">
<li class="nav-item">
<a href="#" class="nav-link">
<i class="nav-icon fa-regular fa-bell"></i>
Thông Báo
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="nav-icon fa-regular fa-circle-question"></i>
Hỗ Trợ
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="nav-icon fa-solid fa-globe"></i>
Tiếng Việt
<i class="fa-solid fa-chevron-down" style="font-size: 1rem; margin-left: 2px;"></i>
</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>
Bước 3: Viết CSS cho Icon, Hiệu ứng @keyframes fadeIn và Popover QR Code
Đầu tiên, vì hiệu ứng hiện dần lên (fadeIn) sẽ được tái sử dụng ở rất nhiều nơi (QR Code, Hộp Thông báo, Giỏ hàng mini), chúng ta hãy đặt @keyframes fadeIn vào cuối file ./assets/css/base.css:
CSS
/* Bổ sung vào cuối file ./assets/css/base.css */
/* Hiệu ứng chuyển động hiện dần mượt mà dùng chung toàn trang */
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
Tiếp theo, mở file ./assets/css/main.css và bổ sung CSS căn chỉnh Icon cùng hộp thoại QR Code vào cuối phần Top Navbar:
CSS
/* Bổ sung vào ./assets/css/main.css */
/* Căn chỉnh cho thẻ .nav-item và .nav-link để chữ và icon nằm thẳng tâm ngang */
.nav-item,
.nav-link,
.nav-icon-link {
display: inline-flex;
align-items: center;
}
/* Khoảng cách và kích thước chuẩn cho các Icon trên Topbar */
.nav-icon-link {
color: var(--white-color);
transition: opacity 0.2s ease;
}
.nav-icon-link:hover {
opacity: 0.7;
}
/* Tạo khoảng cách nhỏ giữa chữ "Kết nối" và icon Facebook đầu tiên */
.nav-title-no-pointer {
margin-right: 0.4rem;
}
.nav-icon {
font-size: 1.6rem; /* Icon to 16px (lớn hơn chữ 13px một chút để cân đối thị giác) */
margin: 0 0.4rem; /* Cách đều trái phải 4px */
}
/* --- KHU VỰC HỘP THOẠI QR CODE (HEADER QR POPOVER) --- */
.header-qr {
width: 18.6rem; /* Rộng 186px chuẩn Shopee */
background-color: var(--white-color);
position: absolute;
left: 0;
top: calc(100% + 0.8rem); /* Đẩy xuống dưới đáy thẻ <li> một khoảng 8px */
padding: 0.8rem;
border-radius: 2px;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.15);
z-index: 10; /* Đảm bảo hộp QR luôn nổi đè lên thanh Tìm kiếm bên dưới */
display: none; /* Mặc định ẩn đi */
animation: fadeIn ease-in 0.2s; /* Kích hoạt hiệu ứng hiện dần trong 0.2 giây */
}
/* TUYỆT CHIÊU: Tạo "Cầu nối vô hình" lấp đầy khe hở 8px để không bị mất Hover */
.header-qr::before {
content: "";
display: block;
position: absolute;
width: 100%;
height: 1.6rem; /* Cao 16px, dư sức che kín khe hở 8px */
left: 0;
top: -1.2rem; /* Đẩy ngược lên phía trên hộp QR */
/* Mẹo debug: Hãy thử bỏ comment dòng background bên dưới để nhìn thấy cây cầu! */
/* background-color: rgba(255, 0, 0, 0.4); */
}
/* Khi rê chuột vào thẻ <li> có class .nav-item--has-qr -> Hiện hộp .header-qr bên trong nó */
.nav-item--has-qr:hover .header-qr {
display: block;
}
.header-qr-img {
width: 100%;
border-radius: 2px;
}
/* Chia lưới 2 cột cho các nút tải App Store / Google Play / AppGallery */
.header-qr-apps {
display: flex;
flex-wrap: wrap; /* Cho phép rớt xuống hàng thứ 2 khi hết chỗ */
justify-content: space-between;
margin-top: 0.6rem;
padding: 0 0.4rem;
}
.header-qr-link {
font-size: 1.1rem;
color: var(--text-primary);
font-weight: 500;
margin-bottom: 0.4rem;
display: flex;
align-items: center;
gap: 0.3rem;
}
.header-qr-link:hover {
color: var(--shopee-primary);
}

4. Giải mã dòng code: Những bí kíp thực chiến đáng giá ngàn vàng
| Kỹ thuật sử dụng | Dòng code tiêu biểu | Tại sao chuyên gia lại viết như vậy? |
|---|---|---|
| Bộ chọn cha - con khi Hover | .nav-item--has-qr:hover .header-qr |
Nếu bạn đặt :hover vào thẻ <a> (Tải ứng dụng), ngay khi kéo chuột xuống hộp QR, chuột đã rời khỏi thẻ <a> làm hộp QR tắt mất. Đặt :hover ở thẻ cha <li> (vốn bọc cả thẻ <a> lẫn hộp .header-qr) giúp giữ trạng thái mở ngay cả khi chuột đang di chuyển bên trong hộp QR! |
Căn tâm Icon và Chữ bằng inline-flex |
display: inline-flex; align-items: center; |
Vì Icon (16px) cao hơn chữ (13px), nếu để mặc định, chữ sẽ bị tụt xuống đáy so với quả chuông. Dùng inline-flex kết hợp align-items: center ép tâm của quả chuông và tâm của dòng chữ nằm trên một đường thẳng tắp. |
Chia hàng tự động bằng flex-wrap: wrap |
display: flex; flex-wrap: wrap; |
Khu vực dưới mã QR có 3 cửa hàng ứng dụng (App Store, Google Play, AppGallery). Mặc định display: flex sẽ ép cả 3 nằm chèn chúc trên 1 hàng rất chật. Thêm flex-wrap: wrap giúp 2 mục đầu nằm hàng 1, mục thứ 3 tự động rớt xuống hàng 2 gọn gàng! |
5. Bài tập trải nghiệm "Bắt tận tay, day tận trán" & Hẹn gặp ở Bài 05!
Hãy lưu toàn bộ file lại và F5 trình duyệt! Giờ đây thanh Top Navbar của bạn đã có nét chữ Roboto sắc sảo, dàn icon mạng xã hội và thông báo cực chuẩn. Hãy thử rê chuột vào chữ "Tải ứng dụng" — hộp mã QR trắng trẻo kèm bóng đổ nhẹ nhàng sẽ hiện ra ngay tức khắc!
Thử thách thực nghiệm thú vị dành cho bạn:
-
Mở file
main.css, tìm đến.header-qr::beforevà bỏ dấu comment (/* ... */) ở dòngbackground-color: rgba(255, 0, 0, 0.4);. Khi rê chuột vào "Tải ứng dụng", bạn sẽ tận mắt nhìn thấy "cây cầu màu đỏ bán trong suốt" nối giữa dòng chữ và hộp QR! -
Sau đó, hãy thử xóa (hoặc comment) toàn bộ khối
.header-qr::befoređi rồi thử rê chuột từ chữ "Tải ứng dụng" xuống bấm vào nút App Store bên dưới xem bạn có bấm được không, hay hộp QR bị tắt phụt ngay lập tức nhé!
Ở Bài 05, chúng ta sẽ chinh phục một thử thách lớn hơn ngay trên thanh Top Navbar này: Xây dựng Hộp thoại Thông báo mới nhận (Notification Dropdown) với hiệu ứng mũi tên tam giác nhọn chỉ lên trên được vẽ hoàn toàn bằng thủ thuật border trong CSS!
All rights reserved