<br> 拿來做間距

這是最普遍的 HTML 誤用,老專案裡幾乎都看得到。

<!-- ❌ 用 <br> 撐間距 -->
<p>第一段內容</p>
<br>
<br>
<p>第二段內容</p>

<br> 的語意是段落內的換行——詩句換行、地址換行、這類「換行本身有意義」的場合。它不是間距工具。

<!-- ✅ 間距是 CSS 的工作 -->
<p>第一段內容</p>
<p>第二段內容</p>
p + p {
  margin-top: 1.5rem;
}

<br> 的確有合理用途:

<!-- ✅ 地址換行(換行本身有語意) -->
<address>
  台北市信義區市府路 1 號<br>
  台北市政府
</address>
 
<!-- ✅ 詩句換行 -->
<p>
  春眠不覺曉,<br>
  處處聞啼鳥。
</p>

<section> 當萬用容器

HTML5 推出之後有一波「全面語意化」的反應,結果很多人把每個 <div> 都換成 <section>

<!-- ❌ section 沒有標題,純粹為了包 -->
<section class="hero-wrapper">
  <img src="hero.jpg" alt="...">
</section>
 
<section class="flex-container">
  <div>...</div>
  <div>...</div>
</section>

<section> 的定義是「有主題的獨立段落,且有自己的標題(<h1><h6>)」。沒有標題的 section 等於告訴螢幕閱讀器「這是一個段落」但讀不出段落的主題——比 <div> 還糟,因為 <div> 至少不製造假語意。

判斷原則

  • 有標題且主題獨立 → <section>
  • 純粹為了 CSS / JS 操作的容器 → <div>
  • 可以獨立存在(拿去別頁也合理)→ <article>

<a> 沒有 href,或 href="#" 用來當按鈕

<!-- ❌ 沒有 href 的 <a> -->
<a onclick="openModal()">開啟說明</a>
 
<!-- ❌ href="#" 跳到頁頂,又不是真的連結 -->
<a href="#" onclick="doSomething(); return false;">執行動作</a>

<a> 的語意是導覽到另一個位置(URL、錨點、下載)。如果點了不會跳頁,它就不是連結,用 <button> 才對。

<!-- ✅ 觸發動作 → button -->
<button onclick="openModal()">開啟說明</button>
 
<!-- ✅ 真的要連到某處 → a with href -->
<a href="/docs/help">查看說明文件</a>

<a> 沒有 href 會讓鍵盤使用者無法聚焦(<a> 只有在有 href 時才預設可以 tab 到),螢幕閱讀器也不會把它識別為互動元素。


標題跳級——拿 <h3> 當「中字體」用

<!-- ❌ 為了字體大小跳過 h2 -->
<h1>文章標題</h1>
<p>...</p>
<h3>小節標題</h3>  <!-- 跳過了 h2 -->
<h5>更小的字</h5>  <!-- 跳過了 h4 -->

標題標籤不是字體大小選項,是文件大綱。螢幕閱讀器使用者靠標題在頁面內快速跳轉,跳級等於大綱裡出現了洞。

如果你想要某個大小的標題字體,而那個層級語意上不對:

<!-- ✅ 語意正確,視覺大小用 CSS 調 -->
<h2 class="text-sm font-semibold">小節標題</h2>

標題的語意層級和視覺大小完全可以分開控制。


<blockquote> 拿來做縮排

這個在舊專案和 CMS 產出的 HTML 裡很常見:

<!-- ❌ 不是引言,只是想要縮排 -->
<blockquote>
  這段文字需要縮排一下看起來比較整齊。
</blockquote>

<blockquote> 的語意是引用另一個來源的內容。用它做縮排,螢幕閱讀器會告訴使用者「這是引言」,然後使用者等著看引用出處,結果什麼都沒有。

<!-- ✅ 縮排是 CSS 的工作 -->
<p class="indented">這段文字需要縮排。</p>
 
<!-- ✅ 真正的引言用法 -->
<blockquote cite="https://example.com/source">
  <p>任何傻瓜都能寫出電腦能看懂的程式。</p>
  <footer>— <cite>Martin Fowler</cite></footer>
</blockquote>

<b><strong> 用反了

semantic.md 有完整的差異說明,這裡只說最常見的誤用場景:

<!-- ❌ 想表達「這很重要!」卻用 <b> -->
<p>請注意:<b>密碼不能少於 8 個字元</b></p>
 
<!-- ❌ 只是想要粗體視覺效果,卻用 <strong> -->
<strong>品牌名稱</strong>  <!-- 不是重要性,是視覺標記 -->
<!-- ✅ 重要性警告 → <strong> -->
<p>請注意:<strong>密碼不能少於 8 個字元</strong></p>
 
<!-- ✅ 純視覺粗體(產品名、關鍵字)→ <b> -->
<b>品牌名稱</b>
 
<!-- ✅ 只是想要粗體外觀,沒有語意 → CSS -->
<span class="font-bold">標題文字</span>

螢幕閱讀器有些實作會對 <strong> 加強語氣朗讀,<b> 則不會。把它們用反,傳遞的語意就錯了。


<p> 裡面放塊狀元素(invalid HTML)

這個瀏覽器會靜默修復,所以很多人不知道這是錯的:

<!-- ❌ <p> 裡面不能放 block-level element -->
<p>
  這是一段說明
  <div class="tip">補充資訊</div>
  繼續說明
</p>

<p> 是 inline 容器,遇到 <div><h2><ul> 等塊狀元素時,瀏覽器會自動把 <p> 截斷。結果是:HTML 你寫了一個 <p>,DOM 裡變成兩個。這種靜默修復會讓 CSS 和 JS 行為難以預測。

<!-- ✅ 用 <div> 包或改用 <span> -->
<div class="paragraph-with-tip">
  <p>這是一段說明</p>
  <div class="tip">補充資訊</div>
  <p>繼續說明</p>
</div>

W3C Validator 或 HTMLHint 就能抓到這類 invalid nesting,建議 CI 加進去。


相關文章