jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

Tailwind, Radix & shadcn/ui · Part 2 — Core Utilities & Layout

The utilities you reach for every day: spacing, sizing, typography and color, then the two layout engines — Flexbox and Grid — the Tailwind way, plus the mobile-first responsive system. With a live flex/grid builder.

Phần 1 đã cho bạn mô hình tư duy và một setup v4 chạy được. Giờ ta xây vốn từ. Đây là 80% Tailwind bạn dùng 80% thời gian — spacing, sizing, typography, màu, và hai cỗ máy layout (Flexbox & Grid). Nắm chắc phần này là bạn dựng được gần như mọi UI tĩnh mà không cần mở docs. Mục tiêu của bài: sau khi đọc xong, mỗi utility bạn gặp đều “đọc” được ngay ra dòng CSS nó sinh ra.

Cách học bài này như một frontend engineer

Đừng học Tailwind bằng cách cố nhớ 500 class. Hãy học theo ý định layout:

Bạn muốn làm gìNhóm utility cần nghĩ tới trước
Tạo nhịp, khoảng thởp-*, m-*, gap-*, space-y-*
Khống chế kích thướcw-*, h-*, min-*, max-*, size-*
Làm chữ dễ đọctext-*, font-*, leading-*, tracking-*, max-w-prose
Sắp hàng một chiềuflex, items-*, justify-*, gap-*, min-w-0
Sắp lưới hai chiềugrid, grid-cols-*, col-span-*, auto-fit/minmax
Thích ứng màn hìnhclass mặc định + sm:/md:/lg:
Component tự thích ứng theo nơi đặt@container + @md:/@lg:

Khi debug layout, hãy hỏi theo thứ tự này:

  1. Cha đang là layout engine gì? block, flex, hay grid?
  2. Trục chính là trục nào? flex-row thì ngang, flex-col thì dọc.
  3. Item có được phép co không? Nếu text dài phá layout, thử min-w-0.
  4. Breakpoint đang áp từ đâu? md: là từ 48rem trở lên, không phải chỉ tablet.
  5. Khoảng cách đến từ đâu? gap, margin của con, hay space-y sinh margin tự động?

Đây là checklist nhỏ nhưng cứu bạn khỏi 80% bug layout trong app thật.


1. Thang spacing — nhịp của toàn UI

Gần như mọi utility liên quan tới khoảng cách (padding, margin, gap, cả width/height ở dạng số) đều rút từ một thang số chung, với quy luật duy nhất:

nn × 0.25rem (và 0.25rem = 4pxfont-size gốc mặc định 16px). Nên muốn ra pixel, cứ nhân 4: p-4 → 16px, p-6 → 24px, p-8 → 32px.

Đây là bảng tra đầy đủ những bước hay gặp (thang còn tiếp tục lên 64, 72, 80, 96):

ClassrempxClassrempx
p-000p-82rem32
p-px1px1p-102.5rem40
p-0.50.125rem2p-123rem48
p-10.25rem4p-143.5rem56
p-1.50.375rem6p-164rem64
p-20.5rem8p-205rem80
p-2.50.625rem10p-246rem96
p-30.75rem12p-328rem128
p-41rem16p-4010rem160
p-51.25rem20p-4812rem192
p-61.5rem24p-5614rem224
p-71.75rem28p-6416rem256

Thang này áp cho mọi tiền tố spacing. Đây là toàn bộ họ utility dùng chung thang đó:

NhómTiền tốSinh ra CSS
Padding mọi cạnhp-*padding
Padding trụcpx-* / py-*padding-inline / padding-block
Padding cạnhpt-* pr-* pb-* pl-*padding-top/right/bottom/left
Padding logicps-* / pe-*padding-inline-start/end (theo hướng đọc)
Margin mọi cạnhm-*margin
Margin trụcmx-* / my-*margin-inline / margin-block
Margin cạnhmt-* mr-* mb-* ml-*margin-top/right/bottom/left
Margin âm-mt-4, -mx-2margin-top: -1rem… (kéo đè)
Căn giữa khốimx-automargin-inline: auto
Gap (flex/grid)gap-* gap-x-* gap-y-*gap / column-gap / row-gap
Khoảng cách conspace-x-* / space-y-*margin giữa các con (xem gotcha)
<div class="px-4 py-2">padding-inline 1rem, padding-block 0.5rem</div>
<div class="mt-6 mb-2">trên 1.5rem, dưới 0.5rem</div>
<div class="mx-auto max-w-sm">căn giữa một khối có chiều rộng tối đa</div>
<div class="-mt-4">kéo lên đè 1rem (margin âm)</div>

