CSSで、ダイアログの背景を暗くぼかす方法

​​CSS​HTML,JavaScript,Playwright

ダイアログの後ろのページを暗くぼかすには、<dialog> を showModal() で開き、後ろに出る幕 dialog::backdrop に、半透明の背景色と backdrop-filter: blur() を指定します。CSS は数行です。開け閉めの際に幕をふわっと出し入れするなら、@starting-style と、overlay と display の transition を足します。

補足

検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。HTML と CSS のページを Playwright で開き、getComputedStyle() の値と、スクリーンショットの画素の明るさを読みました。Firefox と Safari、スマートフォンの実機、性能の低い端末での描画の重さは試していません。

ダイアログを開いた同じページを2枚、縦に並べた画像。上の「色だけ(backdrop-filter なし)」では、後ろの見出し「週末の作業メモ」と「設定を開く」ボタンが暗くなっているが、文字ははっきり読める。下の「色とぼかし(blur(6px))」では、同じ見出しとボタンが暗く、文字が読めないほどぼやけている
上は背景色だけ、下は背景色とぼかしを指定した幕。どちらも幅400pxの画面で、ダイアログを開いたところ

幕に背景色とぼかしを指定する

HTML は、<dialog> と、それを開くボタンです。ダイアログの中のボタンは <form method="dialog"> に入れているので、押すとダイアログが閉じます。

html
<button type="button" id="open">設定を開く</button>

<dialog id="dlg">
  <h2>通知の設定</h2>
  <p>新しいコメントが付いたときに、メールで知らせます。</p>
  <form method="dialog">
    <button value="cancel">キャンセル</button>
    <button value="ok">保存</button>
  </form>
</dialog>

<script>
  const dialog = document.getElementById("dlg");
  document.getElementById("open").addEventListener("click", () => dialog.showModal());
</script>

CSS は次のとおりです。暗くぼかす部分は、dialog::backdrop の中の指定です。

css
dialog {
  border: none;
  border-radius: 12px;
  padding: 24px;
  width: min(90vw, 340px);
  box-shadow: 0 12px 32px rgb(0 0 0 / 0.3);
}

/* ダイアログの後ろの幕。暗くして、ぼかす */
dialog::backdrop {
  background: rgb(31 42 48 / 0.5);
  -webkit-backdrop-filter: blur(6px); /* Safari 17 以前向け */
  backdrop-filter: blur(6px);
}

MDN の ::backdrop の説明にあるとおり、::backdrop は、showModal() で開いたダイアログのすぐ後ろ(重なり順で1つ下)に出る、画面と同じ大きさの箱です。検証したページでは、幕の position は fixed、inset は 0px でした。何も指定しないときの幕の背景色は rgba(0, 0, 0, 0.1) で、うっすら暗くなるだけでした。

backdrop-filter は、要素の後ろにあるものに、ぼかしなどの効果をかけるプロパティです。幕の後ろにはページ全体があるので、ページ全体がぼやけて見えます。MDN の説明にあるとおり、効果が見えるのは、要素やその背景が透明か半透明のときです。この記事の幕も、背景色を不透明度0.5の半透明にしています。半透明でないと効かないことや、blur() の値の決め方、Safari 向けに -webkit- 付きも書く理由は、「CSSのbackdrop-filterですりガラス風の背景を作る」の記事でも取り上げています。

背景色で暗くし、ぼかしで文字を読めなくする

背景色とぼかしが、それぞれ何をしているかを数字で確かめました。後ろのページに、幅8pxの黒と白の縞模様を置き、ダイアログに重ならない部分の1行(128画素)の明るさを読みました。明るさは0(黒)から255(白)までで、「いちばん明るい画素といちばん暗い画素の差」が小さいほど、縞が見分けにくいことを表します。

幕の指定 平均の明るさ いちばん暗い画素 いちばん明るい画素 差
閉じているとき 147.5 40.1 255.0 214.9
何も指定しない幕 132.2 35.3 229.0 193.7
背景色だけ 93.6 40.1 147.2 107.1
ぼかしだけ(blur(6px)) 147.5 139.9 155.2 15.2
背景色と blur(6px) 93.7 89.7 97.6 7.9

背景色だけの幕では、平均の明るさは147.5から93.6に下がりました。ただし、縞の明暗の差は107.1残っていて、上の画像のように、後ろの文字ははっきり読めます。

ぼかしだけの幕では、平均の明るさは147.5のまま変わらず、明暗の差は15.2まで縮みました。blur() はガウスぼかしで、近くの画素の色をならすだけなので、全体は暗くなりません。

両方を指定すると、平均の明るさは93.7に下がり、明暗の差は7.9になりました。暗くするのは背景色、後ろの文字を読めなくするのはぼかしの役目です。

ぼかしの強さは、blur() に渡す長さで変わります。背景色はそのままで、長さだけを変えて同じ1行を読みました。

blur() の長さ 平均の明るさ 差
2px 93.6 94.6
6px 93.7 7.9
12px 93.7 2.7

2pxでは、幅8pxの縞を見分けられるだけの差(94.6)が残りました。6pxで差は7.9、12pxでは2.7まで縮みました。どの長さでも、平均の明るさはほとんど変わりませんでした。

show() で開いたダイアログには、幕が出ない

<dialog> を開くメソッドには、showModal() のほかに show() もあります。同じ CSS のまま show() で開くと、ダイアログは開きます(open は true)。しかし :modal には当てはまらず、後ろの縞の明るさは閉じているときと同じでした(平均147.5、差214.9)。幕は出ず、暗くもぼやけもしません。

同じダイアログを閉じて showModal() で開き直すと、:modal に当てはまり、明るさは平均93.7、差7.9になりました。MDN の showModal() の説明にあるとおり、::backdrop が付くのは、showModal() で最上位レイヤーに表示したときです。

