商用網站架設實務

LESSON-11 · checkpoint

CSS 進階:Position、Sticky 與堆疊

主要結構已經完成,只留下本堂一個核心缺口。先預測,再從指定檔案找出它。 這頁用 可觀察證據anchor 指出下一個動作。

Starter4 個小成果
Checkpoint一個核心缺口
Solution5 個差異

開始前確認

這一頁的結果:先在 Checkpoint 重現 badge 位置不依附 service-card

工作方式:先把這一階段複製到自己的工作資料夾,開啟 files/index.html,記下修改前的畫面、Elements、Styles、Computed、捲動與 Tab狀態。

會用到的檔案:index.htmlstyle.css

本堂焦點:用 Position 控制元素參考點、捲動行為與前後層級

照這個順序做

一次只做一張卡。完成後先重新整理入口,再留下證據,才進入下一張。

CHECKPOINT-1

用最近的 positioned ancestor 建立 containing block,而不是讓 badge 以頁面根部為參考。

目的

用最近的 positioned ancestor 建立 containing block,而不是讓 badge 以頁面根部為參考。

修改檔案

style.css

程式碼位置

.service-card

現在要做:請在 style.css 的「.service-card」位置完成:補上 position: relative,讓 absolute badge 以服務卡為參考

貼上位置:style.css.service-card 區塊,先保留同一檔案的其他內容。

.service-card { position: relative; }

預期結果:推薦標籤停在推薦卡右上角,服務卡移動時 badge 跟著卡片移動。

驗收證據:Elements 父子關係、Computed position 與卡片/badge 的相對座標。

卡住時先看這裡

常見錯誤:只改 badge 的 top/right,不處理父層,或把 relative 加到錯的祖先元素。

第一個檢查:只查看 .featured-badge 最近的 .service-card 是否有 position: relative

階段檔案

完整檔案收在可展開的卡片中;先完成上方微步驟,再用這裡查閱與複製。檔案位置要和目前的相對路徑一致。

開啟 checkpoint 的可執行入口

index.html入口頁:包含服務卡、長內容、重疊卡片、可聚焦控制項與定位任務。

這段要貼在哪裡:先在自己的工作資料夾建立同名相對位置,再複製內容。

<!doctype html>
<html lang="zh-Hant"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>安心維護|定位練習</title><link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect width='16' height='16' rx='4' fill='%230f4c5c'/%3E%3C/svg%3E"><link rel="stylesheet" href="style.css"></head>
<body><header class="site-header"><a class="brand" href="#top">安心維護</a><nav aria-label="主要導覽"><a href="#services">服務</a><a href="#evidence">驗收</a><a href="#contact">聯絡</a></nav></header>
<main id="top" class="page-shell"><p class="eyebrow">LESSON-11 · 用 Position 控制元素參考點、捲動行為與前後層級</p><h1>讓定位關係可以被看見</h1><p class="page-description">本頁用服務卡、長內容、固定入口與重疊卡片,觀察元素相對誰定位,以及如何留下畫面與 DevTools 證據。</p><section class="flow-demo" aria-labelledby="flow-title"><h2 id="flow-title">normal flow 與 relative</h2><article class="flow-card">第一張卡片:static 的 normal flow。</article><article class="flow-card flow-card--moved">第二張卡片:Starter 請讓它使用 relative。</article><article class="flow-card">第三張卡片:觀察它是否保留原本位置。</article></section>
<section id="services" aria-labelledby="services-title"><h2 id="services-title">服務方案</h2><article class="service-card service-card--featured"><span class="featured-badge">推薦</span><h3>定期維護</h3><p>推薦方案的 badge 應貼在這張卡片右上角,不應把其他內容往下推。</p><a href="#evidence">查看定位證據</a></article><article class="service-card"><h3>到府諮詢</h3><p>這張卡片留在 normal flow,方便和推薦卡比較。</p></article></section>
<section class="long-content" aria-labelledby="scroll-title"><h2 id="scroll-title">長內容與捲動</h2><p>內容刻意保留足夠高度,讓 sticky header 的前後狀態可以觀察。</p><p>在這裡按 Tab 也能確認 header、連結與固定入口的操作順序。</p><p>固定元素不會自動替內容讓出空間,所以頁面底部需要安全距離。</p><p>把 viewport 切換到 390px,再檢查長文字、按鈕與卡片是否仍可讀。</p><p>將瀏覽器文字放大到 200%,確認內容沒有依賴固定高度或水平捲軸。</p><p>這些段落讓 sticky、fixed 與 overflow 的差異有實際畫面證據。</p></section>
<section id="evidence" class="stack-demo" aria-labelledby="stack-title"><h2 id="stack-title">z-index 重疊實驗</h2><article class="stack-card stack-card--back">後層卡片:z-index 1</article><article class="stack-card stack-card--front">前層卡片:z-index 2</article></section>
<section id="contact" aria-labelledby="contact-title"><h2 id="contact-title">聯絡與鍵盤驗收</h2><p>請只用 Tab 走過導覽、卡片連結、固定 CTA 與下面的按鈕。</p><button type="button">可聚焦的按鈕</button> <a href="#top">回到頁面頂端</a></section></main><a class="floating-contact" href="#contact">聯絡我們</a>
    <!-- CHECKPOINT-1:service-card 尚未成為 badge 的 containing block,請先預測再修正 style.css。 --></body></html>
