- CSS
CSSのopacityとは|子要素も透ける理由とrgbaとの違いを画素値で検証
結論
CSSの opacity は、要素を中身ごと丸ごと透明にするプロパティで、0(見えない)〜1(そのまま)の数値か0%〜100%で指定します。子要素も一緒に透け、子に opacity: 1 を書いても戻りません。背景だけ透かしたいときは rgba などの色の透明度を使います。
この記事でわかること
- opacity・rgba・transparent・visibility・display:none の違いを画素値と computed style で比べた表
- 子要素を透けさせない3つの直し方と、z-index が効かなくなる理由
- クリック・Tab 移動・アニメーションの再描画回数・コントラスト比の実測
opacity は「この要素を描いた絵を、何割の濃さで背景に重ねるか」を決めます。要素の背景・文字・枠線・子要素をまとめて1枚の絵にしてから重ねるので、子要素だけ濃さを戻すことはできません。
flowchart TD
A[親と子を描く] --> B[1枚の絵にする]
B --> C{opacity}
C -->|1| D[そのまま]
C -->|0.5| E[半分]
C -->|0| F[見えない]
よく調べられることの答えを先にまとめます。
| 知りたいこと | 答え | 詳しい節 |
|---|---|---|
| opacity とは | 要素全体の不透明度。初期値1、継承されない | CSSのopacityとは |
| css opacity の書き方 | opacity: 0.5; か opacity: 50%;。範囲外は0か1に丸められる |
opacityの値の書き方 |
| opacity と rgba の違い | opacity は中身ごと、rgba はその色だけが透ける | opacityとrgbaの違い |
| opacity で子要素も透ける | 仕様どおりの動き。子に opacity:1 を書いても戻らない | opacityで子要素も透ける理由 |
| 子要素を透けさせない方法 | 背景を rgba にする・::before に背景を移す・兄弟要素に分ける | 子要素を透けさせない方法 |
| opacity:0 と visibility:hidden の違い | opacity:0 はクリックも Tab 移動もできる | opacity:0・visibility・displayの違い |
| opacity で z-index が効かない | 1未満で重ね合わせコンテキストができるため | opacityでz-indexが効かない理由 |
| opacity のアニメーションは重いか | 手元の計測では再描画が2回だけ。背景色の変化は242回 | opacityアニメーションの性能 |
確認環境:Ubuntu 24.04.5 LTS の上で、Python 3.13.18 の http.server を 127.0.0.1 のポートに起動して自作の検証ページを置き、Playwright 1.63.0(Python 版)から Chromium 153.0.8010.12(ヘッドレス)で開きました。値は getComputedStyle、見た目はスクリーンショットを Pillow 12.3.0 で読み、指定した座標の画素値(RGB)で確かめています。クリックは Playwright のマウス操作と document.elementFromPoint、Tab 移動はキー入力、読み上げの対象は Playwright のアクセシビリティのスナップショット(aria_snapshot)、アニメーションの再描画は Chromium のトレース(devtools.timeline)の件数で数えました。コントラスト比は WCAG 2.2 の相対輝度の式を Python で計算しています。仕様は MDN(CSS Color Module Level 4 を出典とする解説)と W3C の WCAG 2.2、Safari・Firefox の挙動は MDN の互換性の記述の範囲で確かめました。図は mermaid 11.16.0 で描画を確かめています(確認日 2026年10月29日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「CSSのopacityとは|子要素も透ける理由とrgbaとの違いを画素値で検証」
CSSのopacityとは(意味と基本の書き方)
この節では、opacity が何を決めるプロパティなのかと、最小の書き方が分かります。
opacity は「不透明度」を指定するプロパティで、値が小さいほど要素の向こう側が透けて見えます。 MDN は「不透明度とは、要素の背後にあるコンテンツが隠される度合い」で、透明度の反対だと説明しています。opacity は英語で「不透明さ」という意味で、値1が「完全に不透明(そのまま)」、値0が「完全に透明(見えない)」です。透明度(どれだけ透けるか)で考えると逆になるので、ここで迷う人が多くいます。
.card {
opacity: 0.5; /* 半透明(50%の濃さ) */
}
.card.disabled {
opacity: 40%; /* パーセントでも書ける(0.4 と同じ) */
}
| 項目 | 内容(MDN の公式定義) |
|---|---|
| 初期値 | 1(完全に不透明) |
| 適用対象 | すべての要素(SVG の要素にも使える) |
| 継承 | なし(ただし子要素も一緒に透けて見える) |
| 値 | 0〜1 の数値、または 0%〜100% |
| 計算値 | 指定値を 0〜1 の範囲に切り詰めたもの |
| アニメーション | できる(計算値の型による補間) |
| 対応ブラウザ | Baseline「Widely available」。2015年7月から主要ブラウザで使える |
opacity: 1
初期値。そのまま表示される。
書かなくても1
opacity: 0.5
半分の濃さで背景と混ざる。文字も子要素も一緒に薄くなる。
50% と同じ
opacity: 0
見えないが、場所は取ったまま。クリックもフォーカスもできる。
隠す目的には使わない
1未満すべて
新しい重ね合わせコンテキストができ、z-index の効き方が変わる。
0.99 でも同じ
CSS そのものの書き方や、効かないときの基本の調べ方は、関連記事「CSSとは|HTMLとの違い・書き方・効かない原因を実例で解説」で扱っています。
opacityの値の書き方と範囲外の値(getComputedStyleの実測)
この節では、数値・パーセント・範囲外の値・単位付きの値を書いたとき、ブラウザがどの値として扱うかが分かります。
0〜1 の外の値は誤りにはならず、近いほうの端(0か1)に切り詰められます。 一方で 0.5px のように単位を付けると、その宣言ごと無視されて初期値の1のままになります。いろいろな書き方をして、Chromium の getComputedStyle(要素).opacity を読みました。
| 書いた値 | 計算値(実測) | CSS.supports | 読み取れること |
|---|---|---|---|
opacity: .5 |
0.5 | true | 先頭の0は省ける |
opacity: 50% |
0.5 | true | パーセントは数値に直される |
opacity: 1.5 |
1 | true | 1に切り詰め |
opacity: -0.5 |
0 | true | 0に切り詰め(見えなくなる) |
opacity: 50(% 忘れ) |
1 | true | 50%のつもりが不透明のまま |
opacity: 0.5px |
1 | false | 宣言ごと無視 |
opacity: calc(1/3) |
0.333333 | ― | calc も使える |
opacity: var(--o)(--o: 0.25) |
0.25 | ― | CSS 変数で切り替えられる |
| 親に 0.4、子は指定なし | 子は 1 | ― | 継承されない(見た目は薄い) |
子に opacity: inherit |
0.4 | ― | 親の値を受け取る(さらに薄くなる) |
- いちばん気づきにくいのは
opacity: 50です。 文法としては正しい数値なので、開発者ツールでも打ち消し線は付かず、1に切り詰められて「何も起きない」ように見えます。パーセントで書くなら%を付けます。 - 以前の Internet Explorer 向けの
filter: alpha(opacity=50)は、今のブラウザではCSS.supports('opacity','alpha(opacity=50)')が false で、効きません。 - 親を 0.4 にしたとき、子の計算値は 1 のままでした。「継承されない」のに子も薄く見えるのは、子が親の絵の一部として一緒に混ぜられるからです(次の節で画素値を示します)。
opacityとrgbaの違い(transparent・hsl・filterも画素値で比較)
この節では、opacity と、色に透明度を入れる rgba・hsl・8桁のカラーコード・transparent・filter の違いが、実際の画素値で分かります。
opacity は要素を中身ごと透かし、rgba などは「その色だけ」を透かします。 白い背景の上に、青(#0066cc)の箱(120×80px)を置き、その中に赤い子要素を入れて、透かし方だけを変えました。箱の背景と、子要素の位置の画素値です。
| 指定 | 箱の画素値 | 赤い子の画素値 | 計算値(getComputedStyle) |
|---|---|---|---|
| 何もしない | (0, 102, 204) | (255, 0, 0) | opacity 1 |
opacity: .5 |
(126, 177, 229) | (255, 126, 126) | opacity 0.5 |
background: rgba(0,102,204,.5) |
(127, 178, 229) | (255, 0, 0) | rgba(0, 102, 204, 0.5) |
hsl(210 100% 40% / 50%) |
(127, 178, 229) | (255, 0, 0) | rgba(0, 102, 204, 0.5) |
#0066cc80(8桁) |
(127, 178, 229) | (255, 0, 0) | rgba(0, 102, 204, 0.5) |
color-mix(in srgb, #0066cc 50%, transparent) |
(127, 178, 229) | (255, 0, 0) | color(srgb 0 0.4 0.8 / 0.5) |
filter: opacity(.5) |
(128, 179, 230) | (255, 128, 128) | opacity 1・filter opacity(0.5) |
background: transparent |
(255, 255, 255) | (255, 0, 0) | rgba(0, 0, 0, 0) |
opacity(要素ごと)
背景・文字・枠線・子要素・画像がすべて同じ割合で薄くなる
赤い子は (255, 126, 126) のピンクになった
フェード・無効状態の表現に向く
rgba・hsl・8桁(色だけ)
その色を使った部分だけが透ける
赤い子は (255, 0, 0) のまま
文字の後ろの半透明の背景・薄い枠線・影に向く
- rgba・hsl の「/ 50%」・8桁のカラーコード・color-mix は、計算値がどれも同じ透明度の色になり、画素値も一致しました。 書き方の好みとチームの決まりで選べます。8桁の末尾
80は16進数で128、つまり 128÷255 ≒ 0.502 です。 - 計算どおりなら、青 (0, 102, 204) を白に50%で混ぜると (127.5, 178.5, 229.5) です。opacity は (126, 177, 229)、rgba は (127, 178, 229)、filter は (128, 179, 230) と、1ずつずれました。 合成の経路と丸め方の違いによるもので、目で見て分かる差ではありませんが、スクリーンショットの差分テストでは別物として扱われます。
transparentはrgba(0, 0, 0, 0)と同じ「完全に透明な色」です。途中の濃さは表せず、opacity の代わりにはなりません。filter: opacity()は opacity とほぼ同じ見た目になりますが、計算値の opacity は1のままです。filter はほかの効果(blur など)と並べて書けます。
色の書き方(rgb・hsl・カラーコード)そのものは、関連記事「CSSの色指定まとめ|rgb・hsl・カラーコードと透明度の書き方」で扱っています。この記事は opacity の側から、要素ごと透けるときの動きを掘り下げます。
opacityで子要素も透ける理由(重ね合わせの仕組み)
この節では、opacity が継承されないのに子要素まで透けて見える理由と、子に opacity:1 を書いても戻らないことが、画素値で分かります。
opacity は、要素とその子孫をまとめて1枚の絵に描いてから、その絵全体を指定の濃さで背景に重ねます。 MDN は「opacity は、子要素には継承されないものの、そのコンテンツを含む要素全体に適用されます」と書いています。子要素は親の絵の一部なので、子の値をいくつにしても、親の絵ごと薄くなります。仕様(CSS Color Module Level 4 の 3.3)も、要素を子孫ごと「RGBA のオフスクリーン画像」に描いてから、その画像を opacity の値で混ぜる、という考え方で定義しています。
| 親 | 子 | 赤い子の画素値 | 見え方 |
|---|---|---|---|
opacity: .5 |
指定なし | (255, 126, 126) | 子も半分の濃さ |
opacity: .5 |
opacity: 1 |
(255, 126, 126) | 変わらない |
opacity: .5 |
filter: opacity(2) |
(255, 126, 126) | 変わらない(1を超えて濃くはできない) |
opacity: .5 |
opacity: .5 |
(191, 152, 177) | 掛け算で 0.25 相当に薄くなる |
最後の行は、子が親の中でまず青と半分ずつ混ざり(紫)、その親の絵がさらに白と半分ずつ混ざった色です。入れ子の opacity は掛け算で効くので、0.5 の中の 0.5 は、背景から見ると 0.25 の濃さになります。
よくある直し方の誤り
子の opacity は「親の絵の中での濃さ」で、親の絵より濃くはできない
子要素を透けさせない方法(rgba・::before・兄弟要素)
この節では、背景だけを透かして文字や子要素は不透明のまま残す方法を、場面別に3つ選べます。
透かしたいものが「色」なら rgba、「画像」なら ::before に移す、が基本です。 どうしても要素ごと opacity を使いたい部分があるなら、透かす部分と透かさない部分を兄弟要素に分けます。
背景色を rgba・hsl の透明度にする
親の opacity を消し、background: rgb(0 102 204 / 50%) のように色に透明度を入れる。手元では箱が (127, 178, 229)、赤い子は (255, 0, 0) のまま残った。
背景を ::before に移して、そこだけ opacity をかける
親を position: relative にし、::before に背景画像を置いて opacity: .5。手元では背景が (126, 177, 229)、赤い子は (255, 0, 0) のままだった。
透かす部分と中身を兄弟要素に分ける
背景用の空の要素と中身の要素を同じ親の下に並べ、背景用だけに opacity をかけて position: absolute; inset: 0 で重ねる。手元では子が (255, 0, 0) のまま。
/* 1. 色の透明度にする */
.hero {
background: rgb(0 102 204 / 50%);
}
/* 2. 画像の背景を ::before に移す */
.hero {
position: relative;
isolation: isolate; /* ::before を .hero の中だけで重ねる */
}
.hero::before {
content: "";
position: absolute;
inset: 0;
background: url(bg.jpg) center / cover;
opacity: .5;
z-index: -1; /* 文字より下に置く */
}
3つ目の兄弟要素に分ける形は、HTML を次のようにします(.hero__bg に opacity と absolute を付けます)。
<section class="hero">
<span class="hero__bg" aria-hidden="true"></span>
<p class="hero__text">文字は不透明のまま</p>
</section>
- ::before に z-index: -1 を付けるときは、親に
isolation: isolateを付けておきます。 手元で、黄色の背景を持つ親に z-index: -1 の ::before(青の画像・opacity .5)を置くと、isolation なしでは ::before が親の背景の後ろに回って画素値は黄色の (255, 255, 0) のまま、isolation: isolateを付けると (126, 177, 102) と黄色の上に青が半分重なりました。 - 背景画像を暗くして文字を読みやすくするだけなら、
background: linear-gradient(rgb(0 0 0 / 50%), rgb(0 0 0 / 50%)), url(bg.jpg);と半透明のグラデーションを重ねる方法もあります。手元では青い画像が (0, 51, 102) に暗くなり、要素を増やさずに済みました。
opacity:0・visibility:hidden・display:noneの違い(クリック・Tab・読み上げ)
この節では、「見えなくする」3つの方法が、場所・クリック・キーボード操作・スクリーンリーダーでどう違うかが、実測で分かります。
opacity: 0 は「見えないだけ」で、場所を取り、クリックでき、Tab キーでフォーカスが入り、読み上げの対象にも残ります。 ボタンを縦に並べ、隠し方だけを変えて、その位置をマウスでクリックし、Tab キーを押して順に移る先を記録しました。
| 隠し方 | 場所(幅) | クリック | Tab で移る | 読み上げの対象 |
|---|---|---|---|---|
opacity: 0 |
残る(200px) | 反応した | 移った | 残る |
opacity: 0; pointer-events: none |
残る | 反応しない(下の body に当たる) | 移った | 残る |
opacity: 0+tabindex="-1" |
残る | 反応した | 移らない | 残る |
visibility: hidden |
残る | 反応しない | 移らない | 消える |
display: none |
消える(0px) | 反応しない | 移らない | 消える |
手元の記録の原文です。Tab キーは first → opacity:0 のボタン → pointer-events:none のボタン → last の順に移り、見えないボタンに2回フォーカスが入りました。
clicks ['o0', 'ti']
tab order ['first', 'o0', 'pe', 'last', '']
aria
- button "first"
- button "opacity0"
- button "pe-none"
- button "tabindex-1"
- button "last"
アクセシビリティのスナップショットには、opacity: 0 のボタンがすべて残り、visibility: hidden と display: none のボタンは出てきませんでした。MDN も「スクリーンリーダーへ情報を提供するために不透過度を単独で使用すべきではありません」とし、hidden 属性・visibility・display を使うよう書いています。
1pointer-events だけでは足りない
マウスのクリックは止まったが、Tab キーではフォーカスが入った。キーボードで押せる見えないボタンが残る。
2フェードの終わりは visibility
透明にしたあとも操作させないなら、終わった時点で visibility: hidden か hidden 属性にする。
3見せずに読ませるなら別の方法
画面に出さず読み上げだけさせたいときは、opacity ではなく visually-hidden(clip で切り抜く)の書き方を使う。
display と visibility の違いは、関連記事「display:noneとvisibility:hiddenの違い|非表示比較表」で、pointer-events の詳しい使い方は関連記事「CSS cursor一覧とpointer-events:noneでクリック無効」で扱っています。
opacityでz-indexが効かない理由(重ね合わせコンテキスト)
この節では、opacity を付けただけでドロップダウンメニューなどが別の要素の下に潜る理由と、その直し方が分かります。
opacity が1未満の要素は、新しい重ね合わせコンテキスト(スタッキングコンテキスト)を作ります。 MDN は「opacity の値を 1 以外の値で用いると、その要素は新しい重ね合わせコンテキストを生成します」と書いています。中の子に z-index: 9999 を付けても、その数字は親の中だけで比べられ、親の外の要素とは「親の z-index」で比べられます。
z-index: 9999 の緑のメニューと、z-index: 1 の灰色の帯を重ね、メニューの親(position: relative)だけを変えました。重なった位置の一番上の要素(elementFromPoint)と画素値です。
| メニューの親 | 一番上の要素 | 画素値 | 見え方 |
|---|---|---|---|
| opacity なし | menu | (0, 170, 0) | メニューが上 |
opacity: .99 |
cover | (136, 136, 136) | メニューが帯の下に潜る |
opacity: .99; z-index: 2 |
menu | (2, 170, 2) | メニューが上に戻る(0.99 の濃さ) |
- 0.99 のように見た目がほとんど変わらない値でも、重なり順は変わりました。 「ホバーで少しだけ薄くしたら、メニューが隠れた」はこの動きです。
- 直し方は、opacity を付けた親自身に z-index を付けて、親ごと前に出すことです。親の z-index が、外の要素と比べる数字になります。
- transform・filter・will-change: opacity なども同じく重ね合わせコンテキストを作ります。
重ね合わせコンテキストを作るプロパティの一覧と調べ方は、関連記事「CSS z-indexが効かない|スタッキングコンテキスト原因一覧」で扱っています。
opacityのホバー・transition・フェードインの書き方
この節では、opacity をホバーでふわっと変える方法、キーフレームのフェードイン、display: none から表示するときのフェードが、実測の値で分かります。
ホバーで薄くする(transition は通常時に書く)
.btn {
opacity: 1;
transition: opacity .4s linear; /* 戻るときも滑らかにするため通常時に書く */
}
.btn:hover {
opacity: .5;
}
Chromium でマウスを乗せ、0.2秒後と0.6秒後の計算値を読むと、0.749974 → 0.5 でした。0.4秒の linear の半分の時点で、1と0.5のちょうど中間になっています。transition を :hover の側だけに書くと、マウスを離したときは一瞬で戻ります。
キーフレームのフェードイン(forwards の有無)
@keyframes fadein {
from { opacity: 0; }
to { opacity: .6; }
}
.k1 { animation: fadein .3s linear; } /* 終わると元の値に戻る */
.k2 { animation: fadein .3s linear forwards; } /* 最後の値で止まる */
0.6秒後の計算値は、forwards なしが 1(元の値に戻った)、ありが 0.6 でした。フェードインのあとに一瞬ちらつくときは、forwards を付けるか、元の値を終わりの値にそろえます。
display: none からのフェード(@starting-style)
display: none の要素を表示に切り替えると同時に opacity を0→1にしても、transition は動きません。 表示された時点で「変化前の値」が無いためです。MDN は、この場合に @starting-style と transition-behavior: allow-discrete を両方書くよう説明しています。
.panel {
opacity: 1;
transition: opacity 1s linear, display 1s allow-discrete;
}
@starting-style {
.panel { opacity: 0; } /* 表示された直後の値 */
}
.panel.hidden {
display: none;
opacity: 0;
}
hidden のクラスを外してから、0.05秒・0.3秒・0.6秒後の計算値です。
| 書き方 | 0.05秒後 | 0.3秒後 | 0.6秒後 |
|---|---|---|---|
| transition だけ | 1 | 1 | 1 |
| @starting-style+allow-discrete | 0.050019 | 0.299969 | 0.599967 |
transition だけの書き方では、いきなり1で表示されました。@starting-style を書くと、1秒かけて0から1へ進んでいます。@starting-style に対応していないブラウザでは、フェードせずに表示されるだけで、表示そのものは壊れません。
transition と keyframes の使い分けは、関連記事「CSS アニメーション|transitionとkeyframesの違いと実用20選」で扱っています。
JavaScript で opacity を変える
const el = document.querySelector('.card');
el.style.opacity = 0.3; // 数値を入れてもよい
console.log(typeof el.style.opacity); // "string"
console.log(el.style.opacity); // "0.3"
console.log(getComputedStyle(el).opacity); // "0.3"
style.opacity は、数値を入れても文字列 "0.3" で返りました。計算に使うときは Number() で数値に直します。また style.opacity はインライン指定しか読めないので、CSS ファイルで指定した値は getComputedStyle で読みます(手元では、インライン指定の無い要素の style.opacity は空文字でした)。
opacityアニメーションの性能(再描画回数の実測)
この節では、opacity のアニメーションが軽いと言われる理由と、背景色・位置・幅のアニメーションとの差が、Chromium のトレースの件数で分かります。
opacity と transform のアニメーションは、ブラウザが描き直さずに合成(コンポジット)だけで進められます。 web.dev の記事も、コンポジターだけで扱えるプロパティは transform と opacity の2つだと説明しています。 300×200px の箱に、1秒で往復するアニメーションを無限に繰り返して付け、2秒間のトレースで各処理の件数を数えました。
| 動かしたプロパティ | スタイル計算 | Layout | Paint | Commit |
|---|---|---|---|---|
opacity |
2 | 1 | 2 | 2 |
transform |
2 | 1 | 3 | 2 |
background(色) |
121 | 0 | 242 | 121 |
left |
128 | 127 | 240 | 121 |
width |
125 | 124 | 240 | 121 |
- 背景色・left・width は、2秒間に約120フレームぶん、毎フレームでスタイル計算と描画(Paint)が走りました。left と width はさらに配置の計算(Layout)もやり直しています。
- opacity と transform は、最初の数回のあとは描き直しが0件でした。 変化はメインスレッドの外(コンポジター)で進むため、JavaScript の処理が重いときでもアニメーションが止まりにくくなります。
- フェードと移動を作るなら、opacity と transform の組み合わせが最も軽くなります。
will-change: opacityはあらかじめ別の層にする指示ですが、層が増えるとメモリを使うので、常に付けたままにはしません。 - 件数は、ヘッドレスの Chromium(GPU なし)で測った値です。端末や表示内容で変わりますが、「opacity は描き直さない・背景色は毎フレーム描き直す」という差は同じ仕組みから出ています。
opacityとアクセシビリティ(コントラスト比と透明度を減らす設定)
この節では、文字を opacity で薄くしたときに読みやすさの基準を満たすかと、OS の「透明度を下げる」設定に合わせる書き方が分かります。
薄くした文字は、背景とのコントラスト比が下がります。 WCAG 2.2 の達成基準 1.4.3(AA)は、通常の文字で 4.5:1 以上、大きな文字で 3:1 以上を求めています。白い背景の上に #333 の色を置き、opacity だけを変えて、画面上の実際の色と白とのコントラスト比を計算しました。
| opacity | 画面上の色(実測) | 白とのコントラスト比 | 通常の文字(4.5:1) | 大きな文字(3:1) |
|---|---|---|---|---|
| 1 | #333333 | 12.63:1 | 満たす | 満たす |
| 0.8 | #5c5c5c | 6.69:1 | 満たす | 満たす |
| 0.7 | #707070 | 4.95:1 | 満たす | 満たす |
| 0.6 | #858585 | 3.69:1 | 満たさない | 満たす |
| 0.5 | #999999 | 2.85:1 | 満たさない | 満たさない |
| 0.4 | #adadad | 2.24:1 | 満たさない | 満たさない |
| 0.3 | #c1c1c1 | 1.80:1 | 満たさない | 満たさない |
- #333 の文字なら、opacity 0.7 までは 4.5:1 を満たし、0.6 で下回りました。 無効なボタンなどを opacity: .5 で表すことはよくありますが、WCAG 1.4.3 は「無効な部品」を例外にしています。説明文や注記を薄くする場合は例外に当たりません。
- コントラスト比は「画面に出た色」で計算します。opacity を使うと元の色の値(#333)と画面の色が違うので、開発者ツールの色の値だけを見て判断しないようにします。
- 大きな文字は、MDN の説明では「太字で 18.66px 以上、または 24px 以上」です。
prefers-reduced-transparency に合わせる
MDN は、OS の「透明度を下げる」設定に合わせるには prefers-reduced-transparency メディアクエリーを使うよう書いています。
.overlay-text { opacity: .5; }
@media (prefers-reduced-transparency: reduce) {
.overlay-text { opacity: 1; }
}
Chromium の開発者ツールのプロトコル(Emulation.setEmulatedMedia)でこの設定を「reduce」にすると、計算値が 0.5 → 1 に変わり、matchMedia('(prefers-reduced-transparency: reduce)').matches は true でした。MDN の互換性表では、このメディアクエリーに対応していないブラウザがあり、その場合は通常の 0.5 のまま表示されます。
画像・背景画像を半透明にする(imgと::before)
この節では、img 要素の画像と、CSS の背景画像を半透明にする方法の違いが分かります。
img 要素は、そのまま opacity をかければ画像が透けます。背景画像は opacity を直接指定できないため、::before などに移してからかけます。 青一色の PNG を使い、白い背景の上で確かめました。
| 方法 | 画像の画素値 | 上に置いた赤い子 |
|---|---|---|
img に opacity: .25 |
(190, 216, 242) | ― |
img に opacity: .5 |
(126, 177, 229) | ― |
img に opacity: .75 |
(63, 140, 216) | ― |
背景画像の要素に opacity: .5 |
(126, 177, 229) | (255, 126, 126)(一緒に透けた) |
背景画像を ::before に移して opacity: .5 |
(126, 177, 229) | (255, 0, 0)(不透明のまま) |
| 半透明の黒のグラデーションを重ねる | (0, 51, 102)(暗くなる) | ― |
/* 写真を薄くする */
img.thumb { opacity: .5; }
/* マウスを乗せたら濃くする */
img.thumb:hover { opacity: 1; transition: opacity .3s; }
画像は rgba で透かすことができないので、画像そのものを透かすときは opacity(か filter: opacity())を使います。画像の上に文字を載せる場合は、文字が画像と一緒に透けないよう、::before か兄弟要素に分けます。filter: opacity() は blur・grayscale などほかの効果と並べて1行に書けるので、画像の効果をまとめて指定するときに使えます。
opacityのよくあるエラー・つまずき
この節では、opacity が効かない・思ったように透けないときの原因と直し方が、症状から引けます。
| 症状 | 原因 | 直し方 |
|---|---|---|
| opacity: 50 と書いたのに変わらない | 数値の 50 は1に切り詰められる(計算値 1) | 0.5 か 50% と書く |
| opacity: 0.5px が効かない | 単位付きは無効。CSS.supports が false | 単位を外す |
| 文字まで薄くなる | opacity は中身ごと透ける | 背景を rgba にする・::before に移す |
| 子に opacity: 1 を書いても戻らない | 子は親の絵の一部。親より濃くはできない | 親の opacity をやめて構造を分ける |
| 入れ子で思ったより薄い | opacity は掛け算で効く(0.5×0.5=0.25) | どちらか一方だけにかける |
| 見えないボタンが押せてしまう | opacity: 0 はクリック・フォーカスが残る | visibility: hidden か hidden 属性にする |
| メニューが別の要素の下に潜る | opacity 1未満で重ね合わせコンテキストができた | opacity を付けた親に z-index を付ける |
| display: none からフェードしない | 表示直後に変化前の値が無い | @starting-style と allow-discrete を書く |
| フェードインのあと元に戻る | animation の終わりで元の値に戻る | forwards を付ける |
| filter: alpha(opacity=50) が効かない | 以前の IE 専用の書き方 | opacity を使う |
- 1開発者ツールの計算済みタブで opacity の値を見る指定した値ではなく、切り詰められたあとの値が出る。50 と書いたものは 1 と出る
- 2親の要素にも opacity が付いていないか確かめる掛け算で効くので、親のどこかで 0.5 があると子も薄くなる
- 3透かしたいのが色か、要素全体かを決める色なら rgba・hsl、要素全体なら opacity
- 4opacity: 0 で隠した要素を Tab キーで通ってみるフォーカスが入るなら、visibility か hidden 属性を足す
opacityとrgba・visibilityのどれを使うか(判断表)
この節では、やりたいことから、opacity・色の透明度・visibility・display のどれを選べばよいかが分かります。
迷ったら「透かしたいのは色か、要素全体か、そもそも隠したいのか」を先に決めます。
- 1背景色・枠線・影だけを透かしたい → rgba・hsl・8桁文字や子要素は不透明のまま
- 2背景画像だけを透かしたい → ::before に移して opacity暗くするだけならグラデーションを重ねる
- 3画像・要素を丸ごと薄くしたい → opacity無効状態・写真のホバー
- 4ふわっと出す・消す → opacity の transition消えたあとは visibility: hidden
- 5場所は残して隠す → visibility: hiddenクリック・Tab・読み上げの対象から外れる
- 6場所ごと消す → display: none か hidden 属性フェードするなら @starting-style
| やりたいこと | 使うもの | 理由(手元の結果) |
|---|---|---|
| 写真の上の文字の背景を半透明に | rgba の背景 | 文字は (255, 0, 0) のように元の色のまま |
| 無効なボタンを薄く | opacity+disabled 属性 | opacity だけではクリックできる |
| モーダルの出し入れ | opacity の transition+visibility | 描き直しが少なく、閉じたあと操作できない |
| ホバーで少し薄く | opacity | z-index の重なりが変わる点に注意 |
| 画面には出さず読み上げだけ | visually-hidden の書き方 | opacity: 0 は場所を取り、フォーカスも入る |
よくある質問
Q. CSS の opacity とは何ですか? A. 要素の不透明度を指定するプロパティです。0 で完全に透明、1 でそのまま、0.5 で半透明になります。要素の背景・文字・子要素がまとめて同じ割合で透けます。
Q. opacity と透明度は同じ意味ですか?
A. 向きが逆です。opacity は「不透明度」なので、値が大きいほど濃く、小さいほど透けます。「透明度30%」にしたいなら opacity: 0.7 です。
Q. opacity をパーセントで書けますか?
A. 書けます。opacity: 50% は 0.5 と同じ計算値になりました。% を忘れた opacity: 50 は1に切り詰められて変化しないので注意します。
Q. opacity は子要素に継承されますか? A. 継承はされません(子の計算値は1のまま)。ただし親の opacity は子を含めた要素全体にかかるので、見た目では子も一緒に薄くなります。
Q. 親の opacity を子要素だけ打ち消せますか?
A. できません。子に opacity: 1 を書いても画素値は変わりませんでした。親の opacity をやめて背景を rgba にするか、透かす部分を ::before や兄弟要素に分けます。
Q. 背景だけを透明にするには?
A. 背景色なら background: rgb(0 102 204 / 50%) のように色に透明度を入れます。背景画像なら ::before に移して、そこにだけ opacity をかけます。
Q. opacity: 0 と visibility: hidden の違いは? A. どちらも場所は残りますが、opacity: 0 はクリックでき、Tab キーでフォーカスが入り、読み上げの対象にも残ります。visibility: hidden はどれも外れます。
Q. opacity を付けたら z-index が効かなくなりました。 A. opacity が1未満だと新しい重ね合わせコンテキストができ、子の z-index は親の中だけで比べられます。opacity を付けた親自身に z-index を付けると直りました。
Q. opacity のアニメーションは重いですか? A. 軽い部類です。手元の Chromium では、2秒間の opacity のアニメーションで描き直し(Paint)が2回、背景色のアニメーションでは242回でした。
Q. rgba と 8桁のカラーコードはどちらを使えばよいですか? A. 結果は同じです。手元では rgba・hsl の / 50%・#0066cc80 の画素値が一致しました。チームで読みやすい書き方にそろえます。
Q. SVG でも opacity は使えますか? A. 使えます。MDN は opacity を CSS Color Module Level 4 と SVG 2 で定義されたものとして扱っています。SVG には塗りだけを透かす fill-opacity・線だけを透かす stroke-opacity もあり、rgba と同じように「その部分だけ」を透かせます。
まとめ
- opacity は要素を中身ごと透かすプロパティ。0〜1 か 0%〜100% で書き、範囲外は切り詰められる。
opacity: 50は1になり、0.5pxは無視された。 - 子要素も一緒に透け、子に opacity: 1 を書いても画素値は (255, 126, 126) のままだった。背景だけなら rgba・hsl・8桁、背景画像なら ::before に移す。
- opacity: 0 は見えないだけで、クリック・Tab・読み上げの対象に残った。隠すなら visibility: hidden・display: none・hidden 属性を使う。
- opacity が1未満だと重ね合わせコンテキストができ、0.99 でもメニューが別の要素の下に潜った。親に z-index を付けて直す。
- opacity のアニメーションは描き直しが2回で、背景色の242回より大幅に軽い。文字を薄くするときは画面上の色でコントラスト比を確かめる(#333 なら 0.7 まで 4.5:1 を満たした)。
参考・出典
確認日はいずれも 2026年10月29日です。
- MDN「opacity」:https://developer.mozilla.org/ja/docs/Web/CSS/opacity
- MDN「filter: opacity()」:https://developer.mozilla.org/ja/docs/Web/CSS/filter-function/opacity
- MDN「重ね合わせコンテキスト」:https://developer.mozilla.org/ja/docs/Web/CSS/CSS_positioned_layout/Stacking_context
- MDN「@starting-style」:https://developer.mozilla.org/ja/docs/Web/CSS/@starting-style
- MDN「transition-behavior」:https://developer.mozilla.org/ja/docs/Web/CSS/transition-behavior
- MDN「prefers-reduced-transparency」:https://developer.mozilla.org/ja/docs/Web/CSS/@media/prefers-reduced-transparency
- W3C「CSS Color Module Level 4」3.3 Transparency: the opacity property:https://www.w3.org/TR/css-color-4/#transparency
- W3C「Web Content Accessibility Guidelines (WCAG) 2.2」1.4.3 Contrast (Minimum):https://www.w3.org/TR/WCAG22/#contrast-minimum
- web.dev「Stick to Compositor-Only Properties and Manage Layer Count」:https://web.dev/articles/stick-to-compositor-only-properties-and-manage-layer-count
- Playwright「Browser.start_tracing」:https://playwright.dev/python/docs/api/class-browser#browser-start-tracing
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「CSSのopacityとは|子要素も透ける理由とrgbaとの違いを画素値で検証」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。