space-x/space-y vs gap — cơ chế bên dưới

Hai thứ này trông giống nhau (“tạo khoảng giữa các con”) nhưng sinh CSS hoàn toàn khác:

  • gap-4 đặt gap: 1rem trên cha — nhưng cha phảiflex hoặc grid thì gap mới có tác dụng.
  • space-y-4 không dùng gap. Nó sinh một selector dạng > * + * rồi thêm margin-top: 1rem cho mọi con trừ con đầu:
/* space-y-4 thực chất là: */
.space-y-4 > :not([hidden]) ~ :not([hidden]) {
  margin-top: 1rem;
}

Chốt: nếu cha đã là flex/grid → luôn ưu tiên gap (gọn, không đụng tới margin của con, hoạt động cả hai chiều khi wrap). Chỉ dùng space-y/space-x cho khối tĩnh (không flex/grid), ví dụ một stack đoạn văn. Cảnh báo: space-y “lỗi” khi con bị đảo thứ tự (flex-row-reverse) hoặc khi con có margin riêng.


2. Kích thước — width, height, min/max, size

Sizing trộn ba nguồn giá trị: thang spacing (số), phân số (1/2, 2/3…), và từ khóa (full, screen, min, max, fit).

ClassCSSÝ nghĩa
w-64width: 16remsố → n × 0.25rem
w-1/2width: 50%phân số → phần trăm
w-1/3 w-2/333.33% 66.66%phân số ba
w-fullwidth: 100%đầy theo cha
w-screenwidth: 100vwđầy viewport ngang
w-dvw / h-dvh100dvw / 100dvhviewport động (mobile, trừ thanh URL)
w-minwidth: min-contentco tối đa theo nội dung
w-maxwidth: max-contentgiãn tối đa theo nội dung
w-fitwidth: fit-contentvừa khít nội dung
h-screenheight: 100vhcao đầy viewport
size-10width: 2.5rem; height: 2.5remshorthand v4: cả w lẫn h
min-w-0min-width: 0cho phép co trong flex (rất hay dùng)
max-w-smmax-width: 24remgiới hạn rộng, lý tưởng cho card
min-h-screenmin-height: 100vhtối thiểu đầy màn (layout app)

Thang max-w-* có một bộ tên riêng (không phải thang spacing) để giữ nội dung dễ đọc:

Classmax-widthClassmax-width
max-w-xs20remmax-w-2xl42rem
max-w-sm24remmax-w-4xl56rem
max-w-md28remmax-w-7xl80rem
max-w-lg32remmax-w-full100%
max-w-xl36remmax-w-prose65ch (đẹp cho văn bản)
<div class="w-64">16rem cố định</div>
<div class="w-full h-screen">đầy ngang, cao 100vh</div>
<div class="mx-auto max-w-prose">cột chữ ~65 ký tự — độ dài dòng lý tưởng để đọc</div>
<button class="size-10">nút vuông 2.5rem × 2.5rem</button>

Mẹo min-w-0: một flex item mặc định có min-width: auto, nghĩa là nó không co nhỏ hơn nội dung — đây là lý do text dài làm vỡ layout/không xuống dòng. Thêm min-w-0 (kèm truncate) để item chịu co lại đúng cách.


3. Typography — kích thước, độ đậm, giãn chữ, giãn dòng

text-* cho kích thước. Điểm hay của Tailwind v4: mỗi cỡ chữ kèm sẵn một line-height mặc định hợp lý, nên text-lg vừa đặt font-size vừa đặt line-height cho bạn.

Classfont-sizeline-height đi kèm
text-xs0.75rem (12px)1rem
text-sm0.875rem (14px)1.25rem
text-base1rem (16px)1.5rem
text-lg1.125rem (18px)1.75rem
text-xl1.25rem (20px)1.75rem
text-2xl1.5rem (24px)2rem
text-3xl1.875rem (30px)2.25rem
text-4xl2.25rem (36px)2.5rem
text-5xl3rem (48px)1
text-6xltext-9xl3.75rem8rem1

Độ đậm font-* ánh xạ thẳng sang font-weight:

ClassweightClassweight
font-thin100font-medium500
font-extralight200font-semibold600
font-light300font-bold700
font-normal400font-extrabold800
font-black900

