CSS Anchor Positioning:工具提示、Popover 定位與後備方案
DDEVELOPER
開發發布: 11 分鐘閱讀

CSS Anchor Positioning:工具提示、Popover 定位與後備方案

工具提示、下拉選單或補充資料卡,看似只需放在按鈕旁邊,實際上卻受捲動、畫面寬度、縮放、文字長度與包含區塊影響。過往常用 JavaScript 量度座標,再計算合適的位置。

CSS Anchor Positioning 讓瀏覽器以另一個元素為基準配置浮動內容,放不下時再嘗試其他位置。相關實作正在推進,但規格仍屬 W3C 工作草案。本文會說明它的原理、與 Popover API 的分工,以及仍須自行設計的互動和後備方案。

日文原文發布: 2026-07-11

錨點定位要處理甚麼問題?

CSS Anchor Positioning 讓定位元素以另一個「錨點元素」為基準,決定位置或大小。錨點就是參照對象;工具提示的錨點通常是需要說明的按鈕或輸入欄位。

  • 圖示、輸入欄位旁的工具提示及錯誤訊息
  • 按鈕開啟的下拉選單、Popover 或設定面板
  • 文字選取位置附近的格式工具列:需要另外放置基準元素,文字 Range 本身不能直接當作錨點元素
  • 跟隨商品或圖表元素的補充資料卡

把父元素設為 position: relative、子元素設為 position: absolute,本來已可將內容放在附近。較棘手的是 DOM 中相隔較遠的元素、進入頂層(top layer)的 Popover,以及靠近畫面邊緣時需要改變位置的情況。錨點定位可明確指定基準,不必只依賴父子關係。

傳統 JavaScript 定位如何運作?

常見做法是透過 getBoundingClientRect() 量度按鈕和浮動面板,再比較可用空間,計算 top、left 等座標。捲動頁面或調整視窗大小後,亦可能需要重新計算。

// 已取得 button 和 popup;popup 必須顯示後才量度。
// 橫排例子:fixed 定位的包含區塊是視口。
popup.style.position = "fixed";
const anchorRect = button.getBoundingClientRect();
const popupRect = popup.getBoundingClientRect();

const fitsAbove = anchorRect.top >= popupRect.height + 8;
popup.style.top = fitsAbove
  ? anchorRect.top - popupRect.height - 8 + "px"
  : anchorRect.bottom + 8 + "px";

以上只示範垂直位置的判斷:popup 必須已顯示,而 fixed 定位的包含區塊須為視口(viewport)。水平位置、開關行為、重新計算的事件,以及上下兩側都放不下的處理均未包括。若經變形的祖先元素建立了另一個 fixed 包含區塊,就不能直接套用視口座標。

完整的定位程式庫亦會處理捲動容器、縮放、由右至左的文字、變形元素、提示箭頭及碰撞規則。錨點定位可把追蹤基準及選擇位置交給瀏覽器,但不會自動取代每一項程式庫功能。複雜的避讓、動畫、瀏覽器支援要求及現有元件整合,仍可能適合使用 JavaScript。

基本流程:命名、關聯、定位

使用明確命名的錨點時,先為基準元素命名,再讓浮動元素參照該名稱,最後選擇位置。

如表格未能完整顯示,可左右捲動。

步驟CSS作用
命名錨點anchor-name為基準元素設定 --menu-button 等名稱。
建立關聯position-anchor選擇定位元素要參照的錨點。
選擇位置position-area/anchor()放在錨點上方、下方或旁邊。

錨點名稱是以 -- 開頭的識別字,寫法與 CSS 自訂屬性名稱相似。被定位的元素須使用 position: absolute 或 position: fixed,並以 position-anchor 指定參照名稱。

最小例子:在按鈕上方放置工具提示

這個例子只展示定位,沒有實作工具提示的開關。請將 HTML 和 CSS 部分放到各自的位置。按鈕加上 type="button",避免在表單內意外提交資料。

<button type="button" class="help-button" aria-describedby="help-tip">說明</button>
<div id="help-tip" class="tooltip" role="tooltip">查看輸入例子</div>

.help-button {
  anchor-name: --help;
}

.tooltip {
  position: fixed;
  position-anchor: --help;
  position-area: block-start;
  margin-block-end: 8px;
}

在一般橫排模式下,position-area: block-start 會選擇錨點上方。這裏的 block-start 依據包含區塊的書寫模式;fixed 元素通常以視口為包含區塊,所以只把子元素改為直排,不會令定位方向隨之旋轉。也可使用 top 等實體方向;需要配合書寫模式時,可考慮 block-start、inline-end 等邏輯方向。

