CSS Popover API 活用ガイド - JavaScriptなしで実装するモダンなポップオーバー
CSS Popover APIとは
CSS Popover APIは、ポップオーバー(一時的に表示されるコンテンツ)をJavaScriptなしで実装できる新しいWeb標準APIです。2023年にChrome 114で正式にサポートされ、モダンブラウザでの採用が進んでいます。
従来、ポップオーバーやツールチップ、モーダルダイアログなどを実装するには、複雑なJavaScriptコードやサードパーティライブラリが必要でした。Popover APIを使用することで、HTML属性だけでこれらの機能を簡単に実装できるようになります。
主な特徴
- ゼロJavaScript: HTML属性のみで動作
- 自動的なz-index管理: トップレイヤーに自動配置
- キーボード操作対応: Escキーで閉じる動作が標準搭載
- アクセシビリティ: ARIA属性の自動設定
- 軽量: 追加のライブラリ不要
基本的な使い方
シンプルなポップオーバー
最もシンプルなポップオーバーの実装例です。
<button popovertarget="my-popover">ヒントを表示</button>
<div id="my-popover" popover>
<p>これはポップオーバーコンテンツです。</p>
<p>Escキーまたは外側をクリックで閉じます。</p>
</div>
ポイント:
popover属性をポップオーバー要素に追加popovertarget属性でトリガーボタンとポップオーバーを関連付けidで要素を識別
ポップオーバーの動作モード
Popover APIには3つの動作モードがあります。
<!-- 自動モード(デフォルト): 外側クリックやEscで閉じる -->
<div id="auto-popover" popover="auto">
自動的に閉じるポップオーバー
</div>
<!-- 手動モード: 明示的な操作でのみ閉じる -->
<div id="manual-popover" popover="manual">
手動で閉じる必要があるポップオーバー
</div>
<!-- 暗黙的な自動モード -->
<div id="implicit-popover" popover>
popover="auto"と同じ動作
</div>
autoモードの特徴:
- 外側クリックで自動的に閉じる
- Escキーで閉じる
- 他のautoポップオーバーが開くと閉じる
- 軽量なインタラクション向け(ツールチップ、ドロップダウンなど)
manualモードの特徴:
- 明示的な閉じる操作が必要
- 複数同時に開ける
- 重要な情報の表示向け(警告、確認ダイアログなど)
ポップオーバーの開閉制御
トリガーボタンの動作を細かく制御できます。
<!-- 開く専用ボタン -->
<button popovertarget="menu" popovertargetaction="show">
メニューを開く
</button>
<!-- 閉じる専用ボタン -->
<button popovertarget="menu" popovertargetaction="hide">
メニューを閉じる
</button>
<!-- トグルボタン(デフォルト) -->
<button popovertarget="menu" popovertargetaction="toggle">
メニューを切り替え
</button>
<div id="menu" popover>
<h3>メニュー</h3>
<ul>
<li>項目1</li>
<li>項目2</li>
<li>項目3</li>
</ul>
<!-- ポップオーバー内に閉じるボタンを配置 -->
<button popovertarget="menu" popovertargetaction="hide">
閉じる
</button>
</div>
スタイリング
基本的なスタイリング
ポップオーバーは通常のHTML要素なので、自由にスタイリングできます。
/* ポップオーバーの基本スタイル */
[popover] {
padding: 1.5rem;
border: 2px solid #333;
border-radius: 8px;
background: white;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
max-width: 400px;
}
/* トリガーボタンのスタイル */
[popovertarget] {
padding: 0.5rem 1rem;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
}
[popovertarget]:hover {
background: #0056b3;
}
アニメーション
開閉時のアニメーションを追加できます。
/* 開くアニメーション */
[popover]:popover-open {
animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 閉じるアニメーション(@starting-styleを使用) */
@starting-style {
[popover]:popover-open {
opacity: 0;
transform: translateY(-10px);
}
}
/* トランジション */
[popover] {
transition: opacity 0.3s, transform 0.3s, display 0.3s allow-discrete;
}
バックドロップのスタイリング
::backdrop疑似要素でオーバーレイをカスタマイズできます。
[popover]::backdrop {
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(3px);
}
実践的な実装例
ツールチップ
情報表示用のシンプルなツールチップです。
<style>
.tooltip-trigger {
position: relative;
text-decoration: underline;
text-decoration-style: dotted;
cursor: help;
}
.tooltip {
padding: 0.5rem 0.75rem;
background: #333;
color: white;
border: none;
border-radius: 4px;
font-size: 0.875rem;
max-width: 200px;
}
.tooltip::backdrop {
display: none;
}
</style>
<p>
この機能は
<span class="tooltip-trigger">
<button popovertarget="tooltip-1" class="tooltip-trigger">
ベータ版
</button>
</span>
です。
</p>
<div id="tooltip-1" popover class="tooltip">
この機能は現在開発中です。予告なく変更される可能性があります。
</div>
ドロップダウンメニュー
ナビゲーション用のドロップダウンメニューです。
<style>
.nav-button {
padding: 0.5rem 1rem;
background: transparent;
border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
}
.dropdown-menu {
padding: 0;
border: 1px solid #ddd;
border-radius: 4px;
background: white;
min-width: 200px;
list-style: none;
}
.dropdown-menu li {
padding: 0.75rem 1rem;
border-bottom: 1px solid #eee;
cursor: pointer;
}
.dropdown-menu li:last-child {
border-bottom: none;
}
.dropdown-menu li:hover {
background: #f5f5f5;
}
</style>
<button popovertarget="user-menu" class="nav-button">
ユーザーメニュー ▼
</button>
<ul id="user-menu" popover class="dropdown-menu">
<li>プロフィール</li>
<li>設定</li>
<li>ヘルプ</li>
<li>ログアウト</li>
</ul>
モーダルダイアログ風
重要な確認を求める場合に使用します。
<style>
.dialog {
padding: 2rem;
border: none;
border-radius: 8px;
background: white;
max-width: 500px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
}
.dialog::backdrop {
background: rgba(0, 0, 0, 0.6);
}
.dialog-header {
margin: 0 0 1rem 0;
font-size: 1.5rem;
font-weight: bold;
}
.dialog-actions {
margin-top: 1.5rem;
display: flex;
gap: 1rem;
justify-content: flex-end;
}
.btn {
padding: 0.5rem 1.5rem;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
}
.btn-primary {
background: #dc3545;
color: white;
}
.btn-secondary {
background: #6c757d;
color: white;
}
</style>
<button popovertarget="confirm-dialog" class="btn btn-primary">
アカウントを削除
</button>
<div id="confirm-dialog" popover="manual" class="dialog">
<h2 class="dialog-header">本当に削除しますか?</h2>
<p>この操作は取り消せません。アカウントとすべてのデータが完全に削除されます。</p>
<div class="dialog-actions">
<button popovertarget="confirm-dialog" popovertargetaction="hide" class="btn btn-secondary">
キャンセル
</button>
<button class="btn btn-primary">削除する</button>
</div>
</div>
JavaScriptとの連携
必要に応じてJavaScriptで制御することも可能です。
プログラムでの開閉
const popover = document.getElementById('my-popover');
// 表示
popover.showPopover();
// 非表示
popover.hidePopover();
// トグル
popover.togglePopover();
イベントリスナー
ポップオーバーの状態変化を検知できます。
const popover = document.getElementById('my-popover');
// 表示前
popover.addEventListener('beforetoggle', (event) => {
console.log('状態:', event.newState); // "open" または "closed"
console.log('前の状態:', event.oldState);
// 開く前の処理
if (event.newState === 'open') {
console.log('ポップオーバーが開きます');
// データの読み込みなど
}
});
// 表示後
popover.addEventListener('toggle', (event) => {
if (event.newState === 'open') {
console.log('ポップオーバーが開きました');
} else {
console.log('ポップオーバーが閉じました');
}
});
動的なコンテンツ読み込み
const popover = document.getElementById('dynamic-popover');
const trigger = document.querySelector('[popovertarget="dynamic-popover"]');
trigger.addEventListener('click', async () => {
// データを取得
const data = await fetchData();
// ポップオーバーのコンテンツを更新
popover.innerHTML = `
<h3>${data.title}</h3>
<p>${data.description}</p>
`;
});
async function fetchData() {
// APIからデータを取得(例)
return {
title: '最新情報',
description: 'ポップオーバーの内容を動的に更新しました。'
};
}
アクセシビリティ
Popover APIは基本的なアクセシビリティを自動で提供しますが、さらに改善できます。
ARIA属性の追加
<button
popovertarget="accessible-popover"
aria-label="詳細情報を表示"
aria-describedby="popover-description">
詳細
</button>
<div
id="accessible-popover"
popover
role="dialog"
aria-labelledby="popover-title"
aria-describedby="popover-description">
<h3 id="popover-title">詳細情報</h3>
<p id="popover-description">
この機能についての追加説明がここに表示されます。
</p>
</div>
キーボードナビゲーション
<style>
.menu-item {
display: block;
width: 100%;
padding: 0.75rem 1rem;
border: none;
background: transparent;
text-align: left;
cursor: pointer;
}
.menu-item:focus {
background: #e9ecef;
outline: 2px solid #007bff;
outline-offset: -2px;
}
</style>
<button popovertarget="keyboard-menu" aria-haspopup="menu">
メニュー
</button>
<div id="keyboard-menu" popover role="menu">
<button class="menu-item" role="menuitem" tabindex="0">
新規作成
</button>
<button class="menu-item" role="menuitem" tabindex="0">
開く
</button>
<button class="menu-item" role="menuitem" tabindex="0">
保存
</button>
</div>
ブラウザサポートと代替手段
対応ブラウザ
- Chrome/Edge: 114+
- Safari: 17+
- Firefox: 125+ (2024年4月リリース予定)
フィーチャー検出
if (HTMLElement.prototype.hasOwnProperty('popover')) {
// Popover APIがサポートされている
console.log('Popover API利用可能');
} else {
// フォールバック処理
console.log('Popover API非対応、代替実装を使用');
usePolyfill();
}
ポリフィル
対応していないブラウザ向けのポリフィルも利用可能です。
<script type="module">
import { applyPopoverPolyfill } from 'https://cdn.skypack.dev/@oddbird/popover-polyfill';
// ポリフィルを適用
applyPopoverPolyfill();
</script>
まとめ
CSS Popover APIは、モダンWebアプリケーションにおけるポップオーバー実装を大幅に簡素化します。
メリット
- 開発効率の向上: JavaScriptライブラリが不要
- パフォーマンス: ネイティブ実装のため高速
- 保守性: シンプルなHTML属性で管理
- アクセシビリティ: 標準的なキーボード操作をサポート
使用を検討すべきケース
- ツールチップ、ヘルプテキスト
- ドロップダウンメニュー
- コンテキストメニュー
- 軽量な確認ダイアログ
- 通知やアラート
注意点
- 複雑なモーダルダイアログは
<dialog>要素の使用を検討 - 位置指定が必要な場合はAnchor Positioning APIと組み合わせる
- ブラウザサポートを確認し、必要に応じてポリフィルを使用
Popover APIを活用することで、よりシンプルで保守しやすいUIを実装できます。ぜひプロジェクトに導入してみてください。