Các trục chữ còn lại bạn sẽ chạm hằng ngày:

NhómVí dụCSS
Giãn dòng (đè mặc định)leading-none leading-tight leading-relaxedline-height: 1 / 1.25 / 1.625
Giãn chữtracking-tight tracking-normal tracking-wideletter-spacing: -0.025em / 0 / 0.025em
Căn lềtext-left text-center text-right text-justifytext-align
Kiểu chữitalic not-italicfont-style
Hoa/thườnguppercase lowercase capitalize normal-casetext-transform
Gạch chânunderline line-through no-underlinetext-decoration
Cắt ngắn 1 dòngtruncateoverflow:hidden; text-overflow:ellipsis; white-space:nowrap
Cắt ngắn n dòngline-clamp-2 line-clamp-3giới hạn còn n dòng rồi
Font familyfont-sans font-serif font-monofont-family
<h1 class="text-3xl font-bold tracking-tight">Tiêu đề lớn</h1>
<p class="text-base leading-relaxed text-slate-600">
  Đoạn văn dễ đọc với line-height thoáng.
</p>
<p class="truncate">Dòng rất dài này sẽ bị cắt và thêm dấu ba chấm…</p>
<p class="line-clamp-2">Đoạn này tự cắt còn đúng 2 dòng dù nội dung dài bao nhiêu.</p>

4. Màu sắc — palette + bộ chỉnh độ mờ

Mỗi màu trong Tailwind có 11 sắc độ, từ 50 (nhạt nhất) tới 950 (đậm nhất). 500 là “chuẩn”. Cấu trúc tên utility luôn là thuộc-tính-tên-sắc-độ:

Tiền tốSinh raVí dụ
text-*colortext-slate-700
bg-*background-colorbg-indigo-500
border-*border-colorborder-slate-200
ring-*box-shadow (vòng focus)ring-indigo-500
fill-* / stroke-*fill / stroke (SVG)fill-emerald-500
from-* via-* to-*điểm dừng gradientfrom-indigo-500 to-purple-600
divide-*màu đường kẻ giữa các condivide-slate-200
accent-*accent-color (checkbox/radio)accent-indigo-600

Một số họ màu có sẵn (mỗi họ đều có đủ 50→950): slate gray zinc neutral stone (xám), red orange amber yellow lime green emerald teal cyan sky blue indigo violet purple fuchsia pink rose, cùng black white transparent current.

Quy ước chọn sắc độ để UI hài hòa mà không phải nghĩ nhiều:

Vai tròSắc độ thường dùng
Chữ chính trên nền sáng700900
Chữ phụ / mô tả500600
Nền nhấn (nút primary)500600, hover +100
Đường viền nhẹ200300
Nền khối nhạt (badge, panel)50100

Bộ chỉnh độ mờ /NN — cơ chế

Thêm / và một số 0–100 vào bất kỳ utility màu để đặt độ mờ chỉ cho màu đó, không ảnh hưởng con bên trong (khác hẳn opacity-* làm mờ cả phần tử):

<p class="text-indigo-500/70">indigo ở 70% độ đục</p>
<div class="bg-black/50">lớp phủ đen 50% — overlay kinh điển</div>
<div class="border-white/10">viền trắng rất mờ trên nền tối</div>

Bên dưới, Tailwind v4 sinh ra giá trị màu dùng color-mix hoặc kênh alpha, ví dụ text-indigo-500/70color: color-mix(in oklab, var(--color-indigo-500) 70%, transparent). Vì mọi token màu ở v4 là biến CSS, bạn có thể tự thấy chúng trong DevTools.

/opacity vs opacity-50: bg-black/50 chỉ làm trong suốt nền; chữ và icon bên trong vẫn đục. opacity-50 làm mờ cả phần tử lẫn mọi thứ bên trong. Dùng /NN cho overlay; dùng opacity-* khi muốn làm mờ nguyên cụm (ví dụ trạng thái disabled:opacity-50).