關聯與位置是兩回事。anchor-name 和 position-anchor 指定參照對象;position-area 或 anchor() 才指定實際位置。

position-area:選擇錨點周圍的區域

position-area 把錨點周圍視作 3×3 區域,讓簡單的工具提示或選單直接選擇位置,而不用逐一計算 top 和 left。

.above  { position-area: block-start; }
.below  { position-area: block-end; }
.right  { position-area: inline-end; }
.corner { position-area: block-end inline-end; }

包含區塊是定位時的參照區域。錨點與包含區塊的邊界共同劃分出可選區域,能直接表達「上方」或「區塊結束側加行內結束側」等位置。需要細調偏移,或結合不同錨點的邊緣時,可使用 anchor()。

anchor() 與 anchor-size():參照位置和尺寸

anchor() 可在 top、left 等 inset 屬性內參照錨點邊緣或中心,也能配合 calc()。anchor-size() 則提供錨點的尺寸。

.menu {
  position: fixed;
  position-anchor: --menu-button;
  top: calc(anchor(bottom) + 8px);
  left: anchor(left);
  min-width: anchor-size(width);
}

這段 CSS 將選單上緣放在按鈕下緣以下 8px,對齊左緣,並把最小寬度設為按鈕寬度。基準按鈕需要設定 anchor-name: --menu-button。這是定位規則,並不是完整選單元件。

普通的上下左右配置可先用 position-area,細節才用錨點函式,不必把每個位置都寫成複雜算式。

用 position-try 處理邊緣的替代位置

工具提示放在按鈕上方,但按鈕靠近畫面頂部時,提示可能被裁切。替代位置可嘗試把它移到另一側。

.tooltip {
  position: fixed;
  position-anchor: --help;
  position-area: block-start;
  position-try-fallbacks: flip-block;
}

flip-block 是沿區塊軸翻轉位置的預設策略,在一般橫排模式下就是上下翻轉。多個方案可用逗號分隔,@position-try 可定義包括位置、寬度等設定的自訂方案。position-try 簡寫則同時設定替代方案及排序方式。

候選位置並不保證一定有空間容納內容。MDN 的例子指出所有替代方案失敗時會回到初始位置;現行 CSSWG 草案則保留當前樣式,而這些樣式可能已包含較早採用的替代位置。不要假定必定回到第一個位置,應在目標瀏覽器測試捲動及調整視窗大小。最大寬度、邊緣留白與必要的捲動安排也要一併設計。

Popover API 負責開關,錨點定位負責位置

Popover API 處理顯示、隱藏及頂層呈現。popover="auto"(屬性省略值時的預設模式)與 popover="hint" 支援點擊外側關閉等 light dismiss 行為。錨點定位則決定 Popover 在觸發按鈕的哪一側出現。

<button type="button" popovertarget="account-menu">帳戶</button>
<div id="account-menu" popover>...</div>

#account-menu {
  position-area: block-end;
  position-try-fallbacks: flip-block;
  margin-block-start: 8px;
}

透過 popovertarget 連結按鈕與 Popover,也可建立隱含錨點關係。在這類設定下,能省略明確的 anchor-name 和 position-anchor。可能位於表單內的觸發按鈕仍須保留 type="button"。

Popover 不會自動具備完整選單或工具提示的語意。顯示狀態、位置、HTML 語意和鍵盤操作須分別確認。例子中的省略號代表有待加入的內容,不是完整的帳戶選單。

無障礙設計不能只靠位置正確

CSS Anchor Positioning 本身不會建立、刪除或改變元素之間的無障礙關聯。即使位置看來正確,螢幕閱讀器也不會單憑視覺距離知道按鈕與說明的關係。

CSS 可接手量度及位置切換,卻不代表整個介面的互動都已完成。

  • 鍵盤使用者能否取得與滑鼠使用者相同的說明?
  • 選單如何處理方向鍵、Escape 及焦點移動?
  • Popover 關閉後,焦點應回到哪裏?
  • 控制項與內容的關係是否有適當語意,能傳達給輔助技術?
  • 放大文字或使用較長的翻譯時,內容是否仍然可讀?

Popover API 有實用的預設行為,但加上 popover 屬性,不代表所有選單、對話框與工具提示的要求都已滿足。應按元件需要選用 HTML、必要的 ARIA,並設計及測試焦點與鍵盤操作。

重複元件與 anchor-scope

