🚪 CSS :open がBaseline到達 — details・dialog・selectの「開いてる」を1つのセレクタで書ける

開閉状態を持つ要素(details/dialog/select/inputピッカー)を横断して選択できる:open擬似クラスが、Safari 26.5の対応で全主要ブラウザに揃った。details[open]との違い、@starting-styleと組む開閉アニメーションまで整理する。

読む深さ

details[open]dialog[open]、そしてselectの展開状態は取る方法なし。「開いている」というありふれた状態ひとつ拾うのに、要素ごとに別の書き方をしてきたわけです。地味に嫌な非対称だった。

:open 擬似クラスがこれを1本にまとめる。対象はdetails、dialog、select、そして日付や色のピッカーを持つinput。Chrome/Edge 133、Firefox 136に続いてSafari 26.5が対応し、Baseline(Newly available)に到達した。もう試験機能ではない。

属性ではなく「状態」を見る

details[open] と何が違うのか。属性セレクタはDOMに open 属性が書かれているかを見る。:open は要素が実際に開いているかを見る。detailsでは両者はほぼ一致するけど、selectのドロップダウン展開のように対応する属性が存在しない開閉は、:openでしか取れない。

/* アコーディオン: 開いている項目だけ強調 */
details:open > summary { font-weight: 700; }
details:open { border-color: var(--accent); }

/* 展開中のselectと、その親ラベルまで反応させる */
select:open { background: #f8f2dc; }
label:has(select:open) { border-color: #cd4631; }

:has() と組み合わせられるので、「開いているフォーム部品の親コンテナごとスタイルを変える」がCSSだけで書ける。子の状態を親のセレクタ条件にできるのが効き所だ。ドロップダウンを開いた瞬間にカード全体をふわっと持ち上げる、みたいな演出は今までJSでクラスを付け外ししていた領域だ。

dialogのアニメーションは3点セットで完成する

:openが一番仕事をするのはdialogだと思う。モーダルの開閉アニメーションはずっと鬼門だった。display: none との行き来はtransitionが効かないからだ。これが :open + @starting-style + transition-behavior: allow-discrete の3点セットで、JSゼロで書けるようになっている。

dialog {
  opacity: 0;
  translate: 0 8px;
  transition: opacity 0.25s, translate 0.25s,
              overlay 0.25s allow-discrete,
              display 0.25s allow-discrete;
}
dialog:open {
  opacity: 1;
  translate: 0 0;
}
@starting-style {
  dialog:open { opacity: 0; translate: 0 8px; }
}

allow-discrete がdisplayとoverlayの離散遷移を許可し、@starting-style が「表示された直後の初期値」を与える。閉じるときは、同じtransitionが終了値へ向かって走るので逆再生のように見える。この3つの役割分担、初見だと呪文に見えるが、一度書けば全モーダルに使い回せる。

ひとつ落とし穴を書いておくと、:openは表示されているかではなく開いているかを見るので、visibility: hidden で隠したdetailsも開いていればマッチする。状態と可視性は別、と覚えておけば混乱しない。

なお、popover属性の要素には :open ではなく :popover-open(こちらは2024年にBaseline済み)を使う。同じ「開く」でも、開閉モデルが違うのでセレクタも別、という整理だ。

Zennにも Qiitaにも:open自体の記事はまだほぼ無い。この1年、:has()やpopoverや@starting-styleで「JSの領分だった状態スタイリング」が次々CSSに移管されてきて、:openはその欠けていたピースが埋まった感じがする。あなたのコードベースで dialogの開閉にクラスをtoggleしているJS、そろそろ消せるんじゃないですか。

元ネタ: https://developer.mozilla.org/en-US/docs/Web/CSS/:open