開け閉めのときに、幕をふわっと出し入れする

幕を、開くときはだんだん暗く、閉じるときはだんだん明るくしたいときは、上の CSS のあとに次の CSS を足します。

css
/* 開くときと閉じるときに、ふわっと出し入れする */
dialog,
dialog::backdrop {
  opacity: 0;
  transition:
    opacity 0.3s,
    overlay 0.3s allow-discrete,
    display 0.3s allow-discrete;
}

dialog[open],
dialog[open]::backdrop {
  opacity: 1;
}

@starting-style {
  dialog[open],
  dialog[open]::backdrop {
    opacity: 0;
  }
}
「設定を開く」ボタンにマウスポインターが近づいて押すと、右上の文字が「クリック 1回目/2回」に変わってピンクの地になる。後ろのページがだんだん暗く、ぼやけていき、ダイアログが現れる。次にポインターがダイアログの「保存」ボタンへ移って押すと、「クリック 2回目/2回」になり、ダイアログと幕がだんだん薄くなって、もとのくっきりしたページに戻る
ボタンでダイアログを開き、「保存」で閉じる様子。開くときも閉じるときも、幕は0.3秒かけて出入りする

ダイアログ本体と幕の opacity を、0.3秒かけて変えています。幕の opacity を下げると、背景色だけでなく、ぼかしも薄くなりました。開いてからの経過時間で止めて、幕の opacity と後ろの縞の明るさを読むと、次のようになりました。

開いてからの時間 幕の opacity 平均の明るさ 差
0秒 0.00 147.5 214.9
0.075秒 0.41 124.9 106.1
0.15秒 0.80 103.8 32.2
0.225秒 0.96 95.9 12.0
0.3秒 1.00 93.7 7.9

CSS の各部分は、次の役目をしています。

@starting-style は、開いた直後の値を決めます。MDN の @starting-style の説明にあるとおり、transition は、display: none から表示に変わったときには、ふだんは起きません。@starting-style の部分だけを除いて開くと、transition は1つも始まらず、開いた瞬間に幕の opacity が1になりました。

overlay と display の transition は、閉じるときのためのものです。どちらも、ふだんは transition しないプロパティで、transition-behavior の allow-discrete を付けると transition するようになります。閉じてからの経過時間で止めて読むと、指定を外したときとの違いは次のとおりでした。

transition の指定 閉じた直後の幕の opacity 0.15秒後の幕の opacity 0.15秒後のダイアログの display
上の CSS のまま 1.00 0.20 block
overlay を外す 0.00 0.00 block
overlay と display を外す 0.00 0.00 none

上の CSS のままなら、閉じてから0.15秒後も幕は opacity 0.20で残り、後ろの縞の明るさは平均136.6でした。transition が終わると、ダイアログは display: none になり、明るさは閉じているときと同じ147.5に戻りました。

overlay を外すと、ダイアログ本体は0.15秒後も opacity 0.20で見えていましたが、幕は閉じた瞬間に消え、後ろのページはすぐにくっきりした状態に戻りました。MDN の overlay の説明にあるとおり、overlay を transition に入れると、最上位レイヤーから外すのが transition の終わりまで延びます。::backdrop は最上位レイヤーにある間だけ出るので、overlay がないと、幕だけ先に消えます。

overlay と display の両方を外すと、閉じた瞬間にダイアログも display: none になり、ダイアログも幕もすぐに消えました。

濃さとぼかしの強さを、dialog に置いたカスタムプロパティで変える

ダイアログごとに幕の濃さやぼかしの強さを変えたいときは、dialog にカスタムプロパティを置き、::backdrop で var() を使って読めます。

css
#dlg {
  --blur: 12px;
  --shade: rgb(6 122 152 / 0.4);
}

dialog::backdrop {
  backdrop-filter: blur(var(--blur));
  background: var(--shade);
}

Chromium 153 で開くと、幕の --blur は 12px、backdrop-filter は blur(12px)、背景色は rgba(6, 122, 152, 0.4) になっていました。幕が、ダイアログに置いた値を受け継いでいます。最初の CSS のように Safari 17 以前向けの -webkit-backdrop-filter も書いているときは、その行も blur(var(--blur)) にそろえます。

MDN の ::backdrop の説明には、::backdrop はどの要素からも継承しないと書かれています。一方、同じ MDN の互換性データ(@mdn/browser-compat-data 8.1.8)には、もとの要素から継承する機能が載っていて、Chrome 122、Firefox 120、Safari 17.4 から使えるとされています。検証した Chromium 153 の結果は、互換性データのほうと合っていました。

対応しているブラウザ

MDN の互換性データ(@mdn/browser-compat-data 8.1.8)で、この記事のコードで使っている機能を調べました。

機能 Chrome Firefox Safari
showModal() 37 98 15.4
dialog の ::backdrop 32 98 15.4
backdrop-filter 76 103 18
@starting-style 117 129 17.5
transition-behavior 117 129(一部のみ) 17.4
display の transition 117 なし 18
overlay 117 なし なし

幕を暗くぼかすだけなら、showModal()、::backdrop、backdrop-filter の3つで足ります。Safari の backdrop-filter は、互換性データでは、接頭辞なしは18から、-webkit-backdrop-filter は9からです。最初の CSS に -webkit-backdrop-filter の行も入れているのは、このためです。

閉じるときのフェードに使う overlay は、互換性データでは Chrome だけが対応していて、試験的(experimental)な機能とされています。Firefox の transition-behavior は「一部のみ」で、互換性データの注記には、display を allow-discrete で transition することには対応していないと書かれています。

​Posted by 86