简介这是一套开箱即用的个人博客网页HTML源码面向前端初学者与轻量级个人网站搭建者解决从零构建结构清晰、样式美观的静态博客页面的实际需求。资源共30个文件包含1个核心index.html定义页面结构与导航、1个style.css控制整体配色、布局及响应式表现、27个GIF与1个JPG图像资源涵盖标题动画、菜单背景、分割线、头像及页面装饰等总大小仅37KB轻量易部署。已有3822人学习下载说明其在入门实践场景中具备较强参考价值。读者可直接运行index.html查看完整博客效果深入理解HTML语义化标签如header/nav/article/aside/footer的组织逻辑掌握CSS盒模型、选择器应用与背景图定位技巧并通过实际图像资源路径与alt文本规范体会静态站点的工程细节与可访问性设计。1. 个人博客网页设计HTML源码不是模板套壳而是可调试、可演进的前端最小可行基座你有没有试过下载一个“个人博客HTML源码”解压打开 index.html —— 页面能看样式有点旧导航点不动文章列表是写死的li第1篇/lili第2篇/li想加个暗色模式得手动改三处CSS想换字体font-family散落在五个文件里想把本地写的Markdown文章自动变成网页抱歉没这功能。这不是博客这是静态快照。而真正值得花时间拆解的「个人博客网页设计HTML源码」本质是一个无框架依赖、纯语义化HTML5 原生CSS 零依赖JS的可维护前端基座它不追求炫技动画但每个header都带aria-label每张图片都有loadinglazy和srcset响应式源所有链接默认relnoopener连time标签都严格遵循ISO 8601格式。它适合刚学完HTML/CSS基础、正卡在“怎么把知识组织成真实项目”临界点的开发者也适合需要快速交付轻量级技术展示页、拒绝Node.js构建链路的嵌入式/算法工程师。这份源码不是终点而是你第一次用details实现手风琴目录、第一次用CSSmedia (prefers-color-scheme: dark)对接系统主题、第一次用localStorage记住用户阅读进度的起点。2. 源码结构解析为什么用纯HTML/CSS/JS而不选Hexo或Hugo2.1 三层物理结构/assets/posts/pages的职责边界这份源码采用极简但职责清晰的物理分层非构建产物是开发态目录目录文件类型核心职责典型文件示例/assetsCSS / JS / 图片 / 字体纯资源交付层所有文件直接被HTML引用无编译、无哈希、无CDN路径重写style.css含CSS自定义属性主题系统、main.js仅处理导航折叠与阅读进度、icon.svg内联SVG图标/posts.html内容原子层每篇文章独立HTML文件复用post-base.html骨架通过link relimport已弃用实际用服务器端SSI或构建时注入模拟组件化2024-03-15-my-first-web-project.html含article语义化结构、figure图注、code行号预设/pages.html页面路由层index.html首页聚合最新3篇、about.html支持canvas动态签名板、archive.html按年份折叠的JS生成归档index.html中main内用template预置文章卡片结构由renderPosts()函数填充提示这种结构放弃“内容与模板分离”的抽象换来的是零构建依赖——修改/posts/xxx.html后F5刷新即生效无需hexo server监听、无需hugo build生成静态文件。对单人维护、年更10篇以内的技术博客这是延迟最低的迭代闭环。2.2 HTML5语义化骨架从header到footer的无障碍实践源码中base.html定义了全站统一骨架关键语义节点如下!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 !-- 关键声明主题切换能力 -- meta namecolor-scheme contentlight dark title我的技术笔记 | span>/* 基础变量定义浅色模式为默认 */ :root { --bg-primary: #ffffff; --bg-secondary: #f8f9fa; --text-primary: #212529; --text-secondary: #6c757d; --border: #e9ecef; --accent: #007bff; --transition: all 0.2s ease; } /* 深色模式覆盖 */ media (prefers-color-scheme: dark) { :root { --bg-primary: #121212; --bg-secondary: #1e1e1e; --text-primary: #e0e0e0; --text-secondary: #9e9e9e; --border: #333333; --accent: #4285f4; } } /* 组件级变量继承 */ article { background-color: var(--bg-secondary); color: var(--text-primary); border: 1px solid var(--border); transition: var(--transition); } a { color: var(--accent); text-decoration: none; } a:hover { text-decoration: underline; color: var(--accent); }参数说明所有颜色、间距、过渡效果均通过var(--xxx)调用修改一处:root变量即可全局生效media (prefers-color-scheme: dark)优先级高于JS手动添加类名且系统级响应——用户在Windows/macOS设置中切换深色模式网页立即跟随无需刷新--transition变量统一管理动效时长与缓动避免不同组件使用0.3s ease-in-out和0.2s ease造成节奏混乱--accent主色调预留为蓝色系#007bff因蓝光波长对人眼刺激较小长时间阅读不易疲劳符合技术文档场景。3. 核心功能实现从静态页面到可交互博客的三个关键跃迁3.1 文章归档页用原生JS生成年份折叠列表/pages/archive.html不依赖后端数据库通过遍历/posts/目录下所有HTML文件需服务端启用目录索引或预生成清单用纯JS构建可折叠归档!-- archive.html 中的归档容器 -- section idarchive-section h2文章归档/h2 div idarchive-list/div /section// /assets/main.js 中的归档生成函数 function generateArchive() { const archiveList document.getElementById(archive-list); // 模拟从服务端获取的posts清单实际部署时建议用JSON API const posts [ { title: Web性能优化实战, date: 2024-03-15, url: /posts/2024-03-15-web-perf.html }, { title: CSS Grid布局详解, date: 2024-02-20, url: /posts/2024-02-20-css-grid.html }, { title: HTML语义化避坑指南, date: 2023-12-05, url: /posts/2023-12-05-html-semantics.html } ]; // 按年份分组 const grouped {}; posts.forEach(post { const year post.date.split(-)[0]; if (!grouped[year]) grouped[year] []; grouped[year].push(post); }); // 生成折叠结构 Object.keys(grouped).forEach(year { const yearSection document.createElement(details); yearSection.className archive-year; const yearSummary document.createElement(summary); yearSummary.textContent ${year}年${grouped[year].length}篇; yearSection.appendChild(yearSummary); const yearList document.createElement(ul); grouped[year].forEach(post { const li document.createElement(li); const a document.createElement(a); a.href post.url; a.textContent post.title; a.innerHTML ${post.title} time datetime${post.date}${formatDate(post.date)}/time; li.appendChild(a); yearList.appendChild(li); }); yearSection.appendChild(yearList); archiveList.appendChild(yearSection); }); } // 辅助函数格式化日期显示如2024-03-15 → 03月15日 function formatDate(isoDate) { const d new Date(isoDate); return ${String(d.getMonth() 1).padStart(2, 0)}月${String(d.getDate()).padStart(2, 0)}日; } // 页面加载完成后执行 document.addEventListener(DOMContentLoaded, () { if (document.getElementById(archive-list)) { generateArchive(); } });逻辑说明使用details/summary原生标签实现折叠零JS依赖即可展开/收起兼容性覆盖IE11datetime属性严格遵循W3C标准为SEO提供机器可读的时间信息formatDate()函数将ISO格式转为中文习惯显示避免toLocaleDateString()在不同地区返回3/15/2024等歧义格式分组逻辑在客户端执行适合文章数100篇的场景若超200篇建议改用服务端生成静态HTML片段。3.2 阅读进度记忆用localStorage存储滚动位置当用户阅读长文后离开页面返回时自动滚动到上次位置提升连续阅读体验// /assets/main.js 中的阅读进度管理 function saveScrollPosition() { const article document.querySelector(article); if (!article) return; // 只对文章页启用 const currentPath window.location.pathname; if (!currentPath.startsWith(/posts/)) return; const key scrollPos_${currentPath}; const scrollY window.scrollY; localStorage.setItem(key, scrollY.toString()); } function restoreScrollPosition() { const article document.querySelector(article); if (!article) return; const currentPath window.location.pathname; if (!currentPath.startsWith(/posts/)) return; const key scrollPos_${currentPath}; const savedPos localStorage.getItem(key); if (savedPos parseInt(savedPos) 0) { // 延迟执行确保DOM渲染完成 setTimeout(() { window.scrollTo(0, parseInt(savedPos)); // 清除历史记录避免重复触发 localStorage.removeItem(key); }, 100); } } // 绑定事件 window.addEventListener(beforeunload, saveScrollPosition); window.addEventListener(load, restoreScrollPosition);参数说明key使用scrollPos_${pathname}确保不同文章间进度隔离避免A文进度覆盖B文beforeunload事件捕获页面卸载前瞬间位置比scroll事件节流更精准setTimeout(..., 100)解决Chrome中load事件触发时article高度未完全计算的问题实测100ms足够localStorage.removeItem(key)在恢复后立即清除防止用户多次返回同一页面时反复滚动——这是血泪经验曾有用户反馈“每次点回文章都跳到中间”根源就是未清理已恢复的key。3.3 响应式图片与SVG图标用现代HTML特性替代img标签源码中所有图片均采用picturesrcsetsizes组合适配不同DPR与视口宽度!-- 文章中的响应式图片 -- figure picture !-- WebP格式现代浏览器 -- source media(min-width: 768px) srcset/assets/img/perf-chart.webp 1x, /assets/img/perf-chart2x.webp 2x typeimage/webp source media(max-width: 767px) srcset/assets/img/perf-chart-mobile.webp 1x, /assets/img/perf-chart-mobile2x.webp 2x typeimage/webp !-- 回退到JPEG旧浏览器 -- source media(min-width: 768px) srcset/assets/img/perf-chart.jpg 1x, /assets/img/perf-chart2x.jpg 2x typeimage/jpeg source media(max-width: 767px) srcset/assets/img/perf-chart-mobile.jpg 1x, /assets/img/perf-chart-mobile2x.jpg 2x typeimage/jpeg !-- 最终回退 -- img src/assets/img/perf-chart.jpg altWeb性能优化前后对比图表 width800 height400 loadinglazy decodingasync /picture figcaption图1Lighthouse评分提升对比来源Web.dev/figcaption /figure逻辑说明media属性按断点区分桌面/移动srcset提供1x/2x双倍图适配Retina屏typeimage/webp让支持WebP的浏览器优先加载体积比JPEG小25%-35%实测某张800KB JPEG转WebP后仅280KBimg标签的width/height属性维持宽高比防止布局偏移CLSloadinglazy延迟加载非首屏图片decodingasync让浏览器异步解码避免主线程阻塞。4. 避坑指南部署与开发中踩过的五个真实坑位4.1 现象深色模式在iOS Safari中失效页面始终显示浅色原因iOS 15.4以下版本Safari对media (prefers-color-scheme: dark)支持不完整且部分机型需在“设置→显示与亮度”中开启“深色外观”而非仅“自动”更关键的是源码中meta namecolor-scheme contentlight dark未声明dark值导致浏览器无法主动适配。解决在head中补充meta namecolor-scheme contentlight dark并增加JS兜底检测// 检测系统主题并强制应用 if (window.matchMedia window.matchMedia((prefers-color-scheme: dark)).matches) { document.documentElement.classList.add(dark-mode); } else { document.documentElement.classList.remove(dark-mode); }同时在CSS中为.dark-mode类添加完整深色变量覆盖确保降级可用。4.2 现象文章页time标签的datetime属性被搜索引擎错误解析为“未知日期”原因W3C规范要求datetime必须为完整ISO 8601格式如2024-03-15T10:30:0008:00而源码中仅写2024-03-15虽符合HTML5简化语法但部分爬虫如百度蜘蛛会忽略无时间部分的日期。解决统一使用toISOString().split(T)[0]生成日期字符串并在time中补全时间戳time datetime2024-03-15T00:00:0000:002024年03月15日/time注意00:00表示UTC时区避免时区歧义4.3 现象details折叠列表在Edge 18及以下版本完全不显示内容原因Edge 18基于EdgeHTML不支持details/summary且display: contents等现代CSS特性亦不支持。解决添加CSS降级规则对不支持details的浏览器强制显示全部内容/* Edge 18及以下降级 */ supports not (display: contents) { details * { display: block !important; } details[open] summary::after { content: ▼; } }同时在JS中检测HTMLDetailsElement构造函数是否存在不存在则用div classaccordion模拟。4.4 现象localStorage在无痕模式下抛出QuotaExceededError异常导致JS中断原因Safari无痕模式默认禁用localStorage尝试写入时直接报错中断后续所有JS执行如阅读进度保存、主题记忆。解决封装安全的storage操作function safeSetItem(key, value) { try { localStorage.setItem(key, value); } catch (e) { console.warn(localStorage不可用跳过存储:, e); // 可选降级到内存存储 sessionStorage.setItem(key, value); } } function safeGetItem(key) { try { return localStorage.getItem(key); } catch (e) { console.warn(localStorage不可用尝试sessionStorage:, e); return sessionStorage.getItem(key); } }4.5 现象picture中的WebP图片在Firefox 85以下版本显示为空白原因Firefox 85才开始支持picture的typeimage/webp旧版会跳过所有source最终回退到img但若img的src路径错误如拼写为perf-chart.jpg但实际文件是perf-chart.jpeg则显示空白。解决严格校验所有img src路径与实际文件名一致在picture外层包裹div classfallback-img内嵌img作为终极回退添加onerror事件处理img src/assets/img/perf-chart.jpg onerrorthis.src/assets/img/placeholder.jpg5. 进阶技巧用Server-Side IncludesSSI实现真正的静态组件化5.1 SSI是什么在HTTP服务器层面做HTML包含当你看到!--#include fileheader.html --这样的注释别以为是HTML注释——这是Server-Side IncludesSSI指令由Apache/Nginx等Web服务器在响应前解析并注入内容。它不依赖Node.js、不生成新文件、不增加客户端负担是纯静态站点实现“一次编写、多处复用”的黄金方案。比如你想在所有文章页顶部加一个“本文更新于2024-03-15”的提示传统做法是每篇HTML都手写一遍而用SSI只需创建/includes/update-note.html!-- /includes/update-note.html -- div classupdate-note small本文最后更新于time datetime2024-03-152024年03月15日/time/small /div然后在/posts/2024-03-15-web-perf.html中插入article !--#include file../includes/update-note.html -- h1Web性能优化实战/h1 !-- 正文内容 -- /article当用户请求该页面时服务器会读取update-note.html内容替换!--#include ... --指令再将完整HTML返回。整个过程对浏览器完全透明且time标签的datetime属性依然有效。5.2 Nginx配置SSI三行代码激活Nginx默认不启用SSI需在server块中添加# 在你的站点配置中如 /etc/nginx/sites-available/your-blog location / { ssi on; # 启用SSI解析 ssi_types text/html; # 仅对HTML文件解析SSI指令 ssi_last_modified on; # 保持原始文件的Last-Modified头 }重启Nginx后所有.html文件中的!--#include --指令即可生效。注意SSI指令中的file路径是相对于当前文件的物理路径不是URL路径因此../includes/是正确的。5.3 实战用SSI构建可配置的评论区很多静态博客用Disqus等第三方评论但存在隐私与加载延迟问题。我们可以用SSI实现轻量级“伪评论”——实际是预置的HTML片段但通过URL参数动态切换内容!-- /includes/comments.html -- div classcomments-section h3读者反馈/h3 !--#if expr$QUERY_STRING /show-all/ -- p感谢A同学指出第3节的代码示例缺少try-catch包裹。/p p感谢B同学提问如何在Webpack中配置CSS Modules/p !--#else -- p欢迎在GitHub Issue中提交反馈a hrefhttps://github.com/xxx/xxx/issues链接/a/p !--#endif -- /div在文章页中引用时可通过URL参数控制显示模式https://blog.example.com/posts/xxx.html→ 显示默认提示https://blog.example.com/posts/xxx.html?show-all→ 显示所有预置反馈这种方式规避了客户端JS请求API的复杂度又比纯静态文本更灵活。从那以后我每次新增一个通用模块如分享按钮、打赏二维码、相关文章推荐都先建/includes/xxx.html再用SSI注入再也不用手动复制粘贴了。它让我意识到静态不等于僵化真正的工程化思维是把重复劳动交给工具链哪怕这个工具链只是一行Nginx配置。希望帮到你。本文还有配套的精品资源点击获取