Cần một màu ngoài palette? Dùng arbitrary value: bg-[#1da1f2], text-[oklch(0.7_0.15_250)] — nhưng tiết chế, vì nó nằm ngoài thang chung.


5. Flexbox — kiểu Tailwind

Mỗi khái niệm Flexbox ánh xạ thành đúng một utility. Đây là bảng tra đầy đủ:

CSSTailwindGhi chú
display: flexflexbật flex container
display: inline-flexinline-flexcontainer inline
flex-direction: rowflex-rowmặc định, ngang
flex-direction: columnflex-coldọc
flex-direction: row-reverseflex-row-reverseđảo ngang
flex-wrap: wrapflex-wrapcho xuống dòng
flex-wrap: nowrapflex-nowrapkhông xuống dòng (mặc định)
justify-content: flex-startjustify-startdồn đầu trục chính
justify-content: centerjustify-centergiữa trục chính
justify-content: flex-endjustify-enddồn cuối
justify-content: space-betweenjustify-betweenđẩy ra hai biên
justify-content: space-aroundjustify-aroundđều, có khoảng hai rìa
justify-content: space-evenlyjustify-evenlymọi khoảng bằng nhau
align-items: stretchitems-stretchkéo đầy trục phụ (mặc định)
align-items: centeritems-centergiữa trục phụ
align-items: flex-startitems-startđầu trục phụ
align-items: baselineitems-baselinethẳng đường baseline chữ
align-self: centerself-centerđè cho riêng một item
flex: 1 1 0%flex-1chiếm phần còn lại, co giãn
flex: 1 1 autoflex-autoco giãn theo nội dung
flex: 0 1 autoflex-initialco nhưng không giãn
flex: noneflex-nonekhóa kích thước
flex-grow: 1 / 0grow / grow-0cho/cấm giãn
flex-shrink: 1 / 0shrink / shrink-0cho/cấm co
order: 1 / -1order-1 / -order-1đổi thứ tự hiển thị

Hai trục cần nhớ: justify-* luôn theo trục chính (mặc định ngang), items-* theo trục phụ (vuông góc trục chính). Khi đổi sang flex-col, trục chính thành dọc → justify-* giờ căn theo chiều dọc, items-* theo chiều ngang. Đây là nhầm lẫn kinh điển.

flex-row (mặc định):              flex-col:
  justify → ngang →                 justify → dọc ↓
  items   → dọc ↓                    items   → ngang →

Recipe phổ biến nhất trong mọi UI — một thanh ngang, logo trái, nav phải:

<header class="flex items-center justify-between gap-4 px-6 py-4">
  <span class="font-semibold">Logo</span>
  <nav class="flex items-center gap-4">
    <a href="#">Home</a><a href="#">Docs</a><a href="#">About</a>
  </nav>
</header>

flex-1 + min-w-0: khi một item flex-1 chứa text dài cần truncate, nhớ thêm min-w-0, nếu không item từ chối co và text tràn ra ngoài (xem mục 2).


6. Grid — cho layout hai chiều

Flex giỏi một chiều; Grid giỏi hàng × cột cùng lúc. Bảng tra:

CSSTailwindGhi chú
display: gridgridbật grid container
grid-template-columns: repeat(3, 1fr)grid-cols-33 cột bằng nhau
grid-template-columns: repeat(12, …)grid-cols-12lưới 12 cột kinh điển
grid-template-columns: nonegrid-cols-nonebỏ template
grid-template-rows: repeat(2, …)grid-rows-22 hàng
grid-column: span 2col-span-2item chiếm 2 cột
grid-column: 1 / -1col-span-fulltrải hết hàng
grid-column-start: 2col-start-2bắt đầu ở cột 2
grid-row: span 2row-span-2item cao 2 hàng
gap: 1remgap-4khe cả hai chiều
column-gap / row-gapgap-x-4 / gap-y-2khe theo trục
grid-auto-flow: columngrid-flow-colđổ item theo cột
grid-auto-rows: minmax(0,1fr)auto-rows-frhàng tự sinh đều nhau
place-items: centerplace-items-centercăn giữa cả 2 trục
place-content: centerplace-content-centercăn cả khối lưới
<div class="grid grid-cols-3 gap-4">
  <div class="col-span-2">chiếm 2/3</div>
  <div>1/3</div>
  <div class="col-span-full">trải hết hàng dưới</div>
</div>

Lưới “tự lấp đầy” bằng auto-fit + minmax

Một lưới responsive không cần breakpoint — cột tự co theo chỗ trống, mỗi cột tối thiểu 16rem. CSS gốc là repeat(auto-fit, minmax(16rem, 1fr)). Tailwind không có utility sẵn cho cú pháp này, nên dùng arbitrary value (nhớ thay khoảng trắng bằng _):

<div class="grid grid-cols-[repeat(auto-fit,minmax(16rem,1fr))] gap-4">
  <!-- thêm bao nhiêu card cũng tự xuống hàng, không cần md:/lg: -->
</div>

auto-fit vs auto-fill: auto-fit gộp các cột trống lại để các card hiện có giãn đầy; auto-fill giữ các cột rỗng (card không giãn). Muốn card luôn lấp đầy chiều ngang → auto-fit.

Dựng cả hai layout ngay bên dưới — bật utility, rồi đổi viewport để xem override md: kích hoạt:


7. Responsive — mobile-first

Đây là khái niệm làm người mới vấp nhiều nhất, nên hãy thấm cho kỹ:

Utility không tiền tố áp ở MỌI cỡ màn. Tiền tố như md: nghĩa là “từ breakpoint này trở LÊN” — nó không bao giờ trừ bớt ở dưới.

Bảng breakpoint mặc định (đều là min-width):

Tiền tốmin-widthpxThiết bị điển hình
(không)0mặc định, mobile-first
sm:40rem640điện thoại ngang / màn nhỏ
md:48rem768tablet
lg:64rem1024laptop
xl:80rem1280desktop
2xl:96rem1536màn lớn

Mỗi tiền tố sinh một khối @media (min-width: …). Ví dụ md:grid-cols-2 sinh ra:

@media (min-width: 48rem) {
  .md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
<!-- 1 cột trên mobile, 2 từ md, 3 từ lg -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">…</div>

Bạn thiết kế màn nhỏ nhất trước (class không tiền tố), rồi thêm override chỉ kích hoạt khi to lên. Đọc từ trái sang phải như kể chuyện: “xếp chồng trên mobile, hai cột từ tablet, ba cột từ laptop”.

Gotcha kinh điển: viết md:flex rồi mong nó cũng có hiệu lực trên mobile. Không — md: chỉ bắt đầu từ 768px, dưới đó phần tử về mặc định (display: block). Muốn “flex ở mọi nơi, cột trên mobile, hàng từ desktop” thì viết flex flex-col md:flex-row: flex áp toàn bộ, flex-col là mặc định nhỏ, md:flex-row đè khi rộng.

Nhắm một khoảng hẹp: thêm tiền tố max-* để chặn trên. md:max-lg:hidden nghĩa là ẩn trong khoảng 768px ≤ w < 1024px.

Container queries — phản ứng theo cha, không phải viewport

Đôi khi một card cần đổi layout theo bề rộng vùng chứa nó, chứ không phải bề rộng màn hình (cùng một card có thể nằm trong sidebar hẹp hoặc khu nội dung rộng). Đó là việc của container queries, hỗ trợ sẵn ở Tailwind v4:

  1. Đánh dấu phần tử cha là container với @container.
  2. Trên con, dùng tiền tố @sm:, @md:, @lg:… để phản ứng theo bề rộng cha.
Tiền tố containermin-width của cha
@sm:24rem (384px)
@md:28rem (448px)
@lg:32rem (512px)
@xl:36rem (576px)
<div class="@container">
  <!-- 1 cột khi cha hẹp, 2 cột khi cha ≥ 28rem — bất kể viewport -->
  <article class="grid grid-cols-1 @md:grid-cols-2 gap-4">…</article>
</div>

Khi nào dùng cái gì: md: cho layout trang (theo viewport); @md: cho component tái sử dụng phải tự thích ứng dù được đặt ở đâu. Container query là vũ khí thật sự để viết component “đặt đâu cũng đẹp”.


8. Recipe thực tế

Ba layout này lặp lại trong gần như mọi app — học thuộc khung là xong.

Holy-grail (header / sidebar / main / footer): dùng grid với hàng và cột đặt tên kích thước, main chiếm phần còn lại.

<div class="grid min-h-screen grid-rows-[auto_1fr_auto]">
  <header class="px-6 py-4 border-b">Header</header>
  <div class="grid md:grid-cols-[16rem_1fr]">
    <aside class="hidden md:block border-r p-4">Sidebar</aside>
    <main class="p-6">Nội dung chính</main>
  </div>
  <footer class="px-6 py-4 border-t">Footer</footer>
</div>
  • grid-rows-[auto_1fr_auto]: header/footer cao theo nội dung, vùng giữa 1fr giãn đầy → footer luôn dính đáy dù nội dung ngắn.
  • md:grid-cols-[16rem_1fr]: sidebar 16rem cố định, main chiếm phần còn lại — và hidden md:block ẩn sidebar trên mobile.

Lưới card responsive (tự lấp đầy, không breakpoint):

<div class="grid grid-cols-[repeat(auto-fit,minmax(15rem,1fr))] gap-6">
  <article class="rounded-xl border border-slate-200 bg-white p-6 shadow-sm">
    <h3 class="text-lg font-semibold text-slate-900">Card</h3>
    <p class="mt-2 text-sm text-slate-600">Mô tả ngắn.</p>
  </article>
  <!-- nhân bản article — lưới tự co cột theo chỗ trống -->
</div>

Header dính đỉnh (sticky) có nền mờ:

<header class="sticky top-0 z-10 flex items-center justify-between
               border-b border-slate-200 bg-white/80 px-6 py-3 backdrop-blur">
  <span class="font-semibold">Brand</span>
  <nav class="flex items-center gap-4 text-sm">
    <a href="#">Docs</a><a href="#">Pricing</a>
  </nav>
</header>
  • sticky top-0: dính đỉnh khi cuộn (cần cha không có overflow: hidden).
  • z-10: nổi trên nội dung cuộn bên dưới.
  • bg-white/80 backdrop-blur: nền trắng mờ 80% + làm mờ phần phía sau → hiệu ứng kính.

Checklist layout production

Khi một layout trông ổn trong mockup nhưng vỡ ở dữ liệu thật, lỗi thường không nằm ở “Tailwind khó”, mà nằm ở giả định quá đẹp: tên ngắn, số lượng card vừa đủ, viewport lý tưởng. Trước khi coi một layout là xong, hãy kiểm qua các ca này:

Câu hỏiClass/pattern thường cần
Text dài có làm vỡ hàng không?min-w-0, truncate, line-clamp-*, break-words
Card có cao lệch khi nội dung dài không?h-full, flex flex-col, mt-auto cho action cuối
Grid có đẹp khi chỉ có 1-2 item không?auto-fit thay vì ép grid-cols-4
Sidebar có làm main bị tràn ngang không?minmax(0,1fr) trong grid hoặc min-w-0 trên main
Header sticky có che nội dung không?top-*, z-*, padding/scroll-margin cho anchor
Mobile có thao tác được không?gap-*, size-* đủ lớn cho target bấm, không chỉ hover

Một pattern rất đáng thuộc: hàng có avatar cố định, phần nội dung co giãn, action bên phải không bị đẩy vỡ.

<div class="flex items-center gap-3">
  <img class="size-10 shrink-0 rounded-full" src="/avatar.png" alt="" />
  <div class="min-w-0 flex-1">
    <p class="truncate text-sm font-medium text-slate-900">
      Tên người dùng rất rất dài có thể đến từ dữ liệu thật
    </p>
    <p class="truncate text-sm text-slate-500">email-rat-dai@example.com</p>
  </div>
  <button class="shrink-0 rounded-md border px-3 py-1.5 text-sm">Mời</button>
</div>

Điểm đáng chú ý:

  • shrink-0 giữ avatar và button không bị bóp méo.
  • min-w-0 flex-1 cho vùng text được phép co.
  • truncate chỉ hoạt động đúng khi phần tử có bề rộng bị giới hạn, nên nó thường đi cùng min-w-0.

9. Gotchas thường gặp

Triệu chứngNguyên nhânCách xử lý
gap-4 không tạo khoảng cáchCha chưa là flex/gridThêm flex hoặc grid vào cha, hoặc dùng space-y-* cho khối thường
space-y lệch khi đảo thứ tựspace-y dùng > * + * theo thứ tự DOMChuyển sang gap trên flex/grid
md:flex không ăn trên mobilemd: chỉ áp từ 768px trở lênĐặt mặc định không tiền tố: flex flex-col md:flex-row
Text dài làm vỡ flex layoutFlex item mặc định min-width: auto, không chịu coThêm min-w-0 (+ truncate nếu cần)
grid-cols-4 đẩy item tràn ngangItem con quá rộng, không comin-w-0 trên con, hoặc giảm số cột ở mobile
sticky top-0 không dínhMột tổ tiên có overflow: hidden/autoBỏ overflow ở tổ tiên, hoặc đổi chiến lược layout
bg-black/50 làm mờ cả chữ bên trongNhầm với opacity-50Dùng /NN (chỉ màu) thay vì opacity-* (cả phần tử)
grid-cols-[repeat(auto-fit,minmax(16rem,1fr))] không buildCó khoảng trắng trong arbitrary valueThay mọi space bằng _: minmax(16rem,1fr)

10. Bài tập

1. Dựng navbar: full width, item căn giữa dọc, logo bên trái và nhóm link bên phải, gap 1rem.

Lời giải
<header class="flex items-center justify-between gap-4 px-6 py-4">
  <span class="font-semibold">Logo</span>
  <nav class="flex items-center gap-4"><a>Home</a><a>Docs</a><a>About</a></nav>
</header>

justify-between đẩy logo và nav ra hai biên; items-center căn giữa theo chiều dọc.

2. Lưới card responsive: 1 cột mobile, 2 từ sm, 3 từ lg, gap 1.5rem.

Lời giải
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">…</div>

Mobile-first: grid-cols-1 là mặc định, hai tiền tố sau chỉ đè khi màn to lên.

3. Căn giữa một box cả ngang và dọc trong một section cao full màn hình.

Lời giải
<section class="flex items-center justify-center min-h-screen">
  <div class="max-w-sm">Centered</div>
</section>

items-center (trục phụ) + justify-center (trục chính) = căn giữa hai chiều. Dùng min-h-screen thay h-screen để box không bị cắt nếu nội dung cao hơn màn.

4. Không tra cứu: gap-10 sinh ra khoảng cách bao nhiêu pixel, và p-3.5 là bao nhiêu?

Lời giải

gap-10 = 10 × 0.25rem = 2.5rem = 40px. p-3.5 = 3.5 × 0.25rem = 0.875rem = 14px. Quy luật nhân-4 áp cho cả số lẻ .5.

5. Một flex item chứa tiêu đề rất dài cần cắt bằng truncate, nhưng text vẫn tràn ra ngoài. Thiếu class nào?

Lời giải

Thiếu min-w-0. Flex item mặc định min-width: auto nên không co nhỏ hơn nội dung; truncate chỉ có tác dụng khi item được phép hẹp lại: class="min-w-0 truncate".

6. Viết một lưới card tự co (mỗi card tối thiểu 12rem, lấp đầy hàng) không dùng bất kỳ breakpoint sm:/md:/lg: nào.

Lời giải
<div class="grid grid-cols-[repeat(auto-fit,minmax(12rem,1fr))] gap-4">…</div>

auto-fit gộp cột trống để card hiện có giãn đầy; minmax(12rem,1fr) đặt sàn 12rem rồi cho giãn. Nhớ thay khoảng trắng bằng _ trong arbitrary value.

Nâng cao: trong builder ở mục 6, tạo layout cột xếp chồng khi hẹphàng khi rộng bằng override md:, rồi đọc khối @media sinh ra để xác nhận breakpoint là 48rem.


Điểm chính

  • Một thang spacing (n × 0.25rem, nhân-4 ra px) chi phối padding, margin, gap và sizing dạng số — đó là lý do UI Tailwind có nhịp nhất quán.
  • gap cần cha là flex/grid và là lựa chọn ưu tiên; space-y/space-x dùng selector > * + * cho khối thường, nhưng dễ lỗi khi đảo thứ tự.
  • Sizing trộn số, phân số và từ khóa (full, screen, min/max/fit); nhớ min-w-0 để flex item chịu co, và max-w-prose cho độ dài dòng dễ đọc.
  • Typography: mỗi text-* kèm sẵn line-height hợp lý; truncate/line-clamp-n để cắt gọn nội dung dài.
  • Màu theo cấu trúc thuộc-tính-tên-sắc-độ (50→950); bộ chỉnh /NN làm mờ chỉ màu đó, khác opacity-* làm mờ cả phần tử.
  • Flexbox cho một chiều (justify-* theo trục chính, items-* theo trục phụ); Grid cho hai chiều, và grid-cols-[repeat(auto-fit,minmax(…))] cho lưới responsive không cần breakpoint.
  • Responsive là mobile-first: không tiền tố = mọi cỡ, md:/lg: = breakpoint đó trở lên. Container queries (@container + @md:) cho component phản ứng theo cha — đặt đâu cũng đẹp.

Tiếp theo

Phần 3 — Design token & theming ở v4: directive @theme, tự định nghĩa màu/font/spacing dưới dạng biến CSS, cách làm dark mode kiểu v4, và cách mỗi token tự trở thành một utility.