必须启用doctype-first规则,否则html文件开头缺失或错位doctype会导致浏览器进入怪异模式,引发css盒模型、flex布局及js api行为异常,且该规则检查字节流开头,注释、bom、空格均会使其失效。

为什么 HTMLHint 的 doctype-first 规则必须启用
不启用它,就等于默认允许文件开头没有 —— 浏览器立刻进入怪异模式(Quirks Mode),CSS 盒模型、<code>flex 行为、甚至 JS 的 getBoundingClientRect() 都可能错位。这不是“偶尔出问题”,而是所有旧版 IE、部分 Safari 版本、以及无头浏览器(如 Puppeteer 默认环境)的确定性行为。
-
doctype-first检查的是文件字节流开头,不是 DOM 解析后的位置;所以注释、BOM 字节、空格都会让它失效 - CI 中跑
npx htmlhint --config .htmlhintrc src/**/*.html时,若没配这条规则,...这类文件会静默通过 - VS Code 的 HTMLHint 插件默认不开此规则,需手动在
.htmlhintrc里显式写"doctype-first": true
lang 属性值写 zh 还是 zh-CN?
写 zh 会导致 NVDA、VoiceOver 等主流屏幕阅读器无法加载对应语音库,用户听到的是生硬的英文合成音;搜索引擎也会弱化页面中文相关性评分。W3C 和 IANA 明确要求使用 BCP 47 格式,中文站点应固定用 zh-CN(简体大陆)、zh-TW(繁体台湾)等带区域码的写法。
-
document.documentElement.lang在 JS 中读取时,只返回初始化时的静态值,动态改无效 - 服务端渲染(SSR)场景下,
lang必须由模板引擎注入,不能靠 JS 后补 - 多语言站点不要用
lang="auto"或留空,那等于没设
<meta charset="UTF-8"> 放错位置的后果比你想象得更早
它必须出现在 内前 1024 字节,且不能被任何内容(包括注释、空行、<title></title>)前置。否则 Chrome 会按 Latin-1 解码后续 HTML,导致中文乱码、class="标题" 变成乱码 class 名,连 CSS 选择器都匹配不上。
- Webpack / Vite 构建时若用
html-webpack-plugin注入<script></script>标签到,必须设inject: 'head'并确认它排在<meta charset>之后 - HTMLHint 的
meta-charset-require规则只检查是否存在,不校验位置 —— 你需要额外加meta-charset-placement(社区插件)或自己写脚本扫描字节偏移 - PHP 模板中常见错误:
<?php echo $title ?><meta charset="UTF-8">——$title输出含 UTF-8 字符时,BOM 已提前触发编码判定
语义标签误用:为什么 <div role="button"> 比 <code><button></button> 更危险
它绕过了浏览器原生按钮的所有保障:键盘焦点管理(Tab/Shift+Tab)、空格/Enter 触发、禁用状态样式、屏幕阅读器 announce 逻辑。用户用键盘操作时,会直接跳过这个“按钮”,或按了没反应,但视觉上它又像按钮 —— 这是可访问性灾难。
-
role="button"只应在极端场景下用(比如已有复杂 DOM 结构无法重构),且必须手动实现tabindex="0"、onkeydown捕获、aria-pressed状态同步 - React/Vue 中用
<div> 替代 <code><button></button>,本质是同一类问题 - HTMLHint 无法检测这类语义误用,得靠 Axe 或 pa11y 做运行时检查,CI 里要集成
npx axe-cli http://localhost:3000
真正卡住技术债务的点,不在工具链是否装全,而在这些约束是否成为提交前的硬性门禁 —— 比如 Git Hooks 里跑
htmlhint + axe-core 扫描,失败直接拒绝 commit。否则,每次“先这么提上去再说”,就是债务复利的开始。











