No.183
MODAL CSSJS

Fullscreen Menu Overlay

メニューボタンを押すと、画面全体を覆う黒い一覧が上から降りてきて、項目が少しずつ遅れて浮かび上がるナビゲーション。3本線は×印に変わり、開いている間はフォーカスをメニューの中にとどめます。

MY COLLECTION RANDOM ↗

DEMO

フルスクリーンメニュー

右上のボタンでメニューを開くと、画面全体を覆う一覧が上から降りてきて、項目が1つずつ現れます。

CODE

HTML
<div class="demo-stage fsm-stage">
  <div class="fsm-site" id="fsm-site">
    <header class="fsm-head">
      <span class="fsm-logo">KAPPY</span>
      <button type="button" class="fsm-toggle" id="fsm-toggle" aria-expanded="false" aria-controls="fsm-nav" aria-label="メニューを開く"><span class="fsm-lines" aria-hidden="true"><i></i><i></i><i></i></span></button>
    </header>
    <div class="fsm-body" id="fsm-body">
      <h3>フルスクリーンメニュー</h3>
      <p>右上のボタンでメニューを開くと、画面全体を覆う一覧が上から降りてきて、項目が1つずつ現れます。</p>
      <p class="fsm-status" id="fsm-status" role="status" aria-live="polite"></p>
    </div>
    <nav class="fsm-nav" id="fsm-nav" aria-label="サイトメニュー">
      <ul>
        <li><a href="#works">WORKS</a></li>
        <li><a href="#about">ABOUT</a></li>
        <li><a href="#journal">JOURNAL</a></li>
        <li><a href="#contact">CONTACT</a></li>
      </ul>
    </nav>
  </div>
