CSS Pro Tips
Tổng hợp tips thực dụng giúp viết CSS nhanh, sạch, ít bug — kèm so sánh Bad ↔ Good.
💡Cách đọc: mỗi tip có pattern Bad (cách cũ) ↔ Good (cách hiện đại). Học xong áp dụng được ngay.
0. Reset chuẩn ngay từ đầu
Khử inconsistency giữa browser, làm tiền đề cho mọi tip phía dưới.
css*, *::before, *::after { box-sizing: border-box; } * { margin: 0; } body { line-height: 1.5; -webkit-font-smoothing: antialiased; } img, picture, video, canvas, svg { display: block; max-width: 100%; } input, button, textarea, select { font: inherit; }
💡Hiểu box model trước → khi học Tailwind, các utility
p-*, m-*, border-* sẽ trở nên rất tự nhiên.1. Center — Flexbox > absolute hack
Bad
css.classic { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
Good
css.classic { display: flex; justify-content: center; align-items: center; } /* Hoặc 1 dòng với grid */ .center { display: grid; place-items: center; }
2. Layout 2 chiều — Grid > table / flex lồng
Bad
css/* Dùng <table> hoặc flex lồng nhiều cấp để chia multi-column + multi-row */
Good
css.layout { display: grid; grid-template-columns: 1fr 500px 1fr; grid-template-rows: 100px 200px; place-items: center; }
3. Center một <div> trong viewport — 1 block
html<div class="parent"> <div>I'm centered</div> </div>
css.parent { display: grid; place-content: center; height: 100vh; }
4. Responsive width — clamp() thay 3 media query
Bad
cssarticle { width: 50%; } @media (max-width: 600px) { article { width: 200px; } } @media (min-width: 1200px) { article { width: 800px; } }
Good
cssarticle { /* MIN, Preferred, MAX */ width: clamp(200px, 50%, 600px); }
clamp() hoạt động tốt cho cả font-size, padding, gap… Đây là cách “responsive không cần media query”.
5. Aspect ratio — 1 dòng thay padding-top hack
Bad
css.container-16x9 { position: relative; padding-top: 56.25%; } .container-16x9 video { position: absolute; inset: 0; width: 100%; }
Good
cssvideo { width: 100%; aspect-ratio: 16 / 9; }
6. Variables for variables — CSS custom properties
Bad
cssp { color: rgb(255, 0, 0); } h1 { color: rgb(255, 0, 0); } h2 { color: rgb(255, 0, 0); }
Good
css:root { --r: 255; --g: 0; --b: 0; --text-color: rgb(var(--r), var(--g), var(--b)); } p { --text-color: green; /* override */ color: var(--text-color); } h1 { color: var(--text-color); } h2 { color: var(--text-color); }
7. Dark mode bằng custom properties
css:root { --bg: #fff; --fg: #111; } .dark { --bg: #111; --fg: #eee; } body { background: var(--bg); color: var(--fg); }
Toggle dark mode chỉ cần thêm class .dark lên <html> — không cần đổi từng selector.
8. Logical properties — RTL friendly
css/* Thay vì margin-left/right (vật lý) */ margin-inline: auto; /* ngang theo writing direction */ padding-block: 1rem; /* dọc theo writing direction */ border-inline-start: 2px solid;
Tự động đảo khi dir="rtl" — không cần viết CSS riêng.
9. Stagger animation — calc() + custom property
Bad
css.drop { animation: dropIn 1s ease forwards; } .ace { animation-delay: 100ms; } .deuce { animation-delay: 200ms; } .trey { animation-delay: 300ms; } @keyframes dropIn { from { transform: translateY(-500px); } to { transform: translateY(0); } }
Good
css.drop { animation: dropIn 1s ease forwards; animation-delay: calc(var(--order) * 100ms); } @keyframes dropIn { from { transform: translateY(-500px); } to { transform: translateY(0); } } /* HTML */ /* <i class="drop" style="--order: 1">1</i> */ /* <i class="drop" style="--order: 2">2</i> */ /* <i class="drop" style="--order: 3">3</i> */
10. State — counter() đánh số tự động
Bad
html<h1>1. Awesome</h1> <h1>2. Cool</h1> <h1>3. Radical</h1>
Good
html/* CSS */ :root { counter-reset: headings; } h1 { counter-increment: headings; } h1::before { content: counter(headings) ". "; } <!-- HTML — không cần đánh số --> <h1>Awesome</h1> <h1>Cool</h1> <h1>Radical</h1>
11. :focus-within — bắt focus của descendant
Bad
css.dropdown { opacity: 0; visibility: hidden; } button:focus .dropdown { /* chỉ bắt focus của chính button */ opacity: 1; visibility: visible; }
Good
css.dropdown { opacity: 0; visibility: hidden; } /* Bắt focus ở BẤT KỲ con nào trong button */ button:focus-within .dropdown { opacity: 1; visibility: visible; }
12. :has() — parent selector (đã hỗ trợ rộng rãi)
css/* Card có ảnh thì giảm padding */ .card:has(img) { padding: 0.5rem; } /* Form có input invalid thì mờ button submit */ form:has(input:invalid) button { opacity: 0.5; } /* Body có sidebar mở thì đẩy main */ body:has(.sidebar.open) main { margin-left: 240px; }
13. Truncate text (1 dòng / nhiều dòng)
css/* 1 dòng */ .truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* Nhiều dòng */ .clamp-3 { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
14. Focus visible — đẹp & a11y
Bad
cssbutton:focus { outline: none; /* phá a11y, người dùng tab keyboard sẽ lạc */ }
Good
cssbutton:focus { outline: none; } /* Chỉ hiện outline khi user tab bằng keyboard */ button:focus-visible { outline: 2px solid #6366f1; outline-offset: 2px; }
⚠️Đừng tắt
outline mà không thay bằng focus state khác.15. Smooth scroll & scroll-margin (sticky header)
csshtml { scroll-behavior: smooth; } section { scroll-margin-top: 5rem; } /* offset cho sticky header */
16. will-change — dùng đúng chỗ
💡Chỉ thêm
will-change NGAY TRƯỚC khi animate, gỡ khi xong. Lạm dụng sẽ tốn GPU memory không cần thiết.css/* Bad: luôn bật cho mọi card */ .card { will-change: transform; } /* Good: chỉ bật khi sắp animate */ .card.is-animating { will-change: transform; }
17. DevTools tip — Firefox > Chrome khi debug layout
- Firefox có Grid Inspector & Flex Inspector trực quan hơn Chrome.
- Hiện grid/flex line, gap, item order chỉ với 1 click.
- Font Inspector của Firefox cũng vượt trội cho debug typography.