ZATYのBLOG

お問い合わせする

【CSS】blurの3つの罠と解決方法

default thumbnail

要素のぼかしに使うfilter: blur();と背景ぼかしに使うbackdrop-filter: blur();を使っていると、思いよらない挙動をすることがあります。

今回はblurでハマりがちな問題と解決方法を解説します。

※ コード例とサンプルプレビューはブログの埋め込み都合上異なることがあります。

filterとbackdrop-filterのblur()

まずは、CSSプロパティを簡単に解説します。

filter: blur();

このプロパティは要素自体をぼかすことができます。

<style>
.filter {
  filter: blur(2px);
}
</style>
<p class="filter">ぼやけた文章</p>

ぼやけた文章

backdrop-filter: blur();

<style>
.backdrop-filter {
  position: absolute;
  inset: 5%;
  backdrop-filter: blur(10px);
}
</style>
<div class="sample-block">
  <img src="example.png" />
  <p class="backdrop-filter">背景がぼやける要素</p>
</div>

背景がぼやける要素

blurが祖先要素にあるときのposition: fixed

position:fixedは画面全体に固定表示させる時に使用するプロパティです。画面にくっついたヘッダーや上に戻るボタンなどでよく使用します。

filter: blur()backdrop-filter: blur()どちらかを指定した要素の下でfixedを使うと、blurを指定した祖先要素が基準で表示されてしまいます。

まずは通常のposition: fixedの挙動を確認します。

<style>
.fixed {
  position: fixed;
  top: 10px;
  left: 10px;
  padding: 8px;
  background-color: skyblue;
}
</style>
<div class="fixed">固定された要素</div>

固定された要素

画面の左上に固定された要素が表示されます。上下の基準が画面になっていることがわかります。

次に親要素にblurが指定されている時は、blurを指定した要素が基準になってしまう例を見てみます。

filter: blur()の例

<div class="sample-block">
  <div class="filter">
    <div class="fixed">固定された要素</div>
    <p>テキスト</p>
    <p>テキスト</p>
  </div>
</div>
固定された要素

テキスト

テキスト

backdrop-filter: blur()の例

<div class="sample-block">
  <img src="example.png" />
  <div class="backdrop-filter">
    <div class="fixed">固定された要素</div>
    <p>テキスト</p>
    <p>テキスト</p>
  </div>
</div>
固定された要素

テキスト

テキスト

どちらもposition: fixedを指定していますが、画面基準ではなくblurを指定した要素が基準になってしまっていることがわかります。

blur要素内に配置したままfixedを効かせることは現状不可能です。fixedさせたい要素をブラー要素の外に設置するしかありません。

背景ブラーの要素の中からモーダルを起動する仕様を作成するときに、この挙動の問題によく衝突します。今回はそのパターンの回避方法を紹介します。

ポータルを使った回避方法

ReactやVueを使っている時、コンポーネント内にモーダルコンポーネントを起動したい状況がよくあります。

<BlurBox>
  <Button />
  <Modal>
    <p>モーダル</p>
  </Modal>
</BlurBox>

ブラーのコンポーネント外にモーダルコンポーネントを設置することができれば解決できますが、コンポーネントの仕様上不可能であることも多々あります。

そんな時は、ポータルと呼ばれる仕組みを利用することで、ブラー要素の中に設置しながらfixedの挙動をさせることができます。

ReactではPortal、Vueではteleport(Vue3以前ではモジュールを利用する必要があります)と呼ばれる仕組みを利用します。これらはどちらもコンポーネント内に記述しながら、描画の際はあらかじめ指定した要素にモーダルのHTMLを設置するという仕組みになっています。

JSフレームワークでは、一般的に、#root要素のなかで描画をします。ポータルを使う場合は、#root要素の下(body直前が一般的)に#portalRootのような要素を作成し、このポータル要素を描画するときは、この要素の中に描画するという仕組みです。

<body>
  <div id="root">
    <!-- ここに普通のコンポーネントが表示 -->
  </div>
  <div id="portalRoot">
    <!-- ポータル要素はここに表示させる -->
  </div>
</body>

この仕組みを使うことでfilter: blur()の要素の外にposition: fixedの要素を移動させることができます。

それぞれの詳しい使い方は下記を参考にしてください。

Reactでポータルを使う

Vueでポータルを使う

Vueの3.0系以前でポータルを使う

ポータルはモーダルだけに限らず、エラーや成功状態を表示するトースト、一定部分だけ画面追従するスティッキーナビゲーションダイアログにも活用することができます。

もし、ReactやVueのフレームワークを使用せずにポータルを使いたい場合も、JavaScriptで同様のアプローチの処理を作ることで回避可能です。

blurが祖先要素にあるときのmix-blend-mode

mix-blend-modeは指定した要素のテキストの文字色が背景色を考慮した色になるプロパティです。

<style>
.background {
  background-image: linear-gradient(90deg, black, white);
}
.blend-text {
  color: #fff;
  mix-blend-mode: difference;
}
</style>

いろはにほへと ちりぬるを

祖先要素にblur要素があると、このmix-blend-modeの処理が変わってしまいます。

<div class="color-block">
  <div class="filter">
    <p class="blend-text">親要素がぼやけた文字</p>
  </div>
</div>

ぼやけた文章

filterクラスが

<div class="sample-block">
  <img src="example.png" />
  <div class="backdrop-filter">
    <p class="blend-text">背景がぼやける要素</p>
  </div>
</div>

背景がぼやける要素

回避方法

一番単純な解決法として、blurを設定している要素とmix-blend-modeを設定する要素を同一にすることでmix-blend-modeの予期しない挙動を回避することができます。

<div class="color-block">
  <p class="filter blend-text">親要素がぼやけた文字</p>
</div>
<div class="sample-block">
  <img src="example.png" />
  <p class="backdrop-filter blend-text">背景がぼやける要素</p>
</div>

ぼやけた文章

背景がぼやける要素

Safariでbackdrop-filterを使うとき

Safariでbackdrop-filterプロパティの挙動を使う場合はベンダープレフィックスを使う必要があります。

.complete-blur {
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}