
Web Development Essentials 035.3: Khái niệm cơ bản về SQL
Tạo bảng SQLite, hiểu khóa chính và CHECK, dùng CRUD có WHERE và bind tham số từ Node.js; kiểm tra transaction rollback, mở lại dữ liệu và API driver.

Tính content-box và border-box, hiểu luồng, position, float, Flexbox và Grid; thực hành bố cục responsive, kiểm tra chuỗi dài, tràn ngang và focus bàn phím.
Một trang hướng dẫn có hai cột ở máy tính nhưng tràn ngang trên điện thoại. Người sửa giảm cỡ chữ, rồi đặt overflow hidden để giấu phần thừa. Nội dung biến mất mà nguyên nhân vẫn còn: kích thước hộp vượt vùng chứa, phần tử có chiều rộng tối thiểu quá lớn hoặc một khối được định vị ngoài luồng. Bài này bắt đầu từ cách tính hộp để chọn phương án bố cục thích hợp.
Bài 033.4, Mô hình và Bố cục Hộp trong CSS, tham chiếu giáo trình Web Development Essentials 030-100 trang in 188–207. Nội dung độc lập được đối chiếu ngày 23/10/2026. Bạn cần biết HTML, selector và đơn vị CSS. Sau bài học, bạn có thể tính kích thước border box, phân biệt luồng và định vị, giải thích float/clear, đồng thời kiểm tra một bố cục responsive ở hai chiều rộng.
Box model giúp mô tả content, padding, border và margin. Content chứa nội dung; padding nằm giữa content và border; margin tạo khoảng cách phía ngoài border. Nền thường được vẽ qua padding theo quy tắc nền của phần tử, còn margin trong suốt. Đừng dùng margin để làm nền rộng hơn.
Với box-sizing: content-box, width đặt chiều rộng content. Hộp rộng 240px, padding trái/phải mỗi bên 16px và border mỗi bên 2px có border box rộng 276px. Margin không thuộc 276px này. Với box-sizing: border-box, width 240px bao gồm padding và border, nên content còn 204px trong ví dụ đó. Giá trị width không tự giới hạn mọi nội dung con; chuỗi dài hoặc ảnh lớn vẫn có thể tràn.
Một phần tử inline có thể tạo nhiều mảnh trên nhiều dòng. display: none không tạo hộp bố cục cho phần tử và cây con. Vì vậy, phát biểu “mọi thẻ đều có đúng một hình chữ nhật” chỉ là hình dung ban đầu, không phải quy tắc tổng quát. Đổi display cũng không đổi ngữ nghĩa HTML: h2 vẫn là heading, còn div được hiển thị block không tự trở thành section có ý nghĩa.
Shorthand margin/padding có thứ tự trên, phải, dưới, trái khi dùng bốn giá trị. Hai giá trị lần lượt áp dụng cho trên/dưới và trái/phải. Với các khối thông thường trong cùng block formatting context, margin dọc có thể collapse; đừng mặc định hai margin luôn cộng. Trong flex/grid, gap thường diễn đạt khoảng cách giữa các item rõ hơn. Thuộc tính logical như padding-inline giúp thích nghi với hướng viết, nhưng bài này giữ phép tính trái/phải đơn giản để dễ đo.
Trong ngữ cảnh thông thường với hướng viết ngang tiếng Việt, block thường xếp dọc còn inline đi theo dòng văn bản. Block có width auto thường dùng không gian ngang khả dụng theo các quy tắc của ngữ cảnh, không phải mọi block luôn rộng đúng bằng cha. Một block đặt width nhỏ vẫn không tự cho block kế tiếp chen vào cạnh bên như các cột.
Không suy từ kiểu hiển thị sang mô hình nội dung HTML. P và heading nhận nội dung phrasing theo quy tắc của chúng; li có thể chứa đoạn văn và danh sách lồng. Chèn div vào p có thể làm parser đóng p sớm. Đó là vấn đề cấu trúc tài liệu, không chỉ là một lựa chọn CSS kém đẹp.
Với inline không thay thế thông thường như span, width/height không hoạt động giống block; padding dọc có thể vẽ ra ngoài mà không đẩy các dòng như bạn dự đoán. Ảnh là phần tử thay thế, có cách tính kích thước khác. Khi cần một nhóm điều khiển nằm theo dòng nhưng bố trí con bằng Flexbox, inline-flex là lựa chọn có mục đích rõ ràng.
display: flex làm phần tử thành flex container với các con trực tiếp là flex item; cách bố trí còn phụ thuộc flex-direction, wrap và các giới hạn kích thước. Không phải mọi flex container luôn là một hàng ngang. inline-flex thay cách container tham gia bố cục bên ngoài thành inline-level; nó không đơn giản biến các con thành inline thông thường.
Flexbox phù hợp với một trục chính như thanh hành động. Grid phù hợp khi cần các hàng và cột liên hệ nhau. Một container grid cũng có thể nằm trong flex item; không cần chọn duy nhất một cơ chế cho cả website. Giữ thứ tự DOM hợp lý, tránh đảo thứ tự nhìn thấy đến mức người dùng bàn phím đọc theo trình tự khác.
Responsive dùng không gian và nhu cầu nội dung để đổi bố cục. Media query (min-width: 48rem) kiểm tra chiều rộng viewport, không nhận diện chắc chắn máy tính hay điện thoại. Một cửa sổ máy tính hẹp cũng dùng bố cục hẹp. Thêm meta viewport để browser di động áp dụng viewport phù hợp; sau đó kiểm tra nội dung dài, cỡ chữ tăng và liên kết có focus thay vì chỉ co trang cho vừa ảnh chụp.

