フロストナビバー

スクロールするコンテンツの上で背後が透けるフロストガラスのナビバー。スクロール量に応じて濃さが変わり、ヘッダーに活用できます。

#css#glassmorphism#navbar#javascript#scroll

ライブデモ

使用例(お題: カフェ MOON BREW)

この技法を「カフェ MOON BREW」というテーマのダミーサイトで実際に使った例です。

HTML
<!-- MOON BREW:フロストナビ+ヒーロー(内部スクロール) -->
<div class="mb-app" id="mbScroll">
  <!-- スクロールに応じて濃さが変わるフロストナビ -->
  <header class="mb-nav" id="mbNav">
    <a class="mb-nav__logo" href="#">
      <span class="mb-nav__moon">☾</span> MOON BREW
    </a>
    <nav class="mb-nav__menu">
      <a href="#">メニュー</a>
      <a href="#">豆を選ぶ</a>
      <a href="#">店舗</a>
      <a class="mb-nav__cta" href="#">予約する</a>
    </nav>
  </header>

  <!-- ヒーロー -->
  <section class="mb-hero">
    <span class="mb-hero__tag">SPECIALTY COFFEE</span>
    <h1 class="mb-hero__title">月夜に、<br>一杯の余白を。</h1>
    <p class="mb-hero__lead">厳選した自家焙煎のスペシャルティコーヒーを、静かな夜にゆっくりと。</p>
    <a class="mb-hero__btn" href="#">本日のおすすめを見る</a>
  </section>

  <!-- スクロール用の続きコンテンツ -->
  <section class="mb-strip">
    <article class="mb-bean"><h3>エチオピア イルガチェフェ</h3><p>華やかな柑橘とフローラルな香り。</p></article>
    <article class="mb-bean"><h3>グァテマラ アンティグア</h3><p>チョコのコクと心地よい余韻。</p></article>
    <article class="mb-bean"><h3>ケニア ニエリ</h3><p>ジューシーな果実味と明るい酸。</p></article>
  </section>
</div>
CSS
/* MOON BREW:フロストナビ+ヒーロー */
:root {
  --cream: #f5ede1;
  --brown: #2b1d12;
  --amber: #c98a3b;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  height: 400px;
  font-family: "Hiragino Mincho ProN", "Yu Mincho", "Segoe UI", serif;
  background: var(--brown);
  overflow: hidden;
}

/* 内部スクロールするアプリ枠 */
.mb-app {
  position: relative;
  height: 400px;
  overflow-y: auto;
  background: var(--cream);
  scroll-behavior: smooth;
}
.mb-app::-webkit-scrollbar { width: 0; }

/* フロストナビ:背後が透けるガラス。スクロールで濃さ変化 */
.mb-nav {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 22px;
  background: rgba(245, 237, 225, var(--nav-alpha, 0.15));
  -webkit-backdrop-filter: blur(10px) saturate(1.2);
  backdrop-filter: blur(10px) saturate(1.2);
  border-bottom: 1px solid rgba(43, 29, 18, var(--nav-line, 0.05));
  transition: background 0.3s ease, border-color 0.3s ease;
}
.mb-nav__logo {
  font-size: 17px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--brown);
  text-decoration: none;
}
.mb-nav__moon { color: var(--amber); margin-right: 4px; }

.mb-nav__menu { display: flex; align-items: center; gap: 18px; }
.mb-nav__menu a {
  font-size: 13px;
  color: var(--brown);
  text-decoration: none;
  font-family: "Hiragino Kaku Gothic ProN", system-ui, sans-serif;
  opacity: 0.85;
  transition: opacity 0.2s ease;
}
.mb-nav__menu a:hover { opacity: 1; }
.mb-nav__cta {
  padding: 7px 16px;
  border-radius: 999px;
  background: var(--amber);
  color: #fff !important;
  font-weight: 700;
  opacity: 1 !important;
}

