折りたたみサイドナビ
アイコンとラベルを並べた管理画面向けサイドナビ。トグルでアイコンだけの細いレールへ折りたたみ、その状態でもホバーするとラベルがツールチップで表示されます。
ライブデモ
コード
HTML
<!-- アイコン+ラベルの折りたたみ式サイドナビ。トグルでアイコンだけのレールに縮小し、縮小時はホバーでラベルをツールチップ表示 -->
<div class="nsc-frame" id="nscFrame">
<aside class="nsc-side">
<div class="nsc-brand">
<span class="nsc-brand__mark" aria-hidden="true">◆</span>
<span class="nsc-brand__text nsc-label">Ledger</span>
</div>
<button class="nsc-toggle" id="nscToggle" type="button" aria-label="サイドナビの折りたたみ切替" aria-expanded="true">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M15 6l-6 6 6 6"></path></svg>
<span class="nsc-toggle__label nsc-label">折りたたむ</span>
</button>
<nav class="nsc-nav" aria-label="サイドナビゲーション">
<p class="nsc-group nsc-label">メイン</p>
<a href="#" onclick="return false" class="is-active" data-label="ダッシュボード">
<span class="nsc-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="9" rx="1.5"></rect><rect x="14" y="3" width="7" height="5" rx="1.5"></rect><rect x="14" y="12" width="7" height="9" rx="1.5"></rect><rect x="3" y="16" width="7" height="5" rx="1.5"></rect></svg></span>
<span class="nsc-label">ダッシュボード</span>
</a>
<a href="#" onclick="return false" data-label="取引履歴">
<span class="nsc-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M4 7h16M4 12h10M4 17h13"></path></svg></span>
<span class="nsc-label">取引履歴</span>
</a>
<a href="#" onclick="return false" data-label="請求書">
<span class="nsc-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M6 3h9l3 3v15H6z"></path><path d="M9 9h6M9 13h6M9 17h4"></path></svg></span>
<span class="nsc-label">請求書</span>
</a>
<p class="nsc-group nsc-label">管理</p>
<a href="#" onclick="return false" data-label="メンバー">
<span class="nsc-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><circle cx="9" cy="8" r="3"></circle><path d="M2.5 20c1-3.5 3.6-5.5 6.5-5.5s5.5 2 6.5 5.5"></path><circle cx="18" cy="8" r="2.4"></circle><path d="M15.8 14.7c2.3.4 4 2.1 4.7 5.3"></path></svg></span>
<span class="nsc-label">メンバー</span>
</a>
<a href="#" onclick="return false" data-label="設定">
<span class="nsc-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"></circle><path d="M12 2v3M12 19v3M4.2 4.2l2.1 2.1M17.7 17.7l2.1 2.1M2 12h3M19 12h3M4.2 19.8l2.1-2.1M17.7 6.3l2.1-2.1"></path></svg></span>
<span class="nsc-label">設定</span>
</a>
</nav>
<div class="nsc-user">
<span class="nsc-user__avatar" aria-hidden="true">田</span>
<span class="nsc-label nsc-user__name">田中 沙耶</span>
</div>
</aside>
<main class="nsc-main">
<header class="nsc-topbar">
<h1>ダッシュボード</h1>
</header>
<div class="nsc-cards">
<div class="nsc-card"><span>今月の収支</span><b>+¥482,300</b></div>
<div class="nsc-card"><span>未払い請求書</span><b>3件</b></div>
<div class="nsc-card"><span>取引件数</span><b>128</b></div>
</div>
</main>
</div>
CSS
* { box-sizing: border-box; }
body { margin: 0; display: block; background: #eef1f7; font-family: "Hiragino Sans", "Segoe UI", system-ui, sans-serif; }
/* ルート: width:100% + height:100vh。折りたたみは grid-template-columns の transition で滑らかに */
.nsc-frame {
width: 100%; height: 100vh;
display: grid; grid-template-columns: 220px 1fr;
transition: grid-template-columns .25s ease;
background: #eef1f7;
}
.nsc-frame.is-collapsed { grid-template-columns: 68px 1fr; }
.nsc-side {
background: #0f1729; color: #aab3ca;
padding: 16px 12px; display: flex; flex-direction: column; gap: 6px;
overflow-y: auto; overflow-x: hidden;
}
.nsc-brand { display: flex; align-items: center; gap: 8px; padding: 4px 6px 10px; white-space: nowrap; }
.nsc-brand__mark { color: #5eead4; font-size: 17px; flex-shrink: 0; }
.nsc-brand__text { font-weight: 800; letter-spacing: .03em; color: #f1f5f9; font-size: 15px; }
.nsc-toggle {
display: flex; align-items: center; gap: 10px; white-space: nowrap;
width: 100%; background: none; border: none; cursor: pointer;
color: #7c869c; padding: 8px 10px; margin: 0 0 10px; border-radius: 9px;
font: inherit; font-size: 12.5px; font-weight: 600;
transition: background .18s ease, color .18s ease;
}
.nsc-toggle:hover { background: #1a2440; color: #e2e8f0; }
.nsc-toggle svg { width: 15px; height: 15px; flex-shrink: 0; transition: transform .25s ease; }
.nsc-toggle svg path { fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.nsc-frame.is-collapsed .nsc-toggle svg { transform: rotate(180deg); }
.nsc-frame.is-collapsed .nsc-toggle { justify-content: center; }
.nsc-group { margin: 6px 0 2px; padding: 0 10px; font-size: 10.5px; font-weight: 700; letter-spacing: .1em; color: #5c6684; white-space: nowrap; }
.nsc-nav { display: flex; flex-direction: column; gap: 2px; flex: 1; }
.nsc-nav a {
position: relative; display: flex; align-items: center; gap: 11px; white-space: nowrap;
color: #aab3ca; text-decoration: none; font-size: 13px; font-weight: 600;
padding: 9px 10px; border-radius: 9px; transition: background .18s ease, color .18s ease;
}
.nsc-nav a:hover { background: #1a2440; color: #e2e8f0; }
.nsc-nav a.is-active { background: #17223f; color: #5eead4; }
.nsc-nav a.is-active::before {
content: ""; position: absolute; left: -12px; top: 8px; bottom: 8px; width: 3px;
background: #5eead4; border-radius: 0 3px 3px 0;
}
.nsc-icon { width: 18px; height: 18px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; }
.nsc-icon svg { width: 18px; height: 18px; }
.nsc-icon svg path, .nsc-icon svg rect, .nsc-icon svg circle { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.nsc-user { display: flex; align-items: center; gap: 10px; padding: 10px; margin-top: auto; border-top: 1px solid #1c2540; white-space: nowrap; }
.nsc-user__avatar { width: 26px; height: 26px; border-radius: 50%; background: #223056; color: #cbd3e6; font-size: 11px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.nsc-user__name { font-size: 12.5px; font-weight: 600; color: #cbd3e6; }
/* 折りたたみ時:ラベルを隠しアイコンだけに。ホバーでラベルをツールチップ表示 */
.nsc-frame.is-collapsed .nsc-label { display: none; }
.nsc-frame.is-collapsed .nsc-nav a,
.nsc-frame.is-collapsed .nsc-brand,
.nsc-frame.is-collapsed .nsc-user { justify-content: center; }
.nsc-frame.is-collapsed .nsc-nav a::after {
content: attr(data-label);
position: absolute; left: calc(100% + 10px); top: 50%; transform: translateY(-50%) translateX(-4px);
background: #1b2740; color: #e7ecf6; font-size: 12px; font-weight: 600; white-space: nowrap;
padding: 6px 10px; border-radius: 7px; box-shadow: 0 8px 20px rgba(0, 0, 0, .3);
opacity: 0; pointer-events: none; transition: opacity .16s ease, transform .16s ease;
z-index: 10;
}
.nsc-frame.is-collapsed .nsc-nav a:hover::after {
opacity: 1; transform: translateY(-50%) translateX(0);
}
.nsc-main { padding: 20px 22px; overflow: auto; }
.nsc-topbar { margin-bottom: 16px; }
.nsc-topbar h1 { margin: 0; font-size: 17px; font-weight: 800; color: #1c2333; }
.nsc-cards { display: flex; flex-wrap: wrap; gap: 12px; }
.nsc-card { flex: 1 1 140px; background: #fff; border: 1px solid #e0e5ef; border-radius: 12px; padding: 14px; display: flex; flex-direction: column; gap: 6px; }
.nsc-card span { font-size: 11px; color: #7c869c; font-weight: 700; letter-spacing: .02em; }
.nsc-card b { font-size: 19px; color: #161c2c; }
/* 狭幅:初期状態からアイコンレールにし、トグルでさらに完全に隠せる */
@media (max-width: 640px) {
.nsc-frame { grid-template-columns: 68px 1fr; }
.nsc-label { display: none; }
.nsc-nav a, .nsc-brand, .nsc-user { justify-content: center; }
.nsc-frame.is-collapsed { grid-template-columns: 0 1fr; }
.nsc-frame.is-collapsed .nsc-side { padding: 0; overflow: hidden; }
}
@media (prefers-reduced-motion: reduce) {
.nsc-frame, .nsc-toggle svg { transition: none; }
}
JavaScript
// サイドバーの折りたたみトグル+ナビのアクティブ切替(null安全)
(() => {
const frame = document.getElementById('nscFrame');
const toggle = document.getElementById('nscToggle');
if (!frame || !toggle) return;
toggle.addEventListener('click', () => {
const collapsed = frame.classList.toggle('is-collapsed');
toggle.setAttribute('aria-expanded', String(!collapsed));
});
const links = frame.querySelectorAll('.nsc-nav a');
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="nsc-frame" id="nscFrame">
<aside class="nsc-side">
<div class="nsc-brand">
<span class="nsc-brand__mark" aria-hidden="true">◆</span>
<span class="nsc-brand__text nsc-label">Ledger</span>
</div>
<button class="nsc-toggle" id="nscToggle" type="button" aria-label="サイドナビの折りたたみ切替" aria-expanded="true">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M15 6l-6 6 6 6"></path></svg>
<span class="nsc-toggle__label nsc-label">折りたたむ</span>
</button>
<nav class="nsc-nav" aria-label="サイドナビゲーション">
<p class="nsc-group nsc-label">メイン</p>
<a href="#" onclick="return false" class="is-active" data-label="ダッシュボード">
<span class="nsc-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="9" rx="1.5"></rect><rect x="14" y="3" width="7" height="5" rx="1.5"></rect><rect x="14" y="12" width="7" height="9" rx="1.5"></rect><rect x="3" y="16" width="7" height="5" rx="1.5"></rect></svg></span>
<span class="nsc-label">ダッシュボード</span>
</a>
<a href="#" onclick="return false" data-label="取引履歴">
<span class="nsc-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M4 7h16M4 12h10M4 17h13"></path></svg></span>
<span class="nsc-label">取引履歴</span>
</a>
<a href="#" onclick="return false" data-label="請求書">
<span class="nsc-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M6 3h9l3 3v15H6z"></path><path d="M9 9h6M9 13h6M9 17h4"></path></svg></span>
<span class="nsc-label">請求書</span>
</a>
<p class="nsc-group nsc-label">管理</p>
<a href="#" onclick="return false" data-label="メンバー">
<span class="nsc-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><circle cx="9" cy="8" r="3"></circle><path d="M2.5 20c1-3.5 3.6-5.5 6.5-5.5s5.5 2 6.5 5.5"></path><circle cx="18" cy="8" r="2.4"></circle><path d="M15.8 14.7c2.3.4 4 2.1 4.7 5.3"></path></svg></span>
<span class="nsc-label">メンバー</span>
</a>
<a href="#" onclick="return false" data-label="設定">
<span class="nsc-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"></circle><path d="M12 2v3M12 19v3M4.2 4.2l2.1 2.1M17.7 17.7l2.1 2.1M2 12h3M19 12h3M4.2 19.8l2.1-2.1M17.7 6.3l2.1-2.1"></path></svg></span>
<span class="nsc-label">設定</span>
</a>
</nav>
<div class="nsc-user">
<span class="nsc-user__avatar" aria-hidden="true">田</span>
<span class="nsc-label nsc-user__name">田中 沙耶</span>
</div>
</aside>
<main class="nsc-main">
<header class="nsc-topbar">
<h1>ダッシュボード</h1>
</header>
<div class="nsc-cards">
<div class="nsc-card"><span>今月の収支</span><b>+¥482,300</b></div>
<div class="nsc-card"><span>未払い請求書</span><b>3件</b></div>
<div class="nsc-card"><span>取引件数</span><b>128</b></div>
</div>
</main>
</div>
【CSS】
* { box-sizing: border-box; }
body { margin: 0; display: block; background: #eef1f7; font-family: "Hiragino Sans", "Segoe UI", system-ui, sans-serif; }
/* ルート: width:100% + height:100vh。折りたたみは grid-template-columns の transition で滑らかに */
.nsc-frame {
width: 100%; height: 100vh;
display: grid; grid-template-columns: 220px 1fr;
transition: grid-template-columns .25s ease;
background: #eef1f7;
}
.nsc-frame.is-collapsed { grid-template-columns: 68px 1fr; }
.nsc-side {
background: #0f1729; color: #aab3ca;
padding: 16px 12px; display: flex; flex-direction: column; gap: 6px;
overflow-y: auto; overflow-x: hidden;
}
.nsc-brand { display: flex; align-items: center; gap: 8px; padding: 4px 6px 10px; white-space: nowrap; }
.nsc-brand__mark { color: #5eead4; font-size: 17px; flex-shrink: 0; }
.nsc-brand__text { font-weight: 800; letter-spacing: .03em; color: #f1f5f9; font-size: 15px; }
.nsc-toggle {
display: flex; align-items: center; gap: 10px; white-space: nowrap;
width: 100%; background: none; border: none; cursor: pointer;
color: #7c869c; padding: 8px 10px; margin: 0 0 10px; border-radius: 9px;
font: inherit; font-size: 12.5px; font-weight: 600;
transition: background .18s ease, color .18s ease;
}
.nsc-toggle:hover { background: #1a2440; color: #e2e8f0; }
.nsc-toggle svg { width: 15px; height: 15px; flex-shrink: 0; transition: transform .25s ease; }
.nsc-toggle svg path { fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.nsc-frame.is-collapsed .nsc-toggle svg { transform: rotate(180deg); }
.nsc-frame.is-collapsed .nsc-toggle { justify-content: center; }
.nsc-group { margin: 6px 0 2px; padding: 0 10px; font-size: 10.5px; font-weight: 700; letter-spacing: .1em; color: #5c6684; white-space: nowrap; }
.nsc-nav { display: flex; flex-direction: column; gap: 2px; flex: 1; }
.nsc-nav a {
position: relative; display: flex; align-items: center; gap: 11px; white-space: nowrap;
color: #aab3ca; text-decoration: none; font-size: 13px; font-weight: 600;
padding: 9px 10px; border-radius: 9px; transition: background .18s ease, color .18s ease;
}
.nsc-nav a:hover { background: #1a2440; color: #e2e8f0; }
.nsc-nav a.is-active { background: #17223f; color: #5eead4; }
.nsc-nav a.is-active::before {
content: ""; position: absolute; left: -12px; top: 8px; bottom: 8px; width: 3px;
background: #5eead4; border-radius: 0 3px 3px 0;
}
.nsc-icon { width: 18px; height: 18px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; }
.nsc-icon svg { width: 18px; height: 18px; }
.nsc-icon svg path, .nsc-icon svg rect, .nsc-icon svg circle { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.nsc-user { display: flex; align-items: center; gap: 10px; padding: 10px; margin-top: auto; border-top: 1px solid #1c2540; white-space: nowrap; }
.nsc-user__avatar { width: 26px; height: 26px; border-radius: 50%; background: #223056; color: #cbd3e6; font-size: 11px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.nsc-user__name { font-size: 12.5px; font-weight: 600; color: #cbd3e6; }
/* 折りたたみ時:ラベルを隠しアイコンだけに。ホバーでラベルをツールチップ表示 */
.nsc-frame.is-collapsed .nsc-label { display: none; }
.nsc-frame.is-collapsed .nsc-nav a,
.nsc-frame.is-collapsed .nsc-brand,
.nsc-frame.is-collapsed .nsc-user { justify-content: center; }
.nsc-frame.is-collapsed .nsc-nav a::after {
content: attr(data-label);
position: absolute; left: calc(100% + 10px); top: 50%; transform: translateY(-50%) translateX(-4px);
background: #1b2740; color: #e7ecf6; font-size: 12px; font-weight: 600; white-space: nowrap;
padding: 6px 10px; border-radius: 7px; box-shadow: 0 8px 20px rgba(0, 0, 0, .3);
opacity: 0; pointer-events: none; transition: opacity .16s ease, transform .16s ease;
z-index: 10;
}
.nsc-frame.is-collapsed .nsc-nav a:hover::after {
opacity: 1; transform: translateY(-50%) translateX(0);
}
.nsc-main { padding: 20px 22px; overflow: auto; }
.nsc-topbar { margin-bottom: 16px; }
.nsc-topbar h1 { margin: 0; font-size: 17px; font-weight: 800; color: #1c2333; }
.nsc-cards { display: flex; flex-wrap: wrap; gap: 12px; }
.nsc-card { flex: 1 1 140px; background: #fff; border: 1px solid #e0e5ef; border-radius: 12px; padding: 14px; display: flex; flex-direction: column; gap: 6px; }
.nsc-card span { font-size: 11px; color: #7c869c; font-weight: 700; letter-spacing: .02em; }
.nsc-card b { font-size: 19px; color: #161c2c; }
/* 狭幅:初期状態からアイコンレールにし、トグルでさらに完全に隠せる */
@media (max-width: 640px) {
.nsc-frame { grid-template-columns: 68px 1fr; }
.nsc-label { display: none; }
.nsc-nav a, .nsc-brand, .nsc-user { justify-content: center; }
.nsc-frame.is-collapsed { grid-template-columns: 0 1fr; }
.nsc-frame.is-collapsed .nsc-side { padding: 0; overflow: hidden; }
}
@media (prefers-reduced-motion: reduce) {
.nsc-frame, .nsc-toggle svg { transition: none; }
}
【JavaScript】
// サイドバーの折りたたみトグル+ナビのアクティブ切替(null安全)
(() => {
const frame = document.getElementById('nscFrame');
const toggle = document.getElementById('nscToggle');
if (!frame || !toggle) return;
toggle.addEventListener('click', () => {
const collapsed = frame.classList.toggle('is-collapsed');
toggle.setAttribute('aria-expanded', String(!collapsed));
});
const links = frame.querySelectorAll('.nsc-nav a');
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で提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。