將下列結構建立在 sessions/NN-slug/。資料夾名為歷史相容保留,實際單位是「章節」,不是「一次 180 分鐘課程」。章節時數與課次切片以 course-allocation.json 為準。
必備檔案
NN-slug/
├── index.html
├── teacher-notes.md
├── practice-manifest.json
├── answer-map.html
├── answer-map.md
├── starter/index.html
├── checkpoint-1/index.html
├── checkpoint-2/index.html
└── solution/index.html
practice-manifest.json
{
"contractVersion": 2,
"chapterNumber": 1,
"slug": "web-files-tools",
"title": "網頁、檔案、瀏覽器與開發工具",
"durationMinutes": 180,
"visibleOutcome": "第一個可開啟頁面",
"objectives": [
{ "id": "OBJ-01", "behavior": "替換頁面標題", "condition": "重新整理後查看瀏覽器分頁", "evidence": "分頁顯示自己的作品名稱", "taskIds": ["TASK-01"] },
{ "id": "OBJ-02", "behavior": "完成自我介紹", "condition": "重新整理後閱讀介紹區", "evidence": "姓名與介紹均為個人內容", "taskIds": ["TASK-02"] },
{ "id": "OBJ-03", "behavior": "建立聯絡入口", "condition": "點擊聯絡連結", "evidence": "瀏覽器辨識 mailto 或開啟 HTTPS 頁面", "taskIds": ["TASK-03"] }
],
"prerequisites": [
{ "id": "PRE-01", "skill": "辨識資料夾、檔案與瀏覽器", "check": "指出目前要修改與查看的檔案", "remediationHref": "../../README.html" },
{ "id": "PRE-02", "skill": "辨識四階段", "check": "指出 TASK-01 與 Checkpoint 1", "remediationHref": "#practice" },
{ "id": "PRE-03", "skill": "描述可見證據", "check": "說出操作後預期看見什麼", "remediationHref": "#learning-goals" }
],
"artifact": {
"type": "mainline",
"input": "本機 Starter 專案與個人化內容素材",
"output": "第一個可開啟頁面",
"reusedIn": [2]
},
"schedule": [
{ "label": "回顧與成果預覽", "type": "teaching", "minutes": 15 },
{ "label": "生活比喻與必要術語", "type": "teaching", "minutes": 20 },
{ "label": "核心觀念與錯誤預測", "type": "teaching", "minutes": 25 },
{ "label": "教師示範與刻意錯誤", "type": "teaching", "minutes": 35 },
{ "label": "休息", "type": "break", "minutes": 10 },
{ "label": "學生跟做與 checkpoint-1", "type": "practice", "minutes": 35 },
{ "label": "獨立任務與 checkpoint-2", "type": "practice", "minutes": 25 },
{ "label": "成果證據與口頭說明", "type": "review", "minutes": 10 },
{ "label": "出口檢核與下堂銜接", "type": "review", "minutes": 5 }
],
"tasks": [
{
"id": "TASK-01",
"doneId": "DONE-01",
"title": "替換頁面標題",
"files": ["index.html"],
"visibleResult": "瀏覽器分頁顯示自己的作品名稱",
"observe": {
"prepare": "先記住目前瀏覽器分頁文字。",
"action": "修改、儲存並重新整理頁面。",
"expected": "瀏覽器分頁顯示自己的作品名稱"
},
"acceptance": { "mode": "dom", "assertions": ["title 文字為學生作品名稱"] },
"evidence": ["成果截圖包含瀏覽器分頁與頁面標題。"]
},
{
"id": "TASK-02",
"doneId": "DONE-02",
"title": "完成自我介紹",
"files": ["index.html"],
"visibleResult": "頁面顯示姓名與一段自我介紹",
"observe": {
"prepare": "先找到介紹區的範例文字。",
"action": "替換內容、儲存並重新整理。",
"expected": "頁面顯示姓名與一段自我介紹"
},
"acceptance": { "mode": "dom", "assertions": ["介紹區不再保留範例姓名"] },
"evidence": ["成果截圖包含完整介紹區。"]
},
{
"id": "TASK-03",
"doneId": "DONE-03",
"title": "建立聯絡入口",
"files": ["index.html"],
"visibleResult": "聯絡連結可實際點擊",
"observe": {
"prepare": "確認連結目前沒有指向個人聯絡方式。",
"action": "設定 mailto 或 HTTPS 後實際點擊。",
"expected": "瀏覽器辨識 mailto 或開啟 HTTPS 頁面"
},
"acceptance": { "mode": "interaction", "assertions": ["點擊會觸發 mailto 或開啟 HTTPS"] },
"evidence": ["短錄影呈現點擊前後結果。"]
}
],
"extensions": []
}
規則:
chapterNumber、title、slug與durationMinutes必須與course-allocation.json相同;不再使用sessionNumber。slug不含兩位數編號,必須與資料夾NN-slug的後半部相同。schedule合計精確等於本章durationMinutes。各章依實際教學重新設計活動,禁止等比壓縮舊時間表。- 每段
type: "break"固定 10 分鐘;28 章全部 manifest 合計必須對應 28 次授課的 280 分鐘休息。 - tasks 從
TASK-01連續編號,doneId使用相同數字。 contractVersion固定為2;objectives恰三項,且每個 Task 至少被一個taskIds映射。prerequisites直接使用 2–4 項陣列;每項含進場技能、可作答檢核與主講義基準的補強連結。artifact.type只允許mainline、prototype、extension;除第 28 章外,mainline 的reusedIn不可空白。- 每個 Task 都要保留
observe,另加acceptance.mode與至少一項assertions、evidence。mode 只允許dom、interaction、console、network、external-manual。 files以各階段資料夾為基準,不寫starter/或solution/前綴。- 額外挑戰只列在
extensions,不可讓標準 solution 依賴延伸功能。
本章創作挑戰合約
創作挑戰的唯一維護來源是根目錄 creation-path.json,不手動修改 28 份主講義中的產生區塊。每章 challenge 合約為:
{
"chapterNumber": 1,
"title": "把範例改成你的第一張個人頁",
"summary": "用一句短摘要說明學生要做的變化與可見成果。",
"sessionHref": "sessions/01-web-files-tools/index.html",
"level": "imitate",
"gate": false,
"brief": "一句可驗收、但不指定唯一做法的創作任務。",
"decisions": ["學生要自己決定的內容一", "學生要自己決定的內容二"],
"antiCopy": "明確說明不能照搬範例的部分。",
"evidence": ["可被他人看見或操作的證據", "學生能說明的決策理由"],
"hints": ["第一層只協助定位", "第二層協助拆解步驟", "第三層提供局部校準方向"],
"solutionPolicy": "說明可以看什麼,以及必須自己完成什麼。"
}
能力層級固定為 01–06 imitate、07–13 modify、14–20 transfer、21–28 create;第 13、20、28 章 gate 必須為 true。summary 必須是每章唯一的短摘要,並與 CREATION_PATH.html 卡片內的 <small> 精確同步。decisions 與 evidence 至少兩項,hints 恰好三項,內容不可整段複製其他章。第 26–28 章不得提供學生自選創作挑戰的完整答案;既有 Solution 只核對課堂既定 TASK,不是自選創作的答案。
node tools/build-creation-path.mjs
node tools/check-learning-transfer.mjs
Builder 會在 #practice 與 #evidence 之間產生 <aside id="creation-challenge">,並用 PC_CREATION_CHALLENGE_START / PC_CREATION_CHALLENGE_END marker 安全重跑。區塊顯示 level、brief、decisions、antiCopy、evidence、三個 <details> 提示、solutionPolicy,並連回 ../../CREATION_PATH.html。marker 內不手動加字。
四階段標記
Starter 在要修改的位置保留可見或程式註解標記:
<h1 class="hero__title" id="portfolio-title">請輸入作品名稱</h1>
Checkpoint 可以保留未完成 TASK,已完成項目改成 DONE:
<h1 class="hero__title" id="portfolio-title">小明的作品集</h1>
固定三任務章節的階段集合必須精確一致:Checkpoint-1 恰有 DONE-01;Checkpoint-2 恰有 DONE-01、DONE-02;Solution 恰有 DONE-01、DONE-02、DONE-03。不可提前完成後續任務,也不可在後一階段退回已完成任務。
Solution 的相同位置保留 DONE,HTML 標籤、id、class 不可改:
<h1 class="hero__title" id="portfolio-title">小明的作品集</h1>
如果任務在 CSS 或 JavaScript,標記放在對應檔案。四個階段應保留相同檔名;可改屬性值與程式內容,但不可為了完成答案任意改寫 HTML 結構。
若本章任務本身就是 class rename,可在 manifest 加上明確的一對一例外:
{
"domContract": {
"allowedClassRenames": {
"old-class": "new-class"
}
}
}
這個例外只允許 Starter 使用舊 class、Solution 使用新 class;標籤結構、id 與其他 class 仍必須一致,Solution 也不可同時保留新舊名稱。
若教學任務刻意讓某階段保留失效的同頁連結,必須把 stage 與缺失 id 精確列在 manifest:
{
"domContract": {
"allowedMissingFragmentsByStage": {
"starter": ["projects"]
}
}
}
這個例外只容許列出的 stage 與 id。該頁必須真的存在 href="#projects"、同時沒有 id="projects";連結被移除或目標補上後,過期豁免也會驗收失敗。除此之外,每個階段頁必須恰好有一個 <main>,所有 href="#id" 都必須找到同頁目標。
answer-map.md 與 answer-map.html
# 練習答案對照
| 任務 | 完成標記 | 修改檔案 | 修改位置 | 可見結果 |
|---|---|---|---|---|
| TASK-01 | DONE-01 | index.html | `#portfolio-title` | 分頁與主標題顯示作品名稱 |
| TASK-02 | DONE-02 | index.html | `.about` | 頁面顯示姓名與自我介紹 |
| TASK-03 | DONE-03 | style.css | `.project-card` | 作品卡片有一致間距與邊框 |
answer map 必須恰好三列任務、五個欄位(任務 ID、完成標記、修改檔案、修改位置、可見結果),並與 practice-manifest.json 的三個 Task 逐列精確一致。每列只能回答一個主要動作。若答案需要一次貼上大量新程式,先把任務拆小,或提供含函式骨架的 starter。
answer-map.md 是教師與維護者編輯的來源;學生只從講義開啟 answer-map.html。修改 Markdown 後執行:
node tools/build-student-docs.mjs
不要在任何學生可見 HTML 寫入指向 .md 的 href。
teacher-notes.md 最低內容
- 本章成果與上一章銜接。
- 今日必會、只需辨識、刻意不教。
- 等於本章
durationMinutes的逐段流程,與 manifest 一致,並標出跨課次的分割點。 - 生活比喻如何接回實作。
- 教師示範步驟與刻意製造的錯誤。
- Checkpoint-1、checkpoint-2 的驗收方式。
- 常見卡關、排查順序與缺課接回點。
- 出口檢核與下一堂銜接。
index.html 唯一 DOM contract
28 章主講義都使用同一份頁面骨架。章節只能補內容,不可自行刪除、改名或調換下列區塊。
Head 與主內容入口
<link rel="icon" href="../../favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="../../assets/course.css">
<a class="skip-link" href="#lesson-content">跳到主內容</a>
<main id="lesson-content">
</main>
<script src="../../assets/course.js" defer></script>
- 每頁只有一個
h1,放在#hero。 - 共用 JavaScript 必須加上
defer,避免在 DOM 尚未建立時執行。 - favicon、CSS、JavaScript 均使用重構資料夾內的共用檔案,不連回原始 34 章。
- 學生看得到的程式名詞使用
<code>,不可直接顯示 Markdown 的反引號。
固定頁首
<header class="site-header">
<a class="brand" href="../../index.html">
<img class="brand__mark" src="../../favicon.svg" alt="" aria-hidden="true" width="42" height="42">
<span>零基礎前端課程</span>
</a>
<nav class="header-actions" aria-label="課程導覽">
<a href="../../index.html#course-list">課程總覽</a>
<a href="../上一章目錄/index.html" rel="prev">上一章</a>
<a href="../下一章目錄/index.html" rel="next">下一章</a>
</nav>
</header>
- 品牌名稱、課程總覽與前後章導覽的文字、順序及 class 固定;全課程統一使用淺色模式。
- 第 01 章沒有上一章,使用
<span class="nav-link is-disabled" aria-disabled="true">上一章</span>。 - 第 28 章沒有下一章,使用
<span class="nav-link is-disabled" aria-disabled="true">下一章</span>。 - 第 02 至 28 章的上一章連結必須加
rel="prev";第 01 至 27 章的下一章連結必須加rel="next"。
固定八段順序
固定八個 <section> 之外,builder 會在 practice 後插入創作挑戰 <aside>,不納入八段 section 順序。
<section class="hero" id="hero">...</section>
<section class="principles" id="learning-goals">...</section>
<section class="course-section" id="problem">...</section>
<section class="course-section" id="scope">...</section>
<section class="course-section" id="core-ideas">...</section>
<section class="course-section" id="debugging">...</section>
<section class="course-section" id="practice">...</section>
<section class="course-section" id="evidence">...</section>
八段內容責任依序為:
hero:章次、名稱、可見成果與章節時數。learning-goals:學生下課時能實際完成的三件事。problem:本章問題、生活比喻、上一章或上一課次銜接與共同作品位置。scope:必會、只需辨識、刻意不教與本堂術語。core-ideas:能解釋畫面責任的核心觀念。debugging:刻意錯誤、觀察證據與固定排查順序。practice:Starter → checkpoint-1 → checkpoint-2 → solution 練習路線,以及網頁版answer-map.html。evidence:成果證據、檢核清單與下一章或下一課次銜接。
結構與內容最低契約:
#hero恰好包含一個h1;其餘七段各恰好包含一個h2。#learning-goals與#scope各至少包含三個article,不可只留下標題。#core-ideas、#debugging、#evidence各至少包含一份ul或ol清單。#practice必須直接說明TASK-01、TASK-02、TASK-03,並以.pc-flow四張全卡連結依序連到 Starter、兩個 Checkpoint 與 Solution。#practice必須在.pc-flow前放置獨立下載區塊,並使用PC_STARTER_DOWNLOAD_START/PC_STARTER_DOWNLOAD_END產生標記。區塊使用.pc-starter-download,按鈕文字固定為「下載 Starter ZIP」,連到../../starter-downloads/NN-session-slug-starter.zip並帶download屬性;不可把連結巢狀包進另一個連結。- Starter 下載包固定以
node tools/build-starter-zips.mjs產生,不手動壓縮。ZIP 頂層包含初學者README.md與starter-project/;專案版 HTML 移除教材站步驟導覽與 pager,並把教材共用資產改寫到starter-project/assets/。 - 下載包頂層 README 必須提醒學生解壓縮、用 VS Code「開啟資料夾」而非只開單檔、不要修改
assets/、依固定順序排錯,並列出 manifest 的三項任務。外部圖片、嵌入或 CDN 章節要明寫網路相依。 .pc-flow每一步都要有大號 01–04、階段名稱、原步驟說明、累積進度與清楚操作文字;共用 class 使用pc-前綴。- 每個階段頁都要載入
../../../favicon.svg,並有.pc-stage-nav四步進度與.pc-stage-pager上一步/下一步。Starter 的上一步回../index.html#practice,Solution 的下一步必須先到本章創作挑戰../index.html#creation-challenge。 - 每個階段頁都要在本堂作品 CSS 之後載入
../../../assets/practice-standard.css,使用共用practice-standard外框,統一章節主視覺、四階段路線、工作區與繼續入口。 - 共用版型只負責練習頁的閱讀與操作節奏;各章原本的作品
<main>必須完整保留在.practice-standard__content,不得把章節專屬互動抽成共用功能。 - 四階段頁不再產生
.pc-guide-summary、STEP GUIDE、dialog.pc-guide-dialog或「查看步驟說明」按鈕;任務細節仍以主講義、manifest、答案對照與頁面本身的可見結果為準。 - Checkpoint 與 Solution 只顯示相較上一階段的真實異動;Starter 只顯示預計修改檔案,不得提前放入完成答案。
practice-manifest.json是 observe 的唯一來源;每項 task 都必須包含observe.prepare、observe.action、observe.expected。需要調整視窗、捲動、hover、Tab、點擊、等待、重新整理、開 Console、第二視窗或外部服務時,必須在observe明寫。- 修改四階段檔案或 manifest 後,執行
node tools/build-practice-step-guides.mjs重新套用 112 頁共用練習版型;產生器只讀 manifest、不回寫 observe,且必須可安全重跑。--sync-observe已移除,不得用工具內建文案覆蓋 manifest。 - 第 15、16、20、21、23、24、25、26、27、28 章會接觸不可信輸入,所有階段都不得把這些值寫入
innerHTML、outerHTML、insertAdjacentHTML、srcdoc或 jQuery.html()。 - 真正負責動態渲染的第 20、21、24、26、28 章,其 Solution 必須同時使用
createElement、textContent、replaceChildren三種安全 DOM API。 - 四階段導覽使用完全相同的標籤、
id與 class 結構;當前頁只以aria-current="step"區分。 - 八段去除 HTML 標籤後,仍須有足以授課的中文或英文說明文字;檢查器會依各段責任套用最低字數,避免用空標籤通過驗收。
固定頁尾
<footer class="site-footer">
<span>第 NN 章 · 本章可見成果</span>
<a href="../../index.html#course-list">返回課程總覽</a>
</footer>
頁尾成果文字可以依章節調整;兩個直接子元素及順序固定為「成果 span → 返回課程總覽」。頁尾不可放答案對照;#practice 中的網頁版 answer-map.html 連結仍必須保留。