网页主标题放哪,老铁才不迷路?从语义到可访问性的实操指南

2,360字
10–15 分钟
in

都说HTML标签是网页的骨架,但骨架要是搭错位置,那画面可就太美不敢看了。尤其是那个掌管页面灵魂的<h1>主标题,到底该塞进<header>里,还是跟<main>玩贴贴,这事儿在圈子里简直能吵上三天三夜。今儿个咱就掰扯掰扯,这主标题的“风水宝地”到底在哪儿,顺便把那些一不留神就踩坑的细节给盘个明明白白。

目录

语义化标签是啥,能吃吗?

简单说,语义化标签就是让代码自己会说话。好比给人穿衣服,<header>是帽子,<main>是身子,<h1>就是脸上最显眼的那个大标牌。浏览器和屏幕阅读器全靠这些标签来理解页面结构,要是标牌挂到了帽子上,人家可能就绕过去了,压根没瞅见你想强调的重点。

<h1><header>,这俩货啥关系

先看两种常见的“翻车”写法。第一种,把主标题塞进页头的<header>里:

<header>
  <h1>今天吃啥,这是个问题</h1>
</header>
<main>
  <!-- 这儿才是干饭攻略 -->
</main>

第二种,把<header>套在<main>里面,标题再往里塞:

<main>
  <header>
    <h1>今天吃啥,这是个问题</h1>
  </header>
  <!-- 干饭攻略详情 -->
</main>

这俩看着都挺像那么回事,但都有点小尴尬。第一种吧,<header>自带“横幅”属性,屏幕阅读器可能会把里头的内容当成站点导航之类的东西,要是访客用“跳过导航”直接跳到正文,那主标题就被完美错过了。第二种更离谱,<header>一旦进了<main>,它的“横幅”身份就失效了,变成了个普通盒子,屏幕阅读器就搞不清到底哪个才是页面的“大门”。

给屏幕阅读器留条活路

这时候就需要给<header>一个明确的角色定位。可以在<header>上加上role="banner"来强化它的身份,但这玩意儿毕竟不是长久之计。关键是结构得清晰,让辅助技术一眼就能看明白。比如用第三种方案,把主标题从<header>里解放出来:

<header role="banner">
  <!-- 站点LOGO、导航栏啥的 -->
</header>
<main>
  <h1>今天吃啥,这是个问题</h1>
  <!-- 各种干饭推荐 -->
</main>

这样一来,页头的<header>还是那个熟悉的横幅,主标题直接成了<main>的亲儿子,无论是用快捷键跳转还是用“跳过链接”,都能稳稳当当地找到它。

流体标题,别让字号把眼睛整瞎了

搞定了标题的位置,还得操心它长啥样。现在流行用clamp()函数做流体标题,让字号跟着屏幕宽度走,但这里头有个大坑,搞不好就把可访问性给干翻了。

别再只用vw了,那是给眼睛上刑

简单粗暴地用4vw做理想字号,看着挺美,但要是用户把浏览器页面放大了,这字号可能就跟不上节奏了,反而变得贼小。正确的做法是给“理想”值里掺点“非视口单位”的料。

看个例子,比如想把标题字号控制在18px36px之间:

.article-heading {
  font-size: clamp(1.125rem, 2vw + 1rem, 2.25rem);
}

这里2vw + 1rem就是那个“理想”值。2vw让它能跟着屏幕大小动,1rem又让它尊重浏览器的默认字号设置。就算用户把字体调到24px,这标题也能跟着变大,不至于让人趴屏幕上看。

算个明明白白的流体标题

要是觉得这种写法还不够得劲,可以整点高级的。比如根据最小和最大字号,以及最理想的行字符数(一般40到80个字符),来精确计算这个理想值。

.article-heading {
  --heading-smallest: 2.5rem;  /* 最小40px */
  --heading-largest: 5rem;     /* 最大80px */
  --ideal-char-range: 30;      /* 30rem - 20rem,假设屏幕宽度从320px到1440px */
  --m: calc((var(--heading-largest) - var(--heading-smallest)) / var(--ideal-char-range));
  font-size: clamp(
    var(--heading-smallest),
    calc(var(--m) * 100vw),
    var(--heading-largest)
  );
}

这套路子的精髓在于把字号的变化跟视口宽度绑定的同时,还考虑了最小和最大字号的差值,让标题在不同尺寸的屏幕上都能保持合适的视觉比重。不过得注意,这写法在某些浏览器里可能水土不服,尤其是Firefox对calc()里直接带单位做除法有点过敏,最好在var(--m)里就处理好单位的转换。

快看,CSS出了个新玩意儿:heading

聊完标题的“安家”和“美容”,再来说说CSS里一个即将闪亮登场的新朋友——:heading伪类。这玩意儿能让你给所有标题一次性上样式,不用再写那又臭又长的h1, h2, h3, h4, h5, h6选择器了。

一招搞定所有标题

比如要给所有标题来个默认样式,以前得写一堆:

h1, h2, h3, h4, h5, h6 {
  margin-top: 1.5em;
  font-weight: bold;
  line-height: 1.2;
}

现在直接:

:heading {
  margin-top: 1.5em;
  font-weight: bold;
  line-height: 1.2;
}

看着就清爽多了,再也不用担心漏掉哪个级别的标题了。

精准打击,只挑特定的标题

:heading()函数更灵活,能精确选择特定级别的标题。比如只给<h2><h3>加点下划线:

:heading(2, 3) {
  border-bottom: 2px solid #f0f0f0;
}

而且,:heading:heading()只认正宗的标题标签,对那些用role="heading"伪装的家伙一概不理,既保证了语义的纯粹,又让样式控制更精准。