- CSS
CSS box-shadowの使い方|おしゃれな影のコピペ集と値一覧
box-shadow とは、要素の箱(ボックス)の外側または内側に影を付ける CSS プロパティで、box-shadow: 横 縦 ぼかし 広がり 色; の順に値を書きます。 画像を使わずに、カードの立体感、ボタンの押し心地、入力欄のフォーカス表示までを1行で表現できます。
この記事では、5つの値と inset を1つずつ変えたときに影がどこまで広がるかを、Playwright(Chromium 141)でレンダリングして画素単位で測りました。 その結果をもとに、影の強さ5段階を CSS 変数で定義した一式と、用途別のコピペ集をまとめています。ダークモードで影が見えにくくなる理由と対処、ホバーで影を動かすときに描画を軽くする書き方も、測った数値つきで紹介します。
box-shadowの早見表
| やりたいこと | 書き方 |
|---|---|
| 下にふんわりした影 | box-shadow: 0 4px 12px rgb(0 0 0 / 0.15); |
| 全方向に均等な影 | box-shadow: 0 0 12px rgb(0 0 0 / 0.15); |
| ぼかさない影 | box-shadow: 4px 4px 0 #1f2937; |
| 内側の影(くぼみ) | box-shadow: inset 0 2px 4px rgb(0 0 0 / 0.12); |
| 下だけに影 | box-shadow: 0 8px 6px -6px rgb(0 0 0 / 0.3); |
| 枠線の代わり(レイアウトが動かない) | box-shadow: 0 0 0 2px #2563eb; |
| フォーカスリング | box-shadow: 0 0 0 3px rgb(37 99 235 / 0.3); |
| 複数の影を重ねる | box-shadow: 0 1px 2px rgb(0 0 0 / 0.06), 0 2px 6px rgb(0 0 0 / 0.1); |
| 光らせる | box-shadow: 0 0 16px rgb(37 99 235 / 0.6); |
| 影を消す | box-shadow: none; |
| 値の位置 | 意味 | 省略したとき | 負の値 |
|---|---|---|---|
| 1つ目(offset-x) | 横方向のずれ。正で右 | 省略不可 | 左へ |
| 2つ目(offset-y) | 縦方向のずれ。正で下 | 省略不可 | 上へ |
| 3つ目(blur) | ぼかしの大きさ | 0(くっきり) | 指定できない(宣言ごと無効) |
| 4つ目(spread) | 影そのものの拡大・縮小 | 0 | 影が小さくなる |
| 色 | 影の色 | 文字色(currentColor) |
なし |
inset |
影を内側に付ける | 外側 | なし |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「CSS box-shadowの使い方|おしゃれな影のコピペ集と値一覧」
確認した環境
| 項目 | 内容 |
|---|---|
| ブラウザ | Chromium 141.0.7390.37(Playwright から起動) |
| 測り方 | 100×100px の白い箱に影を付けてスクリーンショットを撮り、箱の外側で背景色と違う画素がどこまで続くかを数える |
| 計算値 | getComputedStyle(要素).boxShadow |
数値はこの環境での実測です。ぼかしの端はごく薄くなるため、影の色の濃さによって 1〜数px 前後します。
5つの値とinsetのしくみ
box-shadow: 4px 8px 16px 2px rgb(0 0 0 / 0.2) inset;
│ │ │ │ │ └ 内側に付ける(省略すると外側)
│ │ │ │ └ 色
│ │ │ └ spread:影を全方向に広げる(負で縮める)
│ │ └ blur:ぼかしの大きさ
│ └ offset-y:下へずらす(負で上)
└ offset-x:右へずらす(負で左)
影は、次の順で作られると考えるとわかりやすくなります。
flowchart LR
A[箱と同じ形の影を用意] --> B[spread の分だけ拡大・縮小]
B --> C[offset-x と offset-y の分だけ移動]
C --> D[blur の分だけ端をぼかす]
D --> E[箱と重なる部分は描かない]
最後の「箱と重なる部分は描かない」がポイントです。 外側の影は箱の裏に敷かれるのではなく、箱の外にはみ出した部分だけが描かれます。背景が半透明の箱でも、箱の内側に影が透けて見えることはありません。
値ごとの影の範囲(実測)
100×100px の箱に rgba(0,0,0,.3) の影を付け、箱の上下左右にそれぞれ何px 影が出ているかを測りました。
| box-shadow の値 | 上 | 右 | 下 | 左 | 見た目 |
|---|---|---|---|---|---|
4px 4px 0 0 |
0 | 4 | 4 | 0 | 右下にくっきりした影 |
0 4px 0 0 |
0 | 0 | 4 | 0 | 下にだけくっきりした影 |
0 0 10px 0 |
11 | 11 | 11 | 11 | 全方向にふんわり |
0 0 20px 0 |
23 | 23 | 23 | 23 | さらに広くふんわり |
0 0 0 4px |
4 | 4 | 4 | 4 | 4px の枠線のような見た目 |
0 0 10px 4px |
15 | 15 | 15 | 15 | 広がり+ぼかし |
0 8px 16px 0 |
10 | 18 | 26 | 18 | 下が厚い、よく使う影 |
0 8px 16px -4px |
6 | 14 | 22 | 14 | 上と同じ形で、全体が 4px 細い |
0 8px 6px -6px |
0 | 1 | 9 | 1 | ほぼ下だけ |
この表から、影の届く範囲は次の式でほぼ決まることがわかります。
影が届く距離(その方向) = おおよそ offset + spread + blur
たとえば 0 8px 16px 0 の下側は 8 + 0 + 16 = 24 に対して実測 26、上側は −8 + 0 + 16 = 8 に対して実測 10 でした(ぼかしの端のごく薄い部分が少し外まで続きます)。blur は「影の端から外へ広がるぼかしの幅」とほぼ同じと覚えておくと、値を決めやすくなります。
blur と spread の違い
| 値 | すること | 影の濃さ | 使いどころ |
|---|---|---|---|
| blur | 端をぼかす。大きいほどやわらかい | 外へ行くほど薄くなる | ふんわりした立体感 |
| spread(正) | 影をそのまま大きくする | 均一 | 枠線・フォーカスリング |
| spread(負) | 影をそのまま小さくする | 均一 | 片側だけの影、控えめな影 |
0 0 0 4px(blur なし・spread だけ)は、箱の外 3px の位置の画素が RGB(178,178,178) で均一でした。0 0 10px 0(blur だけ)は同じ位置が RGB(231,231,231) と薄く、外へ行くほどさらに薄くなります。
色を省略すると文字色になる
#d { box-shadow: 0 4px 12px; } /* 色なし */
#e { color: red; box-shadow: 2px 2px; } /* 色なし・文字色が赤 */
#g { box-shadow: 0 0 -5px red; } /* blur が負 */
計算値(getComputedStyle)です。
#d rgb(0, 0, 0) 0px 4px 12px 0px
#e rgb(255, 0, 0) 2px 2px 0px 0px
#g none
色を省略すると、その要素の文字色(currentColor)で影が付きます。 文字が黒なら不透明の黒い影になり、かなり強く見えます。影の色は rgb(0 0 0 / 0.15) のように透明度つきで明示するのがおすすめです。blur に負の値を書くと宣言全体が無効になり、影は付きません。
影はレイアウトに影響しない
#i { width: 100px; height: 100px; box-shadow: 0 0 0 50px red; }
offsetWidth: 100 / getBoundingClientRect().width: 100
50px の影を付けても、要素の大きさは 100px のままでした。影は周りの要素を押しのけず、スクロール幅にも数えられません。 隣の要素と重なる場合は、影のぶんだけ margin を取っておきます。枠線(border)を付けるとサイズが変わる場面で、代わりに 0 0 0 2px 色 を使えるのもこの性質のおかげです。
insetで内側に影を付ける
inset を付けると、影は箱の内側(padding 領域の縁の内側)に描かれます。方向の考え方も内側向きになります。
| 値 | 上の縁 | 縁から 6px 内側 | 中央 | 下の縁 | 見た目 |
|---|---|---|---|---|---|
inset 0 0 10px rgba(0,0,0,.4) |
217 | 247 | 255 | 217 | 四辺の内側がふんわり暗い |
inset 0 4px 0 0 rgba(0,0,0,.4) |
153 | 255 | 255 | 255 | 上の縁だけ 4px の帯 |
inset 0 0 0 4px rgba(0,0,0,.4) |
153 | 255 | 255 | 153 | 内側に 4px の枠線 |
(数値は白い箱の画素の明るさ。255 が白で、小さいほど暗い。箱の外側はどれも 255 のまま)
inset の offset-y を正にすると、上の縁に影が出ます。 光が上から当たって、くぼみの上側が陰になるイメージです。入力欄やトグルの溝など、へこんだ表現に使います。
複数の影は「先に書いたものが上」
カンマで区切ると、影をいくつでも重ねられます。
.a { box-shadow: 0 0 0 6px red, 0 0 0 12px blue; }
.b { box-shadow: 0 0 0 12px blue, 0 0 0 6px red; }
| 書き方 | 箱の外 3px の色 | 箱の外 9px の色 |
|---|---|---|
.a(赤 6px → 青 12px の順) |
赤 | 青 |
.b(青 12px → 赤 6px の順) |
青 | 青 |
先に書いた影が手前に描かれます。 .b では、先に書いた青の 12px が赤を覆ってしまい、赤は見えません。小さい影を先、大きい影を後に書くのが基本です。
影の強さ5段階をCSS変数で定義する
影を画面ごとにばらばらの値で書くと、あとで調整するときに探して回ることになります。「高さ(どれくらい浮いているか)」を5段階の変数で決めておき、部品はその変数を使う形にすると、全体の統一感が出て、ダークモードへの対応も1か所で済みます。
/* ===== 影の変数(ライトモード) ===== */
:root {
--shadow-color: 220 30% 10%; /* 影の色(H S L)。真っ黒より少し青みを入れる */
--shadow-k: 1; /* 濃さの倍率。ダークモードで上げる */
--surface: #ffffff; /* カードなどの面の色 */
--page: #f5f6f8; /* ページの背景色 */
--ring: transparent; /* 面の輪郭線。ダークモードで使う */
--shadow-1: 0 1px 2px hsl(var(--shadow-color) / calc(0.10 * var(--shadow-k)));
--shadow-2: 0 1px 2px hsl(var(--shadow-color) / calc(0.06 * var(--shadow-k))),
0 2px 6px hsl(var(--shadow-color) / calc(0.10 * var(--shadow-k)));
--shadow-3: 0 2px 4px hsl(var(--shadow-color) / calc(0.06 * var(--shadow-k))),
0 6px 16px hsl(var(--shadow-color) / calc(0.12 * var(--shadow-k)));
--shadow-4: 0 4px 8px hsl(var(--shadow-color) / calc(0.06 * var(--shadow-k))),
0 12px 28px hsl(var(--shadow-color) / calc(0.16 * var(--shadow-k)));
--shadow-5: 0 8px 16px hsl(var(--shadow-color) / calc(0.08 * var(--shadow-k))),
0 24px 56px hsl(var(--shadow-color) / calc(0.22 * var(--shadow-k)));
}
/* ===== ダークモード ===== */
@media (prefers-color-scheme: dark) {
:root {
--shadow-color: 0 0% 0%;
--shadow-k: 4;
--surface: #23262d;
--page: #121417;
--ring: rgb(255 255 255 / 0.10);
}
}
body { background: var(--page); }
| 変数 | 高さのイメージ | 向いている部品 |
|---|---|---|
--shadow-1 |
面にほぼ接している | 入力欄、一覧の行、押し込んだボタン |
--shadow-2 |
少し浮いている | カード、ボタン |
--shadow-3 |
はっきり浮いている | ホバー中のカード、ドロップダウン |
--shadow-4 |
手前に出ている | ポップオーバー、固定のフローティングボタン |
--shadow-5 |
画面の最前面 | モーダル、ダイアログ |
使うときは box-shadow: var(--shadow-2); と書くだけです。ライトモードとダークモードでの計算値を確認しました。
=== light
--shadow-1 rgba(18, 23, 33, 0.1) 0px 1px 2px 0px
--shadow-2 rgba(18, 23, 33, 0.06) 0px 1px 2px 0px, rgba(18, 23, 33, 0.1) 0px 2px 6px 0px
--shadow-3 rgba(18, 23, 33, 0.06) 0px 2px 4px 0px, rgba(18, 23, 33, 0.12) 0px 6px 16px 0px
--shadow-4 rgba(18, 23, 33, 0.06) 0px 4px 8px 0px, rgba(18, 23, 33, 0.16) 0px 12px 28px 0px
--shadow-5 rgba(18, 23, 33, 0.08) 0px 8px 16px 0px, rgba(18, 23, 33, 0.22) 0px 24px 56px 0px
=== dark
--shadow-1 rgba(0, 0, 0, 0.4) 0px 1px 2px 0px
--shadow-2 rgba(0, 0, 0, 0.24) 0px 1px 2px 0px, rgba(0, 0, 0, 0.4) 0px 2px 6px 0px
--shadow-3 rgba(0, 0, 0, 0.24) 0px 2px 4px 0px, rgba(0, 0, 0, 0.48) 0px 6px 16px 0px
--shadow-4 rgba(0, 0, 0, 0.24) 0px 4px 8px 0px, rgba(0, 0, 0, 0.64) 0px 12px 28px 0px
--shadow-5 rgba(0, 0, 0, 0.32) 0px 8px 16px 0px, rgba(0, 0, 0, 0.88) 0px 24px 56px 0px
この一式の設計の考え方
| 工夫 | 理由 |
|---|---|
| 各段階を「近くて濃い影」+「遠くて薄い影」の2枚にする | 物の影は、接地面の近くは濃く小さく、離れるほど薄く大きい。2枚重ねると自然に見える |
| 段階が上がるほど offset-y と blur を大きく、ほぼ2倍ずつにする | 「高く浮くほど影は遠く・大きく・やわらかく」なり、段階の差が目で見てわかる |
| blur を offset-y の2〜3倍にする | ぼかしが小さいと輪郭が硬く、大きすぎると影がぼやけて見えなくなる |
| 色を真っ黒にせず、背景に近い色相の暗い色にする | 純粋な黒の影は、色のついた背景の上でくすんで見える |
濃さを --shadow-k で一括調整できるようにする |
ダークモードや、背景色の違うセクションで倍率だけ変えればよい |
hsl(var(--shadow-color) / calc(0.10 * var(--shadow-k))) のように、色の成分と透明度を別々の変数にしておくのがコツです。CSS 変数そのものの書き方やフォールバックは、CSS 変数の記事で詳しく扱っています。
用途別のbox-shadowコピペ集
以下は、上の変数一式を読み込んだうえで使う部品です。変数を使わない場合は、var(--shadow-2) の部分を早見表の値に置き換えてください。すべて Chromium 141 で計算値を確認しています。
カードに影を付ける
.card {
background: var(--surface);
border-radius: 12px;
padding: 20px;
box-shadow: 0 0 0 1px var(--ring), var(--shadow-2);
}
先頭の 0 0 0 1px var(--ring) は、ダークモードでだけ見える細い輪郭線です。ライトモードでは --ring が透明なので影響しません。
ボタンに影を付ける(押すと沈む)
.btn {
border: 0;
border-radius: 8px;
padding: 10px 20px;
background: #2563eb;
color: #fff;
box-shadow: var(--shadow-2);
transition: transform 0.15s, box-shadow 0.15s;
}
.btn:active {
transform: translateY(1px);
box-shadow: var(--shadow-1);
}
押したときに影を1段階下げ、1px 下へ動かすと、「押し込んだ」手応えが出ます。
入力欄のフォーカスリング
.input {
border: 1px solid #c5cad3;
border-radius: 8px;
padding: 8px 12px;
outline: 2px solid transparent; /* 強制カラーモード用 */
}
.input:focus-visible {
border-color: #2563eb;
box-shadow: 0 0 0 3px rgb(37 99 235 / 0.3);
}
キーボードでフォーカスを当てたときの計算値です。
input focus-visible: rgba(37, 99, 235, 0.3) 0px 0px 0px 3px matches=true
spread だけの影は、角丸に沿った輪になり、レイアウトも動かしません。 Windows のハイコントラスト表示(強制カラーモード)では box-shadow が描かれないため、透明の outline を残しておきます。Playwright で forced-colors: active を再現して計算値を比べました。
通常 : box-shadow rgba(37, 99, 235, 0.3) 0px 0px 0px 3px / outline-color rgba(0, 0, 0, 0)
強制カラーモード : box-shadow none / outline-color rgb(0, 0, 0)
強制カラーモードでは影が none になり、透明だった outline がシステムの色に置き換わって見えるようになります。
モーダルの影
.modal {
background: var(--surface);
border-radius: 16px;
padding: 24px;
box-shadow: 0 0 0 1px var(--ring), var(--shadow-5);
}
モーダルは画面の最前面なので、最も強い --shadow-5 を使います。背景を暗くする幕(dialog::backdrop など)と組み合わせると、影が弱めでも手前にあることが伝わります。
浮き上がるホバー
.lift {
position: relative;
background: var(--surface);
border-radius: 12px;
padding: 20px;
box-shadow: 0 0 0 1px var(--ring), var(--shadow-1);
transition: transform 0.25s ease;
}
.lift::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
box-shadow: var(--shadow-4);
opacity: 0;
transition: opacity 0.25s ease;
pointer-events: none;
}
@media (hover: hover) {
.lift:hover { transform: translateY(-4px); }
.lift:hover::after { opacity: 1; }
}
.lift:focus-visible::after { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
.lift, .lift::after { transition: none; }
.lift:hover { transform: none; }
}
状態ごとの計算値です。
通常 : transform none ::after の opacity 0
ホバー : matrix(1, 0, 0, 1, 0, -4) ::after の opacity 1
ホバー(動きを減らす設定) : transform none ::after の opacity 1
タッチ端末でタップ後 : transform none ::after の opacity 0
影を直接変えずに、「強い影を持った疑似要素を重ねておき、その透明度だけを変える」書き方です。理由は後半の「影を軽く動かす書き方」で数値つきで説明します。@media (hover: hover) で囲んであるので、タッチ端末でタップしたあとに浮いたままになることもありません。
重ね影(なめらかな影)
.layered {
background: var(--surface);
border-radius: 12px;
padding: 20px;
box-shadow:
0 1px 1px hsl(var(--shadow-color) / calc(0.05 * var(--shadow-k))),
0 2px 2px hsl(var(--shadow-color) / calc(0.05 * var(--shadow-k))),
0 4px 4px hsl(var(--shadow-color) / calc(0.05 * var(--shadow-k))),
0 8px 8px hsl(var(--shadow-color) / calc(0.05 * var(--shadow-k))),
0 16px 16px hsl(var(--shadow-color) / calc(0.05 * var(--shadow-k)));
}
offset-y と blur を 1・2・4・8・16 と倍にしながら、薄い影を5枚重ねます。箱の近くは5枚ぶん重なって濃く、遠くは1枚だけになるので、1枚の影より階調がなめらかになります。
ニューモーフィズム
.neu-wrap { background: #e6e9ef; padding: 40px; }
.neu {
background: #e6e9ef; /* 背景と同じ色 */
border-radius: 16px;
padding: 20px;
box-shadow: 8px 8px 16px #c3c6cb, -8px -8px 16px #ffffff;
}
.neu-pressed {
background: #e6e9ef;
border-radius: 16px;
padding: 20px;
box-shadow: inset 6px 6px 12px #c3c6cb, inset -6px -6px 12px #ffffff;
}
背景と同じ色の面に、右下の暗い影と左上の明るい影を1組で付けると、面が盛り上がって見えます。inset に変えると、へこんだ表現になります。色の決め方は「背景色を基準に、明度を 12〜15% 下げた色と、白に近づけた色」です。面と背景の境目が影だけで表されるため、ボタンとして使う場合は文字やアイコンの色でしっかり差を付けておくと、押せる場所が伝わりやすくなります。
内側の影(くぼみ)
.inset {
background: #eef0f4;
border-radius: 8px;
padding: 12px;
box-shadow: inset 0 2px 4px rgb(0 0 0 / 0.12);
}
片側だけに影を付ける
.shadow-bottom { box-shadow: 0 8px 6px -6px rgb(0 0 0 / 0.3); }
.shadow-top { box-shadow: 0 -8px 6px -6px rgb(0 0 0 / 0.3); }
.shadow-right { box-shadow: 8px 0 6px -6px rgb(0 0 0 / 0.3); }
.shadow-left { box-shadow: -8px 0 6px -6px rgb(0 0 0 / 0.3); }
実測した影の範囲です。
| クラス | 上 | 右 | 下 | 左 |
|---|---|---|---|---|
.shadow-bottom(0 8px 6px -6px) |
0 | 1 | 9 | 1 |
.shadow-right(8px 0 6px -6px) |
1 | 9 | 1 | 0 |
.shadow-top(0 -8px 6px -6px) |
9 | 1 | 0 | 1 |
コツは「spread を blur と同じ大きさの負の値にする」ことです。 blur で広がる分を spread で打ち消し、offset でずらした方向にだけ影が残ります。左右に 1px だけ残るのは、ぼかしの端のごく薄い部分です。
そのほかの影
/* 固定ヘッダーの下だけ */
.header {
position: sticky;
top: 0;
background: var(--surface);
box-shadow: 0 1px 0 var(--ring),
0 4px 8px -4px hsl(var(--shadow-color) / calc(0.2 * var(--shadow-k)));
}
/* ぼかさない影(ポップな見た目) */
.hard {
background: #fff;
border: 2px solid #1f2937;
border-radius: 8px;
box-shadow: 4px 4px 0 #1f2937;
}
/* 光る影(グロー) */
.glow {
background: #2563eb;
color: #fff;
border-radius: 8px;
box-shadow: 0 0 16px rgb(37 99 235 / 0.6);
}
/* レイアウトを動かさない枠線 */
.outline-ring { border-radius: 8px; box-shadow: 0 0 0 2px #2563eb; }
/* 二重の枠線(白のすき間+色の輪) */
.outline-double { border-radius: 8px; box-shadow: 0 0 0 2px #fff, 0 0 0 4px #2563eb; }
drop-shadow・text-shadowとの違い
影を付ける方法は3つあり、影が付く対象が違います。
| 項目 | box-shadow |
filter: drop-shadow() |
text-shadow |
|---|---|---|---|
| 影の形 | 要素の箱(border-radius には沿う) | 描かれた内容の形(透明部分を除く) | 文字の形 |
| 値 | x y blur spread 色 inset | x y blur 色 | x y blur 色 |
| spread | 使える | 使えない | 使えない |
| inset | 使える | 使えない | 使えない |
| 複数指定 | カンマ区切り | drop-shadow() を並べる |
カンマ区切り |
| 向いている対象 | カード、ボタン、入力欄 | 透過 PNG、SVG アイコン、三角形や吹き出し、clip-path で切り抜いた形 |
見出し、写真の上の文字 |
三角形で比べる
border で作った三角形(幅・高さ 100px)に、赤い影を右下に 6px ずらして付けました。
| 書き方 | 箱の右外側 | 斜辺のすぐ横 | 下側 |
|---|---|---|---|
box-shadow: 6px 6px 0 red |
赤 | 白 | 赤 |
filter: drop-shadow(6px 6px 0 red) |
白 | 赤 | 赤 |
box-shadow は三角形ではなく、それを囲む四角い箱に影を付けます。drop-shadow は実際に描かれた三角形の形に沿います。 吹き出しのしっぽや、背景が透明なロゴ画像には drop-shadow が合います。
spread と blur の書き方の違い
#a { filter: drop-shadow(0 0 4px 2px red); } /* 4つ目の値(spread)を書いた */
#c { text-shadow: 1px 1px 2px 3px red; }
#d { filter: drop-shadow(2px 4px 6px rgba(0,0,0,.3)); }
#a filter: none
#c text-shadow: none
#d filter: drop-shadow(rgba(0, 0, 0, 0.3) 2px 4px 6px)
drop-shadow と text-shadow に4つ目の長さを書くと、宣言ごと無効になります。
もう1つ、同じ blur の数値でも、drop-shadow のほうが約2倍広くぼけます。
| 書き方 | 影が届いた距離(下側) | 箱の外 5px の明るさ |
|---|---|---|
box-shadow: 0 0 10px black |
15px | 205 |
filter: drop-shadow(0 0 10px black) |
24px | 172 |
filter: drop-shadow(0 0 5px black) |
11px | 213 |
box-shadow の blur は「ぼかしの幅」ですが、drop-shadow の blur は、ぼかし処理の標準偏差として使われるためです。box-shadow の値を drop-shadow に書き換えるときは、blur を半分にすると近い見た目になります。
ダークモードで影が見えにくくなる理由と対処
同じ影を、白い背景と暗い背景(#121212)に置いて、箱のすぐ下の画素が背景とどれくらい違うかを測りました。
| 背景 | 影 | 背景との差(箱の 1px 下) | 背景との差(6px 下) |
|---|---|---|---|
#ffffff |
0 4px 12px rgba(0,0,0,.15) |
27 | 13 |
#121212 |
0 4px 12px rgba(0,0,0,.15) |
2 | 1 |
#121212 |
0 4px 12px rgba(0,0,0,.6) |
8 | 4 |
#121212 |
0 4px 12px rgba(0,0,0,1) |
13 | 7 |
#121212 |
1px の明るい輪郭線+ rgba(0,0,0,.6) |
21 | 4 |
(差は RGB 各 0〜255 のうち何段階ちがうか)
影は「背景を暗くする」表現なので、もともと暗い背景では、暗くできる余地がほとんどありません。 白い背景で 27 段階あった差が、暗い背景では 2 段階になりました。影を真っ黒(不透明)にしても 13 段階です。
そこで、ダークモードでは影だけに頼らず、次の3つを組み合わせます。
| 対処 | 内容 | 変数での書き方 |
|---|---|---|
| 1. 面を明るくする | 高い位置の面ほど、背景より明るい色にする | --surface: #23262d;(背景 #121417 より明るく) |
| 2. 細い輪郭線を足す | 半透明の白で 1px の線を付ける | --ring: rgb(255 255 255 / 0.10); |
| 3. 影を濃くする | 透明度を 3〜4 倍にし、色は黒にする | --shadow-k: 4; --shadow-color: 0 0% 0%; |
表の最後の行のとおり、1px の明るい輪郭線を足すだけで、境目の差は 8 から 21 に上がります。 先に紹介した変数一式は、この3つをまとめて切り替える形になっています。
light card: rgba(0, 0, 0, 0) 0px 0px 0px 1px, rgba(18, 23, 33, 0.06) 0px 1px 2px 0px, ... | 背景 rgb(255, 255, 255)
dark card: rgba(255, 255, 255, 0.1) 0px 0px 0px 1px, rgba(0, 0, 0, 0.24) 0px 1px 2px 0px, ... | 背景 rgb(35, 38, 45)
サイト側の切り替えボタンでテーマを変える場合は、@media (prefers-color-scheme: dark) の中身を [data-theme="dark"] のようなセレクタにも書いておくと、同じ変数で切り替えられます。
影を軽く動かす書き方(疑似要素+opacity)
ホバーで影を強くするとき、まず思いつくのは transition: box-shadow です。
/* A:影を直接変える */
.card { box-shadow: 0 1px 3px rgb(0 0 0 / 0.12); transition: box-shadow 0.3s; }
.card:hover { box-shadow: 0 12px 32px rgb(0 0 0 / 0.2); }
/* B:強い影を持つ疑似要素の透明度だけを変える */
.card { position: relative; box-shadow: 0 1px 3px rgb(0 0 0 / 0.12); }
.card::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
box-shadow: 0 12px 32px rgb(0 0 0 / 0.2);
opacity: 0;
transition: opacity 0.3s;
pointer-events: none;
}
.card:hover::after { opacity: 1; }
48 枚のカードを同時に変化させ、0.3 秒のアニメーション中にブラウザが行った処理を Chromium のトレース機能で数えました(2回測って同じ傾向)。
| 書き方 | Paint(描画命令の作り直し)回数 | ラスタライズの合計時間 | スタイル再計算の回数 |
|---|---|---|---|
A:transition: box-shadow |
882 | 約 140〜149 ms | 19〜20 |
B:疑似要素+ opacity |
51 | 約 23〜29 ms | 6 |
C:B に transform の移動を追加 |
99 | 約 23 ms | 6 |
box-shadow の値を変えると、ブラウザはコマごとに影を描き直します。opacity と transform は、一度描いた絵を合成の段階で重ね直すだけで済むため、描き直しの回数が大きく減ります。 この測定では、Paint の回数が 882 回から 51 回になりました。
| 場面 | おすすめ |
|---|---|
| カード一覧など、同じ部品がたくさん並ぶ | B(疑似要素+opacity) |
| 影と一緒に少し浮かせたい | C(transform も足す) |
| ボタン1つなど、対象が少なく変化も小さい | A でも十分 |
疑似要素を使うときの注意点は3つです。
- 親に
position: relativeを付ける(疑似要素の基準にする) border-radius: inheritで角丸をそろえるpointer-events: noneで、疑似要素がクリックを受け取らないようにする
transition と @keyframes の使い分けや、動きを減らす設定への対応は、CSS アニメーションの記事で扱っています。
影が切れる・角が合わないときの直し方
overflow: hidden で影が切れる
親要素に overflow: hidden があると、子の影は親の箱の外に出た部分が描かれません。子に 8px の赤い影を付けて、親の overflow を変えて確かめました。
| 親の指定 | 子の影(箱の 4px 下) |
|---|---|
overflow: visible(初期値) |
見える |
overflow: hidden |
切れる |
overflow: clip |
切れる |
overflow: auto |
切れる |
overflow: hidden + padding: 12px; margin: -12px; |
見える |
overflow: clip + overflow-clip-margin: 12px |
見える |
直し方は次の3つです。
/* 1. 親に、影のぶんの余白を持たせる(位置がずれないよう同じだけ負の margin) */
.parent { overflow: hidden; padding: 12px; margin: -12px; }
/* 2. 切り抜きの範囲を外へ広げる */
.parent { overflow: clip; overflow-clip-margin: 12px; }
/* 3. 影は外側の要素に、切り抜きは内側の要素に分ける */
.card { border-radius: 12px; box-shadow: var(--shadow-2); }
.card__inner { border-radius: inherit; overflow: hidden; }
角丸のカードの中に画像を入れる場合は、3 の形がいちばん扱いやすくなります。 カード自身に overflow: hidden を付けても、カード自身の影は切れません(切れるのは子孫の影です)が、横スクロールのカルーセルなど、スクロール用の箱の中にカードを並べる場合は 1 の余白が必要です。overflow-clip-margin は overflow: clip との組み合わせで効くプロパティで、対応状況は MDN で確認してください。
border-radius と影の形
影は border-radius に沿って丸くなります。border-radius: 50% の箱に 0 0 0 4px red を付けたところ、箱の角の外側は白のまま、上辺の中央の外側は赤でした。四角い箱では角の外側も赤です。
| 状況 | 起きること | 直し方 |
|---|---|---|
| 疑似要素に影を付けた | 疑似要素の角が四角く、影の角も四角くなる | 疑似要素に border-radius: inherit |
画像(img)に影を付けた |
影は画像の四角い箱に付く | img 自身に border-radius を付ける。透過画像の形に沿わせたいなら filter: drop-shadow() |
clip-path で切り抜いた要素 |
影も一緒に切り抜かれて見えなくなる | 親要素に filter: drop-shadow() を付ける |
そのほかの確認ポイント
| 症状 | 確認すること |
|---|---|
| 影が出ない | 色を省略していて文字色が透明・背景と同色になっていないか。blur が負になっていないか |
| 影が隣の要素の下に隠れる | 後ろに描かれた要素が上に重なっている。position: relative と z-index で前に出す |
| 影が強すぎる | 色に透明度を付ける(0.1〜0.2 が目安)。黒ではなく背景に近い暗い色にする |
| 印刷で影が出ない | ブラウザの印刷設定で背景のグラフィックが省かれている。印刷用には border を用意する |
重なり順が思いどおりにならないときの調べ方は、z-index の記事で扱っています。
よくある質問
Q. box-shadow の値は何個まで省略できますか?
A. 必須なのは offset-x と offset-y の2つです。box-shadow: 2px 2px; と書くと、blur と spread は 0、色は文字色になります。長さを3つ書くと3つ目が blur、4つ書くと4つ目が spread です。色は透明度つきで明示しておくと、文字色を変えたときに影の色が一緒に変わることを防げます。
Q. 影を下だけに付けるにはどう書きますか?
A. box-shadow: 0 8px 6px -6px rgb(0 0 0 / 0.3); のように、spread を blur と同じ大きさの負の値にします。実測では、下に 9px、左右に 1px、上は 0px でした。offset-y を負にすれば上だけ、offset-x に入れれば左右どちらかだけにできます。
Q. box-shadow と drop-shadow はどう使い分けますか?
A. 四角い箱(角丸を含む)に影を付けるなら box-shadow、透過画像や SVG、三角形など「描かれた形」に沿わせたいなら filter: drop-shadow() です。drop-shadow には spread と inset がなく、同じ blur の数値で約2倍ぼけるので、書き換えるときは blur を半分にします。
Q. ダークモードで影が見えません。どうすればよいですか?
A. 暗い背景では影との差が出にくいので、面の色を背景より明るくし、半透明の白で 1px の輪郭線(box-shadow: 0 0 0 1px rgb(255 255 255 / 0.1))を足し、影の透明度を 3〜4 倍にします。影の値を CSS 変数にしておけば、prefers-color-scheme: dark の中で変数を上書きするだけで切り替えられます。
まとめ
- box-shadow は
横 縦 ぼかし 広がり 色の順。 影が届く距離は、おおよそ offset+spread+blur で決まる - 色を省略すると文字色になる。
rgb(0 0 0 / 0.15)のように透明度つきで明示する - 影の強さは5段階の CSS 変数にまとめる。 各段階を「近くて濃い影」と「遠くて薄い影」の2枚重ねにすると自然に見える
- 片側だけの影は、spread を blur と同じ大きさの負の値にする。 枠線の代わりには
0 0 0 2px 色 - 描かれた形に沿わせたいときは
filter: drop-shadow()。 blur は box-shadow の半分が目安 - ダークモードでは、面を明るく・1px の輪郭線・影を濃く、の3つを組み合わせる
- 影を動かすときは、疑似要素に強い影を持たせて opacity を変える。 測定では Paint が 882 回から 51 回に減った
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「CSS box-shadowの使い方|おしゃれな影のコピペ集と値一覧」
株式会社RJCでは、CSSの影の付け方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。