CSSのbox-shadowで自然な影をつけるコツ|不自然に見える原因と対処
box-shadow は値を4つ並べるだけの単純なプロパティですが、適当に設定すると「なんとなく安っぽい」見た目になります。原因はほぼ決まっているため、いくつかの原則を押さえれば改善できます。
構文の確認
box-shadow: offset-x offset-y blur spread color;
| 値 | 意味 | 効果 |
|---|---|---|
offset-x | 横方向のずれ | 正で右、負で左 |
offset-y | 縦方向のずれ | 正で下、負で上 |
blur | ぼかしの半径 | 大きいほど柔らかい |
spread | 影の拡大・縮小 | 正で大きく、負で小さく |
color | 影の色 | 通常は半透明で指定 |
/* 下方向に4px、8pxぼかし、黒の15% */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
spread は省略できます。実務では省略するか、負の値で使うことが多い値です。
不自然に見える4つの原因
原因1:横方向にずらしている
現実世界の光は、ほとんどの場合「上から」当たります。 天井の照明も太陽も上にあるため、影は真下に落ちます。
box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.2); /* 右下にずれて不自然 */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); /* 真下。自然に見える */
offset-x は原則 0 にしてください。 これだけで印象が大きく変わります。横にずらすのは、意図的に斜光を表現したい場合だけです。
原因2:影が濃すぎる・硬すぎる
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.5); /* 濃くて硬い */
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); /* 薄くて柔らかい */
ぼかしを大きく、透明度を低くするのが基本です。目安として、blur は offset-y の2〜3倍を取ります。
| offset-y | blur の目安 | 不透明度の目安 |
|---|---|---|
| 1〜2px | 3〜6px | 0.05〜0.10 |
| 4px | 8〜12px | 0.10〜0.15 |
| 8px | 16〜24px | 0.12〜0.20 |
「影があることに気づかないが、外すと平坦に見える」くらいが適量です。
原因3:影の色が純粋な黒
これは見落とされがちですが、効果が大きい点です。
現実の影は、周囲の光を反射してわずかに色味を帯びます。 純粋な黒(rgba(0,0,0,…))の影は、背景に色がある場合に濁って見えます。
/* 背景が青みがかった白の場合 */
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12); /* 灰色っぽく濁る */
box-shadow: 0 4px 12px rgba(30, 41, 59, 0.12); /* 背景に馴染む */
影の色は、背景色を暗くした色相を使うと自然になります。適切な色を探すにはカラーコード変換ツールでHSLの明度だけを下げると調整しやすくなります。
原因4:影が1段しかない
現実の影には、接地面近くの濃い影と、遠くまで広がる薄い影の2種類があります。1つのシャドウでは、このどちらかしか表現できません。
box-shadow はカンマ区切りで複数指定できます。
box-shadow:
0 1px 2px rgba(0, 0, 0, 0.08),
0 4px 12px rgba(0, 0, 0, 0.10);
- 1つ目:小さく濃い影 → 接地感を出す
- 2つ目:大きく薄い影 → 浮遊感を出す
この2段構成が、質の高いUIで広く使われている手法です。 1行増えるだけで、見た目の完成度が明確に変わります。
高さ(elevation)で使い分ける
UIでは、要素の重要度=浮いている高さとして影を使い分けます。高さが上がるほど、offset-y と blur を大きく、影を薄く広げます。
| 用途 | 例 |
|---|---|
| ごく浅い(カード) | 0 1px 3px rgba(0,0,0,0.08) |
| 中程度(ドロップダウン) | 0 4px 12px rgba(0,0,0,0.12) |
| 高い(モーダル) | 0 12px 32px rgba(0,0,0,0.16) |
重要なのは、サイト全体で段階を統一することです。3段階程度を決めて、それだけを使い回してください。ページごとに違う影を使うと、統一感が失われます。
CSS変数にしておくと管理が楽になります。
:root {
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.12);
--shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.16);
}
inset:内側の影
inset を付けると、影が内側に描画されます。
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
へこんだ表現に使います。入力欄、押された状態のボタン、進捗バーの溝などが該当します。
spread を負の値にして枠線のように使う応用もあります。
/* レイアウトを崩さない内側の枠線 */
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
border と違って要素のサイズに影響しないため、ホバー時に枠線を出したい場合に便利です。border で同じことをすると、ホバーのたびにレイアウトが1pxずれます。
ダークモードでの注意
暗い背景では、黒い影はほとんど見えません。 ライトモードで作った影をそのままダークモードに適用すると、階層が完全に失われます。
対処法は2つあります。
/* 方法1:影を濃くする */
@media (prefers-color-scheme: dark) {
.card { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); }
}
/* 方法2:背景の明度差で階層を表現する(推奨) */
@media (prefers-color-scheme: dark) {
.card {
background: #1e293b; /* 地の色より明るくする */
box-shadow: none;
}
}
ダークモードでは、影ではなく「背景を明るくする」ことで浮いて見せるのが定石です。 暗い環境では、光が当たる面が明るくなるほうが物理的にも自然です。
パフォーマンス上の注意
box-shadow は描画コストがあるプロパティです。通常は問題になりませんが、次のケースでは注意してください。
- 大きな
blur値を多数の要素に適用する(一覧の全カードなど) - スクロールやアニメーションと同時に影を変化させる
ホバー時に影を変える場合、box-shadow のアニメーションは負荷が高くなることがあります。滑らかさが必要な場面では、transform や opacity の変化と組み合わせるか、影を持つ疑似要素の opacity を変える手法が使われます。
/* 影そのものを animate するより軽い書き方の例 */
.card { transition: transform 0.2s; }
.card:hover { transform: translateY(-2px); }
値を決める手順
数値を頭の中で決めるのは非効率です。視覚的に調整して、決まった値をコピーするのが早道です。
CSSボックスシャドウ生成ツールでは、各値をスライダーで調整しながらリアルタイムでプレビューでき、複数シャドウの重ねがけと inset にも対応しています。プリセットから始めて微調整するのが効率的です。
グラデーションと組み合わせて立体感を出す場合は、CSSグラデーション生成ツールも併用できます。
まとめ
offset-xは 0。 光は上から当たるblurはoffset-yの2〜3倍、不透明度は0.1前後- 影の色は純粋な黒を避け、背景色を暗くした色を使う
- 2段重ねで接地感と浮遊感を両立させる
- サイト全体で3段階程度に統一し、CSS変数で管理する
- ダークモードでは影ではなく背景の明度差で階層を表す
影は「あることに気づかれない」のが正解です。目立つ影は、たいてい強すぎます。