多張卡片使用相同錨點名稱時,要確認每個浮動面板參照哪個元素。現行 CSSWG 草案優先選擇最近而且符合條件的同名祖先;沒有這類祖先時,才選樹狀順序中最後一個符合條件的元素。錨點資格與名稱作用範圍都會影響結果,不能只看名稱相同。

anchor-scope 將具名錨點的參照範圍限制在子樹內,方便重複元件各自使用自己的基準。

.product-card {
  anchor-scope: --actions;
}

.product-card__button {
  anchor-name: --actions;
}

.product-card__menu {
  position: absolute;
  position-anchor: --actions;
  position-area: block-end;
}

單一展示能正常運作,不代表放進清單後也一樣;多個面板可能都指向同一個按鈕。可以選擇 Popover 的隱含關係、不同的唯一名稱,或 anchor-scope。草案與實作可能存在差異,應在目標瀏覽器測試同名兄弟元素與合適的祖先錨點。

相容性與不支援時的後備方案

截至 2026 年 11 月 11 日,CSS Anchor Positioning Level 1 仍屬 W3C Working Draft,日後可能更新、被取代或撤回。MDN 將 anchor-name 標示為 Baseline 2026、Newly available,同時提醒舊裝置、舊瀏覽器與部分功能的支援程度可能不同。

年份或草案標籤都不足以決定是否採用。應核對實際需要的屬性,以及使用者所用的瀏覽器。

以下後備方案適用於橫排(writing-mode: horizontal-tb),工具提示與觸發按鈕須位於同一個 .trigger-wrap。不支援時使用傳統絕對定位,瀏覽器辨識全部四項宣告後,才啟用錨點定位。

.trigger-wrap {
  position: relative;
  width: fit-content;
}

.tooltip {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
}

@supports (anchor-name: --trigger) and
          (position-anchor: --trigger) and
          (position-area: block-start) and
          (position-try-fallbacks: flip-block) {
  .trigger { anchor-name: --trigger; }
  .tooltip {
    position: fixed;
    position-anchor: --trigger;
    position-area: block-start;
    position-try-fallbacks: flip-block;
    inset: auto;
    margin-block-end: 8px;
    transform: none;
  }
}

實體方向的 left 配合 translateX(),讓這個橫排後備方案在 LTR 與 RTL 文字方向下都保持水平置中。直排需要另外設計。@supports 只檢查宣告支援,不保證實際位置正確;Popover API 亦要分開確認支援情況。

後備方案不必重現所有視覺細節。可在一般文件內容保留重要資料、使用固定在單側的簡單提示,或在複雜介面保留成熟的定位程式庫。重點是保留資訊與主要操作。

實際採用前的檢查項目

不要只看漂亮的展示,應使用真實內容與操作條件測試。

  1. 選擇基準:決定錨點,以及隱含或明確的關聯方式。
  2. 選擇初始位置:根據書寫模式與邏輯方向,設定自然的位置。
  3. 測試困難情況:四個邊緣、窄螢幕、200% 縮放、長文字和 RTL。
  4. 精簡候選位置:保持符合介面需要的嘗試順序。
  5. 調整提示箭頭:位置翻轉後,箭頭亦要跟隨。
  6. 測試操作:檢查鍵盤、焦點、朗讀及關閉行為。
  7. 測試後備方案:確保功能不支援時,仍可取得資訊及完成主要操作。

面板已移到另一側,箭頭卻仍朝原來方向,可能令使用者誤認參照對象。錨點與面板的視覺關係也要納入替代位置的測試。

總結:由 CSS 接手定位計算

錨點定位改變的是位置計算的分工,而不是工具提示或 Popover 的用途。

  • anchor-name 和 position-anchor 建立明確參照。
  • position-area 選擇錨點周圍區域。
  • anchor() 和 anchor-size() 提供位置及尺寸。
  • position-try 系列提供替代位置。
  • Popover API 處理顯示行為,亦可建立隱含的觸發按鈕關係。
  • 草案狀態、瀏覽器相容性及實用的後備方案仍然重要。

把定位計算交給 CSS,JavaScript 就能專注於狀態、資料及互動。不過,無障礙設計與不支援的環境不會自動解決。可先從不影響重要操作的輔助提示、設定面板開始,按實際內容和目標瀏覽器逐步驗證。

參考資料及來源

編輯說明

本文使用 AI 協助編輯,並在發布前由編輯核對。內容仍可能包含事實、理解或時效上的錯誤;作出重要決定前,請查閱所列的一手資料或官方文件。

相關文章