/* ヒーロー:コーヒー写真の上 */
.mb-hero {
  position: relative;
  min-height: 340px;
  padding: 60px 28px 40px;
  color: #fff;
  background:
    linear-gradient(180deg, rgba(43,29,18,0.55), rgba(43,29,18,0.75)),
    url("https://picsum.photos/700/500?random=42") center/cover no-repeat;
}
.mb-hero__tag {
  display: inline-block;
  font-size: 10px;
  letter-spacing: 0.3em;
  color: var(--amber);
  font-family: "Hiragino Kaku Gothic ProN", system-ui, sans-serif;
}
.mb-hero__title { margin: 12px 0 12px; font-size: 30px; line-height: 1.4; font-weight: 700; }
.mb-hero__lead {
  margin: 0 0 22px;
  font-size: 13px;
  line-height: 1.8;
  max-width: 320px;
  color: rgba(255,255,255,0.9);
  font-family: "Hiragino Kaku Gothic ProN", system-ui, sans-serif;
}
.mb-hero__btn {
  display: inline-block;
  padding: 11px 22px;
  border-radius: 999px;
  background: var(--amber);
  color: #fff;
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  font-family: "Hiragino Kaku Gothic ProN", system-ui, sans-serif;
  box-shadow: 0 8px 20px rgba(201,138,59,0.4);
  transition: transform 0.2s ease;
}
.mb-hero__btn:hover { transform: translateY(-2px); }

