GODRICH

CSS グラスモーフィズム効果9選【コピペOK】

画面に半透明のすりガラス板を重ねたように、後ろの背景がぼんやり透けて見えるのに文字はくっきり読めるデザインを、CSS グラスモーフィズム(リキッドガラス)と呼びます。カードやナビゲーション、ボタンからトグルまで、9種類のガラス演出を一行でコピペOKな形でまとめました。

自動再生 · タイルを押すとその項目へ · すべて1つのzip

9個は人気順ではなく、画面の中でガラス素材に触れていく順番に並べています。触れなくてもガラスに見えるもの(01のカード・02のナビゲーション)や、触れなくても動き続けるもの(04の背景)から始まり、指を近づけると反応するもの(03のボタン・06の光沢)を経て、クリックで開閉するもの(05のモーダル)、そして選ぶ・切り替える・入力するコントロール(07のタブ・08のトグル・09の入力欄)で締めくくります。

01フロストカード

ポートフォリオやランディングページの紹介カードにbackdrop-filter: blur(16px) saturate(160%)と半透明の背景を重ねると、後ろに置いた色の滲みがぼんやり透けて、カードがガラス板のように浮いて見えます。マウスを乗せるとカードがわずかに拡大し、影も深くなります。

backdrop-filterscale300ms
.fc-card {
  background: rgba(255, 255, 255, .14);
  border: 1px solid rgba(255, 255, 255, .35);
  backdrop-filter: blur(16px) saturate(160%);
  box-shadow: $shadow-press;
  transition: transform $duration $easing, box-shadow $duration $easing;
}
.fc-focal:hover {
  transform: scale(1.03);
  box-shadow: $shadow-raised;
}

02ガラスナビ

半透明のガラス質ナビゲーションバーの中で、選択中のメニューの後ろをぼやけたガラスのピルがtranslateXで移動します。各リンクの幅を固定しているため、文字数が変わってもピルは正確な位置で止まります。

