CSS CORE · 50
Responsive / Position / States:讓同一份 UI 在不同限制下仍成立
Responsive 不是做「桌機版 + 手機版兩個網站」,而是從 flexible defaults 出發,當內容真的在某個寬度壞掉時才引入 breakpoint。
Learning outcomes
- 能以 content/layout failure 決定 breakpoint。
- 能區分 static/relative/absolute/sticky/fixed 的 positioning context。
- 能建立 hover/focus-visible/disabled states。
- 能用 viewport testing 找 layout regression。
1. Mobile-first / flexible defaults
.layout {
display: grid;
gap: 1rem;
}
@media (
min-width: 50rem
) {
.layout {
grid-template-columns:
16rem 1fr;
}
}Breakpoint 應服務 layout need,而不是因為「某手機是 768px」。
2. Positioning context
.card {
position: relative;
}
.badge {
position: absolute;
top: .75rem;
right: .75rem;
}Absolute element 相對哪個 containing block 定位,是 debug 時第一個問題。
3. Sticky / Fixed 不一樣
.toolbar {
position: sticky;
top: 0;
}
.help {
position: fixed;
right: 1rem;
bottom: 1rem;
}Sticky 仍參與原本 flow 並在 scroll constraint 下黏住;fixed 通常相對 viewport 固定。
4. Interaction states
button:hover {
transform:
translateY(-1px);
}
button:focus-visible {
outline:
3px solid #315fcb;
}
button:disabled {
opacity: .5;
cursor: not-allowed;
}不要只設 hover,鍵盤使用者也需要可見 focus state。
5. Reduced motion / user preference
@media (
prefers-reduced-motion:
reduce
) {
* {
scroll-behavior: auto;
}
}Responsive 也包含使用者與裝置能力偏好,不只 viewport width。
Project checkpoint:Course Profile v7
.profile-layout {
display: grid;
gap: 1rem;
}
@media (
min-width: 56rem
) {
.profile-layout {
grid-template-columns:
18rem 1fr;
}
}
.course-nav {
position: sticky;
top: 1rem;
}現在 Course Profile 已從 semantic HTML 走到真正 responsive layout。下一步才適合用 JavaScript 加互動 state。
Debug evidence:手機版被遮住
- DevTools 切到問題 viewport。
- 看 element box 是否超出。
- 檢查 fixed/sticky/absolute containing context。
- 檢查 z-index/stacking context,而不是盲目把 z-index 加到 999999。
Knowledge check
- Breakpoint 應依裝置名稱還是 content failure?
- absolute badge 相對誰定位?
- hover-only interaction 有什麼 accessibility 問題?
- 用 360px、768px、1440px 三種 viewport 驗證 Course Profile。