ポップアップの矢印がコンテンツに重なる問題を解決

shoelace-style/webawesome

Web Awesome の <wa-popup> コンポーネントにおいて、矢印がポップアップのコンテンツに重なる視覚的な問題が解決されました。この変更により、矢印のサイズを大きくしたりパディングを減らしたりしても、コンテンツが矢印に隠されることがなくなります。

背景

<wa-popup> コンポーネントの矢印は三角形ではなく45度回転させた正方形(菱形)として実装されていました。この形状がポップアップのコンテンツ領域に侵入し、テキストや UI 要素を隠してしまうという問題がありました。

#1437 で最初の修正が試みられましたが、ポップアップ要素にボーダーを適用した際に視覚的な不具合が発生したため、マージされませんでした。クリッピングマスクがボーダーの外側エッジで切り取られ、ボーダーが矢印の下に残ってしまっていました。この問題は Home Assistant のフォークで手動修正されていましたが、バージョンアップのたびに失われるため、上流への反映が求められていました。

技術的な変更

矢印の実装は、クリッピングマスクの適用とポップアップのボーダーとの視覚的な統合という2つの側面で改善されました。

クリッピングマスクの適用

packages/webawesome/src/components/popup/popup.styles.ts で、矢印要素に clip-path プロパティが追加されました。このポリゴンマスクは、菱形の下半分を三角形として切り取り、コンテンツ領域への侵入を防ぎます。

.arrow {
  position: absolute;
  width: var(--arrow-size-div);
  height: var(--arrow-size-div);
  background: var(--arrow-color);
  z-index: 3;
  clip-path: polygon(
    var(--arrow-clipping-corner) 100%,
    var(--arrow-base-offset) calc(100% - var(--arrow-base-offset)),
    calc(var(--arrow-base-offset) - 2px) calc(100% - var(--arrow-base-offset)),
    calc(100% - var(--arrow-base-offset)) calc(var(--arrow-base-offset) - 2px),
    calc(100% - var(--arrow-base-offset)) var(--arrow-base-offset),
    100% var(--arrow-clipping-corner),
    100% 100%
  );
}

ポリゴンの各頂点は、矢印がポップアップのボーダーの内側エッジと正確に接続されるように計算されています。--arrow-clipping-corner は22.5度の角度でマイターカットを実現し、矢印のボーダーとポップアップのボーダーが視覚的に連続するようにしています。

ボーダー幅の考慮

新しいカスタムプロパティ --popup-border-width が導入され、ポップアップのボーダー幅を指定できるようになりました。矢印はこの値を使用して、ボーダーの内側エッジに正確に配置されます。

:host {
  --arrow-color: black;
  --arrow-size: var(--wa-tooltip-arrow-size);
  --popup-border-width: 0px;
  --show-duration: 100ms;
  --hide-duration: 100ms;

  --arrow-base-offset: var(--popup-border-width);
  --arrow-size-diagonal: calc((var(--arrow-size) + var(--arrow-base-offset)) * 0.7071);
  --arrow-padding-offset: calc(var(--arrow-size-diagonal) - var(--arrow-size));
  --arrow-size-div: calc(var(--arrow-size-diagonal) * 2);
  --arrow-clipping-corner: calc(var(--arrow-base-offset) * 1.4142);
}

--arrow-base-offset がボーダー幅と等しく設定され、矢印がその分だけ内側に平行移動されます。同時に、矢印のサイズもこのオフセット分だけ拡大され、ボーダーの外側エッジを完全にカバーします。定数0.7071は sin(45°) から導かれ、回転後の矢印の対角線長を計算するために使用されます。定数1.4142は 1 + tan(22.5°) から導かれ、マイターカットの角度を定義します。

矢印の配置ロジックも更新され、ボーダーとの重なりが考慮されるようになりました。