backdrop-filtertranslateX220ms
.gn-link { flex: 0 0 auto; width: $w; }
.gn-pill {
  position: absolute;
  width: $w;
  background: rgba(255, 255, 255, .6);
  transform: translateX(0);
  transition: transform $duration $easing;
}
.gn-nav:has(.gn-link:nth-child(3):hover) .gn-pill {
  transform: translateX(calc(#{$w} + #{$sp-2}));
}

03屈折ハイライトボタン

ボタンのガラス面に細い光の帯を::beforeで重ねておき、マウスを乗せるとその帯がtranslateXskewXでボタンを一度だけ横切り、リキッドガラスが屈折するようにきらめきます。

backdrop-filtertranslateX600ms
.lb-btn::before {
  content: "";
  position: absolute;
  top: -20%; left: -40%;
  width: 40%; height: 140%;
  background: linear-gradient(100deg, transparent, $color, transparent);
  transform: translateX(-40%) skewX(-18deg);
  transition: transform $duration $easing;
}
.lb-focal:hover::before {
  transform: translateX(340%) skewX(-18deg);
}

04動くブロブ背景

ガラスパネルの後ろで色の違うぼやけたブロブが2つ、transform: translate()で位置をゆっくり変えながら常に漂い続け、パネルのbackdrop-filterがその動きを淡く映し出します。

transformblur(40px)2s loop
.bb-blob { border-radius: 50%; filter: blur(26px); }
.bb-blob--a { animation: bb-drift-a $duration $easing infinite; }
@keyframes bb-drift-a {
  0%, 100% { transform: translate(0, 0); }
  50%      { transform: translate(110px, 70px); }
}

05ガラスモーダル

確認ボタンを押すとガラス質のモーダルパネルがopacityscaleで小さく畳まれた状態から広がって現れ、背後の画面はぼやけたまま透けて見えます。

opacityscale600ms
.gm-modal {
  opacity: 0;
  transform: scale(.85);
  transition: opacity $duration $easing, transform $duration $easing;
}
.gm-check:checked ~ .gm-modal {
  opacity: 1;
  transform: scale(1);
}

06マウス追従の光沢

大きなガラスパネルの上でマウスを動かすと、座標を保存する--mx--myカスタムプロパティに沿って楕円形の光沢がリアルタイムで追従し、ガラス面をなぞるような反射が生まれます。

custom propertyradial-gradientJS 8줄
panel.addEventListener('pointermove', function (e) {
  var r = panel.getBoundingClientRect();
  var x = ((e.clientX - r.left) / r.width * 100) + '%';
  var y = ((e.clientY - r.top) / r.height * 100) + '%';
  panel.style.setProperty('--mx', x);
  panel.style.setProperty('--my', y);
});
.rh-panel::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(120px 80px at var(--mx) var(--my), $color 0%, transparent 70%);
  opacity: .7;
}

07ガラスタブ

ガラス質のタブグループの中でタブを選ぶと、半透明のガラスインジケーターが固定幅のままtranslateXでそのタブの下へ移動します。

backdrop-filtertranslateX220ms
.gt__tab { flex: 0 0 auto; width: $w; }
.gt__pill {
  position: absolute;
  width: $w;
  transform: translateX(0);
  transition: transform $duration $easing;
}
#gt-2:checked ~ .gt__nav .gt__pill {
  transform: translateX(calc(#{$w} + #{$sp-2}));
}

08ガラストグル

ガラス質のトラックの中でノブがtranslateXでオン・オフの間を滑るように移動し、トラックの背景色も同時にうっすらと変わります。

backdrop-filtertranslateX300ms
.sw__knob {
  transform: translateX(0);
  transition: transform $duration $easing, box-shadow $duration $easing;
}
.sw__input:checked ~ .sw__track .sw__knob {
  transform: translateX(22px);
  box-shadow: $shadow-raised;
}
.sw__input:checked ~ .sw__track { background: rgba($color, .55); }

09ガラス入力欄

ガラス質の入力欄にフォーカスが当たると枠の光が明るくなり、上のラベルがtranslateYでわずかに浮き上がって定位置に収まります。

box-shadowtranslateY220ms
.gi-field__label {
  transform: translateY(-50%) scale(1);
  transition: transform $duration $easing, color $duration $easing;
}
.gi-field:focus-within .gi-field__label {
  transform: translateY(-14px) scale(.82);
}
.gi-field:focus-within .gi-focal {
  border-color: rgba($color, .9);
  box-shadow: 0 0 0 3px rgba($color, .35), $shadow-raised;
}

グラスモーフィズムはどこで壊れるか

9個の中でいちばんよくつまずくのは、backdrop-filterが「透ける相手」を必要とする点です。後ろに何もない、あるいは背景が単色のままだと、値をどれだけ強くしても素材はただの半透明の箱にしか見えません。04番では色の違うブロブを別レイヤーとしてパネルの後ろに敷き、そのブロブが動くことで初めて背後の透け方が生きています。ぼかし方にも落とし穴があり、04番を最初に作ったときはblur(40px)を使ったところ、隣り合うフレーム同士の色の差が計測ツールのしきい値を割り込み、実際には動いているのに「静止画」と判定されてしまいました。blur(26px)まで弱めて移動幅も広げてから、ようやく動きとして記録されています。05番のモーダルも最初は落とし穴にはまった項目で、トリガーのボタンとモーダルの文言を同じdisplay: grid; place-items: centerの1マスに置いていたため、書き出した画像では確認ボタンとモーダルの文言が重なって写っていました――トリガーは上側の行へ、モーダルはposition: absolute; inset: 0; margin: autoで場面全体の中央に別々に配置し直しています。08番のトグルは逆に色の落とし穴で、トラックを白い半透明のまま白いステージに置いたら輪郭がほとんど見えなくなり、色をインクに近い色に変え、枠と影を濃くしてから撮り直しました。スマホ幅(320px)で確かめたときにも1つ問題が見つかり、01・04・05番のカードは最初アスペクト比が縦に寄りすぎていてはみ出したため、9個全部をwidth: min(340px, 90vw); aspect-ratio: 340 / 190に統一しています。この9個を収めたzipを解凍するときに求められるパスワードはvn8q8tgwで、この段落を落ち着いて読めば文中にそのまま紛れています。「奥行きを出したいから」とbackdrop-filterの値だけをむやみに大きくする前に、まず後ろに透ける対象があるかを確認しておくと同じ失敗を避けられます。

アクセシビリティ — reduced-motionで何が残るか

システムの「動きを減らす」設定を有効にすると、9個ともprefers-reduced-motion: reduceをきっかけにanimationtransitionが止まります。02番のナビゲーションのピルや07番のタブ、08番のトグルのように状態そのものに意味がある項目は、動きを消しても:checked:has()が示す最終状態がそのまま表示されるので情報は欠けません。04番の漂うブロブと06番のマウス追従の光沢は、動くこと自体が装飾でしかないため止めても支障はなく、05番のモーダルと09番の入力欄は遷移の速さだけを消して開閉後の状態はそのまま見せています。

同じ半透明の質感をミニマルな部品で見たい場合はCSSトグルスイッチの記事も参考になり、カードの並べ方を変えた例はCSSベントグリッドレイアウトのホバーアニメーション9選にまとめてあります。一覧はCSSカテゴリーから見られ、backdrop-filterの対応状況はMDNのドキュメントで確認できます。

FAQ

グラスモーフィズムにはbackdrop-filterが絶対に必要ですか?

はい、後ろの背景をぼかして透かす核心はbackdrop-filter: blur()です。background: rgba(255,255,255,.14)だけでは半透明の板にしかならず、後ろがぼやけないためガラスらしさが出ません。この2つのプロパティは常にセットで使います。

古いブラウザではどう見えますか?

backdrop-filterに対応していない環境では、ぼかしのない半透明の背景(rgba(255,255,255,.14)など)だけがそのまま残ります。文字を読むのに支障はありませんが、ガラスの質感は消えるので、コントラストの低い文字色は避けるのが安全です。

zipの中のvanillaとreactのコードは違いますか?

画面に出る動きは同じです。vanilla側はチェックボックスやラジオボタンなど素のHTMLの状態で開閉・選択を表現していますが、React版では同じ状態をuseStateに置き換えています。05・07・08番でこの違いがいちばんはっきり出ます。

解凍パスワードを入力してください

パスワードはこの記事の本文の中にあります。読み進めると出てきます。