UIデザインは奥が深い、CSSで美しいシャドウを実装する方法
Post on:2021年9月30日
Webページやスマホアプリなど、UIデザインで使用されるシャドウをより美しく実装するテクニックを紹介します。
シャドウをぼんやりとしたグレーにするだけでなく、美しく最適化して実装します。また、シャドウのサイズで要素に標高差をつけ目立たせたり、彩度と明度の関係など、UIデザインに効果的なテクニックも詳しく解説されています。
CSSでシャドウを実装する前に、一読をお勧めします。
Designing Beautiful Shadows in CSS
by Josh W. Comeau
下記は各ポイントを意訳したものです。
デモは元記事ではインタラクティブですが、当記事では画像にしています。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
- はじめに
- シャドウをなぜ使用するのか
- 一貫性のある環境の構築
- 美しいシャドウの実装方法1: レイヤー化
- 美しいシャドウの実装方法2: 色を合わせたシャドウ
- ここまでのまとめ
- 旅を続けるために
- ボーナス: drop-shadowで実装するシャドウ
はじめに
私の考えでは、優れたWebサイトやWebアプリにはなんらかのリアルなクオリティがあります。このクオリティを実現するためにはさまざまな要素がありますが、中でもシャドウは重要な要素です。
しかし、Webを見渡してみると、ほとんどのシャドウがそれほどリッチではないことがわかります。Web上には、シャドウとは思えないような、ぼんやりとしたグレーのボックスであふれています。
この記事では、典型的なボックスシャドウを美しく、生き生きとしたシャドウにする方法を解説します。
左: 典型的なシャドウ、右: 最適化されたシャドウ
対象とする人
この記事では、CSSの基本になれているデベロッパーを対象としています。box-shadow、hsl()カラー、CSS変数に関する知識があることを前提としています。
参考: CSSの変数(カスタムプロパティ)便利な使い方を詳しく解説
参考: CSS変数の優れた使い方、コンポーネントのバリエーションを実装するのに役立つ
シャドウをなぜ使用するのか
CSSの実装テクニックは、この後すぐに解説します。その前に、CSSにシャドウが存在する理由とシャドウを最大限に活用する方法について解説したいと思います。
シャドウは高さを意味し、より大きなシャドウはより高いことを意味します。シャドウを戦略的に使用すれば、ページ上のさまざまな要素が異なるレベルで背景から浮かんでいるかのような奥行きのある錯覚を作り出すことができます。
実際に例を見てましょう。
ヘッダとダイアログボックスでは、シャドウが異なる
私が実装するUIは、ブラウザが別世界へのウインドウであるかのような、触覚的で本物のように感じてもらいたいと思っています。そのためには、シャドウの存在が欠かせません。
また、ここには戦術的な利点があります。ヘッダとダイアログボックスに異なるシャドウを使用することで、ヘッダよりもダイアログボックスの方が近いという印象を与えます。人は身近な要素に注意を向ける傾向があるので、ダイアログボックスを高くすることで、ユーザーが最初に注目する可能性が高くなります。注目を集めるためのツールとして、シャドウによる高さを利用することができます。
シャドウは次のいずれかの目的で使用します。特定の要素を目立たせたい、アプリの手触り感や臨場感を高めたい、などです。
ただし、これらの目的を達成するためには、UI内のシャドウを全体的に捉える必要があります。
一貫性のある環境の構築
私は長い間、シャドウを正しく使用していませんでした😬
要素にシャドウをつけたいときは、box-shadowプロパティを追加して、その見た目が気に入るまで数値をいじっていただけでした。
問題は、このように要素のシャドウを実装してしまうと、不調和なシャドウができてしまうことです。奥行きを感じさせるためには、すべてのシャドウが一致していなければなりません。そうでなければ、それらのシャドウはただのぼやけた境界線の集まりです。
要素ごとにシャドウが異なると、それはただのぼやけた境界線の集まり
自然界では、光源から影が落とされます。Webでのシャドウの方向は、光源の位置によって異なります。
シャドウの方向は、光源の位置によって異なる
一般的には、ページ上のすべての要素に対して1つの光源を決定する必要があります。その光源は、左上にあるのが一般的です。
光源は、左上にあるのが一般的
もしCSSに光源システムがあれば、1つまたは複数の光源の位置を指定できます。残念ながら、CSSにはそのようなものはありません。
しかしその代わりに、水平と垂直方向のオフセットを指定して、シャドウを実装できます。たとえば、上記の画像では、垂直に4pxと水平に2pxのオフセットを持つシャドウが実装されています。
これが一貫性のあるシャドウを実装する1つ目のポイントです。ページ上のすべてのシャドウの比率を同じにする必要があります。そうすることで、すべての要素が同じ光源から照らされているように見えます。
次は、高さについて解説します。
要素がユーザーに向かって持ち上げられているような錯覚を与えるには、どうすればよいでしょうか。
まとまりのあるエクスペリエンスを生み出すには、box-shadowの4つの値すべてを連携させて調整する必要があります。
box-shadowの4つの値すべてを連携させて調整する
※元記事では、スライダーを動かすと高さが変化します。
最初の2つの値(水平と垂直のオフセット)は連携しています。垂直のオフセットは、常に水平のオフセットの2倍にしています。
要素が高くなると、次の2つのことが起こります。
- ブラーの半径が大きくなる。
- シャドウの不透明度が低くなる。
※元記事のデモでは、さらにリアリズムを高めるために、カードのサイズも大きくなります。実際には、この手順をスキップする方が簡単な場合があります。
このようなことが起こる理由は、複雑な数学的な理由がありますが、わたし達はライトな世界に存在する人間として、直感を活用することができます。
あなたが明るい部屋にいる場合は、手を机(または近くの表面)に押し付け、ゆっくりと持ち上げてみてください。手の影がどのように変化するかに注目してください。影は手から離れ(オフセットが大きくなり)、ぼやけて(ブラーの半径が大きくなり)、消えていきます(不透明度が低くなります)。手を動かすことができない場合は、部屋の中にある物で試してみてください。他の物の影との違いを比べてみてください。
わたし達は影のある環境での経験が豊富なので、新しいルールを覚える必要はありません。シャドウをデザインする際には、直感を働かせればいいのです。ただし、そのためには、HTMLの要素を物理的なオブジェクトとして考えるという発想が必要になります。
ここまでを要約すると、
- ページ上の各要素は、同じグローバルな光源から照らされる必要があります。
- box-shadowプロパティは、水平と垂直のオフセットを使用して光源の位置を表します。一貫性を持たせるために、各シャドウはこの2つの値の比率を同じにする必要があります。
- 要素がユーザーに近づくと、オフセットが増え、ブラーの半径が大きくなり、シャドウの不透明度が低くなります。
- 直感を働かせることで、これらの計算の一部を省略できます。
美しいシャドウの実装方法1: レイヤー化
Blenderなどの3Dイラストツールは、レイトレーシングと呼ばれる技術(光の屈折と反射をシミュレート)を用いて、リアルなシャドウと光源を作り出すことができます。
3Dイラストツールのシャドウと光源
レイトレーシングではカメラから何百本もの光のビームが発射され、シーンの表面で何百回も跳ね返されます。これは計算コストの高い技術で、1つの画像を作成するのに数分から数時間かかることもあります。
Webユーザーにはそのような忍耐力はありませんので、box-shadowのアルゴリズムははるかに基本的です。このアルゴリズムでは、要素の形をしたボックスを作成し、そこに基本的なぼかしアルゴリズムを適用します。
その結果、シャドウは決して写真のようにリアルに見えることはありませんが、レイヤーという巧妙なテクニックを使えば、かなり改善されます。
つまり、1つのbox-shadowを使うのではなく、オフセットと半径を少しずつ変えながら、いくつかのbox-shadowを重ねます。
box-shadowのレイヤーを重ねる
複数のシャドウを重ねる(レイヤー化)ことで、現実の影のような微妙なニュアンスを表現します。
このテクニックは、Tobias Ahlinのブログ記事「Smoother and Sharper Shadows with Layered box-shadow」で詳しく解説されています。
参考: CSSは奥が深い!グラデーションとシャドウをこれまでより美しく実装するテクニック
また、Philipp Brummはレイヤードシャドウの生成に役立つ素晴らしいツールを作成しました。
パフォーマンスとのトレードオフ
レイヤードシャドウは紛れもなく美しいものですが、コストがかかります。5つのシャドウを重ねると、デバイスは5倍の作業をしなければなりません。最近のハードウェアではそれほど問題ではありませんが、古い安価なスマホではレンダリングが遅くなる可能性があります。
使用する時は、必ずテストしてください。私の経験では、レイヤードシャドウがパフォーマンスに大きな影響を与えることはありませんが、同時に何十、何百と使用したことはありません。
また、レイヤードシャドウをアニメーションさせるのはあまりお勧めできません。
美しいシャドウの実装方法2: 色を合わせたシャドウ
これまでのシャドウはすべて、hsl(0deg 0% 0% / 0.4)のように半透明のブラックを使用していました。これは実は理想的ではありません。
背景色の上にブラックを重ねると、単に暗くなるだけでなく、彩度をかなり下げてしまいます。
下記の2つのボックスを比較してください。
明度だけが異なる2つのボックス
左のボックスは透明なブラックを使用しています。右のボックスは色相と彩度を一致させ、明度を下げています。より鮮やかなボックスができあがりました。
同じような効果は、シャドウに暗い色を使用したときにも起こります。
異なる2つのシャドウ
私の目には、どちらのシャドウもしっくりきません。左は彩度が高すぎますが、右は彩度が足りず、シャドウというよりは光っているように感じられます。
適度なカラーを見つけるには、いくつかの実験が必要です。
いろいろなシャドウを試す
色相を合わせ、彩度・明度を下げることで、色あせたグレーがない本物のシャドウを作ることができます。
彩度と明度の関係
hslカラー形式をご存知の方は、彩度と明度が独立してコントロールされていることを知っているでしょう。では、明度を下げると彩度にも影響があるように見えるのは、少し変だと思いませんか?
例えば、彩度が同じ(100%)なのに、彩度の感じ方が大きく異なる2つのボックスがあるとします。
同じ彩度だけど、彩度の感じ方が異なる2つのボックス
これは、明度の高い値と低い値に色の色素が少ないために起こります。彩度が全体の色にそれほど影響を与えられないのです。
これは、極端な場合に顕著です。
- hsl(0deg 0% 100%)は彩度0%の純白です。
- hsl(0deg 100% 100%)も彩度をフルにしても純白です。
もし明度を95%にすると、違いはありますが、微妙です。
明度95%だと、違いは微妙
同じことが非常に暗い色にも当てはまります。
明度10%でも、違いは微妙
しかし、明度スペクトルを中央にすると、彩度の全範囲が利用可能です。
明度50%にすると、違いは明らかに
私はこう考えています。50%の明度はすべての色相のデフォルトバージョンです。つまり、明度が50%の場合は彩度に影響を与えません。
その50%のスイートスポットから明度を上げたり下げたりすると、色に含まれる顔料の量が減ります。色が完全に飽和して、明るくも暗くもないということはありえません。
彩度の%は、ある明度でどれだけの顔料が使えるかという相対的な指標です。
先ほどのシャドウの例で、彩度を下げなければならなかったのはこのためです。明るさが50%のスイートスポットに近づいたため、より広い範囲の彩度を利用できるようになったのです。鮮やかさを維持するためには、彩度の割合を下げる必要がありました。
ここまでのまとめ
この記事では、3つの異なるアイデアについて解説しました。
- シャドウを調整することで、まとまりのある環境を作る。
- レイヤーを重ねることで、よりリアルなシャドウを作成する。
- シャドウの色がグレーになるのを防ぐための色を調整する。
これらのアイデアを応用した例を紹介します。
デザインシステムへの組み込み
ここで見てきたシャドウは、標高と環境に応じてカスタマイズする必要があります。これは、デザインシステムや有限のデザイントークンがある世界では、逆効果になるかもしれません。このようなシャドウをトークン化できるでしょうか。
絶対にできます!
それには最新ツールの助けが必要です。例えば、React、styled-components、CSS変数を使用して解決する方法は以下の通りです。
静的なELEVATIONSオブジェクトがあり、small, medium, largeの3つの標高を定義しています。それぞれのシャドウの色データはCSS変数の--shadow-colorを使用します。
背景色を(WrapperやBlueWrapperで)変更するたびに、--shadow-colorも変更されます。これで、シャドウを使用する子は自動的にこのプロパティが継承されます。
CSSの変数に慣れていない人には、まるで魔法のように見えるかもしれません。しかし、これはあくまでも例であり、自由に構造を変えてみてください。
参考: CSSの変数(カスタムプロパティ)便利な使い方を詳しく解説
旅を続けるために
私のシャドウの戦略は「見た目が良くなるまで値をいじる」というものだったと書きました。正直に言うと、これはすべてのCSSに対する私のアプローチでした 😅
CSSは暗黙の了解であるため、非常に厄介な言語です。positionやflexやoverflowなどのプロパティについてはすべて学びましたが、スタックコンテクストや仮想サイズやスクロールコンテナなどのプロパティを動かす原理については何も知りませんでした。
CSSでは、プロパティは関数のパラメータのようなものです。レイアウトのアルゴリズムやその他の複雑な内部メカニズムで使用される入力です。
数年前に、私は時間をかけてCSSが実際にどのように機能するかを学ぶことにしました。MDNに入り込み、時には核心に迫ることもありました。そして、どうしても理解できない問題に遭遇したときには、その問題を解決し、何が起こっているのかを理解するまで突き詰めることにしたのです。
これは決して簡単なことではありませんでしたが、非常に効果がありました。そして突然、物事が非常に理にかなっているようになりました。CSSは、深く掘り下げた人が報われる言語です。
1年ほど前、私の経験が他のデベロッパーのためにそのプロセスを促進するのに役立つのではないかと考え始めました。結局のところ、わたし達のほとんどはドキュメントや仕様書を何年もかけて読み解く時間(あるいはエネルギー)を持ち合わせていません。
私はGatsby Inc.のソフトウェアエンジニアとしての仕事を辞め、この1年間に他に類を見ないCSS講座の構築に全力を注いできました。
これはCSS for JavaScript Developersと呼ばれるもので、CSSが実際にどのように機能するかを示す包括的なオンラインの講座です。
200以上のレッスンが10のモジュールに分かれています。このシャドウのデザインのチュートリアルは、このコースから抜粋したものです!
つまり、あなたはすでにそのうちの1つを終えたということです。コースの中には、動画やエクササイズ、ミニゲームなどもあります。
もし、CSSで混乱したり、イライラしているなら、私はそれを変える手助けをしたいと思っています。詳しくは、css-for-js.devをご覧ください。
ボーナス: drop-shadowで実装するシャドウ
この記事では、box-shadowプロパティを使用してきました。box-shadowは非常に優れたツールですが、CSSでのシャドウはこれだけではありません 😮
filter: drop-shadow;を見てみましょう。
box-shadowとdrop-shadowによるシャドウ
構文はほぼ同じに見えますが、生成されるシャドウは異なります。これはfilterプロパティが実際にはSVGフィルタへのCSSフックであるからです。drop-shadowはSVGのガウスぼかしを使用していますが、これはbox-shadowが使用しているものとは異なるぼかしのアルゴリズムです。
この2つには他にも重要な違いがありますが、ここではdrop-shadowの優れた機能である要素の形状に沿った輪郭を描くことに注目したいと思います。
例えば、透明なピクセルと不透明なピクセルがある画像にdrop-shadowの機能を使用すると、不透明なピクセルにのみシャドウが適用されます。
drop-shadowで実装したシャドウ
※box-shadowだと、画像の矩形にシャドウが適用されます。
上記は画像で機能していますが、HTMLの要素でも機能します!
この機能を使用してツールチップにシャドウをつける方法を見てみましょう。
ツールチップの形にシャドウが適用される
ソフトなシャドウを使用しているため、繊細ですが、ブラーの半径を小さくすると輪郭がはっきりします。
もうひとつ、簡単なヒント。
box-shadowとは異なり、filterプロパティはChromeなどのブラウザではハードウェアアクセラレートされます。つまり、CPUではなくGPUで管理されます。その結果、特にアニメーションを行う際のパフォーマンスが大幅に向上します。ただし、Safariのグリッチのバグを避けるため、必ずwill-change: transform;を設定してください。
話が逸れてしまいましたが、filterプロパティは非常に魅力的であるということだけお伝えしておきます。将来的にはもっと詳しく書こうと思っています。もちろん、CSS for JavaScript Developersにも詳しく書かれています。
この記事にインスパイアされて、シャドウを実装したり、微調整したりしていただけたら幸いです。正直なところ、シャドウにここまで気を配っているデベロッパーはほとんどいません。つまり、多くのユーザーは、豊かでリアルなシャドウを見慣れていないということです。わたし達のプロダクトは、シャドウにもう少し力を入れることで、他のプロダクトと差をつけることができます。
sponsors