TailwindCSS 間距系統:padding、margin、space、gap 完整指南 | TailwindCSS 完整教學
在 TailwindCSS 裡,間距系統(Spacing) 是決定版面「呼吸感」的靈魂——元素內部要留多少
padding、彼此之間要隔多遠margin、一排子項要用什麼方式拉開,全靠這套工具。只要用p-*/px-*/py-*控制內距、m-*/mx-auto控制外距與置中、space-x-*/space-y-*或gap-*控制子元素間隔,就能把一個「擠成一團」的版面調成「疏密有致」。這是 TailwindCSS 完整教學 系列的第十二篇,我們會把 padding、margin、space 與 gap 一次講透,並帶你理解 v4 由單一--spacing變數動態推導間距刻度的設計,以 v4 為預設、標註與 v3 的差異。
前言
間距(Spacing) 指的是元素「內部空間」與「外部空間」的控制——前者是 CSS 的 padding(內距,內容到邊框的距離),後者是 margin(外距,邊框到相鄰元素的距離)。上一篇 《定位系統》 我們學會了怎麼把元素精準釘在畫面任意位置、疊在正確的層級上;但一個賞心悅目的介面,光有「位置」還不夠,元素與元素之間、內容與邊框之間,都需要恰到好處的留白。間距拿捏得好,版面就從「能看」進化到「耐看」;拿捏不好,再漂亮的配色與字體也會顯得侷促雜亂。間距系統就是這門「留白的藝術」的工具箱。
用一個現實世界的類比來理解 padding 與 margin 的差別:想像一幅裱框的畫。 畫作本身是「內容(content)」,畫框內側到畫作之間那圈白色卡紙就是 padding——它把內容從邊框往內推、留出內部的呼吸空間;而這幅裱好的畫掛上牆後,它和旁邊那幅畫之間的牆面距離,就是 margin——決定畫與畫之間隔多遠。padding 屬於元素「自己內部」的事、會被背景色包住;margin 是元素「對外」的事、是透明的、不含背景。搞懂這個「內卡紙 vs 畫間距」的分野,你就抓住了間距系統一半的精髓。
而 TailwindCSS 的妙處在於,它不讓你隨手亂填 13px、27px 這種零散數字,而是提供一套一致的間距刻度(spacing scale):以 0.25rem(4px)為基準單位,p-1 是 4px、p-2 是 8px、p-4 是 16px……整個網站的留白都落在這套刻度上,視覺節奏自然統一。到了 v4,這套刻度更進化成由「單一變數動態推導」,讓刻度既一致又能無限延伸。
本篇你將學到:
- padding 內距:
p-*(四邊)、px-*/py-*(水平/垂直軸)、pt-*/pr-*/pb-*/pl-*(單邊)、以及 v4 的邏輯屬性ps-*/pe-* - margin 外距:
m-*家族、mx-auto水平置中招式、ml-auto/mt-auto推擠技巧、以及負值-mt-4 - 子元素間距:
space-x-*/space-y-*的運作機制、與gap-*的關鍵差異、該用哪一個 - v4 間距刻度:單一
--spacing變數如何以calc(var(--spacing) * n)推導所有刻度,讓p-13這種任意倍數開箱即用,以及任意值p-[18px]寫法
本系列以 TailwindCSS v4 為預設版本,本篇會明確標註每一項與 v3 的差異。
核心概念
間距工具類的分類地圖
間距系統的工具乍看很多,但其實高度規律。抓住兩條軸線就能秒懂:一是「控制的是內距(p)還是外距(m)」,二是「作用在哪個方向(全部/水平軸/垂直軸/單邊)」。下面這張表是整篇文章的地圖:
| 方向 | padding(內距) | margin(外距) | 對應 CSS |
|---|---|---|---|
| 四邊 | p-* | m-* | padding / margin |
| 水平(左右) | px-* | mx-* | padding-inline / margin-inline |
| 垂直(上下) | py-* | my-* | padding-block / margin-block |
| 上 | pt-* | mt-* | padding-top / margin-top |
| 右 | pr-* | mr-* | padding-right / margin-right |
| 下 | pb-* | mb-* | padding-bottom / margin-bottom |
| 左 | pl-* | ml-* | padding-left / margin-left |
| 邏輯起始 | ps-* | ms-* | padding-inline-start / margin-inline-start |
| 邏輯結束 | pe-* | me-* | padding-inline-end / margin-inline-end |
規律非常清楚:字首 p 管 padding、m 管 margin;第二個字母決定方向——x 是水平軸、y 是垂直軸、t/r/b/l 是上右下左四個單邊、s/e 是邏輯上的起始/結束(logical start/end,RTL 友善)。記住這套命名法,你就能反推出任何一顆間距 class,不必死背。而 space-* 與 gap-* 則是另一類——它們不作用在「單一元素自己」,而是控制「一群子元素彼此之間」的間隔,稍後專章討論。
v4 的核心:單一 --spacing 變數推導整套刻度
Tailwind 的間距數字(p-4 的 4、m-8 的 8)不是像素,而是刻度值。基準單位是 0.25rem(在預設 16px 根字級下等於 4px),所有數字刻度都是這個基準的倍數:
| Tailwind 值 | 計算 | rem | px | 常見用途 |
|---|---|---|---|---|
0 | — | 0 | 0px | 重置間距 |
px | — | — | 1px | 精細 1px 微調 |
1 | 1 × 0.25rem | 0.25rem | 4px | 緊密元素間距 |
2 | 2 × 0.25rem | 0.5rem | 8px | 圖示內距 |
3 | 3 × 0.25rem | 0.75rem | 12px | 緊湊 UI |
4 | 4 × 0.25rem | 1rem | 16px | 標準基礎間距 |
6 | 6 × 0.25rem | 1.5rem | 24px | 卡片 padding |
8 | 8 × 0.25rem | 2rem | 32px | 區塊間距 |
12 | 12 × 0.25rem | 3rem | 48px | section 內距 |
16 | 16 × 0.25rem | 4rem | 64px | 大型 section 間距 |
24 | 24 × 0.25rem | 6rem | 96px | 英雄區間距 |
這裡就是 v4 最重要的變化。在 v3 時代,上表是一張「硬編碼的靜態對照表」——表上有的數字才存在,p-13、p-15 這種表上沒有的值,預設一律不成立,要嘛用任意值、要嘛去 config 擴充。v4 徹底翻新:它把每個數字刻度定義成一條公式——
/* v4:每個數字刻度都由這條公式動態推導 */
/* padding: calc(var(--spacing) * <n>) */
/* 預設 --spacing 為 0.25rem,所以: */
/* p-4 → calc(0.25rem * 4) = 1rem (16px) */
/* p-13 → calc(0.25rem * 13) = 3.25rem (52px) ← 不必擴充,直接成立! */
換句話說,在 v4 裡任意正整數(甚至小數)倍數都開箱即用:p-13、m-15、gap-25、pt-17 全部有效,因為它們只是把 13、15、25、17 代進公式而已。這在過去是不可能的,你會得到「class 不存在」的結果。
更妙的是「單一基準」帶來的一致性:不只 p-*、m-*,連 gap-*、w-*、h-*、inset-*、translate-*、scroll-m-* 等所有走數字刻度的 utility,統統共用這同一個 --spacing 變數。所以只要在 @theme 裡覆寫一次基準,整套間距與尺寸系統就會等比縮放:
@import "tailwindcss";
@theme {
--spacing: 0.2rem; /* 把基準從 0.25rem 縮成 0.2rem,全站數字刻度一次縮緊 */
}
改這一行,p-4、m-8、gap-6、w-64 全都同步變小——這種「牽一髮動全身」的可控性,正是 v4 間距系統的設計哲學。
v4 差異標註:v3 的間距刻度是靜態對照表,表外的值不存在;v4 改為由單一
--spacing變數以calc()動態推導,任意倍數(如p-13)皆成立,且p-*/m-*/gap-*/w-*等共用同一基準,覆寫--spacing即可等比縮放整套系統。
space 與 gap:控制「子元素之間」的兩種思路
前面的 p-*/m-* 都是作用在「元素自己」。但實務上更常見的需求是:一整排/一整列子元素,彼此之間要有均等的間隔。 Tailwind 提供兩種工具,思路完全不同:
| 工具 | 機制 | 作用位置 | 適用場景 |
|---|---|---|---|
gap-* / gap-x-* / gap-y-* | CSS 原生 gap 屬性 | 容器層級,統一在子項「之間」開縫 | Flexbox、Grid(首選) |
space-x-* / space-y-* | 在第 2 個起的子項加 margin | 子元素層級(相鄰選擇器) | 非 flex/grid 的單純堆疊清單 |
兩者的差別用一張示意圖最清楚:
gap-4(容器統一開縫,只在「之間」,換行也自動套用)
┌──────┐ ←4→ ┌──────┐ ←4→ ┌──────┐
│ A │ │ B │ │ C │ ← 首尾外緣無多餘間距
└──────┘ └──────┘ └──────┘ 換行時列與列之間也自動有 gap
space-x-4(在第 2 個起的子項加 margin-left 模擬間距)
┌──────┐ ┌──────┐ ┌──────┐
│ A │ │ B │ │ C │
└──────┘ └──────┘ └──────┘
第一個 ←加 ml-4 ←加 ml-4 ← 不理解換行,換到第二列間距會亂
無 margin
結論是:只要容器是 flex 或 grid,一律優先用 gap-*。 它是 CSS 的原生機制,由容器統一負責、只在子項「之間」開縫、首尾不會多出間距,而且 flex-wrap 換行時,列與列之間也會自動套用縱向間距——這正是格狀排版最想要的行為。space-x-*/space-y-* 則是 Tailwind 用「相鄰選擇器」在第二個以後的子項加 margin 來模擬間距,它不理解換行、換到第二列時間距會亂掉,只適合保留給「不能/不想把容器變成 flex 或 grid」的單純垂直堆疊清單。
v4 差異標註:v3 的
space-x-*/space-y-*是用> * + *選擇器搭配偽元素方案實作;v4 改為直接在子項計算margin,行為更可預測、也更容易和其他 margin 共存。無論版本,都不要在同一個容器同時寫gap-*和space-*——兩者會疊加,間距變成非預期的兩倍。
實作範例
理論看完,我們用幾個高頻的可執行範例把工具串起來。以下都是完整的 HTML + Tailwind,可直接貼進頁面觀察效果。
範例一:mx-auto 水平置中容器
最經典的間距招式,就是用 mx-auto(margin-left: auto; margin-right: auto)把一個有最大寬度的區塊在頁面水平置中——這是幾乎每個網站主內容容器的標準寫法:
<!-- 標準內容容器:限制最大寬度 + 水平置中 + 響應式左右內距 -->
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<h1 class="text-3xl font-bold text-gray-900">頁面標題</h1>
<p class="mt-4 text-gray-600">這段內容永遠置中,兩側留有隨螢幕變化的內距。</p>
</div>
這裡三顆 class 各司其職。max-w-4xl 限制容器最大寬度(避免在寬螢幕上文字拉得太長)、mx-auto 讓左右外距自動均分把容器推到中央、px-4 sm:px-6 lg:px-8 則給左右內距,並用響應式前綴讓手機留白小、桌面留白大。要特別注意:mx-auto 要生效,元素必須有明確的寬度限制(如這裡的 max-w-4xl),否則區塊會撐滿整行、左右沒有多餘空間可分配,看起來就不會置中。另外 mt-4 示範了單邊外距——標題與段落之間拉開 1rem 的縱向呼吸。
範例二:卡片的 padding 與內部節奏
卡片是 padding 的主場——用 p-* 把內容從邊框往內推、留出內部呼吸空間,再用 margin 或 space 控制內部元素的縱向節奏:
<!-- 卡片:外層 padding 決定內部留白,內部用間距建立節奏 -->
<div class="max-w-sm p-6 bg-white rounded-2xl shadow-sm border border-gray-100">
<span class="inline-block px-3 py-1 bg-cyan-50 text-cyan-700 text-xs font-semibold rounded-full">
教學
</span>
<h3 class="mt-4 text-lg font-bold text-gray-900">TailwindCSS 間距系統</h3>
<p class="mt-2 text-sm text-gray-600 leading-relaxed">
用一致的 spacing scale,讓版面從「能看」進化到「耐看」。
</p>
<button class="mt-6 w-full py-2.5 bg-cyan-600 text-white text-sm font-medium rounded-lg hover:bg-cyan-700">
開始閱讀
</button>
</div>
這個範例展示了 padding 的兩種尺度。外層卡片的 p-6(1.5rem)是「容器級」內距,決定整張卡片內容離邊框多遠;而徽章的 px-3 py-1 則是「元件級」內距——水平 0.75rem、垂直 0.25rem,讓小標籤的文字四周有剛好的留白。內部元素之間則靠遞增的單邊上外距建立節奏:mt-4(標題)、mt-2(描述,較近表示關聯緊密)、mt-6(按鈕,較遠表示是獨立動作)。用 padding 管內部留白、用 margin 管元素間距,各司其職,版面就會條理分明。
範例三:space-y 堆疊清單 vs gap 格狀排版
這個範例並列兩種子元素間距工具,讓你直接感受它們各自的舞台:
<!-- space-y-*:適用於非 flex/grid 的單純垂直堆疊清單 -->
<ul class="space-y-3">
<li class="p-4 bg-white rounded-lg border border-gray-200">清單項目 1</li>
<li class="p-4 bg-white rounded-lg border border-gray-200">清單項目 2</li>
<li class="p-4 bg-white rounded-lg border border-gray-200">清單項目 3</li>
</ul>
<!-- gap-*:Flexbox/Grid 的首選,換行時列與列也自動有間距 -->
<div class="mt-8 grid grid-cols-2 sm:grid-cols-3 gap-4">
<div class="p-4 bg-white rounded-lg border border-gray-200">格子 A</div>
<div class="p-4 bg-white rounded-lg border border-gray-200">格子 B</div>
<div class="p-4 bg-white rounded-lg border border-gray-200">格子 C</div>
<div class="p-4 bg-white rounded-lg border border-gray-200">格子 D</div>
</div>
上半的 <ul> 是一個普通的區塊容器(不是 flex/grid),用 space-y-3 在相鄰項目之間加 0.75rem 的縱向間距——這是 space-* 的正當用途:單純的垂直堆疊、不需要換行邏輯。下半的 grid ... gap-4 才是主流做法:grid-cols-2 sm:grid-cols-3 切出格狀骨架,gap-4 統一控制格子之間的橫向與縱向間距,當格子換到下一列時,列與列之間也自動有 1rem 縫隙——這種「換行自動保持間距」的能力是 space-* 給不了的。記住原則:格狀/彈性排版用 gap,純堆疊清單才考慮 space。
範例四:ml-auto 推擠與負 margin 頭像堆疊
margin 還有兩個進階招式:用 auto 在 flex 容器中「推擠」元素,以及用「負值」製造重疊效果。
<!-- ml-auto 把右側元素推到最右端 -->
<div class="flex items-center px-4 py-3 bg-white border-b border-gray-200">
<span class="text-lg font-bold text-gray-900">Logo</span>
<nav class="ml-auto flex gap-6 text-sm text-gray-600"> <!-- ml-auto 吃掉左側所有剩餘空間 -->
<a href="#" class="hover:text-gray-900">首頁</a>
<a href="#" class="hover:text-gray-900">關於</a>
</nav>
</div>
<!-- 負值 space:讓頭像互相重疊,做出堆疊效果 -->
<div class="mt-6 flex items-center">
<div class="flex -space-x-3"> <!-- 負間距讓每個頭像向左偏移,製造重疊 -->
<img src="/avatar1.jpg" alt="用戶1" class="w-10 h-10 rounded-full border-2 border-white object-cover">
<img src="/avatar2.jpg" alt="用戶2" class="w-10 h-10 rounded-full border-2 border-white object-cover">
<img src="/avatar3.jpg" alt="用戶3" class="w-10 h-10 rounded-full border-2 border-white object-cover">
<div class="w-10 h-10 rounded-full border-2 border-white bg-gray-100 flex items-center justify-center text-xs font-medium text-gray-600">
+9
</div>
</div>
<span class="ml-4 text-sm text-gray-600">12 位成員</span>
</div>
上半展示 ml-auto 推擠:在 flex 容器裡,auto 外距會「吃掉」該方向所有剩餘空間,於是 nav 被推到最右端——這比手動算寬度或用定位乾淨太多,是做導覽列、工具列右側元素對齊的招牌手法(同理 mt-auto 可在 flex-col 容器把元素推到底部)。下半展示 負值間距:-space-x-3 讓每個頭像向左偏移 0.75rem,做出社群產品常見的頭像重疊堆疊;末尾的 border-2 border-white 白邊讓重疊的頭像界線分明。負值就是「往反方向拉」,是製造重疊、突破容器邊界(breakout)等效果的利器。
範例五:邏輯屬性 ps-/pe- 與任意值
最後補上兩個進階寫法:v4 完整支援的邏輯屬性,以及跳脫刻度的任意值。
<!-- ps-/pe-:邏輯內距,在 RTL 語言自動翻轉方向 -->
<div class="relative">
<svg class="absolute inset-y-0 left-3 my-auto w-5 h-5 text-gray-400"><!-- 搜尋圖示 --></svg>
<!-- ps-10 給圖示留位、pe-4 給右側留白;RTL 時會自動左右對調 -->
<input type="search" placeholder="搜尋..."
class="w-full ps-10 pe-4 py-2 border border-gray-300 rounded-lg">
</div>
<!-- 任意值:刻度不夠精準時,直接指定確切數值 -->
<div class="p-[18px]">內距剛好 18px</div>
<div class="mt-[72px]">上外距固定 72px(如避開固定頁首)</div>
<div class="-mx-[1.5rem]">負任意值:突破容器左右邊界的全出血效果</div>
上半的 ps-10 pe-4 是 v4 的邏輯屬性——ps-* 對應 padding-inline-start(邏輯起始側)、pe-* 對應 padding-inline-end(邏輯結束側)。在中文、英文這種左到右(LTR)的語言中,ps- 等同 pl-、pe- 等同 pr-;但在阿拉伯文等右到左(RTL)語言中會自動翻轉。若網站要支援多語系,優先用 ps-/pe-/ms-/me-,比寫死方向的 pl-/pr- 省下大量 RTL 重構成本。下半展示任意值:當一致刻度剛好沒有你要的數字、或需求本身就是某個固定像素(例如避開一個 72px 高的固定頁首),用方括號 p-[18px]、mt-[72px] 就能指定確切值,負任意值 -mx-[1.5rem] 則常用來做「全出血」——不過任意值屬於「逃生艙」,能用刻度就盡量用刻度,才能維持全站的視覺節奏。
常見錯誤與最佳實踐
四個最容易踩的間距坑
坑一:垂直 margin 的「邊距折疊(margin collapsing)」。 在正常文件流的垂直方向上,相鄰區塊元素的 margin-bottom 與 margin-top 不會相加,而是折疊成「較大的那一個」。上元素 mb-4(16px)、下元素 mt-8(32px),最終間距是 32px 而非 48px。這常讓人誤以為間距工具壞了,其實是 CSS 原生行為。根治法:把父容器設成 flex flex-col 再用 gap-*——Flexbox/Grid 的子元素之間不會折疊,gap 設多少就是多少。
<!-- ❌ 靠各自的 margin 堆間距,會發生邊距折疊,間距不如預期 -->
<div>
<p class="mb-4">段落 A</p>
<p class="mt-8">段落 B</p> <!-- 實際間距只有 32px,不是 48px -->
</div>
<!-- ✅ 改用 flex-col + gap,不折疊、完全可預測 -->
<div class="flex flex-col gap-8">
<p>段落 A</p>
<p>段落 B</p> <!-- 穩定的 32px -->
</div>
坑二:在同一容器同時用 gap-* 和 space-*。 兩者機制不同卻都會產生間距,寫在一起會疊加,導致間距變成非預期的兩倍。同一容器請二選一,且在 flex/grid 中一律優先 gap。
<!-- ❌ gap 和 space 疊加,間距變兩倍 -->
<div class="flex gap-4 space-x-4">...</div>
<!-- ✅ flex/grid 只用 gap -->
<div class="flex gap-4">...</div>
坑三:用 margin 代替 padding 處理「背景色內部的留白」。 margin 是透明的、在元素邊框之外,不會被背景色包住;padding 在邊框之內、會被背景色包含。想讓帶背景色的區塊內容四周有留白,必須用 padding,用 margin 只會把整塊往外推、內容仍緊貼邊緣。
<!-- ❌ 背景色不會包含 mt-4 的空間,內容仍緊貼上緣 -->
<div class="bg-cyan-100 mt-4">內容</div>
<!-- ✅ padding 在背景色之內,內容四周有留白 -->
<div class="bg-cyan-100 p-4">內容</div>
坑四:mx-auto 沒有寬度限制卻期待置中。 mx-auto 靠「把剩餘水平空間均分給左右外距」來置中,若元素本身撐滿整行(如區塊元素預設 width: auto),就沒有剩餘空間可分配,置中不會發生。務必搭配 max-w-*(或明確 w-*)給元素一個比容器窄的寬度。
間距系統最佳實踐清單
- 內距用 padding、間距用 margin/gap:背景色內的留白一律
p-*,元素之間的距離用m-*或(在 flex/grid 中)gap-*。 - flex/grid 一律用
gap-*:比space-*更可預測、支援換行、首尾不多間距,是子元素間距的首選。 space-*只留給純堆疊清單:非 flex/grid 的單純垂直列表才用space-y-*,且切勿與gap混用。- 多用刻度、少用任意值:
p-4/p-6這類刻度值能維持全站節奏;任意值p-[18px]是逃生艙,非必要不用。 - 響應式間距建立層次:
py-12 md:py-16 lg:py-24讓手機留白小、桌面留白大,是打造專業節奏感的關鍵。 - 多語系優先用
ps-/pe-/ms-/me-:v4 的邏輯屬性能在 RTL 語言自動翻轉,比寫死pl-/pr-更穩健。 - 善用
--spacing統一基準:需要全站等比縮放時,在@theme覆寫一次--spacing即可,別逐個 class 硬改。
小結
這是 TailwindCSS 完整教學 系列的第十二篇。上一篇 《定位系統》 我們把「脫離文件流」的定位世界講透——用 relative/absolute/fixed/sticky 決定定位模式、inset-* 控制偏移、z-* 與堆疊脈絡控制層疊,做出黏性導覽、徽章、彈窗這些疊層 UI。那些工具決定元素「擺在哪、疊在哪」;而這一篇則補上「留白的藝術」——讓元素之間、內容與邊框之間,有恰到好處的呼吸感。回顧幾個重點:
- padding 管內、margin 管外:
p-*是被背景色包住的內部留白、m-*是透明的元素間距,命名法p/m+x/y/t/r/b/l/s/e一眼看穿方向。 - v4 用單一
--spacing動態推導刻度:每個刻度都是calc(var(--spacing) * n),任意倍數如p-13開箱即用,p-*/m-*/gap-*/w-*共用基準,覆寫一次即等比縮放(v3 是靜態表、表外值不存在)。 - 子元素間距優先
gap:flex/grid 用gap-*(原生、支援換行、首尾乾淨),space-*只留給純堆疊清單,兩者切勿同容器混用。 - margin 進階招式:
mx-auto置中(需搭配寬度限制)、ml-auto/mt-auto推擠、負值-space-x-*做重疊堆疊。 - 兩大常見坑:垂直 margin 會「邊距折疊」(改用
flex-col + gap根治)、背景色內留白必須用 padding 而非 margin。
掌握了 Flexbox、Grid、定位與間距,你已經能精準掌控元素的排列、層疊與留白。但版面上的每個元素到底該「多大」——按鈕多寬、頭像多高、容器最大寬度是多少——還沒系統化地談過。下一篇 《尺寸系統》,我們會把 w-*(width)、h-*(height)、min-w-*/max-w-*、size-*,以及 v4 一樣共用 --spacing 基準的尺寸刻度一次講透,看 Tailwind 如何讓「大小」也落在同一套一致的刻度上,讓你的版面從「疏密有致」再進化到「比例協調」。