3Dチルト・ガラスログイン
マウス位置に合わせて立体的に傾くガラス調のログインパネル。perspective と backdrop-filter を組み合わせた没入感のあるフォームです。
ライブデモ
使用例(お題: SaaS FlowDesk)
この技法を「SaaS FlowDesk」というテーマのダミーサイトで実際に使った例です。
<!-- FlowDesk:3Dチルトするガラスログイン -->
<section class="tl-scene">
<span class="tl-orb tl-orb--1"></span>
<span class="tl-orb tl-orb--2"></span>
<!-- 傾くガラスログインパネル -->
<div class="tl-stage" id="tlStage">
<form class="tl-card" id="tlCard" autocomplete="off">
<div class="tl-card__brand">
<span class="tl-card__mark">◆</span> FlowDesk
</div>
<h2 class="tl-card__title">おかえりなさい</h2>
<p class="tl-card__sub">アカウントにサインインしてください</p>
<label class="tl-field">
<span>メールアドレス</span>
<input type="email" value="taro@flowdesk.io" required>
</label>
<label class="tl-field">
<span>パスワード</span>
<input type="password" value="password" required>
</label>
<div class="tl-card__row">
<label class="tl-check"><input type="checkbox" checked> ログイン状態を保持</label>
<a href="#" class="tl-card__link">お困りですか?</a>
</div>
<button class="tl-card__btn" type="submit">サインイン</button>
<p class="tl-card__signup">アカウント未作成? <a href="#">無料登録</a></p>
</form>
</div>
</section>
/* FlowDesk:3Dチルトするガラスログイン */
:root {
--navy: #0f1b34;
--blue: #4f7cff;
}
* { box-sizing: border-box; }
body {
margin: 0;
height: 400px;
display: grid;
place-items: center;
font-family: "Segoe UI", "Hiragino Kaku Gothic ProN", system-ui, sans-serif;
background: radial-gradient(120% 120% at 20% 10%, #1b2c52 0%, #0f1b34 55%, #0a132a 100%);
overflow: hidden;
}
.tl-scene {
position: relative;
width: 100%;
height: 400px;
display: grid;
place-items: center;
}
/* 背景の発光オーブ(ガラスの透過を引き立てる) */
.tl-orb {
position: absolute;
border-radius: 50%;
filter: blur(40px);
opacity: 0.7;
}
.tl-orb--1 { width: 200px; height: 200px; background: #4f7cff; top: 8%; left: 12%; }
.tl-orb--2 { width: 170px; height: 170px; background: #7a5cff; bottom: 6%; right: 14%; }
/* 3D空間 */
.tl-stage {
perspective: 900px;
}
/* ガラスカード本体(JSで rotateX/Y) */
.tl-card {
position: relative;
width: min(320px, 86vw);
padding: 26px 26px 22px;
border-radius: 22px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.22);
-webkit-backdrop-filter: blur(16px) saturate(1.2);
backdrop-filter: blur(16px) saturate(1.2);
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255,255,255,0.35);
color: #eef2ff;
transform-style: preserve-3d;
transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
transition: transform 0.15s ease-out;
}
.tl-card__brand {
font-size: 13px;
font-weight: 700;
letter-spacing: 0.06em;
color: #cdd8ff;
margin-bottom: 14px;
transform: translateZ(30px);
}
.tl-card__mark { color: var(--blue); }
.tl-card__title { margin: 0 0 4px; font-size: 21px; font-weight: 800; transform: translateZ(24px); }
.tl-card__sub { margin: 0 0 18px; font-size: 12px; color: rgba(255,255,255,0.7); transform: translateZ(18px); }
.tl-field { display: block; margin-bottom: 12px; transform: translateZ(14px); }
.tl-field span {
display: block;
font-size: 11px;
color: rgba(255,255,255,0.75);
margin-bottom: 5px;
}
.tl-field input {
width: 100%;
font: inherit;
font-size: 13px;
padding: 10px 12px;
border-radius: 10px;
border: 1px solid rgba(255,255,255,0.2);
background: rgba(255,255,255,0.08);
color: #fff;
outline: none;
transition: border-color 0.2s ease, background 0.2s ease;
}
.tl-field input:focus { border-color: var(--blue); background: rgba(79,124,255,0.14); }
.tl-card__row {
display: flex;
align-items: center;
justify-content: space-between;
margin: 4px 0 16px;
transform: translateZ(12px);
}
.tl-check { font-size: 11px; color: rgba(255,255,255,0.78); display: flex; align-items: center; gap: 5px; cursor: pointer; }
.tl-check input { accent-color: var(--blue); }
.tl-card__link { font-size: 11px; color: #9db4ff; text-decoration: none; }
.tl-card__link:hover { text-decoration: underline; }
.tl-card__btn {
width: 100%;
font: inherit;
font-size: 14px;
font-weight: 700;
padding: 11px 0;
border: none;
border-radius: 11px;
cursor: pointer;
color: #fff;
background: linear-gradient(135deg, #5f8bff, #4f7cff);
box-shadow: 0 10px 24px rgba(79,124,255,0.45);
transform: translateZ(36px);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.tl-card__btn:hover { box-shadow: 0 14px 30px rgba(79,124,255,0.6); }
.tl-card__btn:active { transform: translateZ(36px) scale(0.98); }
.tl-card__signup { margin: 14px 0 0; font-size: 11px; text-align: center; color: rgba(255,255,255,0.7); transform: translateZ(10px); }
.tl-card__signup a { color: #9db4ff; text-decoration: none; font-weight: 600; }
@media (prefers-reduced-motion: reduce) {
.tl-card { transition: none; transform: none !important; }
}
// マウス位置に合わせてガラスカードを3Dチルトさせる
const stage = document.getElementById("tlStage");
const card = document.getElementById("tlCard");
if (stage && card) {
const MAX = 9; // 最大傾き角(度)
stage.addEventListener("pointermove", (e) => {
const rect = card.getBoundingClientRect();
// カード中心からの相対位置を -0.5〜0.5 に正規化
const px = (e.clientX - rect.left) / rect.width - 0.5;
const py = (e.clientY - rect.top) / rect.height - 0.5;
// 上下=rotateX、左右=rotateY
card.style.setProperty("--rx", `${(-py * MAX).toFixed(2)}deg`);
card.style.setProperty("--ry", `${(px * MAX).toFixed(2)}deg`);
});
// 離れたら水平に戻す
stage.addEventListener("pointerleave", () => {
card.style.setProperty("--rx", "0deg");
card.style.setProperty("--ry", "0deg");
});
// 送信はデモなのでページ遷移させない
card.addEventListener("submit", (e) => {
e.preventDefault();
const btn = card.querySelector(".tl-card__btn");
if (!btn) return;
const label = btn.textContent;
btn.textContent = "サインイン中…";
setTimeout(() => { btn.textContent = label; }, 1400);
});
}
実装ガイド
使いどころ
ログイン/サインアップフォーム、特集モーダル、製品紹介のインタラクティブなヒーローパネルなど、没入感と高級感を出したい場面に向きます。マウス追従の3D傾斜が加わるため、ファーストビューで印象付けたい単体パネルに最適です。
実装時の注意点
親に perspective を、パネルに transform-style: preserve-3d と rotateX/rotateY を与え、回転角は JS が -0.5〜0.5 に正規化したマウス位置から算出して CSS 変数(--rx/--ry)で渡します。傾きと backdrop-filter のガラス質感を両立させ、入力欄も半透明にして一体感を出します。prefers-reduced-motion 時は JS 側で傾斜を無効化し、CSS でも transform: none に戻す二重の配慮が入っています。
対応ブラウザ
backdrop-filter は Chrome/Edge 標準、Firefox 103+ 標準、Safari は -webkit- プレフィックス必須(本デモ併記)です。3D 変換(perspective/rotateX/preserve-3d)は全モダンブラウザ対応ですが、親要素に backdrop-filter があると 3D 描画コンテキストやフィルタ適用が崩れる組み合わせ問題があるため、傾斜とフィルタの掛かる要素の階層設計に注意が必要です。
よくある失敗
暗背景に白半透明パネル+白文字でコントラストが不足しやすく、入力文字やプレースホルダの可読性が落ちがちです。傾斜角を大きくしすぎると文字が読みづらく酔いやすく、フォーム入力中に傾くと操作性を損ないます。backdrop-filter+3D変換は両方とも GPU を使うため低スペック端末で重く、タッチ端末では pointermove での傾斜が働かない(または誤作動する)点も見落としがちです。
応用例
傾斜に連動して光沢グラデの位置も動かすと立体感が増し、影(box-shadow)も角度で動かすと地に足が付きます。入力フォーカス時は傾斜を一時停止して操作性を確保、reduced-motion とタッチ判定でアニメを切る、視差(parallax)で背景オーブを逆方向に動かす、といった発展でリッチさと使いやすさを両立できます。
コード
<!-- 3Dチルトするガラスログインパネル -->
<div class="gt-scene">
<span class="gt-orb gt-orb--1"></span>
<span class="gt-orb gt-orb--2"></span>
<!-- マウスに合わせて傾くガラスパネル -->
<form class="gt-glass" id="gtGlass" novalidate>
<h2 class="gt-glass__title">Welcome back</h2>
<p class="gt-glass__sub">アカウントにサインイン</p>
<label class="gt-field">
<span>メール</span>
<input type="email" placeholder="you@example.com" autocomplete="off" />
</label>
<label class="gt-field">
<span>パスワード</span>
<input type="password" placeholder="••••••••" autocomplete="off" />
</label>
<button class="gt-submit" type="submit">サインイン</button>
<p class="gt-msg" id="gtMsg"></p>
</form>
</div>
/* 3Dチルトするガラスログイン */
:root {
--glass: rgba(255, 255, 255, 0.12);
--stroke: rgba(255, 255, 255, 0.4);
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 360px;
font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
background: linear-gradient(135deg, #0f172a 0%, #312e81 55%, #6d28d9 100%);
overflow: hidden;
}
/* 3D表現のための遠近感 */
.gt-scene {
position: relative;
width: 100%; height: 360px;
display: grid; place-items: center;
perspective: 900px;
}
/* 背景の発光オーブ */
.gt-orb { position: absolute; border-radius: 50%; filter: blur(40px); opacity: 0.7; }
.gt-orb--1 { width: 200px; height: 200px; background: #22d3ee; top: 8%; left: 12%; }
.gt-orb--2 { width: 220px; height: 220px; background: #f472b6; bottom: 4%; right: 10%; }
/* ガラスパネル本体(JSで回転角を渡す) */
.gt-glass {
position: relative;
width: min(320px, 82vw);
padding: 26px 28px 24px;
border-radius: 22px;
background: var(--glass);
border: 1px solid var(--stroke);
-webkit-backdrop-filter: blur(18px) saturate(140%);
backdrop-filter: blur(18px) saturate(140%);
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.4);
color: #fff;
transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
transition: transform 0.12s ease-out;
transform-style: preserve-3d;
}
.gt-glass__title { margin: 0 0 2px; font-size: 22px; font-weight: 700; }
.gt-glass__sub { margin: 0 0 18px; font-size: 12.5px; color: rgba(255, 255, 255, 0.78); }
.gt-field { display: block; margin-bottom: 13px; }
.gt-field span { display: block; font-size: 11px; margin-bottom: 5px; color: rgba(255, 255, 255, 0.8); letter-spacing: 0.04em; }
.gt-field input {
width: 100%;
font: inherit; font-size: 13px;
color: #fff;
padding: 10px 13px;
border-radius: 11px;
border: 1px solid rgba(255, 255, 255, 0.25);
background: rgba(255, 255, 255, 0.1);
outline: none;
transition: border-color 0.2s ease, background 0.2s ease;
}
.gt-field input::placeholder { color: rgba(255, 255, 255, 0.5); }
.gt-field input:focus { border-color: #67e8f9; background: rgba(255, 255, 255, 0.16); }
.gt-submit {
width: 100%;
font: inherit; font-weight: 700; font-size: 14px;
color: #0f172a; cursor: pointer;
margin-top: 6px;
padding: 11px;
border: none;
border-radius: 12px;
background: linear-gradient(135deg, #67e8f9, #a5b4fc);
transition: transform 0.15s ease, filter 0.15s ease;
}
.gt-submit:hover { filter: brightness(1.06); }
.gt-submit:active { transform: scale(0.98); }
.gt-msg { margin: 10px 0 0; min-height: 1em; font-size: 12px; text-align: center; color: #6ee7b7; }
@media (prefers-reduced-motion: reduce) {
.gt-glass { transition: none; transform: none; }
}
// マウス位置に応じてガラスパネルを3D傾斜させる
const glass = document.getElementById("gtGlass");
const scene = glass?.parentElement;
const msg = document.getElementById("gtMsg");
// reduced-motion 時は傾けない
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (glass && scene && !reduce) {
scene.addEventListener("pointermove", (e) => {
const rect = scene.getBoundingClientRect();
// -0.5〜0.5 に正規化した位置から回転角を計算
const px = (e.clientX - rect.left) / rect.width - 0.5;
const py = (e.clientY - rect.top) / rect.height - 0.5;
glass.style.setProperty("--ry", `${px * 12}deg`);
glass.style.setProperty("--rx", `${-py * 12}deg`);
});
// 離れたら水平に戻す
scene.addEventListener("pointerleave", () => {
glass.style.setProperty("--ry", "0deg");
glass.style.setProperty("--rx", "0deg");
});
}
// 送信はデモなので遷移させず、メッセージのみ表示
glass?.addEventListener("submit", (e) => {
e.preventDefault();
if (msg) {
msg.textContent = "サインインしました(デモ)";
setTimeout(() => { msg.textContent = ""; }, 1800);
}
});
🤖 AIエージェント用プロンプト
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「3Dチルト・ガラスログイン」の効果を追加してください。
# 追加してほしい効果
3Dチルト・ガラスログイン(グラス / ニューモーフィズム)
マウス位置に合わせて立体的に傾くガラス調のログインパネル。perspective と backdrop-filter を組み合わせた没入感のあるフォームです。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- 3Dチルトするガラスログインパネル -->
<div class="gt-scene">
<span class="gt-orb gt-orb--1"></span>
<span class="gt-orb gt-orb--2"></span>
<!-- マウスに合わせて傾くガラスパネル -->
<form class="gt-glass" id="gtGlass" novalidate>
<h2 class="gt-glass__title">Welcome back</h2>
<p class="gt-glass__sub">アカウントにサインイン</p>
<label class="gt-field">
<span>メール</span>
<input type="email" placeholder="you@example.com" autocomplete="off" />
</label>
<label class="gt-field">
<span>パスワード</span>
<input type="password" placeholder="••••••••" autocomplete="off" />
</label>
<button class="gt-submit" type="submit">サインイン</button>
<p class="gt-msg" id="gtMsg"></p>
</form>
</div>
【CSS】
/* 3Dチルトするガラスログイン */
:root {
--glass: rgba(255, 255, 255, 0.12);
--stroke: rgba(255, 255, 255, 0.4);
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 360px;
font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
background: linear-gradient(135deg, #0f172a 0%, #312e81 55%, #6d28d9 100%);
overflow: hidden;
}
/* 3D表現のための遠近感 */
.gt-scene {
position: relative;
width: 100%; height: 360px;
display: grid; place-items: center;
perspective: 900px;
}
/* 背景の発光オーブ */
.gt-orb { position: absolute; border-radius: 50%; filter: blur(40px); opacity: 0.7; }
.gt-orb--1 { width: 200px; height: 200px; background: #22d3ee; top: 8%; left: 12%; }
.gt-orb--2 { width: 220px; height: 220px; background: #f472b6; bottom: 4%; right: 10%; }
/* ガラスパネル本体(JSで回転角を渡す) */
.gt-glass {
position: relative;
width: min(320px, 82vw);
padding: 26px 28px 24px;
border-radius: 22px;
background: var(--glass);
border: 1px solid var(--stroke);
-webkit-backdrop-filter: blur(18px) saturate(140%);
backdrop-filter: blur(18px) saturate(140%);
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.4);
color: #fff;
transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
transition: transform 0.12s ease-out;
transform-style: preserve-3d;
}
.gt-glass__title { margin: 0 0 2px; font-size: 22px; font-weight: 700; }
.gt-glass__sub { margin: 0 0 18px; font-size: 12.5px; color: rgba(255, 255, 255, 0.78); }
.gt-field { display: block; margin-bottom: 13px; }
.gt-field span { display: block; font-size: 11px; margin-bottom: 5px; color: rgba(255, 255, 255, 0.8); letter-spacing: 0.04em; }
.gt-field input {
width: 100%;
font: inherit; font-size: 13px;
color: #fff;
padding: 10px 13px;
border-radius: 11px;
border: 1px solid rgba(255, 255, 255, 0.25);
background: rgba(255, 255, 255, 0.1);
outline: none;
transition: border-color 0.2s ease, background 0.2s ease;
}
.gt-field input::placeholder { color: rgba(255, 255, 255, 0.5); }
.gt-field input:focus { border-color: #67e8f9; background: rgba(255, 255, 255, 0.16); }
.gt-submit {
width: 100%;
font: inherit; font-weight: 700; font-size: 14px;
color: #0f172a; cursor: pointer;
margin-top: 6px;
padding: 11px;
border: none;
border-radius: 12px;
background: linear-gradient(135deg, #67e8f9, #a5b4fc);
transition: transform 0.15s ease, filter 0.15s ease;
}
.gt-submit:hover { filter: brightness(1.06); }
.gt-submit:active { transform: scale(0.98); }
.gt-msg { margin: 10px 0 0; min-height: 1em; font-size: 12px; text-align: center; color: #6ee7b7; }
@media (prefers-reduced-motion: reduce) {
.gt-glass { transition: none; transform: none; }
}
【JavaScript】
// マウス位置に応じてガラスパネルを3D傾斜させる
const glass = document.getElementById("gtGlass");
const scene = glass?.parentElement;
const msg = document.getElementById("gtMsg");
// reduced-motion 時は傾けない
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (glass && scene && !reduce) {
scene.addEventListener("pointermove", (e) => {
const rect = scene.getBoundingClientRect();
// -0.5〜0.5 に正規化した位置から回転角を計算
const px = (e.clientX - rect.left) / rect.width - 0.5;
const py = (e.clientY - rect.top) / rect.height - 0.5;
glass.style.setProperty("--ry", `${px * 12}deg`);
glass.style.setProperty("--rx", `${-py * 12}deg`);
});
// 離れたら水平に戻す
scene.addEventListener("pointerleave", () => {
glass.style.setProperty("--ry", "0deg");
glass.style.setProperty("--rx", "0deg");
});
}
// 送信はデモなので遷移させず、メッセージのみ表示
glass?.addEventListener("submit", (e) => {
e.preventDefault();
if (msg) {
msg.textContent = "サインインしました(デモ)";
setTimeout(() => { msg.textContent = ""; }, 1800);
}
});
# 外部ライブラリ
なし(追加ライブラリ不要)
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。