[Series 100 Bài Học HTML/CSS Thực Chiến - Bài 06]: Dựng Vùng Header With Search, Chèn Logo Vector SVG & Bí Thuật Mở Lịch Sử Tìm Kiếm Bằng Bộ Chọn Anh Em (:focus ~)
Chào mừng anh em đã bước sang Bài 06 của chuỗi 100 Bài Học Làm Chủ HTML/CSS Qua Dự Án Clone Shopee!
Sau 5 bài đầu tiên, chúng ta đã hoàn thiện 100% thanh Top Navbar phía trên cùng. Hôm nay, chúng ta chính thức bước xuống tầng thứ hai — khu vực chiếm diện tích lớn nhất và quan trọng nhất của toàn bộ Header Shopee: Header With Search (86px).
Khu vực này là "trái tim" điều hướng người dùng mua sắm, bao gồm 3 thành phần:
-
Logo Shopee dạng Vector (
<svg>) sắc nét tuyệt đối trên mọi màn hình và có thể đổi màu trực tiếp bằng CSS. -
Khung Tìm Kiếm (Search Bar) co giãn thông minh (
flex: 1), đi kèm dãy Từ khóa Hot Trend nằm ngay bên dưới. -
Hộp Lịch Sử Tìm Kiếm (Search History Dropdown): Tự động bật ra khi người dùng bấm chuột vào ô nhập liệu (Click/Focus) chứ không phải khi rê chuột (Hover) như ở Bài 04 và 05!
Làm thế nào để bắt sự kiện bấm chuột vào thẻ <input> mà không cần dùng tới một dòng JavaScript nào? Câu trả lời nằm ở cặp bài trùng input:focus và Bộ chọn anh em liền kề (~ / +) trong CSS. Hãy cùng mổ xẻ ngay bây giờ!
1. Tại sao các trang E-Commerce lớn luôn dùng Logo dạng <svg> thay vì ảnh .png?
Khi chèn Logo vào trang web, người mới thường dùng thẻ <img src="logo.png">. Tuy nhiên, trên trang chủ Shopee, Lazada hay Tiki, bạn sẽ thấy họ nhúng trực tiếp thẻ <svg> (Scalable Vector Graphics) vào trong code HTML.
Hãy nhìn vào sự khác biệt giữa đồ họa Vector (SVG) và đồ họa điểm ảnh (Bitmap / PNG) khi phóng to dưới đây:

