DEMO
CODE
<div class="demo-stage pom-stage">
<button class="pom-btn" type="button" popovertarget="pom-menu">OPEN POPOVER</button>
<div id="pom-menu" popover class="pom-menu">
<p class="pom-title">POPOVER API</p>
<p class="pom-text">Esc キー・外側クリックで閉じます。JavaScript は使っていません。</p>
<button class="pom-close" type="button" popovertarget="pom-menu" popovertargetaction="hide">CLOSE</button>
</div>
</div>
.pom-stage { display:flex; align-items:center; justify-content:center; padding:3rem; background:#0F0F12; min-height:280px; }
.pom-btn {
padding:.85rem 2rem; border:1px solid rgba(233,27,137,.6); border-radius:999px;
background:#15151A; color:#fff; cursor:pointer;
font-family:'Oswald',sans-serif; font-size:.78rem; letter-spacing:.22em;
transition: background .25s ease, border-color .25s ease;
}
.pom-btn:hover { background:#1F1F26; border-color:#E91B89; }
.pom-menu {
max-width:340px; padding:1.8rem; border:1px solid rgba(255,255,255,.12); border-radius:14px;
background:#17171C; color:#EDEDED;
opacity:0; transform:translateY(-10px) scale(.96);
transition: opacity .28s ease, transform .28s cubic-bezier(.23,1,.32,1),
overlay .28s allow-discrete, display .28s allow-discrete;
}
.pom-menu:popover-open { opacity:1; transform:translateY(0) scale(1); }
@starting-style {
.pom-menu:popover-open { opacity:0; transform:translateY(-10px) scale(.96); }
}
.pom-menu::backdrop { background:rgba(0,0,0,0); transition: background .28s ease, overlay .28s allow-discrete, display .28s allow-discrete; }
.pom-menu:popover-open::backdrop { background:rgba(0,0,0,.55); }
@starting-style { .pom-menu:popover-open::backdrop { background:rgba(0,0,0,0); } }
.pom-title { font-family:'JetBrains Mono',monospace; font-size:.9rem; color:#E91B89; margin-bottom:.6rem; }
.pom-text { font-size:.85rem; line-height:1.8; color:#B9B9C2; margin-bottom:1.4rem; }
.pom-close {
padding:.5rem 1.4rem; border:1px solid rgba(255,255,255,.25); border-radius:999px;
background:transparent; color:#EDEDED; cursor:pointer;
font-family:'Oswald',sans-serif; font-size:.7rem; letter-spacing:.2em;
}
.pom-close:hover { border-color:#E91B89; color:#E91B89; }
@media (prefers-reduced-motion: reduce) {
.pom-menu, .pom-menu::backdrop { transition-duration:.01ms; }
}
AI PROMPT
JavaScriptなしで開閉するポップオーバーを作って。開きたい要素に popover 属性を付け、id を振る。開くボタンには popovertarget="その id" を指定するだけで開閉トグルになる。閉じる専用ボタンにしたい場合は popovertargetaction="hide" を追加。Popover API を使うと、Escキーで閉じる・外側クリックで閉じる(light dismiss)・トップレイヤーに表示されz-indexの積み重ね問題が起きない、という挙動がブラウザ標準で付いてくる。アニメーションは :popover-open の状態に最終値を書き、元のセレクタの transition に overlay .28s allow-discrete と display .28s allow-discrete を含め、@starting-style { .menu:popover-open { 開始値 } } を書く。背景の暗幕は ::backdrop 疑似要素に同じ手順で background のトランジションを当てる。モーダルとして使いたい(=外側クリックで閉じない)場合は popover="manual" にする。
Acceptance criteria: works with prefers-reduced-motion (animation disabled), zero console errors, zero layout shift
AIエージェントでの実装ガイド → Claude Code でWebアニメーションを実装する
BOOKS · PR · AMAZON
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
学びを止めない · AMAZONサブスク
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
popover 属性と popovertarget 属性だけで開閉するHTML標準の仕組みです。開閉状態の管理、Escキーでの閉鎖、外側クリックでの閉鎖(light dismiss)、トップレイヤーへの描画、フォーカス管理までがブラウザ側で提供されるため、これまで数十行のJavaScriptと z-index の調整が必要だったドロップダウン実装が属性2つに置き換わります。
当サイトの Modal Fade はオーバーレイ付きの中央モーダル、Slide Drawer は横から出るドロワーで、いずれもJavaScriptで状態を管理する従来型です。本ページはそれらをブラウザ標準の機能で置き換える別テーマとして扱っています。
アニメーションを付けるには @starting-style と transition-behavior: allow-discrete が必要で、さらに overlay プロパティも allow-discrete でトランジションさせないと、閉じるときにトップレイヤーから即座に外れてフェードアウトが見えません。この overlay の指定は忘れやすい落とし穴です。
Popover API と <dialog> はどう使い分けますか?
<dialog> の showModal() は背後の操作を完全にブロックするモーダル用です。Popover API は既定が light dismiss(外側クリックとEscで閉じる)で、ドロップダウンメニュー・ツールチップ・トーストなど「軽い一時表示」に向きます。popover="manual" にすると外側クリックで閉じなくなるため、トーストのように自分で閉じるUIにも使えます。
トップレイヤーに表示されると何が嬉しいのですか?
popover 要素はDOM上の位置に関係なく最前面のレイヤーへ描画されます。親要素の overflow: hidden で切れる、transform を持つ祖先のせいで position: fixed がずれる、z-index の競争に負けて隠れる、といったドロップダウン実装の典型的な事故がまとめて解消します。
コードは無料で使えますか?
はい。Motion LabのコードスニペットとAIプロンプトは、クレジット表記なしで自由に使えます。コピーしてそのままプロジェクトに利用できます。