ポップアップの矢印がコンテンツに重なる問題を解決
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 フォークで長年維持されてきた修正を上流に統合し、コンポーネントの視覚的な品質を向上させています。