CSS Anchor Positioning:工具提示、Popover 與替代配置實作
DDEVELOPER
開發發布: 11 分鐘閱讀

CSS Anchor Positioning:工具提示、Popover 與替代配置實作

工具提示、下拉選單與補充資訊卡,看似只要放在按鈕旁邊,實際上卻會受到捲動、螢幕寬度、縮放、文字長度與包含區塊影響。傳統實作常以 JavaScript 測量座標,再調整位置。

CSS Anchor Positioning 讓瀏覽器依另一個元素配置浮動內容,空間不足時也能嘗試其他位置。瀏覽器實作持續推進,但規格仍是 W3C 工作草案。本文說明錨點定位的原理、與 Popover API 的分工,以及仍須自行設計的互動與替代方案。

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

CSS Anchor Positioning 解決什麼問題?

CSS Anchor Positioning 讓已定位的元素參照另一個「錨點元素」,決定自己的位置或尺寸。錨點就是定位的基準;對工具提示而言,通常是它所說明的按鈕或輸入欄位。

  • 圖示或輸入欄位旁的工具提示、錯誤訊息
  • 由按鈕開啟的下拉選單、Popover 與設定面板
  • 出現在文字選取位置附近的格式工具列:須另設基準元素,不能直接把文字 Range 當成錨點元素
  • 跟隨商品卡片或圖表元素的補充資訊

傳統 CSS 也能把父元素設為 position: relative,子元素設為 position: absolute,將兩者放在一起。較難處理的是兩個元素在 DOM 中相隔很遠、Popover 進入最上層(top layer),或靠近畫面邊緣時需要改變方向。錨點定位可明確指定參照對象,不必只依靠父子關係。

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; }

包含區塊是定位元素時使用的基準區域。position-area 的區域由錨點邊界與包含區塊邊界劃分,能直接表達「上方」或「區塊結束側加行內結束側」等意圖。若要微調偏移量,或結合不同錨點的邊緣位置,則可使用 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 協助編輯,並於發布前由編輯確認。內容仍可能包含事實、解讀或時效上的錯誤;進行重要判斷前,請查閱所列的一手資料或官方文件。

相關文章