Sass 斷點管理:用 map 收攏 RWD 的所有數值
RWD 專案最常見的維護問題不是斷點訂錯,是 768px 這個數字在四十個檔案裡各寫了一次。設計改版要調成 800px,你得全域搜尋替換,然後祈禱沒有漏掉。
為什麼要用 Sass 管
CSS 自訂屬性 在這裡幫不上忙,它不能用在媒體查詢的條件裡:
:root { --md: 768px; }
@media (min-width: var(--md)) { } /* ❌ 無效 */斷點的值必須在編譯期就決定,這正是 Sass 變數擅長的事。
一張 map 管全部
// abstracts/_breakpoints.scss
$breakpoints: (
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
) !default;加上 !default,使用這個模組的人就能用 @use ... with 換掉整組。
取值要有防呆
@use "sass:map";
@function bp($name) {
@if not map.has-key($breakpoints, $name) {
@error "斷點 #{$name} 不存在。可用的有:#{map.keys($breakpoints)}。";
}
@return map.get($breakpoints, $name);
}直接用 map.get() 的話,打錯字會回傳 null,@media (min-width: null) 就變成無效的查詢,樣式安靜地不生效。包一層函式讓它在編譯期就炸掉,見 sass:map。
用在媒體查詢
.card {
padding: 16px;
@media (min-width: bp(md)) {
padding: 24px;
}
}這裡不需要插值
bp(md) 是函式呼叫,回傳的值可以直接放在 min-width: 後面。只有 變數 出現在 @media 條件裡才要 插值。
不過每次都寫 @media (min-width: bp(md)) 還是囉嗦,實務上會再包一層 混入,做法見 響應式 mixin 實作。
mobile-first:只寫 min-width
.card {
padding: 16px; // 手機(預設)
@media (min-width: bp(md)) { padding: 24px; }
@media (min-width: bp(lg)) { padding: 32px; }
}由小到大,後面的覆蓋前面的。好處:
- 只用
min-width一種條件,不會有邊界重疊。 - 手機拿到的是最精簡的樣式,不必先套桌機版再覆蓋回來。
- 新增斷點只是往後追加一段。
混用 min 與 max 時要小心邊界
@media (max-width: 768px) { } // 768 成立
@media (min-width: 768px) { } // 768 也成立 ← 重疊在剛好 768px 時兩條都會套用。要混用的話,max-width 要減去一個極小值:
@function bp-max($name) {
@return bp($name) - 0.02px;
}Bootstrap 就是這樣處理的。改用純 mobile-first 則完全不會遇到這個問題。
不只有寬度
同一張表的概念可以延伸到其他媒體特性:
$media: (
md: "(min-width: 768px)",
dark: "(prefers-color-scheme: dark)",
motion: "(prefers-reduced-motion: reduce)",
hover: "(hover: hover) and (pointer: fine)",
print: "print",
);@media #{map.get($media, hover)} {
.btn:hover { background: #1b4dc1; }
}這裡就需要插值了
map 取出的是字串,要放進 @media 後面必須用 #{} 展開。
(hover: hover) 這個查詢很值得一提,它讓 hover 樣式只在真的有滑鼠的裝置上生效,觸控裝置就不會出現「點一下之後卡在 hover 狀態」的問題。
斷點該怎麼訂
依內容決定,不要對著機型訂。
把瀏覽器從最窄慢慢拉寬,觀察版面在哪裡開始難看:文字行長超過 75 字元、卡片擠成一團、導覽列換行,那裡就是斷點。
訂出來的數字通常會落在常見尺寸附近,但理由完全不同:前者是「這個版面撐不住了」,後者是「某支手機是這麼寬」。裝置尺寸每年都在變,內容的可讀性不會。
單位用 px 還是 em
$breakpoints: (md: 48em); // 48 × 16px = 768pxem 的好處是使用者把瀏覽器預設字級調大時,斷點會跟著放大,對無障礙比較友善。px 則直觀好溝通。
兩者都可接受,重點是整個專案統一。
常見問題
斷點為什麼不能用 CSS 自訂屬性?
因為自訂屬性只能當屬性值使用,不能出現在媒體查詢的條件裡。斷點的數值必須在編譯期就決定,所以要用 Sass 變數或 map 管理。
斷點該怎麼訂?
依內容決定,不要對著特定機型的螢幕尺寸訂。把瀏覽器慢慢拉寬,版面在哪裡開始難看,那裡就是斷點。訂出來的數字通常會落在常見尺寸附近,但理由不同。
max-width 的斷點要減 0.02px 嗎?
混用 min-width 與 max-width 時要,否則在剛好等於斷點的寬度上兩條規則會同時成立。減去一個極小值可避開這個重疊,Bootstrap 就是這樣做的。改用 mobile-first 只寫 min-width 則完全不會遇到。
斷點的單位該用 px 還是 em?
用 em 的話,使用者調大瀏覽器預設字級時斷點會跟著放大,對無障礙比較友善。用 px 則直觀好溝通。兩者都可接受,重點是整個專案統一。