Việc nhúng trực tiếp <svg> cho Logo Shopee mang lại 3 lợi thế vượt trội:
| Tiêu chí | Ảnh .png / .jpg (Bitmap) |
Đồ họa <svg> (Vector) |
|---|---|---|
| Độ sắc nét trên màn hình Retina/4K | Dễ bị mờ hoặc răng cưa khi người dùng zoom trang web trên điện thoại/MacBook. | Được vẽ bằng công thức toán học (tọa độ điểm & đường cong), phóng to 1000 lần vẫn nét căng. |
Đổi màu bằng CSS (fill) |
Muốn đổi Logo từ màu Trắng (trên nền cam) sang màu Cam (ở trang Đăng nhập nền trắng), bạn phải cắt 2 file ảnh khác nhau. | Chỉ cần viết 1 dòng CSS fill: var(--shopee-primary); là Logo tự động đổi màu ngay lập tức! |
| Tốc độ tải trang (HTTP Request) | Trình duyệt phải gửi thêm 1 request lên server để tải file ảnh về. | Nằm sẵn trong code HTML nên hiện ra tức thì (0ms delay), dung lượng chỉ vài KB. |
2. Giải mã bí thuật: Bấm vào <input> hiện Lịch sử tìm kiếm bằng :focus ~
Ở Bài 04 và 05, hộp QR Code và Thông Báo hiện ra khi rê chuột (:hover) vào thẻ cha <li>. Nhưng với Thanh tìm kiếm, nếu vừa rê chuột ngang qua mà hộp Lịch sử tìm kiếm đã xổ xuống thì sẽ rất vướng mắt! Người dùng chỉ muốn hộp Lịch sử hiện ra khi họ thực sự bấm con trỏ chuột vào trong ô <input> để gõ chữ.
Trong CSS, chúng ta có trạng thái giả :focus (Kích hoạt khi con trỏ nhấp nháy bên trong thẻ <input>). Tuy nhiên, thẻ <input> là một thẻ tự đóng (Void Element) — nó không thể chứa thẻ <div class="header-search-history"> ở bên trong như thẻ <li> được!
Vậy làm sao để khi <input> được :focus, nó có thể ra lệnh cho thẻ <div class="header-search-history"> nằm bên ngoài hiện lên? Chúng ta đặt chúng làm hai anh em ruột nằm cạnh nhau và dùng General Sibling Combinator (~):
Plaintext
<div class="header-search-input-wrap"> (Thẻ cha định vị: position: relative)
├── <input class="header-search-input"> (Người anh: Đang được bấm chuột :focus)
│ │
│ └── Dùng dấu ngã (~) phát tín hiệu xuống người em cùng cấp!
│
└── <div class="header-search-history"> (Người em: position: absolute -> Bật display: block!)
</div>
Công thức vàng:
.header-search-input:focus ~ .header-search-history { display: block; }(Dịch nghĩa: Khi thẻ.header-search-inputđang được bấm chuột vào, hãy tìm thẻ em.header-search-historyđứng phía sau nó và bậtdisplay: blocklên!)
3. Thực hành: Code trọn bộ vùng Header With Search Shopee
Bước 1: Cập nhật cấu trúc HTML .header-with-search trong index.html
Hãy mở file index.html, tìm đến thẻ <div class="header-with-search"> nằm ngay bên dưới </nav> (trong .shopee-header) và thay thế bằng đoạn code chuẩn chỉnh sau:
HTML
<!-- Khu vực Logo, Tìm kiếm và Giỏ hàng -->
<div class="header-with-search">
<!-- 1. KHỐI LOGO SHOPEE (Dùng SVG để sắc nét tuyệt đối) -->
<div class="header-logo">
<a href="/" class="header-logo-link">
<svg class="header-logo-svg" viewBox="0 0 192 65" fill="none">
<!-- Icon túi xách Shopee -->
<path d="M27.5 14C27.5 7.6 22.4 2.5 16 2.5C9.6 2.5 4.5 7.6 4.5 14H0.5L3.2 55.5C3.4 58.6 6 61 9.1 61H22.9C26 61 28.6 58.6 28.8 55.5L31.5 14H27.5ZM16 6.5C20.2 6.5 23.5 9.8 23.5 14H8.5C8.5 9.8 11.8 6.5 16 6.5ZM16 48C11.5 48 8.5 45.2 8.2 41.5H12.2C12.5 43.3 13.9 44.5 16 44.5C18.2 44.5 19.8 43.2 19.8 41.2C19.8 39.2 18.4 38.2 15.2 37.3C11.2 36.2 8.5 34.3 8.5 30.2C8.5 26.2 11.7 23.5 16 23.5C20.3 23.5 23.3 26.2 23.6 29.8H19.6C19.3 28 18 27 16 27C14 27 12.5 28.2 12.5 30C12.5 31.8 13.8 32.7 17 33.6C21 34.7 23.8 36.6 23.8 41C23.8 45.3 20.4 48 16 48Z" fill="currentColor"/>
<!-- Chữ Shopee -->
<text x="40" y="47" fill="currentColor" font-size="36" font-weight="500" font-family="Roboto, sans-serif">Shopee</text>
</svg>
</a>
</div>
<!-- 2. KHỐI TÌM KIẾM TRUNG TÂM (Gồm Khung Search + Từ khóa gợi ý bên dưới) -->
<div class="header-search-wrapper">
<!-- Khung Search màu trắng -->
<div class="header-search">
<!-- Bọc Input và History chung một thẻ cha để định vị -->
<div class="header-search-input-wrap">
<input type="text" class="header-search-input" placeholder="Shopee bao ship 0Đ - Đăng ký ngay!">
<!-- HỘP LỊCH SỬ TÌM KIẾM (Hiện khi bấm chuột vào ô input) -->
<div class="header-search-history">
<h3 class="header-search-history-heading">Lịch sử tìm kiếm</h3>
<ul class="header-search-history-list">
<li class="header-search-history-item">
<a href="#">Bàn phím cơ không dây</a>
</li>
<li class="header-search-history-item">
<a href="#">Màn hình LG UltraGear 27 inch</a>
</li>
<li class="header-search-history-item">
<a href="#">Áo thun nam form rộng Cotton</a>
</li>
</ul>
</div>
</div>
<!-- Nút bấm tìm kiếm màu cam -->
<button class="header-search-btn">
<i class="header-search-btn-icon fa-solid fa-magnifying-glass"></i>
</button>
</div>
<!-- Dãy từ khóa Hot Trend nằm ngay dưới thanh tìm kiếm -->
<ul class="header-search-suggest-list">
<li><a href="#" class="header-search-suggest-item">Áo Khoác Dù</a></li>
<li><a href="#" class="header-search-suggest-item">Tai Nghe Bluetooth</a></li>
<li><a href="#" class="header-search-suggest-item">Dép Nam Quai Ngang</a></li>
<li><a href="#" class="header-search-suggest-item">Bình Giữ Nhiệt</a></li>
<li><a href="#" class="header-search-suggest-item">Chuột Không Dây</a></li>
<li><a href="#" class="header-search-suggest-item">Ốp Lưng iPhone</a></li>
</ul>
</div>
<!-- 3. KHỐI GIỎ HÀNG (Sẽ làm Popover chi tiết ở Bài 07) -->
<div class="header-cart">
<div class="header-cart-wrap">
<i class="header-cart-icon fa-solid fa-cart-shopping"></i>
<span class="header-cart-notice">3</span>
</div>
</div>
</div>
Bước 2: Viết CSS dàn trang Flexbox & Hiệu ứng :focus ~ trong main.css
Mở file ./assets/css/main.css và bổ sung đoạn code dưới đây vào cuối phần Header:
CSS
/* ./assets/css/main.css */
/* --- KHU VỰC HEADER WITH SEARCH --- */
.header-with-search {
height: var(--header-with-search-height); /* 86px tự động tính bằng calc() ở Bài 02 */
display: flex;
align-items: center;
padding: 0 0.8rem;
}
/* 1. Khối Logo Shopee */
.header-logo {
width: 20rem; /* Rộng 200px chuẩn Shopee */
flex-shrink: 0;
}
.header-logo-link {
display: inline-block;
color: var(--white-color); /* Truyền màu trắng vào thuộc tính fill="currentColor" của SVG */
}
.header-logo-svg {
width: 15rem; /* Rộng 150px */
height: 5rem;
}
/* 2. Khối Tìm kiếm trung tâm (Chiếm toàn bộ khoảng trống ở giữa bằng flex: 1) */
.header-search-wrapper {
flex: 1;
display: flex;
flex-direction: column; /* Xếp chồng dọc: Thanh Search ở trên, Từ khóa gợi ý ở dưới */
margin-top: 1.2rem; /* Đẩy nhẹ xuống để cân đối thị giác */
}
.header-search {
height: 4rem; /* Chiều cao thanh tìm kiếm chuẩn 40px */
background-color: var(--white-color);
border-radius: 2px;
display: flex;
align-items: center;
padding: 0.3rem; /* Tạo khoảng đệm trắng 3px bọc quanh nút Tìm kiếm màu cam */
box-shadow: 0 0.1rem 0.2rem 0 rgba(0, 0, 0, 0.1);
}
/* Thẻ bọc ô Input và Lịch sử tìm kiếm */
.header-search-input-wrap {
flex: 1; /* Ô input chiếm trọn chiều ngang còn lại của thanh trắng */
height: 100%;
position: relative; /* Làm gốc tọa độ cho hộp Lịch sử tìm kiếm */
}
.header-search-input {
width: 100%;
height: 100%;
font-size: 1.4rem;
color: var(--text-primary);
padding: 0 1.2rem;
border-radius: 2px;
}
/* Nút bấm kính lúp màu cam */
.header-search-btn {
background-color: var(--shopee-primary);
height: 3.4rem; /* Cao 34px (Nhỏ hơn khung trắng 40px đúng 6px = 3px trên + 3px dưới) */
width: 6rem; /* Rộng 60px */
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.15s ease;
}
.header-search-btn:hover {
background-color: #fb6445; /* Sáng lên nhẹ nhàng khi rê chuột vào nút tìm kiếm */
}
.header-search-btn-icon {
font-size: 1.4rem;
color: var(--white-color);
}
/* --- HỘP LỊCH SỬ TÌM KIẾM (SEARCH HISTORY DROPDOWN) --- */
.header-search-history {
position: absolute;
top: calc(100% + 0.6rem); /* Nằm cách đáy thanh tìm kiếm 6px */
left: -0.3rem; /* Bù lại 3px padding của .header-search để thẳng mép trái tuyệt đối */
width: calc(100% - 1.6rem); /* Ngắn hơn ô input một chút giống giao diện Shopee thật */
background-color: var(--white-color);
border-radius: 2px;
box-shadow: 0 1px 5px rgba(189, 189, 189, 0.5);
overflow: hidden; /* Cắt gọn các góc của thẻ <li> bên trong theo border-radius 2px */
z-index: 9;
display: none; /* Mặc định ẩn */
}
/* BÍ THUẬT: Khi bấm chuột vào ô input (:focus), hiện hộp lịch sử đứng sau nó (~) */
.header-search-input:focus ~ .header-search-history {
display: block;
}
.header-search-history-heading {
margin: 0.8rem 1.2rem;
font-size: 1.4rem;
color: rgba(0, 0, 0, 0.26); /* Màu chữ xám mờ cho tiêu đề Lịch sử */
font-weight: 400;
}
.header-search-history-list {
padding-left: 0;
margin: 0;
}
.header-search-history-item {
height: 3.8rem; /* Cao 38px mỗi dòng lịch sử */
padding: 0 1.2rem;
transition: background-color 0.1s ease;
}
.header-search-history-item:hover {
background-color: #fafafa;
}
.header-search-history-item a {
font-size: 1.4rem;
color: var(--text-primary);
line-height: 3.8rem;
display: block; /* Mở rộng vùng bấm ra toàn bộ chiều ngang của dòng */
}
/* --- DÃY TỪ KHÓA HOT TREND BÊN DƯỚI THANH SEARCH --- */
.header-search-suggest-list {
display: flex;
align-items: center;
gap: 1.4rem; /* Khoảng cách 14px giữa các từ khóa */
margin-top: 0.6rem;
height: 1.8rem;
overflow: hidden; /* Ẩn bớt nếu danh sách từ khóa quá dài */
}
.header-search-suggest-item {
font-size: 1.2rem;
color: rgba(255, 255, 255, 0.9);
font-weight: 300;
white-space: nowrap; /* Không cho từ khóa bị gãy làm 2 dòng */
}
.header-search-suggest-item:hover {
color: var(--white-color);
}
/* 3. Khối Giỏ hàng bên phải */
.header-cart {
width: 15rem; /* Rộng 150px */
display: flex;
justify-content: center;
align-items: center;
flex-shrink: 0;
}
.header-cart-wrap {
position: relative;
padding: 0 1.2rem;
cursor: pointer;
}
.header-cart-icon {
font-size: 2.4rem; /* Icon xe đẩy hàng to 24px */
color: var(--white-color);
}
/* Con số thông báo số lượng sản phẩm trên góc xe đẩy */
.header-cart-notice {
position: absolute;
top: -0.8rem;
right: -0.2rem;
padding: 0.1rem 0.6rem;
font-size: 1.4rem;
line-height: 1.4rem;
border-radius: 1rem; /* Bo tròn thành hình viên thuốc (Pill badge) */
border: 2px solid var(--shopee-primary); /* Viền cam tạo khoảng tách biệt với xe đẩy trắng */
color: var(--shopee-primary);
background-color: var(--white-color);
font-weight: 500;
}

