博客导航错位修复记:一个移动端遮罩如何挤歪了桌面端导航

开头:一个"对不齐"的小问题

决定把个人博客正式升级成求职作品集之后,我做的第一件事,是在桌面端把首页从头到尾看了一遍。这一看就看出问题:右上角的 🌙 深色模式按钮,没有和内容区右缘对齐,被挤到了中间。

做前端十年,这种"差几个像素"的问题我修过太多。按惯性,我打开样式表,开始调 padding、调 margin、调 flex 的 gap——十分钟过去,按钮纹丝不动。数值怎么改都无效,这通常意味着:问题不在我改的地方。

排查:把每个子元素标出来

我调整策略,先看结构。开发者工具里把导航栏的每个子元素都标注出来,这时一个奇怪的东西跳了出来:一个宽度为 0 的空 div,占着 justify-content: space-between 布局的最右位置。

它叫 .nav-overlay。这是移动端菜单的全屏遮罩——点开菜单时,一个盖住整个屏幕、带半透明背景的层,用来承接"点击空白处关闭菜单"的交互。它的样式长这样:

/* 原实现 */
.nav-overlay { display: none; }
@media (max-width: 768px) {
  .nav-overlay { display: block; position: fixed; inset: 0; z-index: 999; }
}

问题一目了然:position: fixed 只写在媒体查询里。桌面端它没有 fixed,就是 flex 里的一个普通子项——零宽、透明、但真实存在,稳稳地占住 space-between 的最右锚点,把导航整体往左顶。

这是我自己半年前写的代码。写的时候只想着"移动端要 fixed",忘了问一句:桌面端呢?

根因:媒体查询不是样式隔离

这次 bug 真正的价值,是逼我重新想清楚一件事:@media 到底在做什么?

它做的是"在特定断点下,替换或追加一组规则",而不是"给元素贴一个'只在手机上有样式'的标签"。所以:

  • 桌面端 .nav-overlaydisplay: none 没生效(因为我在媒体查询里才写 display: block,桌面端实际是默认值)
  • 它作为 flex 子项参与桌面端布局——零宽也占锚点
  • position: fixed 只在手机宽度下生效,桌面端它是个"普通流内元素"

响应式样式的作用域,比你想象的要宽。 凡是在媒体查询里写下的 positiondisplaywidth,都要多想一步:它在其他断点下,会以什么默认值参与布局?

修复:全局基础样式 + 断点显隐分离

修复本身很简单,但思路值得记录——把"定位"和"显隐"分开管:

/* 修复后:position 是元素的基础属性,显隐交给断点 */
.nav-overlay { position: fixed; inset: 0; z-index: 999; display: none; }
@media (max-width: 768px) {
  .nav-overlay { display: block; }
}

桌面端遮罩脱离文档流,不再占位;移动端行为不变。同时删掉了媒体查询里那条重复规则,避免两处定义以后再次分叉。同批还处理了两个小细节:.nav-innerpadding-bottom: 1.25rem(避免底边框贴住导航文字);header 底边框从通栏的 .site-header 迁移到 960px 约束内的 .nav-inner,让分隔线宽度和内容区一致。

作品集改造:首页做减法,详情页做加法

既然是奔着"求职作品集"去的,光修 bug 不够。借着这次改版:

  • hero 区砍掉「📝 博客」和「了解更多 →」两个链接——作品集首页信息越聚焦越好
  • 「下载简历」从按钮样式改成和 Email / GitHub 一致的纯文字链接,一行三个入口,干净
  • 新增 projects/blog-system.html 详情页(返回链接 → 标题+在线版 → 简介 → 技术栈 tags → 核心能力 5 条),首页卡片改为跳详情页

首页只留"我是谁、怎么联系、看什么项目"三件事,剩下的交给详情页承接。

部署与意外发现

部署流程走的是:commit → push Gitee → 服务器 git pull → pm2 restart blog(pm2 装在项目 node_modules/.bin,不是全局)。线上验证:hero 只剩 Email / GitHub / 下载简历,详情页 200,遮罩修复生效。

顺手检查基础设施时发现一个更隐蔽的问题:blog.alleria.cn 没有配置 HTTPS。nginx 只有 80 端口的配置,443 端口落到了别的证书上,curl 直接提示证书域名不匹配。这件事已记录在案,等用户决策是否补证书——在求职阶段,简历上挂的域名没有 HTTPS,观感是很伤的。

另外把 .workbuddy/(含服务器密码的 memory 文件)加进了 .gitignore,这类带敏感信息的目录绝不能进仓库。

方法论沉淀

  1. 媒体查询 ≠ 样式隔离。断点外的规则会以默认值参与布局,写 position/display/width 前多想一步它在其他断点的形态
  2. 布局问题先看结构,再调数值。把 flex 子项逐一标注,透明元素、零宽元素都是"幽灵钉子"
  3. 部署即验证。每次上线顺手过一遍线上表现和 HTTPS/证书,隐性风险往往藏在"以为早就配好了"的地方

这次改版前后花了大半天,表面上是修了一个对不齐的按钮,实际上是把"响应式作用域"这个欠账还清了,顺便给作品集补上了详情页。如果你也在打磨自己的求职作品集,建议从桌面端、手机端各过一遍首页——你可能会发现,坑早就埋在那里了。