</div>
CSS
.fsm-stage { display:flex; align-items:center; justify-content:center; padding:1.5rem; background:#0B0B10; min-height:340px; }
.fsm-site { position:relative; width:100%; max-width:380px; height:300px; border-radius:14px; overflow:hidden; background:#F7F5F2; color:#1A1A1A; }
.fsm-head { position:relative; z-index:3; display:flex; align-items:center; justify-content:space-between; padding:.6rem .6rem .6rem 1rem; }
.fsm-logo { font-family:'Oswald',sans-serif; font-size:.9rem; letter-spacing:.2em; }
.fsm-toggle { width:44px; height:44px; border:0; border-radius:50%; background:transparent; color:inherit; cursor:pointer; }
.fsm-toggle:focus-visible { outline:2px solid #E91B89; outline-offset:2px; }
.fsm-logo, .fsm-toggle { transition: color .2s ease .25s; }
.fsm-site.is-open .fsm-logo, .fsm-site.is-open .fsm-toggle { color:#fff; transition-delay:.15s; }
.fsm-lines { position:relative; display:block; width:20px; height:14px; margin:0 auto; }
.fsm-lines i { position:absolute; left:0; width:100%; height:2px; border-radius:1px; background:currentColor; transition: transform .3s ease, opacity .2s ease; }
.fsm-lines i:nth-child(1) { top:0; }
.fsm-lines i:nth-child(2) { top:6px; }
.fsm-lines i:nth-child(3) { top:12px; }
.fsm-site.is-open .fsm-lines i:nth-child(1) { transform:translateY(6px) rotate(45deg); }
.fsm-site.is-open .fsm-lines i:nth-child(2) { opacity:0; }
.fsm-site.is-open .fsm-lines i:nth-child(3) { transform:translateY(-6px) rotate(-45deg); }
.fsm-body { padding:.4rem 1rem; }
.fsm-body h3 { margin:0 0 .4rem; font-size:1.05rem; }
.fsm-body p { margin:0; color:#5A5A5A; font-size:.82rem; line-height:1.7; }
.fsm-body .fsm-status { margin-top:.6rem; min-height:1.2em; color:#1A1A1A; font-size:.78rem; }
.fsm-nav { position:absolute; inset:0; z-index:2; display:flex; align-items:center; padding:3.5rem 1.4rem 1.4rem; background:#1A1A1A; visibility:hidden; clip-path:inset(0 0 100% 0);
  transition: clip-path .45s cubic-bezier(.77,0,.18,1), visibility 0s linear .45s; }
.fsm-site.is-open .fsm-nav { visibility:visible; clip-path:inset(0); transition: clip-path .45s cubic-bezier(.77,0,.18,1), visibility 0s; }
.fsm-nav ul { list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:.2rem; }
.fsm-nav a { display:inline-block; padding:.25rem 0; color:#fff; font-family:'Oswald',sans-serif; font-size:1.7rem; letter-spacing:.08em; text-decoration:none; opacity:0; transform:translateY(16px); transition: opacity .3s ease, transform .3s ease, color .2s ease; }
.fsm-nav a:hover, .fsm-nav a:focus-visible { color:#F472B6; }
.fsm-nav a:focus-visible { outline:2px solid #F472B6; outline-offset:4px; }
.fsm-site.is-open .fsm-nav a { opacity:1; transform:none; transition-delay:calc(.25s + var(--d, 0s)), calc(.25s + var(--d, 0s)), 0s; }
.fsm-nav li:nth-child(2) a { --d:.06s; }
.fsm-nav li:nth-child(3) a { --d:.12s; }
.fsm-nav li:nth-child(4) a { --d:.18s; }
@media (prefers-reduced-motion: reduce) {
  .fsm-nav, .fsm-nav a, .fsm-lines i, .fsm-logo, .fsm-toggle { transition-duration:.01ms !important; transition-delay:0s !important; }
}
JS
(function(){
  var site = document.getElementById('fsm-site');
  var btn = document.getElementById('fsm-toggle');
  var nav = document.getElementById('fsm-nav');
  var body = document.getElementById('fsm-body');
  var status = document.getElementById('fsm-status');
  if (!site || !btn || !nav || !body || !status) return;
  var links = Array.prototype.slice.call(nav.querySelectorAll('a'));
  function set(open){
    site.classList.toggle('is-open', open);
    btn.setAttribute('aria-expanded', open ? 'true' : 'false');
    btn.setAttribute('aria-label', open ? 'メニューを閉じる' : 'メニューを開く');
    body.inert = open;
    (open ? links[0] : btn).focus({ preventScroll: true });
  }
  btn.addEventListener('click', function(){ set(!site.classList.contains('is-open')); });
  site.addEventListener('mousedown', function(e){
    if (site.classList.contains('is-open') && !e.target.closest('a, button')) e.preventDefault();
  });
  site.addEventListener('keydown', function(e){
    if (!site.classList.contains('is-open')) return;
    if (e.key === 'Escape') { e.preventDefault(); set(false); return; }
    if (e.key !== 'Tab') return;
    var last = links[links.length - 1];
    if (e.shiftKey && document.activeElement === btn) { e.preventDefault(); last.focus(); }
    else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); btn.focus(); }
  });
  links.forEach(function(a){
    a.addEventListener('click', function(e){
      e.preventDefault();
      set(false);
      status.textContent = '「' + a.textContent + '」を選びました(デモのため移動しません)';
    });
  });
})();

AI PROMPT

Claude Code Cursor v0
PROMPT
メニューボタンを押すと、画面全体を覆う黒い一覧が上から降りてきて、項目が少しずつ遅れて浮かび上がる全画面メニューを作って。メニュー(nav・aria-label 付き)は position:absolute; inset:0 で(デモでは枠の)全体を覆い、閉じている時は clip-path: inset(0 0 100% 0) と visibility:hidden、開いた時は clip-path: inset(0) と visibility:visible にして、clip-path を .45秒・cubic-bezier(.77,0,.18,1) で動かす(visibility は、開く時はすぐ、閉じる時は .45秒遅らせて切り替え、閉じている間は読み上げやフォーカスの対象から外す)。項目のリンクは opacity:0・translateY(16px) から、開いた時に .25秒遅れて浮かび上がらせ、2つ目以降は nth-child で .06秒ずつ遅らせる(遅れは opacity と transform だけに付け、ホバー時の color の変化は遅らせない)。メニューボタンは3本線で、開くと上下の線を transform: translateY(±6px) rotate(±45度) で真ん中へ寄せて×印にし、真ん中の線を消す(top は動かさない)。ボタンはメニューより手前(z-index を高く)に置いて閉じる時にも押せるようにし、ロゴとボタンの文字色は黒い面がヘッダーに届く・離れるのに合わせて遅らせて切り替え(開く時 .15秒・閉じる時 .25秒)、aria-expanded・aria-controls を付け、aria-label を「メニューを開く」「メニューを閉じる」で切り替える。開いたら最初のリンクへフォーカスを移し、後ろの本文は inert にする。Tab と Shift+Tab ではボタンとリンクの間だけを回り、開いている間は、メニューの背景やロゴを押してもフォーカスが外れないよう、リンクとボタン以外の mousedown は preventDefault する。Esc で閉じてボタンへフォーカスを戻す。デモでは枠の中に作るため position:absolute を使う(サイト全体に使う時は画面に固定する)。@media (prefers-reduced-motion: reduce) では transition と遅れを無効化して、すぐに開閉する。

Acceptance criteria: works with prefers-reduced-motion (animation disabled), zero console errors, zero layout shift

AIエージェントでの実装ガイド → Claude Code でWebアニメーションを実装する

BOOKS · PR · AMAZON

📖 なかしまぁ先生のCSSアニメーション入門 📖 UIデザインの教科書 新版 マルチデバイス時代のインターフェース設計 📖 Fundamentals of Web Animation with GSAP

※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。

学びを止めない · AMAZONサブスク

📚 Kindle Unlimited — 技術書・デザイン書が読み放題。30日無料体験 → 🎧 Audible — 移動中に耳で学ぶオーディオブック。30日無料体験 → 📦 Amazon Prime — Prime Reading・配送特典つき。30日無料体験 → 🎵 Music Unlimited — 作業用BGMに。1億曲が聴き放題・30日無料体験 →

※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。

CREATOR TOOLS · スクール(PR)

🎬 Adobe Creative Cloud — After Effects / Photoshop 業界標準ツール → 💻 SkillHacks — プログラミング・動画編集オンラインスクール → 🚀 デイトラ — Web制作スクール・実案件レベルまで習得 →

※本サイトはアフィリエイトリンクを含みます。リンク経由のご購入・お申し込みで運営者が紹介料を受け取る場合があります。

RELATED EFFECTS

↗ MORE
→ HAMBURGER MENU → SLIDE DRAWER → CURTAIN REVEAL

Fullscreen Menu Overlay(全画面メニュー)とは

メニューボタンを押すと、画面全体をメニューの一覧で覆う形のナビゲーションです。項目を大きな文字で見せられるため、ページ数の少ないブランドサイトやポートフォリオでよく使われます。

当サイトの Hamburger Menu は3本線がなめらかに×印へ変形するアイコン、Slide Drawer は画面の右端からスライドして現れるドロワーメニュー、Curtain Reveal は左右のパネルが開いて中身が現れるカーテン演出です。本ページはアイコンの変形と全画面の一覧を組み合わせ、開いている間のフォーカスの扱いまでを含めた別テーマです。

画面全体を覆う分、開いている間は後ろの内容に触れられない状態をきちんと作ることが大切です。閉じるボタンを常に押せる位置に置き、Esc でも閉じられるようにしておくと、どの操作方法でも迷わずに使えます。

よくある質問

閉じている時に visibility:hidden も付けるのはなぜですか?

clip-path で見えなくしただけでは、リンクはページの中に残っていて、Tab キーで見えないリンクへフォーカスが移ったり、読み上げソフトが読んだりしてしまいます。visibility:hidden にすると、見えないだけでなく操作や読み上げの対象からも外れます。閉じる時は clip-path の動きが終わってから切り替わるよう、visibility にだけ遅れを付けています。

開いている間、フォーカスをメニューの中にとどめるのはなぜですか?

全画面のメニューは後ろの本文を完全に覆うため、Tab キーで後ろの見えないリンクへフォーカスが移ると、今どこを操作しているのか分からなくなります。本実装は後ろの本文を inert にして操作の対象から外し、メニューボタンと最後のリンクの間で Tab を回り込ませています。Esc で閉じた時は、メニューボタンへフォーカスを戻します。

コードは無料で使えますか?

はい。Motion LabのコードスニペットとAIプロンプトは、クレジット表記なしで自由に使えます。コピーしてそのままプロジェクトに利用できます。