- 1 Kiến thức cơ bản
- 2 Flexbox
- 3 Lưới và lưới phụ
- 4 Truy vấn vùng chứa
- 5 Lớp phủ gốc, vị trí neo và bối cảnh xếp chồng
- 6 Theo dõi phần tử tràn và độ ổn định của bố cục
- 7 Cơ chế khung nhìn và phân phối bản nhạc
- 8 làn lưới (còn gọi là bố cục dạng khối)
1 Kiến thức cơ bản
Hãy dựa vào công cụ bố cục của trình duyệt khi có thể để có hiệu suất tốt hơn. Hãy sử dụng kích thước nội tại, thuộc tính logic và aspect-ratio trước khi dùng đến kích thước được cố định giá trị trong mã hoặc truy vấn đa phương tiện phức tạp.
1.1 Nên sử dụng chế độ bố cục nào?
Xem sơ đồ quyết định từ trên xuống dưới và dừng lại ở kết quả phù hợp đầu tiên. Xin lưu ý rằng các bố cục có thể được lồng vào nhau và mỗi quyết định đều dựa trên trường hợp sử dụng cho vùng chứa đó.
- Đó có phải là một hàng HOẶC cột mục đơn giản không? Sử dụng flexbox – 1D, ưu tiên nội dung, nội dung phân phối dọc theo một trục duy nhất.
- Phần tử lồng nhau có cần phải xếp thẳng hàng với các dải của lưới cấp ông bà không? Sử dụng subgrid – 2D, ưu tiên mối quan hệ, kế thừa các dải của phần tử mẹ để các phần tử cháu có thể căn chỉnh trên các phần tử đồng cấp.
- Đó có phải là một trang hoặc cấu trúc thành phần phức tạp có cả hàng VÀ cột không? Sử dụng grid (lưới) – 2D, ưu tiên bố cục, bạn xác định khung và nội dung sẽ lấp đầy khung đó.
- Nội dung có phải là một dòng văn xuôi dài nên được chia thành các cột cân bằng không? Sử dụng nhiều cột – luồng 1D, kiểu báo.
- Các mặt hàng có chiều cao khác nhau có cần được đóng gói chặt không? Sử dụng grid với
grid-auto-flow: densengay hôm nay; chỉ sử dụng bố cục khối xây gốc (hay còn gọi là "đường lưới") khi bố cục này được cung cấp trong mục tiêu Baseline (xem §8). - Một phần tử có cần nổi phía trên trang và luôn được liên kết về mặt không gian với một trình kích hoạt, ngay cả khi vượt qua ranh giới DOM hoặc bối cảnh xếp chồng không? Sử dụng vị trí neo —
anchor-nametrên trình kích hoạt,position-anchortrên lớp phủ (xem §5).
1.2 Nguyên tắc hoạt động
- Sử dụng các thuộc tính logic (
inline-size,block-size,margin-inline,padding-block,inset-inline-start) cho kích thước và khoảng cách bố cục — xem CSS: Kiến trúc hiện đại để biết thông tin đầy đủ. - Áp dụng mô hình tư duy ưu tiên nội dung so với ưu tiên bố cục: flexbox khi các mục quyết định luồng, lưới khi bạn xác định khung trước.
- Sử dụng các thuộc tính viết tắt
place-*(place-content,place-items,place-self) để căn chỉnh trên cả hai trục trong một khai báo. - Sử dụng kích thước nội tại (
min-content,max-content,fit-content()) và các dải linh hoạt (fr,minmax()) trước khi sử dụngwidth/heightcố định — ít truy vấn đa phương tiện hơn, bố cục linh hoạt hơn. - KHÔNG sử dụng
overflow: hiddenđể che giấu các vấn đề về bố cục — hãy khắc phục các vấn đề đó tại nguồn. Ví dụ: một số phần tử được thay thế như<canvas>,<img>,<svg>,<video>,<iframe>sử dụngdisplay: inlinetheo mặc định, điều này có thể thêm khoảng trống trong một số bố cục nhất định. Thay vì cắt khoảng trống bằngoverflow, hãy xoá khoảng trống đó bằng cách áp dụng giá trị hiển thị bên ngoài khối (ví dụ:display: block,grid,flex, v.v.). - Sử dụng
aspect-ratiođể dành không gian cho nội dung nghe nhìn và ngăn bố cục thay đổi trước khi thành phần tải.
.sidebar { inline-size: max-content; } /* Size to longest unbreakable token. */
.main-content { inline-size: fit-content; } /* Grow to available space, no further. */
.media { aspect-ratio: 16 / 9; inline-size: 100%; block-size: auto; }
body.centered { display: grid; place-content: center; min-block-size: 100dvb; }
Để biết thông tin về
calc-size()và kích thước nội tại nhận biết quy tắc ràng buộc, hãy xem bài viết Tính toán bằng kích thước nội tại.
2 Flexbox
Bố cục một chiều – các mục di chuyển dọc theo một trục chính và căn chỉnh trên trục chéo. Hãy sử dụng thành phần này cho thanh điều hướng, thanh công cụ, hàng mục và mọi cách phân phối một hàng hoặc một cột.
Nên làm:
- Thiết lập ngữ cảnh bằng
display: flexvà đặt trục chính bằngflex-direction(mặc định làrow). - Sử dụng
flex-wrap: wrapbất cứ khi nào có thể xảy ra tình trạng tràn —nowrapkhông cóoverflow: auto/hiddensẽ tràn trên khung nhìn hẹp. - Xếp lớp
flex-wrap: balancesauflex-wrap: wraptrên các hàng được gói của các mục nhỏ gọn (chẳng hạn như đám mây thẻ, khối bộ lọc, nhóm nút hoặc đường liên kết điều hướng) để phân phối các mục đồng đều trên các dòng và ngăn một mục đơn lẻ bị tách riêng trên hàng cuối cùng; các trình duyệt không hỗ trợ sẽ quay lạiwrap. - Sử dụng viết tắt
flex<grow> <shrink> <basis>(ví dụ:flex: 1 1 250px) trên các mục thay vì đặtflex-grow/flex-shrink/flex-basisriêng lẻ. - Sử dụng
gap(hoặcrow-gap/column-gapdạng dài) để tạo khoảng cách giữa các mục thay vì lề con. - Thêm tiền tố căn chỉnh vị trí bằng
safe(ví dụ:align-items: safe center) để nội dung có thể làm tâm điểm không bị cắt bớt khi vùng chứa hẹp hơn nội dung. - Đẩy một mục duy nhất đến cuối trục chính bằng
margin-inline-start: auto(hoặcmargin-block-start: auto) — đó là lối thoát tiêu chuẩn. - Ghi đè căn chỉnh trục chéo cho mỗi mục bằng
align-self. - Sử dụng
align-itemsđể căn giữa tất cả các mục trên trục chéo; sử dụngmargin: autotrên một mục để căn giữa mục đó trên cả hai trục một cách độc lập; chỉ sử dụngalign-contentkhi vùng chứa xuống dòng và có thêm không gian giữa các hàng. - Đặt
min-inline-size: 0(hoặcmin-width: 0) trên các mục linh hoạt chứa nội dung dài không thể ngắt (URL, mã, chuỗi dài) — các mục linh hoạt sẽ không thu nhỏ dưới kích thước nội dung theo mặc định, gây ra tình trạng tràn.
Không nên:
- Đừng dùng
justify-selftrên các mục linh hoạt — thuộc tính này chỉ hoạt động trên bố cục lưới, khối và bố cục được định vị tuyệt đối. Thay vào đó, hãy sử dụng lề tự động. - Đừng sử dụng
orderhoặcflex-direction: *-reverseđể sắp xếp lại nội dung tương tác. Các thuộc tính này chỉ thay đổi thứ tự hiển thị; thứ tự DOM vẫn điều khiển tiêu điểm tuần tự, vì vậy, luồng thẻ bàn phím sẽ không khớp với những gì người dùng nhìn thấy. - Đừng nhầm lẫn
space-around(khoảng cách bằng một nửa ở hai đầu) vớispace-evenly(khoảng cách bằng nhau ở trước, giữa và sau). - Đừng quên việc lật trục: khi
flex-direction: column,justify-contentcăn chỉnh trên trục khối vàalign-itemscăn chỉnh trên trục cùng dòng — ngược lại với mặc định. - Đừng đặt kích thước cho cả vùng chứa và các phần tử con của vùng chứa để lấp đầy lẫn nhau — đây là nguyên nhân phổ biến gây ra tình trạng tràn và kết quả không mong muốn. Cung cấp cho một bên một kích thước xác định.
- Đừng đặt cả
flex-basisvàwidth/inline-sizetrên cùng một mục —flex-basisđược ưu tiên trong ngữ cảnh linh hoạt vàwidthbị bỏ qua. Sử dụngflex-basis(hoặc viết tắt làflex) làm nguồn đáng tin cậy duy nhất để định cỡ các mục linh hoạt.
.card-grid { display: flex; flex-flow: row wrap; gap: 1rem; }
.card-item { flex: 1 1 250px; } /* grow, shrink, basis */
.card-item-action { margin-inline-start: auto; } /* Push to main-axis end. */
.toolbar { display: flex; align-items: safe center; }
.tag-list { display: flex; flex-wrap: wrap; flex-wrap: balance; gap: 0.5rem; }
3 Lưới và lưới phụ
Bố cục hai chiều — xác định rõ ràng các hàng VÀ cột hoặc để công cụ tự suy ra. Lưới phụ cho phép lưới lồng nhau kế thừa các dải của lưới mẹ để các lưới con căn chỉnh trên các lưới đồng cấp.
Chọn tính năng lưới:
- Bạn có biết chính xác mình cần bao nhiêu cột không?
- Có — sử dụng các bản nhạc có nội dung phản cảm (
grid-template-columns: 200px 1fr,repeat(3, 1fr), v.v.) - Các cột khác nhau có cần kích thước khác nhau không (thanh bên + chính, tiêu đề trải dài trên tất cả)? → sử dụng
grid-template-areascho các vùng có tên và dễ đọc - Tất cả các cột có đồng nhất hay được định vị hoàn toàn theo số dòng không? → sử dụng
repeat(N, ...)hoặc các đường kẻ được đặt tên - Không (thích ứng, không biết số lượng mục) — sử dụng
repeat(auto-fit, minmax(min, 1fr)) - Các mục ở hàng cuối cùng có nên kéo giãn để lấp đầy không gian còn lại không? →
auto-fit - Các dải trống ở hàng cuối có nên giữ kích thước tối thiểu (giữ lại các ô trống trong cột) không? →
auto-fill
- Có — sử dụng các bản nhạc có nội dung phản cảm (
- Bạn có cần đặt một mặt hàng tại một vị trí cụ thể không?
- Có – sử dụng
grid-column: <start> / <end>hoặcgrid-area: <name> - Không (chỉ trải dài trên nhiều kênh, vị trí luồng không quan trọng) — sử dụng
grid-column: span <n>
- Có – sử dụng
- Các phần tử con có cần kế thừa kích thước bản nhạc của lưới mẹ (căn chỉnh cạnh không đều giữa các phần tử đồng cấp) không?
- Có — sử dụng lưới phụ trên trục bị ảnh hưởng
- Số lượng phần tử con trên mỗi ô có thay đổi không? → lưới phụ chỉ một trục; sử dụng
grid-auto-rows/grid-auto-columnscho trục còn lại - Số lượng trẻ em có cố định không? → lưới phụ trên cả hai trục đều được
- Không — lưới chuẩn, không cần lưới phụ
Nên làm:
- Thiết lập bối cảnh bằng
display: grid. - Sử dụng
grid-template-areascho bố cục phức tạp ở cấp trang – tên vùng là tự ghi lại và nội dung khai báo có thể được căn chỉnh theo hàng và cột để dễ đọc. - Sử dụng
repeat(auto-fit, minmax(200px, 1fr))cho lưới thẻ thích ứng giúp kéo giãn các dải ô đã lấp đầy để lấp đầy hàng hoặcauto-fillđể giữ nguyên các dải ô lặp lại trống ở kích thước tối thiểu. - Sử dụng
frđể phân phối dải theo tỷ lệ vàminmax(min, max)cho các dải linh hoạt nhưng có giới hạn. - Đặt vị trí các mục bằng
grid-column: span <n>để định kích thước trên các dải,grid-column: <start> / <end>để đặt tại các dòng cụ thể hoặcgrid-area: <name>cho các vùng được đặt tên. - Sử dụng lưới phụ (
grid-template-columns: subgridhoặcgrid-template-rows: subgrid) để giải quyết vấn đề "cạnh không bằng phẳng" trong danh sách thẻ – các phần tử nội bộ như tiêu đề, siêu dữ liệu và lời kêu gọi hành động (CTA) xếp hàng ngang với các phần tử đồng cấp. - Ghép nối nội dung khai báo lưới phụ với nội dung khai báo
grid-template-rows/-columnsrõ ràng trước đó làm phương án dự phòng cùng tầng cho các trình duyệt cũ.
Không nên:
- Đừng mong đợi kích thước bản nhạc
auto-fit/auto-fillđến từ nội dung mục — kích thước này đến từ đối số kích thướcrepeat(). - Đừng sử dụng
grid-auto-flow: densetrên nội dung tương tác. Thuộc tính này sắp xếp các mục một cách hiệu quả nhưng lại sắp xếp lại thứ tự hiển thị, làm gián đoạn luồng thẻ bàn phím theo thứ tự DOM. - Đừng áp dụng lưới phụ cho cả hai trục khi số lượng phần tử con có thể thay đổi. Các phần tử bổ sung sẽ nằm trong kênh cuối cùng; hãy sử dụng
grid-auto-rows/grid-auto-columnscho trục ngầm định. - Đừng nhầm lẫn
justify-items/align-items(căn chỉnh nội dung của mục trong dải của mục đó) vớijustify-content/align-content(căn chỉnh các dải lưới trong vùng chứa). Việc sử dụng sai sẽ không có hiệu lực. - Đừng sử dụng
repeat(auto-fit/auto-fill, ...)mà không cóinline-sizexác định trên vùng chứa — bên trongdisplay: inline-gridhoặc một mục linh hoạt không có kích thước, vùng chứa không có chiều rộng để chia, khiến số lượng bản nhạc không thể dự đoán được.
3.1 Ví dụ về mã: lưới và lưới phụ
Page shell: <main class="page-layout"> contains <header>, <aside>, a <section class="card-grid"> with <div class="card"> children, and <footer>.
/* Align grid-template-areas in rows and columns for readability. */
.page-layout {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-areas:
"header header header"
"sidebar main main"
"footer footer footer";
gap: 1.5rem;
}
header { grid-area: header; }
aside { grid-area: sidebar; }
footer { grid-area: footer; }
.card-grid {
grid-area: main;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
grid-template-rows: auto 1fr; /* title block, body block */
gap: 1rem;
}
.card {
grid-row: span 2;
display: grid;
/* Same-cascade fallback: ignored when subgrid is supported. */
grid-template-rows: auto 1fr;
grid-template-rows: subgrid;
}
4 Truy vấn vùng chứa
Truy vấn kích thước (hoặc kiểu được tính toán) của vùng chứa gốc thay vì khung nhìn. Mô hình tư duy: truy vấn vùng chứa = ngữ cảnh thành phần; truy vấn đa phương tiện = bố cục trang chung và lựa chọn ưu tiên của người dùng (prefers-color-scheme, prefers-reduced-motion).
Nên làm:
- Thiết lập bối cảnh vùng chứa bằng
container-type: inline-size(chỉ truy vấn chiều rộng) hoặccontainer-type: size(cả hai trục) trên một trình bao bọc trước khi có thể truy vấn các thành phần con của trình bao bọc đó. - Đặt tên cho vùng chứa bằng
container-name(hoặc viết tắtcontainer:container: card / inline-size) khi các bối cảnh lồng nhau có thể xung đột. - Thêm các đơn vị truy vấn vùng chứa vào việc tính toán kiểu chữ và khoảng cách linh hoạt:
cqi/cqb(nội tuyến/khối logic),cqw/cqh(vật lý),cqmin/cqmax. - Cung cấp cho vùng chứa một
block-sizexác định bất cứ khi nàocontainer-type: sizeđược sử dụng — nếu không có, các phần tử con sẽ thu gọn vì vùng chứa kích thước buộc vùng chứa bỏ qua nội dung của nó.
Không nên:
- Đừng sử dụng
block-sizelàm giá trịcontainer-typevì giá trị này không hợp lệ. Sử dụngsizecho cả hai trục. - Đừng mong đợi kích thước nội tại của thành phần con sẽ ảnh hưởng đến vùng chứa sau khi khai báo
container-type. Vùng chứa được tính toán như thể không có phần tử con sau khi tính năng chứa được kích hoạt. - Đừng dựa vào các đơn vị truy vấn vùng chứa bên trong các phần tử con của một phần tử cấp trên không đủ điều kiện; các đơn vị này sẽ quay về khung nhìn nhỏ (
svw/svh).
4.1 Ví dụ về mã: kiểu chữ linh hoạt sử dụng đơn vị truy vấn vùng chứa
.card-wrapper {
container: card / inline-size; /* shorthand for container-name / container-type */
}
@container card (inline-size > 400px) {
.content {
display: flex;
gap: 2rem;
}
}
.title {
/* Fluid type bound to the container width, not the viewport. */
font-size: clamp(1rem, 4cqi, 2rem);
}
Để biết các mẫu định kiểu thích ứng dựa trên thành phần, hãy xem phần Định kiểu nhận biết kích thước và Điều chỉnh tỷ lệ linh hoạt.
5 Lớp phủ gốc, vị trí neo và bối cảnh xếp chồng
Trường hợp sử dụng từng thành phần gốc của lớp phủ:
- Sử dụng
popovercho giao diện người dùng tạm thời, không có phương thức (cửa sổ bật lên, thông báo ngắn, chú thích) – nằm ở lớp trên cùng, không cần quản lýz-index. - Sử dụng
<dialog>với.showModal()cho các hoạt động tương tác theo phương thức yêu cầu bẫy tiêu điểm và phông nền không hoạt động. - Đừng kết hợp
popovervà.showModal()trên cùng một phần tử – đây là các trạng thái thời gian chạy loại trừ lẫn nhau.
Định vị neo (bố cục không gian của lớp phủ):
- Sử dụng
position-area(hoặcanchor()trên phần lồng ghép) vàanchor-size()để định vị và định kích thước lớp phủ tương ứng với điều kiện kích hoạt. - Sử dụng
position-try-fallbacks: flip-block(hoặcflip-inline) để cho phép trình duyệt định vị lại khi lớp phủ tràn khung nhìn. - Đừng kết hợp từ khoá vật lý và từ khoá logic trong một giá trị
position-areaduy nhất — hãy chọn một hệ toạ độ. - Phát hiện tính năng bằng
@supports (anchor-name: --x)và cung cấp tính năng dự phòng vị trí tuyệt đối.
Để biết thông tin chi tiết đầy đủ về cách triển khai, chiến lược polyfill và tham chiếu giá trị
popover, hãy xem bài viết Hộp thoại khai báo và điều khiển cửa sổ bật lên và Chú giải công cụ nhận biết vị trí. Để biết cách áp dụng tính năng định vị neo cho trình đơn thả xuống, trình đơn thả ra và chỉ báo thẻ, hãy xem phần Trình đơn thả xuống nhận biết khung nhìn và Đường gạch dưới của thẻ định vị neo.
6 Theo dõi phần tử tràn và độ ổn định của bố cục
Quản lý các thay đổi về bố cục, thanh cuộn và việc cắt bớt một cách dễ đoán.
Nên làm:
- Sử dụng
overflow: autođể thanh cuộn chỉ xuất hiện khi nội dung thực sự bị tràn. - Sử dụng
overflow: clipđể cắt nội dung mà không cần thiết lập vùng chứa cuộn; chọn tràn bằngoverflow-clip-margin. - Sử dụng
scrollbar-gutter: stableđể dành không gian cho thanh cuộn và ngăn bố cục thay đổi khi nội dung tăng lên. - Sử dụng
overscroll-behavior: contain(hoặcnone) trên các vùng cuộn lồng nhau (chẳng hạn như thanh bên, cửa sổ phụ và danh sách) để ngăn chuỗi cuộn nổi lên trang hoặcoverscroll-behavior: chaintrên trình bao bọc cuộn trung gian (chẳng hạn như vuốt để hiển thị các bản nhạc) khi thao tác cuộn thừa sẽ truyền đến trình cuộn gốc mà không kích hoạt hiệu ứng nảy cục bộ. Sử dụng bộ ba
-webkit-line-clamp+display: -webkit-box+-webkit-box-orient: verticalđể cắt bớt nhiều dòng — bất kể tiền tố, mẫu này được chỉ định đầy đủ và không bị ngừng sử dụng. Khai báo viết tắtline-clampkhông có tiền tố cùng với thuộc tính đó; các trình duyệt chưa hỗ trợ sẽ bỏ qua thuộc tính này mà không gây hại. Không nên:Đừng sử dụng
overflow: scrollkhiautocó thể làm được —scrollbuộc thanh cuộn ngay cả khi không có gì để cuộn.Đừng dùng
overflow: hiddenkhi bạn chỉ muốn cắt bớt —hiddenthiết lập một vùng chứa cuộn có thể cuộn theo phương thức lập trình.
.scrollable-list {
max-block-size: 400px;
overflow-y: auto;
scrollbar-gutter: stable; /* Reserve scrollbar space. */
overscroll-behavior: contain; /* No scroll chaining into the page. */
}
.snippet {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
line-clamp: 3; /* Ignored where unsupported. */
overflow: clip;
}
Để tìm hiểu chi tiết về
overflow: clipvàoverflow-clip-margin, hãy xem phần Kiểm soát cắt bớt phần tràn. Để biết cách chuyển giao thao tác cuộn lồng nhau bằngoverscroll-behavior: chaintrong giao diện người dùng vuốt để hiện, hãy xem phần Vuốt để hiện. Để biết màu sắc, kích thước và giao diện của thanh cuộn, hãy xem phần Tuỳ chỉnh màu sắc hoặc độ dày của thanh cuộn, Chế độ tối và Điều chỉnh thanh cuộn theo lựa chọn ưu tiên về độ tương phản cao.
7 Cơ chế khung nhìn và phân phối bản nhạc
- Sử dụng
dvh/dvwcho vùng chứa bố cục dành cho thiết bị di động phải tính đến sự thay đổi giao diện người dùng của trình duyệt (thu gọn/mở rộng thanh URL). - Đừng sử dụng
100vwcho bố cục có chiều rộng tối đa vì thuộc tính này bỏ qua chiều rộng của thanh cuộn và gây ra tình trạng tràn theo chiều ngang. Thay vào đó, hãy sử dụng100%,100dvwhoặc100svw.
Để biết điểm truy cập thiết kế đáp ứng (đơn vị khung nhìn động, dành không gian cho nội dung nghe nhìn, kiểu chữ linh hoạt), hãy xem phần Thiết kế đáp ứng.
8 đường lưới (còn gọi là kiểu xếp gạch)
Thông số kỹ thuật này đang được phát triển dưới tên "làn đường lưới" (ví dụ: display: grid-lanes), trong khi các nguyên mẫu thử nghiệm trước đó sử dụng grid-template-rows: masonry.
Nên làm:
- Sử dụng lưới với
grid-auto-flow: denseđể đóng gói chặt chẽ ngay hôm nay, chấp nhận rằng thứ tự DOM có thể không khớp với thứ tự hình ảnh. - Sử dụng nhiều cột (
columns: 3; column-gap: 1rem) cho luồng giống như khối xây có nhiều nội dung khi các mục là các đoạn tài liệu thay vì các thẻ có trọng số bằng nhau. - Chỉ coi
grid-template-rows: masonrylà một cải tiến tăng dần – phát hiện tính năng bằng@supports.
Không nên:
- Đừng cung cấp
grid-template-rows: masonrydưới dạng yêu cầu bắt buộc cho đến khi mục tiêu Cơ sở của bạn bắt kịp.
.gallery { columns: 3 200px; column-gap: 1rem; }
.gallery > * { break-inside: avoid; margin-block-end: 1rem; }
@supports (grid-template-rows: masonry) {
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-template-rows: masonry;
gap: 1rem;
columns: unset;
}
}