Skip to content

Sass 柵格與間距系統:用迴圈產生刻度

用 Sass 迴圈產生柵格與間距

柵格與間距是最適合交給迴圈的東西,規則多、模式一致、手寫既無聊又容易漏。但在開始寫之前,先問一句:這個專案真的需要柵格系統嗎?

先考慮原生 Grid

十年前需要柵格類別,是因為 CSS 沒有排版工具。現在有了:

scss
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 16px;
}

一條規則,自動依容器寬度決定放幾欄,不需要任何 .col-4 類別,也不需要斷點。GridFlex 能處理大部分版面。

真的需要「跨幾欄」這種明確控制時,再用迴圈產生類別。

十二欄柵格

scss
@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; }
}
css
.col-1 { grid-column: span 1; }
/* ... */
.col-12 { grid-column: span 12; }

through 不是 to

to 12 只會產生到 .col-11,最後一欄就不見了。差別見 計次迴圈 @for

用 Flex 的話要自己算百分比:

scss
@for $i from 1 through $columns {
  .col-#{$i} {
    flex: 0 0 math.percentage(math.div($i, $columns));
  }
}

加上斷點

scss
@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; }
    }
  }
}
html
<div class="col-12 col-md-6 col-lg-4">

數量會乘起來

12 欄 × 4 個斷點 = 48 條規則,還沒算間距與其他工具類別。只產生真的會用到的斷點與欄數,或交給能移除未使用樣式的工具,見 效能與產出品質

respond-to 的實作見 響應式 mixin

間距刻度

比柵格更常用、也更有價值的是間距:

scss
$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

scss
.card {
  padding: space(3);
  margin-bottom: space(4);
  gap: space(2);
}

4 的倍數還是 8 的倍數

4 的顆粒度細、8 的節奏感強。都可以,重點是有一套刻度,而不是每次憑感覺填數字。

產生間距工具類別

scss
@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; }
  }
}
css
.m-3 { margin: 16px; }
.mt-3 { margin-top: 16px; }
.pl-2 { padding-left: 8px; }

7 個刻度 × (2 + 8) = 70 條規則。再乘上斷點就會破千,這正是 工具類別 那篇要處理的問題。

加上 x 與 y 軸更實用

scss
.mx-#{$key} { margin-inline: $value; }
.my-#{$key} { margin-block: $value; }

邏輯屬性(inline / block)在需要支援由右至左語系時會自動翻轉。

容器

scss
$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,不要用負外距

scss
// ❌ 舊做法:每欄左右內距 + 容器負外距抵消
.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 支援都很完整。

延伸閱讀