Sass 柵格與間距系統:用迴圈產生刻度
柵格與間距是最適合交給迴圈的東西,規則多、模式一致、手寫既無聊又容易漏。但在開始寫之前,先問一句:這個專案真的需要柵格系統嗎?
先考慮原生 Grid
十年前需要柵格類別,是因為 CSS 沒有排版工具。現在有了:
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 16px;
}一條規則,自動依容器寬度決定放幾欄,不需要任何 .col-4 類別,也不需要斷點。Grid 與 Flex 能處理大部分版面。
真的需要「跨幾欄」這種明確控制時,再用迴圈產生類別。
十二欄柵格
@use "sass:math";
$columns: 12 !default;
$gutter: 16px !default;
.row {
display: grid;
grid-template-columns: repeat($columns, 1fr);
gap: $gutter;
}
@for $i from 1 through $columns {
.col-#{$i} { grid-column: span $i; }
}.col-1 { grid-column: span 1; }
/* ... */
.col-12 { grid-column: span 12; }through 不是 to
to 12 只會產生到 .col-11,最後一欄就不見了。差別見 計次迴圈 @for。
用 Flex 的話要自己算百分比:
@for $i from 1 through $columns {
.col-#{$i} {
flex: 0 0 math.percentage(math.div($i, $columns));
}
}加上斷點
@for $i from 1 through $columns {
.col-#{$i} { grid-column: span $i; }
}
@each $bp in (sm, md, lg) {
@include respond-to($bp) {
@for $i from 1 through $columns {
.col-#{$bp}-#{$i} { grid-column: span $i; }
}
}
}<div class="col-12 col-md-6 col-lg-4">數量會乘起來
12 欄 × 4 個斷點 = 48 條規則,還沒算間距與其他工具類別。只產生真的會用到的斷點與欄數,或交給能移除未使用樣式的工具,見 效能與產出品質。
respond-to 的實作見 響應式 mixin。
間距刻度
比柵格更常用、也更有價值的是間距:
$spacing: (
0: 0,
1: 4px,
2: 8px,
3: 16px,
4: 24px,
5: 32px,
6: 48px,
) !default;
@function space($key) {
@return map.get($spacing, $key);
}有了刻度,元件裡就不會再出現隨手填的 13px:
.card {
padding: space(3);
margin-bottom: space(4);
gap: space(2);
}4 的倍數還是 8 的倍數
4 的顆粒度細、8 的節奏感強。都可以,重點是有一套刻度,而不是每次憑感覺填數字。
產生間距工具類別
@use "sass:map";
$sides: (t: top, r: right, b: bottom, l: left);
@each $key, $value in $spacing {
.m-#{$key} { margin: $value; }
.p-#{$key} { padding: $value; }
@each $abbr, $side in $sides {
.m#{$abbr}-#{$key} { margin-#{$side}: $value; }
.p#{$abbr}-#{$key} { padding-#{$side}: $value; }
}
}.m-3 { margin: 16px; }
.mt-3 { margin-top: 16px; }
.pl-2 { padding-left: 8px; }7 個刻度 × (2 + 8) = 70 條規則。再乘上斷點就會破千,這正是 工具類別 那篇要處理的問題。
加上 x 與 y 軸更實用
.mx-#{$key} { margin-inline: $value; }
.my-#{$key} { margin-block: $value; }邏輯屬性(inline / block)在需要支援由右至左語系時會自動翻轉。
容器
$containers: (sm: 540px, md: 720px, lg: 960px, xl: 1140px) !default;
.container {
width: 100%;
margin-inline: auto;
padding-inline: $gutter;
@each $bp, $max in $containers {
@include respond-to($bp) { max-width: $max; }
}
}間隙用 gap,不要用負外距
// ❌ 舊做法:每欄左右內距 + 容器負外距抵消
.row { margin: 0 -8px; }
.col { padding: 0 8px; }
// ✅ 直接用 gap
.row { display: grid; gap: 16px; }負外距在巢狀時很容易出錯,gap 在 flex 與 grid 上的支援現在都很完整。
常見問題
現在還需要自己刻十二欄柵格嗎?
多數情況不需要。原生 Grid 的 repeat() 與 auto-fit 可以直接做出自適應的欄位,不必產生一堆類別。仍然需要固定欄數類別時,才用迴圈產生跨欄的簡短寫法。
間距刻度該用幾的倍數?
常見的是 4 或 8 的倍數,4 的顆粒度較細、8 的節奏感較強。重點是有一套刻度而不是每次隨手填數字,這樣版面的間距才會有一致的呼吸感。
帶斷點的類別會不會產生太多規則?
會,欄數乘上斷點數就是總量,再加上間距與方向會更多。只產生真正用得到的組合,或搭配能移除未使用樣式的工具,否則產出很快就失控。
柵格的間隙該用 gap 還是 padding?
用 gap。舊做法靠每欄的左右內距加上容器的負外距來抵消,寫起來繞而且容易在巢狀時出錯。現在的瀏覽器對 flex 與 grid 的 gap 支援都很完整。