4. Giải mã dòng code: 4 Kỹ thuật thực chiến cực hay trong Bài 06
-
Bố cục 3 cột co giãn thông minh (
200px-flex: 1-150px):-
Trong
.header-with-search, chúng ta cố định chiều rộng của Logo (.header-logo { width: 20rem; }) và Giỏ hàng (.header-cart { width: 15rem; }). -
Toàn bộ phần tìm kiếm ở giữa được gắn
flex: 1. Thuộc tính này giúp thanh tìm kiếm tự động phình to ra lấp đầy 100% khoảng trống còn lại (1200px - 200px - 150px = 850px). Khi thu nhỏ màn hình, thanh tìm kiếm cũng tự động co lại mượt mà!
-
-
Từ khóa
currentColorbên trong thẻ<svg>:- Bạn có để ý trong thẻ
<svg>của Logo, chúng ta đặtfill="currentColor"không? Từ khóacurrentColorgiúp hình vẽ SVG tự động lấy màu từ thuộc tínhcolorcủa thẻ<a>bọc ngoài nó! Muốn đổi màu cả túi xách lẫn chữ Shopee, bạn chỉ cần đổicolorcủa.header-logo-link.
- Bạn có để ý trong thẻ
-
Mẹo tạo viền trắng bọc quanh nút Tìm kiếm bằng
padding: 0.3rem:- Trên Shopee, nút kính lúp màu cam nằm lọt lòng bên trong thanh tìm kiếm màu trắng và cách 3 mép (trên, dưới, phải) đúng
3px. Thay vì dùngmarginphức tạp, chúng ta chỉ cần cho thanh.header-searchcao40px,padding: 3px, và cho nút bấm.header-search-btncao34px(40 - 3*2 = 34). Nút bấm sẽ nằm khít rịt tuyệt đối!
- Trên Shopee, nút kính lúp màu cam nằm lọt lòng bên trong thanh tìm kiếm màu trắng và cách 3 mép (trên, dưới, phải) đúng
-
Huy hiệu số lượng giỏ hàng (
.header-cart-notice):- Viên thuốc màu trắng chứa số
3đè lên góc trên bên phải của chiếc xe đẩy màu trắng. Nếu không có dòngborder: 2px solid var(--shopee-primary);, viên thuốc trắng và xe đẩy trắng sẽ dính liền vào nhau rất khó nhìn. Đường viền màu cam2pxđóng vai trò như một "rãnh cắt" tách biệt hai vật thể cùng màu trắng!
- Viên thuốc màu trắng chứa số
5. Bài tập thử thách & Hẹn gặp ở Bài 07!
Hãy lưu toàn bộ file lại và F5 trình duyệt! Giờ đây vùng Header của bạn đã mang diện mạo y hệt Shopee thật 99%: Logo trắng sắc nét, thanh tìm kiếm dài bề thế kèm nút kính lúp cam, dãy từ khóa gợi ý bên dưới và chiếc xe đẩy hàng có gắn số 3 nổi bật. Hãy thử click chuột vào trong ô tìm kiếm — danh sách Lịch sử tìm kiếm sẽ bật ra ngay tức khắc, và khi bạn click chuột ra ngoài vùng trắng, nó sẽ tự động đóng lại!
Thử thách khám phá dành cho bạn trước khi sang Bài 07:
-
Hãy thử bấm vào ô input để mở hộp Lịch sử tìm kiếm, sau đó đưa chuột xuống bấm vào dòng "Bàn phím cơ không dây". Bạn có nhận thấy đôi khi vừa bấm chuột xuống thì ô input bị mất
:focuslàm hộp lịch sử đóng sập lại trước khi thẻ<a>kịp chuyển trang không? Hãy thử tìm hiểu xem thuộc tính:active(ví dụ:.header-search-history:hoverhoặc.header-search-history:active) có thể giúp giữ hộp lịch sử đứng yên khi click như thế nào nhé! -
Thử thay đổi con số trong
<span class="header-cart-notice">3</span>thành99+xem viên thuốc màu trắng có tự động giãn dài ra đẹp mắt nhờpadding: 0.1rem 0.6remvàborder-radius: 1remkhông nhé!
Ở Bài 07, chúng ta sẽ hoàn thiện mảnh ghép cuối cùng của vùng Header: Xây dựng Hộp thoại Giỏ Hàng Mini (Mini Cart Popover) với 2 trạng thái thực tế: Giỏ hàng trống (no-cart) và Giỏ hàng có sản phẩm (kèm danh sách sản phẩm, giá tiền, phân loại hàng và nút Xem Giỏ Hàng)!
All rights reserved