| Position | Điều cần hiểu |
|---|---|
| static | Theo luồng; top/left không định vị nó như positioned box |
| relative | Giữ chỗ ban đầu trong luồng, rồi dịch hình hộp bằng inset |
| absolute | Ra khỏi luồng; đặt theo containing block được xác định bởi các điều kiện của tổ tiên |
| fixed | Thường theo viewport, nhưng một số thuộc tính tổ tiên có thể tạo containing block khác |
| sticky | Vẫn tham gia luồng; bám theo ngưỡng inset trong phạm vi scroll/container liên quan |
Một cách thường dùng là đặt position: relative trên thẻ và position: absolute trên nhãn nhỏ của thẻ. Điều đó tạo bối cảnh dễ giải thích, nhưng không có nghĩa absolute luôn theo body hoặc chỉ cần tìm cha trực tiếp. Transform và một số thuộc tính khác cũng có thể ảnh hưởng containing block. Tra nguồn khi tổ tiên có các thuộc tính này.
Top và bottom là các khoảng inset từ các cạnh liên quan. Không dùng công thức top + height = bottom để tính khoảng bottom. Trong trường hợp đơn giản, không margin/padding/border và mọi kích thước xác định, còn phải xét chiều cao containing block; các tình huống thực tế có thêm quy tắc sizing và over-constraint. Đặt một cạnh cùng kích thước khi đủ yêu cầu thường dễ đọc hơn đặt mọi cạnh theo suy đoán.
Sticky cần inset phù hợp, chẳng hạn top, và không gian để di chuyển trong phạm vi chứa. Một tổ tiên có overflow tạo cơ chế cuộn có thể thay đổi nơi nó bám. Header fixed/sticky có thể che tiêu đề neo hoặc focus; kiểm tra thao tác cuộn và bàn phím sau khi thêm. Định vị là công cụ cho nhu cầu cụ thể, không phải cách thay mọi khoảng cách trong luồng.
Float phù hợp khi muốn văn bản quấn quanh hình ở một phía. Nó không phải hệ cột ưu tiên cho giao diện mới. clear: both yêu cầu cạnh liên quan của khối được đặt xuống dưới các float trước đó trong cùng ngữ cảnh phù hợp. display: flow-root trên container là cách rõ ràng để chứa float bên trong. Đừng dùng overflow hidden chỉ để chứa float khi nó còn cắt nội dung, shadow hoặc focus cần thấy.
Tạo index.html và style.css trong thư mục DEMO. Ví dụ không gửi dữ liệu, không dùng thư viện hoặc ảnh ngoài. Tên lớp dùng để kiểm tra bố cục; nội dung và heading vẫn nằm trong HTML. Hãy dự đoán chiều rộng hộp đo trước khi mở DevTools.
<!doctype html>
<html lang="vi">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>DEMO bố cục hỗ trợ</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<h1>Chuẩn bị yêu cầu DEMO</h1>
<p class="measure">Hộp đo riêng: width 240px.</p>
<div class="cards">
<section class="card">
<h2>Trước khi liên hệ</h2>
<p>Ghi triệu chứng và thời điểm, giữ bí mật mật khẩu.</p>
<a href="#next">Đọc bước tiếp</a>
</section>
<section class="card" id="next">
<h2>Thông tin bổ sung</h2>
<p class="long">DEMO_chuoi_dai_khong_co_khoang_trang_0123456789012345678901234567890123456789</p>
<a href="#end">Xem ghi chú</a>
</section>
</div>
<p id="end">Hoàn tất chuẩn bị, chưa gửi dữ liệu ra mạng.</p>
</main>
</body>
</html>
* { box-sizing: border-box; }
body { margin: 0; font-family: system-ui, sans-serif; line-height: 1.6; color: #213641; }
main { max-width: 60rem; margin: 0 auto; padding: 1rem; }
.measure { box-sizing: content-box; width: 240px; padding: 16px; border: 2px solid #135f75; }
.cards { display: grid; gap: 1rem; }
.card { min-width: 0; border: 2px solid #607d8b; padding: 1rem; }
.long { overflow-wrap: anywhere; }
a { color: #135f75; text-decoration: underline; }
a:focus-visible { outline: 3px solid #b45309; outline-offset: 3px; }
@media (min-width: 48rem) {
.cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
Ở viewport rộng 1000px với rem gốc 16px, hai thẻ nằm cùng hàng. Ở 400px, chúng xếp một cột. Kiểm tra trong DevTools hoặc bằng getBoundingClientRect: hộp measure ban đầu rộng 276px. Tạm đổi riêng box-sizing của nó thành border-box, chiều rộng còn 240px. Hoàn tác rồi xác nhận 276px trở lại. Các số đo này không gồm margin.
Chuỗi dài ở thẻ thứ hai phải ngắt được thay vì đẩy cả trang tràn ngang. minmax(0, 1fr) cho track có mức tối thiểu bằng không; min-width 0 và overflow-wrap giải quyết giới hạn item cùng cách ngắt nội dung. Đây không phải quy tắc rằng mọi tràn trang đều cần ba khai báo ấy. Với mã nguồn phải giữ dòng, một vùng cuộn riêng có thể phù hợp hơn ngắt nội dung.
Nhấn Tab theo thứ tự hai liên kết. Focus phải thấy ở cả hai chiều rộng và thứ tự vẫn theo DOM. Đừng dùng order để làm cột nhìn khác nhưng bàn phím đi ngược. Khi dùng browser zoom hoặc cỡ chữ riêng, ghi lại điều kiện mới; lab hai viewport chưa thay thế kiểm thử zoom đầy đủ.
Trong DevTools, tạm đặt .cards { display: flex; flex-wrap: wrap; } và .card { flex: 1 1 18rem; }. Quan sát các thẻ có thể xuống hàng khi không đủ chỗ, rồi xóa hai quy tắc để trở lại Grid. Mục tiêu là nhận biết cơ chế, không giữ hai hệ khai báo cạnh tranh khi bài đã kết thúc.
Trên bản sao riêng, có thể thêm nhãn absolute trong card relative, hoặc một hình float trong đoạn mô tả để quan sát clear. Đó là bài mở rộng cần kiểm tra riêng; đừng nhận kết quả Grid làm bằng chứng rằng sticky, fixed hay float của bản sao cũng đã đúng.

Lab đã chạy ngày 23/10/2026 với Chromium 151.0.7922.34, context mới và mạng bị chặn. Assertion xác nhận hộp 276/240px, Grid hai cột/một cột ở 1000/400px, chuỗi dài không làm tài liệu tràn ngang, thứ tự Tab cùng focus nhìn thấy và Flex wrap. Hoàn tác khôi phục số đo ban đầu. Chưa chạy phần mở rộng position/float, kiểm thử zoom, trình đọc màn hình hoặc mọi browser.
Khi thấy tràn, chọn phần tử vượt viewport và đo border box, padding, border, min-width cùng nội dung dài. Nếu chồng lớp, xem position và containing block; nếu khoảng dọc bất ngờ, xem margin collapse. Giữ cách sửa nhỏ, kiểm tra lại cả chiều rộng hẹp và rộng, rồi xác nhận focus không bị che.
Bạn có tính được content width của hộp 240px border-box không? Vì sao relative dịch hình nhưng còn chỗ cũ? Inline-flex thay container hay biến con thành inline? Clear có phải công tắc khôi phục mọi luồng không? Vì sao breakpoint theo viewport không đồng nghĩa phân loại thiết bị? Trả lời bằng cơ chế và giới hạn áp dụng.

Tạo bảng SQLite, hiểu khóa chính và CHECK, dùng CRUD có WHERE và bind tham số từ Node.js; kiểm tra transaction rollback, mở lại dữ liệu và API driver.

Tạo route Express, phục vụ CSS và render EJS; đọc query, params, body, kiểm tra dữ liệu form, escape HTML và phân biệt XSS với CSRF qua lab loopback.

Chạy Node.js LTS, phân biệt ES module và CommonJS, package.json và lockfile; cài package DEMO cục bộ, dùng npm ci và kiểm tra server HTTP trên loopback.

Chọn node DOM, phân biệt collection sống và tĩnh, textContent và innerHTML; cập nhật class, thuộc tính và sự kiện nút bằng chuột, bàn phím trong lab DEMO.