style.css定位樣式:集中觀察 normal flowrelativeabsolutestickyfixedz-indexfocus-visible

這段要貼在哪裡:先在自己的工作資料夾建立同名相對位置,再複製內容。

:root { font-family: system-ui, -apple-system, "Segoe UI", "Noto Sans TC", sans-serif; color: #17324d; background: #f5f8fb; } * { box-sizing: border-box; } body { margin: 0; min-height: 100vh; line-height: 1.7; }
.site-header { width: 100%; padding: .9rem max(1rem, calc((100% - 820px) / 2)); display: flex; justify-content: space-between; gap: 1rem; align-items: center; background: #0f4c5c; color: #fff; position: sticky; top: 0; z-index: 10; } .site-header nav { display: flex; flex-wrap: wrap; gap: .75rem; } .site-header a { color: #fff; }
.page-shell { width: min(100% - 2rem, 820px); margin: 0 auto; padding: 2rem 2rem 6rem; background: #fff; } .eyebrow { color: #075985; font-weight: 800; } .page-description { color: #52657d; } section { margin-block: 2rem; }
.flow-demo { display: grid; gap: .75rem; } .flow-card { padding: 1rem; border: 1px solid #b9cddd; border-radius: .6rem; background: #f8fafc; } .flow-card--moved { position: relative; top: 1rem; left: 1rem; border-color: #0f766e; background: #ecfdf5; }
.service-card { position: static; min-height: 10rem; padding: 1.25rem; border: 2px solid #b9cddd; border-radius: .75rem; background: #fff; } .service-card--featured { border-color: #0f766e;  } .featured-badge { position: absolute; top: .75rem; right: .75rem; padding: .25rem .6rem; border-radius: 999px; background: #9a3412; color: #fff; font-weight: 800; }
.long-content { min-height: 125vh; padding-block: 1rem; } .stack-demo { position: relative; min-height: 14rem; } .stack-card { position: absolute; width: min(70%, 22rem); padding: 1rem; border: 2px solid #17324d; border-radius: .75rem; } .stack-card--back { top: 1rem; left: 1rem; z-index: 1; background: #dbeafe; } .stack-card--front { top: 4rem; left: 4rem; background: #fed7aa; z-index: 2; }
.floating-contact { position: fixed; right: 1rem; bottom: 1rem; z-index: 20; padding: .7rem 1rem; border-radius: .55rem; background: #9a3412; color: #fff; font-weight: 800; text-decoration: none; } button { padding: .65rem .9rem; border: 0; border-radius: .5rem; background: #0f4c5c; color: #fff; font: inherit; cursor: pointer; }
:focus-visible { outline: 3px solid #1d4ed8; outline-offset: 3px; }
@media (max-width: 560px) { .site-header { align-items: flex-start; flex-direction: column; } .page-shell { width: min(100% - 1rem, 820px); padding-inline: 1rem; } .stack-card { width: calc(100% - 2rem); } .stack-card--front { left: 2rem; } }

完成驗收

下一步

Checkpoint 完成後,先說出你修正的原因,再前往 Solution 對照每個檔案差異。

前往 Solution,逐檔對照差異