单文件 HTML 单页站制作
把「素材 + 参考模板」变成一个 index.html:打开就能看、无外部依赖、动效完整、移动端可用。
什么时候用 / 不用
用:人物传记站、文化/纪念主题站、个人简历站、活动或产品落地页;用户给了模板(模板王/ThemeForest 下载包)要"照着做一版";素材是文案 + 几张照片。
不用:需要后端、登录、数据库的项目;需要多页路由的站(那是框架的活);已有 Next/Vue 工程要加页面。
交付标准(硬约束,逐条自检)
| # | 约束 | 原因 |
|---|---|---|
| 1 | 单个 index.html 文件,CSS 与 JS 全部内联 |
可直接分享、双击打开、无构建 |
| 2 | 零外部库:不引 jQuery / Bootstrap / slick / typist / feather / AOS | 离线可用;CDN 挂了页面就废 |
| 3 | 图片与 HTML 同目录,用相对路径引用(如 照片.webp) |
整目录拷贝即可迁移 |
| 4 | 字体走 Google Fonts <link> 且必须写完整回退栈 |
唯一允许的外部请求;断网时回退到系统宋体/楷体 |
| 5 | 动效一律原生 JS 重写,见 references/animation-library.md |
替代第 2 条禁掉的库 |
| 6 | 尊重 prefers-reduced-motion,并提供 @media 兜底 |
无障碍 |
| 7 | 移动端断点至少覆盖 992px / 768px | 导航折叠、网格降列 |
工作流
Step 0 · 侦察(先看清楚再动手)
解压(本机有 WinRAR,没有 7z/unrar 命令):
mkdir -p "<workdir>/_extract"
"/c/Program Files/WinRAR/UnRAR.exe" x -o+ "<path/to/x.rar>" "<workdir>/_extract/"
辨别"预览外壳"与"真模板" —— 这是最容易踩的第一步坑:
下载的模板包里通常有两个 index.html:
- 根目录那个(常叫「网页模板 - 某某.html」)是预览外壳,里面只有一个 <iframe src="./xxx_files/index.html"> 和站点顶栏,不是模板本身;
- 真正的模板在 xxx_files/index.html(或 xxx/index.html)。
判断方法:外壳文件很小(2–3 KB)且含 iframe + 外站 logo 链接。
清点动效:打开真模板,搜 typist、slick、page-scroll、scrollspy、feather、AOS、swiper、owlCarousel、wow、countUp、scroll-top、color-switcher,列出用到的动效清单 —— 这决定了后面要重写哪几个。
清点区块:按 <section> 顺序列出导航项与区块 id,作为新页面的骨架。
先读素材:用 Read 工具看一下用户给的照片(能判断配色和构图取景),别盲选色。
Step 1 · 定设计 token
在 <style> 顶部用 CSS 变量一次性定死。模板主题色可整体替换为你的主题,但结构变量名保持一致,便于换肤:
:root{
--paper:#F5EFE3; /* 页面底色 */ --paper-2:#FBF7EE; /* 次级底色 */
--ink:#1A1A1A; /* 主文字 */ --ink-soft:#3A3A3A; --muted:#6E6659;
--line:rgba(26,26,26,.14); /* 分隔线 */
--seal:#C8102E; /* 主题强调色 */
--dark:#17140F; /* 深色区块 */ --dark-2:#211C15;
--font-display:'Ma Shan Zheng','STKaiti','KaiTi','楷体',serif; /* 标题/毛笔字 */
--font-serif:'Noto Serif SC','Source Han Serif SC','Songti SC','SimSun',serif;
--font-num:'Cormorant Garamond',Georgia,serif; /* 年份/数字 */
--shadow-1:0 1px 2px rgba(26,26,26,.06),0 12px 32px -18px rgba(26,26,26,.28);
--shadow-2:0 2px 4px rgba(26,26,26,.08),0 26px 60px -26px rgba(26,26,26,.4);
--nav-h:64px; /* 导航高度:平滑滚动的偏移量依赖它 */
}
换肤靠覆盖单个变量,data-theme 挂在 <html> 上:
html[data-theme="jade"] { --seal:#3F6B54; }
html[data-theme="indigo"]{ --seal:#2E4C7E; }
完整配色方案与用法见 references/design-tokens.md。
Step 2 · 搭骨架
按 Step 0 清点出的区块顺序铺结构,每个区块一个 <section class="section" id="...">。导航链接加 class="page-scroll" 并指向 #id。
必备的三件套别漏:<nav id="navbar"> + <button id="scrollTop"> + 可选 <div id="switcher">(换肤器)。id 名要和动效脚本里的 getElementById 完全对上,否则静默失效 —— 校验脚本会替你抓这个(见 Step 5)。
Step 3 · 写样式
结构顺序建议:tokens → base → 章节标题 → 按钮 → 导航 → Hero → 各区块 → 页尾 → 动效 → 响应式。用 /* ---------- 区块名 ---------- */ 分段注释,方便后续定位。
版式要点:
- 容器 .container{max-width:1120px;margin:0 auto;padding:0 24px;},区块 .section{padding:104px 0;}(移动端降到 72px);
- 深色区块用 .section--dark 类切换,内部的 h2/h5/p/.card 都要在 .section--dark 下重新指定颜色,不要依赖继承;
- 字号用 clamp() 做流式:font-size:clamp(28px,3.4vw,40px)。
Step 4 · 写动效
从 references/animation-library.md 里取现成片段,按需拼装。八个动效全部是 (function(){ 'use strict'; ... })() 包裹的独立模块,互不依赖,可只取需要的。
对应关系(模板库 → 原生模块):
| 模板常用库 | 原生模块 | 挂载点 |
|---|---|---|
| typist.js | 打字机 | .typist[data-typist="句1,句2"] |
| page-scroll | 平滑滚动 | a.page-scroll |
| scrollspy | 吸顶 + 导航高亮 | #navbar + #navLinks a |
| slick | 轮播 | [data-slider] + .slider-track |
| AOS / wow | 滚动显现 | .reveal |
| 技能条 | 进度条入场 | [data-bars] + .fill[data-value] |
| scroll-top | 回到顶部 | #scrollTop |
| color-switcher | 换肤 | #switcher + .swatches button |
统一在脚本开头检测一次减弱动效,所有模块复用:
var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
Step 5 · 静态自检(必做)
本机没有 agent-browser,也不要为了验页面去全局装 Chromium(约 500 MB)。用随附脚本做静态体检:
NODE="C:/Users/Administrator/.workbuddy/binaries/node/versions/22.22.2-2/node.exe"
"$NODE" "<skill>/scripts/validate_page.mjs" "<path>/index.html"
它会检查 6 类问题并给出 PASS/FAIL 退出码:
- 内联 JS 语法(
new Function解析,不执行) - CSS 花括号配平(先剥离注释与字符串,避免
data:image/svg+xml里的括号误判) - 容器标签开闭计数(36 类)
- 图片等本地资源引用是否存在(断链)
- JS 里的
getElementById/querySelector('#x')是否都有对应 DOM ← 最实用的检查 - viewport / lang / description / img alt
必须做到 ERROR 0。WARN 逐条判断后决定是否修。
Step 6 · 预览与交付
cd "<交付目录>" && "C:/Users/Administrator/.workbuddy/binaries/python/versions/3.13.12/python.exe" -m http.server 8080
然后用 present_files 把 index.html 交给用户(HTML 会自动进实时预览面板)。
坑位清单
这些是实际踩过的,写代码时直接规避:
-
行高不要小于字号。 毛笔/装饰字体大字号时尤其明显 ——
150px的字配110px行高会被裁切。用line-height:1.02起步。 -
区块透明底会被截图渲染成黑底。 给区块设置背景色时显式写页面底色,不要留
transparent,否则自检截图看起来像配色错了,白白排查半天。 -
网格列数要按内容宽度算,不是拍脑袋。 例:内容宽 1280、卡片 240、gap 16 →
(1280+16)/(240+16)=5.06→ 正好 5 列。改卡片宽度时同步改grid-template-columns。 -
平滑滚动的偏移量要读
--nav-h,不要硬编码 64,否则小屏(--nav-h:58px)会顶到导航下面。 -
轮播的
perView变化时要先layout()再apply(),并重建分页点;窗口 resize 必须重算,否则窄屏下 transform 会错位。 -
IntersectionObserver用完记得unobserve,否则滚动时会反复触发回调。 -
stagger 延迟只取同级
.reveal(parent.querySelectorAll(':scope > .reveal')),跨级取会让延迟乱序。 -
导航折叠态用
max-height过渡而非display,否则没有动画;同时给max-height留够值(如 420px)。 -
.rar里的中文路径:WinRAR 解压后目录名可能是网页制作\金庸生平简介,使用时统一用引号包裹路径。
参考文件
| 文件 | 内容 |
|---|---|
references/animation-library.md |
8 个原生 JS 动效模块 + 配套 CSS,可直接复制 |
references/design-tokens.md |
4 套配色方案(宣纸朱砂 / 竹青 / 靛蓝 / 暗夜)+ 排版规范 |
references/case-jinyong.md |
完整案例拆解:金庸生平简介单页站(78 KB,零依赖) |
assets/example-index.html |
上述案例的成品源码(连同同目录 3 张 .webp),可直接跑起来看 |
scripts/validate_page.mjs |
静态自检脚本(Step 5) |
复用提示
- 新项目直接复制
assets/example-index.html再改,比从零写快得多:结构、动效、响应式都已经跑通。 - 换主题只需改
:root的 6 个颜色变量 + 3 个字体变量。 - 换内容是改区块内的文案与图片引用,不要动 class 名——class 名是动效的挂载点。