/* スクロール用の豆カード */
.mb-strip {
  display: grid;
  gap: 14px;
  padding: 26px 24px 40px;
  background: var(--cream);
  font-family: "Hiragino Kaku Gothic ProN", system-ui, sans-serif;
}
.mb-bean {
  padding: 16px 18px;
  border-radius: 16px;
  background: #fff;
  box-shadow: 0 6px 18px rgba(43,29,18,0.08);
}
.mb-bean h3 { margin: 0 0 6px; font-size: 15px; color: var(--brown); }
.mb-bean p { margin: 0; font-size: 12.5px; color: #6d5b49; line-height: 1.6; }

@media (prefers-reduced-motion: reduce) {
  .mb-nav, .mb-hero__btn { transition: none; }
  .mb-app { scroll-behavior: auto; }
}
JavaScript
// 内部スクロール量に応じてフロストナビの不透明度を変える
const scroller = document.getElementById("mbScroll");
const nav = document.getElementById("mbNav");

if (scroller && nav) {
  const update = () => {
    // 0〜120pxのスクロールで 0→1 に正規化
    const t = Math.min(scroller.scrollTop / 120, 1);
    // 透明(0.15)→ クリーム色が濃く(0.92)
    const alpha = (0.15 + t * 0.77).toFixed(3);
    const line = (0.05 + t * 0.1).toFixed(3);
    nav.style.setProperty("--nav-alpha", alpha);
    nav.style.setProperty("--nav-line", line);
  };
  scroller.addEventListener("scroll", update, { passive: true });
  update(); // 初期反映
}

実装ガイド

使いどころ

コンテンツの上に固定表示するサイトヘッダーやアプリのトップバーに最適で、ヒーロー画像やカラフルな帯の上を流れても背後が透けて没入感を保てます。スクロールに応じて濃くなる挙動はランディングページのナビに向きます。

実装時の注意点

背後を透かすため background は rgba の半透明にし、backdrop-filter: blur() saturate() でぼかしと彩度を補正します。スクロール量を JS で監視し scrollTop>20 で .is-stuck を付け、背景を濃く+box-shadow を足して可読性とヘッダーの存在感を切り替えます。固定要素には z-index を与え、透過部分でも常にメニュー文字が読める不透明度を background に確保しておきます。

対応ブラウザ

backdrop-filter は Chrome/Edge 標準、Firefox 103+ で標準対応、Safari は -webkit-backdrop-filter プレフィックスが必要で、本デモも両方を記述しています。saturate() を併用する複合フィルタも同じ対応状況です。未対応時は blur が効かず半透明バーになるため、.is-stuck 相当の濃い背景をフォールバックにすると安全です。

よくある失敗

透過したまま明るい背景上に濃い文字、暗い背景上に同系色の文字を置くとスクロール位置によってコントラストが破綻します(4.5:1 を背景の最も不利な箇所で満たす必要あり)。固定ナビは backdrop-filter の再描画がスクロール中ずっと走るためモバイルで重くなりがちで、blur 値を上げすぎると背後の内容が判別不能になります。position: fixed と backdrop-filter は親に filter/transform があると効かなくなる点も要注意です。

応用例

IntersectionObserver や scroll で blur 量・背景濃度を連続的に変える可変フロスト、スクロール方向でナビを隠す/出す(hide-on-scroll)との併用、ダークモードでは rgba を暗色側に切り替える、メニューのアクティブ表示を現在地セクションと連動させる、といった発展が定番です。

コード

HTML
<!-- フロストナビバー:スクロールするコンテンツの上に固定された半透明バー -->
<div class="fn-frame">
  <!-- 固定されたフロストナビ -->
  <nav class="fn-nav" id="fnNav">
    <span class="fn-nav__logo">Aurora</span>
    <ul class="fn-nav__menu">
      <li><a href="#" class="is-active" data-nav>ホーム</a></li>
      <li><a href="#" data-nav>製品</a></li>
      <li><a href="#" data-nav>料金</a></li>
      <li><a href="#" data-nav>会社</a></li>
    </ul>
    <button class="fn-nav__cta" type="button">登録</button>
  </nav>

  <!-- ナビ下のスクロール領域(透けて見える背景) -->
  <div class="fn-scroll" id="fnScroll">
    <div class="fn-hero"><h1>Frosted Nav</h1><p>スクロールするとバーの背後が透けます</p></div>
    <div class="fn-band fn-band--a"></div>
    <div class="fn-band fn-band--b"></div>
    <div class="fn-band fn-band--c"></div>
    <div class="fn-band fn-band--d"></div>
  </div>
</div>
CSS
/* フロストナビ:backdrop-filter で背後をぼかす */
:root {
  --nav-bg: rgba(255, 255, 255, 0.55);
  --nav-bg-solid: rgba(255, 255, 255, 0.78);
  --ink: #1f2937;
  --accent: #7c3aed;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
}

/* iframe内に収める固定高フレーム */
.fn-frame {
  position: relative;
  height: 360px;
  overflow: hidden;
  border-radius: 0;
}

/* 上部固定のフロストバー */
.fn-nav {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 13px 22px;
  background: var(--nav-bg);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  backdrop-filter: blur(14px) saturate(160%);
  border-bottom: 1px solid rgba(255, 255, 255, 0.5);
  transition: background 0.3s ease, box-shadow 0.3s ease;
}
/* スクロール時に濃く+影を付ける(JSで .is-stuck 付与) */
.fn-nav.is-stuck {
  background: var(--nav-bg-solid);
  box-shadow: 0 6px 20px rgba(31, 41, 55, 0.12);
}

.fn-nav__logo { font-size: 19px; font-weight: 800; color: var(--accent); letter-spacing: -0.02em; }

.fn-nav__menu { display: flex; gap: 6px; list-style: none; margin: 0; padding: 0; margin-left: auto; }
.fn-nav__menu a {
  display: block;
  font-size: 13px; font-weight: 600;
  color: var(--ink);
  text-decoration: none;
  padding: 7px 13px;
  border-radius: 999px;
  transition: background 0.2s ease, color 0.2s ease;
}
.fn-nav__menu a:hover { background: rgba(124, 58, 237, 0.1); }
.fn-nav__menu a.is-active { background: var(--accent); color: #fff; }

.fn-nav__cta {
  font: inherit; font-size: 13px; font-weight: 700;
  color: #fff; cursor: pointer;
  border: none;
  padding: 8px 18px;
  border-radius: 999px;
  background: linear-gradient(135deg, #7c3aed, #db2777);
  transition: transform 0.15s ease, filter 0.15s ease;
}
.fn-nav__cta:hover { filter: brightness(1.08); transform: translateY(-1px); }

/* スクロール領域 */
.fn-scroll { height: 100%; overflow-y: auto; scroll-behavior: smooth; }

.fn-hero {
  height: 230px;
  display: grid; place-content: center; text-align: center;
  color: #fff;
  background: linear-gradient(135deg, #6366f1, #ec4899);
  padding-top: 40px;
}
.fn-hero h1 { margin: 0; font-size: 32px; }
.fn-hero p { margin: 8px 0 0; font-size: 13px; opacity: 0.92; }

/* カラフルな帯で透過のおもしろさを見せる */
.fn-band { height: 130px; }
.fn-band--a { background: linear-gradient(135deg, #f59e0b, #ef4444); }
.fn-band--b { background: linear-gradient(135deg, #10b981, #06b6d4); }
.fn-band--c { background: linear-gradient(135deg, #8b5cf6, #6366f1); }
.fn-band--d { background: linear-gradient(135deg, #f43f5e, #f59e0b); }

@media (prefers-reduced-motion: reduce) {
  .fn-scroll { scroll-behavior: auto; }
  .fn-nav, .fn-nav__cta { transition: none; }
}
JavaScript
// スクロール量に応じてナビを濃く+メニューのアクティブ切替
const nav = document.getElementById("fnNav");
const scroller = document.getElementById("fnScroll");
const links = document.querySelectorAll("[data-nav]");

// スクロールでナビの見た目を変える
scroller?.addEventListener("scroll", () => {
  if (!nav) return;
  nav.classList.toggle("is-stuck", scroller.scrollTop > 20);
});

// メニュークリックでアクティブ表示を移動(既定遷移は抑止)
links.forEach((link) => {
  link.addEventListener("click", (e) => {
    e.preventDefault();
    links.forEach((l) => l.classList.remove("is-active"));
    link.classList.add("is-active");
  });
});

🤖 AIエージェント用プロンプト

このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「フロストナビバー」の効果を追加してください。

# 追加してほしい効果
フロストナビバー(グラス / ニューモーフィズム)
スクロールするコンテンツの上で背後が透けるフロストガラスのナビバー。スクロール量に応じて濃さが変わり、ヘッダーに活用できます。

# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- フロストナビバー:スクロールするコンテンツの上に固定された半透明バー -->
<div class="fn-frame">
  <!-- 固定されたフロストナビ -->
  <nav class="fn-nav" id="fnNav">
    <span class="fn-nav__logo">Aurora</span>
    <ul class="fn-nav__menu">
      <li><a href="#" class="is-active" data-nav>ホーム</a></li>
      <li><a href="#" data-nav>製品</a></li>
      <li><a href="#" data-nav>料金</a></li>
      <li><a href="#" data-nav>会社</a></li>
    </ul>
    <button class="fn-nav__cta" type="button">登録</button>
  </nav>

  <!-- ナビ下のスクロール領域(透けて見える背景) -->
  <div class="fn-scroll" id="fnScroll">
    <div class="fn-hero"><h1>Frosted Nav</h1><p>スクロールするとバーの背後が透けます</p></div>
    <div class="fn-band fn-band--a"></div>
    <div class="fn-band fn-band--b"></div>
    <div class="fn-band fn-band--c"></div>
    <div class="fn-band fn-band--d"></div>
  </div>
</div>

【CSS】
/* フロストナビ:backdrop-filter で背後をぼかす */
:root {
  --nav-bg: rgba(255, 255, 255, 0.55);
  --nav-bg-solid: rgba(255, 255, 255, 0.78);
  --ink: #1f2937;
  --accent: #7c3aed;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
}

/* iframe内に収める固定高フレーム */
.fn-frame {
  position: relative;
  height: 360px;
  overflow: hidden;
  border-radius: 0;
}

/* 上部固定のフロストバー */
.fn-nav {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 13px 22px;
  background: var(--nav-bg);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  backdrop-filter: blur(14px) saturate(160%);
  border-bottom: 1px solid rgba(255, 255, 255, 0.5);
  transition: background 0.3s ease, box-shadow 0.3s ease;
}
/* スクロール時に濃く+影を付ける(JSで .is-stuck 付与) */
.fn-nav.is-stuck {
  background: var(--nav-bg-solid);
  box-shadow: 0 6px 20px rgba(31, 41, 55, 0.12);
}

.fn-nav__logo { font-size: 19px; font-weight: 800; color: var(--accent); letter-spacing: -0.02em; }

.fn-nav__menu { display: flex; gap: 6px; list-style: none; margin: 0; padding: 0; margin-left: auto; }
.fn-nav__menu a {
  display: block;
  font-size: 13px; font-weight: 600;
  color: var(--ink);
  text-decoration: none;
  padding: 7px 13px;
  border-radius: 999px;
  transition: background 0.2s ease, color 0.2s ease;
}
.fn-nav__menu a:hover { background: rgba(124, 58, 237, 0.1); }
.fn-nav__menu a.is-active { background: var(--accent); color: #fff; }

.fn-nav__cta {
  font: inherit; font-size: 13px; font-weight: 700;
  color: #fff; cursor: pointer;
  border: none;
  padding: 8px 18px;
  border-radius: 999px;
  background: linear-gradient(135deg, #7c3aed, #db2777);
  transition: transform 0.15s ease, filter 0.15s ease;
}
.fn-nav__cta:hover { filter: brightness(1.08); transform: translateY(-1px); }

/* スクロール領域 */
.fn-scroll { height: 100%; overflow-y: auto; scroll-behavior: smooth; }

.fn-hero {
  height: 230px;
  display: grid; place-content: center; text-align: center;
  color: #fff;
  background: linear-gradient(135deg, #6366f1, #ec4899);
  padding-top: 40px;
}
.fn-hero h1 { margin: 0; font-size: 32px; }
.fn-hero p { margin: 8px 0 0; font-size: 13px; opacity: 0.92; }

/* カラフルな帯で透過のおもしろさを見せる */
.fn-band { height: 130px; }
.fn-band--a { background: linear-gradient(135deg, #f59e0b, #ef4444); }
.fn-band--b { background: linear-gradient(135deg, #10b981, #06b6d4); }
.fn-band--c { background: linear-gradient(135deg, #8b5cf6, #6366f1); }
.fn-band--d { background: linear-gradient(135deg, #f43f5e, #f59e0b); }

@media (prefers-reduced-motion: reduce) {
  .fn-scroll { scroll-behavior: auto; }
  .fn-nav, .fn-nav__cta { transition: none; }
}

【JavaScript】
// スクロール量に応じてナビを濃く+メニューのアクティブ切替
const nav = document.getElementById("fnNav");
const scroller = document.getElementById("fnScroll");
const links = document.querySelectorAll("[data-nav]");

// スクロールでナビの見た目を変える
scroller?.addEventListener("scroll", () => {
  if (!nav) return;
  nav.classList.toggle("is-stuck", scroller.scrollTop > 20);
});

// メニュークリックでアクティブ表示を移動(既定遷移は抑止)
links.forEach((link) => {
  link.addEventListener("click", (e) => {
    e.preventDefault();
    links.forEach((l) => l.classList.remove("is-active"));
    link.classList.add("is-active");
  });
});

# 外部ライブラリ
なし(追加ライブラリ不要)

# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。