Object.assign(this.arrowEl.style, {
  top,
  right,
  bottom,
  left,
  [staticSide]: 'calc(var(--arrow-base-offset) - var(--arrow-size-diagonal))',
});

staticSide の位置が --arrow-base-offset を考慮して調整され、矢印がボーダーの内側に正確に配置されます。

ツールチップでの適用

<wa-tooltip> コンポーネントは内部的に <wa-popup> を使用しているため、同様の更新が適用されました。

.tooltip {
  --popup-border-width: var(--wa-tooltip-border-width);

  &::part(arrow) {
    border-bottom: var(--wa-tooltip-border-width) var(--wa-tooltip-border-style) var(--wa-tooltip-border-color);
    border-right: var(--wa-tooltip-border-width) var(--wa-tooltip-border-style) var(--wa-tooltip-border-color);
  }
}

ツールチップ固有のボーダー幅が --popup-border-width にマッピングされ、矢印の配置計算が自動的に適用されます。

設計判断

矢印の拡大とクリッピングマスクを組み合わせるアプローチが採用されました。単純にクリッピングマスクだけを適用する方法では、ボーダーとの視覚的な不連続性が解決できませんでした。

--popup-border-width という明示的なカスタムプロパティの導入により、開発者はボーダーを適用する際にその幅を宣言する必要があります。これは手動での設定が必要になる一方で、矢印の配置計算を自動化し、サブピクセルレンダリングの問題を回避しています。

ドキュメントでは、ボーダーを追加する際の使用例が詳細に説明されています。

When adding borders to the popup element which has an arrow, make sure to set the `--popup-border-width` custom property to match the width of the border of the popup. Setting this will allow the arrow to overlap the border of the popup so that they visually appear connected.

この設計により、既存の実装(ボーダーなし)との後方互換性を保ちながら、ボーダーを持つポップアップでも視覚的に正確な矢印表示を実現しています。

まとめ

本PRは、ポップアップ矢印の形状と配置の幾何学的な課題を、クリッピングマスクとボーダー幅考慮の組み合わせで解決しました。--popup-border-width カスタムプロパティの導入により、開発者は明示的にボーダー幅を指定することで、矢印がボーダーの内側エッジと正確に接続される配置を自動的に得られます。この変更は、Home Assistant フォークで長年維持されてきた修正を上流に統合し、コンポーネントの視覚的な品質を向上させています。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文(総論)→背景・技術詳細・設計判断(各論)→まとめ(結論)という理想的な3部構成が明確に適用されています。各セクションの役割が明確で、非常に読みやすいです。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライトやPR番号のリンク記法など、すべてのカスタムMarkdown構文が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

`clip-path`やCSSカスタムプロパティに関する詳細な解説は、専門知識を持つエンジニアという対象読者に完全に適合しています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成になっており、各パラグラフもトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が徹底されています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内のすべてのコードブロックとファイル名は、提供されたDiff情報と完全に一致しており、変更内容を正確に引用しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`clip-path`, `マイターカット`, `サブピクセルレンダリング`など、技術用語が正確かつ適切な文脈で使用されています。PR内のコメントで言及されている数学的な定数についても正確に記述されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

クリッピングマスクの役割やボーダー幅を考慮したオフセット計算など、技術的な説明はPRの内容と整合性が取れており、論理的で正確です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張(過去のPRの経緯、Home Assistantフォークでの対応など)は、PRのDescriptionで裏付けられており、ハルシネーションは一切見られません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#2070, #1437)やコード内の定数(0.7071, 1.4142)など、すべての数値・固有名詞が正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトル「ポップアップの矢印がコンテンツに重なる問題を解決」は、元のPRタイトル「Fix popup arrow overlapping text」の内容を的確に表現しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

PR情報に含まれない外部知識(バージョンサポート情報など)の追加はなく、記事内容は提供された情報源に忠実です。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「解決されました」「マージされませんでした」といった時間表現は、PRの文脈と一致しており、事実関係を正確に伝えています。