HTML / CSS / JS
その他
2026/08/17
2026/8/13
サービスや機能の紹介ページで、複数の画面を直感的に切り替えて見せたい場面にぴったりなUIコンポーネントです。
中央に大きなカードを配置し、その周囲を馬蹄型(U字型)の軌道に沿って6つのアイコンナビが囲みます。
ナビをクリックするとカード内のモック画面と右側(モバイルでは下側)のテキストが、ブラー+フェードのなめらかなトランジションで切り替わります。
アクティブなナビアイコンはダークカラーに反転して拡大し、どの機能を見ているかが一目でわかります。
Tailwind CSSとLucideアイコンを活用した、洗練されたデザインのUIです。
<div class="kumonosu-interaction-container">
<div class="kumonosu-visual-area">
<div class="kumonosu-central-stage">
<div id="kumonosu-card-body" class="kumonosu-content-fade w-full h-full"></div>
</div>
<div class="kumonosu-horseshoe-track">
<svg class="kumonosu-horseshoe-svg" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M 80,120 A 190,190 0 1 0 420,120" stroke="#f1f5f9" stroke-width="75" stroke-linecap="round" fill="none" />
</svg>
</div>
<div id="kumonosu-nav-layer" class="kumonosu-nav-layer"></div>
</div>
<div class="kumonosu-text-content-area flex flex-col space-y-6">
<h1 id="kumonosu-main-title" class="text-2xl lg:text-3xl font-extrabold text-slate-900 kumonosu-content-fade tracking-tight leading-tight"> - </h1>
<p id="kumonosu-main-desc" class="text-base lg:text-lg text-slate-500 leading-relaxed kumonosu-content-fade"> - </p>
</div>
</div>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@300;400;700&family=Plus+Jakarta+Sans:wght@300;400;600;700&display=swap');
body {
font-family: 'Plus Jakarta Sans', 'Noto Sans JP', sans-serif;
background-color: #ffffff;
color: #1a1a1a;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
overflow-x: hidden;
margin: 0;
}
.kumonosu-interaction-container {
position: relative;
width: 100%;
max-width: 1300px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20px;
padding: 40px 20px;
transition: all 0.3s ease;
}
@media (max-width: 1120px) {
.kumonosu-interaction-container {
padding: 0 60px;
max-height: 630px;
height: 100vh;
display: grid;
grid-template-columns: 1.1fr 0.9fr;
gap: 20px;
overflow: hidden;
}
}
@media (min-width: 1121px) {
.kumonosu-interaction-container {
display: grid;
grid-template-columns: 1.2fr 0.8fr;
height: 800px;
padding: 0 40px;
gap: 40px;
}
}
.kumonosu-visual-area {
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
transform-origin: center center;
z-index: 1;
}
@media (max-width: 640px) {
.kumonosu-interaction-container {
display: flex;
flex-direction: column;
max-height: none;
height: auto;
padding: 40px 20px;
}
.kumonosu-visual-area {
transform: scale(0.45);
height: 400px;
margin: -60px 0;
}
}
@media (min-width: 641px) and (max-width: 1120px) {
.kumonosu-visual-area {
transform: scale(0.55);
}
}
@media (min-width: 1121px) {
.kumonosu-visual-area {
transform: scale(0.9);
}
}
@media (min-width: 1400px) {
.kumonosu-visual-area {
transform: scale(1);
}
}
.kumonosu-central-stage {
position: relative;
width: 400px;
height: 520px;
background: #ffffff;
border-radius: 48px;
box-shadow: 0 40px 80px -15px rgba(0, 0, 0, 0.08);
z-index: 1;
overflow: hidden;
border: 1px solid #f1f5f9;
flex-shrink: 0;
}
.kumonosu-horseshoe-track {
position: absolute;
width: 780px;
height: 780px;
z-index: 5;
pointer-events: none;
display: flex;
align-items: center;
justify-content: center;
transform: translateY(120px);
}
.kumonosu-horseshoe-svg {
width: 100%;
height: 100%;
overflow: visible;
filter: drop-shadow(0 15px 35px rgba(0, 0, 0, 0.03));
}
.kumonosu-nav-layer {
position: absolute;
inset: 0;
z-index: 100;
pointer-events: none;
transform: translateY(120px);
}
.kumonosu-nav-item {
position: absolute;
top: 50%;
left: 50%;
width: 72px;
height: 72px;
border-radius: 24px;
background: white;
border: 1px solid #f1f5f9;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
color: #94a3b8;
pointer-events: auto;
margin-left: -36px;
margin-top: -36px;
box-shadow: 0 10px 20px -5px rgba(0, 0, 0, 0.05);
}
.kumonosu-nav-item:hover {
transform: scale(1.1) translateY(-5px);
color: #334155;
box-shadow: 0 20px 30px -10px rgba(0, 0, 0, 0.15);
}
.kumonosu-nav-item.kumonosu-active {
background: #0f172a;
color: #ffffff;
border-color: #0f172a;
box-shadow: 0 20px 40px -8px rgba(15, 23, 42, 0.3);
transform: scale(1.2);
}
.kumonosu-content-fade {
transition: all 0.5s ease;
}
.kumonosu-content-hidden {
opacity: 0;
transform: translateY(10px);
filter: blur(4px);
}
.kumonosu-text-content-area {
text-align: left;
width: 100%;
z-index: 10;
}
@media (max-width: 640px) {
.kumonosu-text-content-area {
text-align: center;
}
}
const appData = [{
id: 'oversight',
title: '業務の全体像を把握',
icon: 'layout-grid',
description: '事務所のあらゆる動きを、一つの画面で明確に可視化します。'
}, {
id: 'profiles',
title: 'データの一元管理',
icon: 'users',
description: '案件、書類、クライアント情報を安全なハブに集約します。'
}, {
id: 'portal',
title: '安全な専用ポータル',
icon: 'shield-check',
description: 'クライアントが安心してファイルを共有できる、暗号化された専用スペース。'
}, {
id: 'drafting',
title: '書類作成の自動化',
icon: 'wand-2',
description: '登録データから遺言書や信託書類を即座に自動生成します。'
}, {
id: 'ai',
title: 'AIアシスタント',
icon: 'sparkles',
description: 'AIが下書きや要約を担当。ルーチンワークを効率化します。'
}, {
id: 'billing',
title: 'スマートな請求管理',
icon: 'credit-card',
description: '稼働時間を正確に記録し、請求書の発行を自動化します。'
}];
const fixedPositions = [{
x: 290,
y: -130
}, {
x: 270,
y: 55
}, {
x: 130,
y: 200
}, {
x: -130,
y: 200
}, {
x: -270,
y: 55
}, {
x: -290,
y: -130
}];
let activeIdx = 0;
const navLayer = document.getElementById('kumonosu-nav-layer');
const cardBody = document.getElementById('kumonosu-card-body');
const titleEl = document.getElementById('kumonosu-main-title');
const descEl = document.getElementById('kumonosu-main-desc');
function init() {
appData.forEach((item, i) => {
const btn = document.createElement('div');
btn.className = `kumonosu-nav-item ${i === 0 ? 'kumonosu-active' : ''}`;
btn.innerHTML = `<i data-lucide="${item.icon}" class="w-8 h-8"></i>`;
const pos = fixedPositions[i];
btn.style.transform = `translate(${pos.x}px, ${pos.y}px)`;
btn.onclick = () => selectItem(i);
navLayer.appendChild(btn);
});
selectItem(0, true);
}
function selectItem(index, isInitial = false) {
if (!isInitial && activeIdx === index) return;
activeIdx = index;
const els = [cardBody, titleEl, descEl];
if (!isInitial) els.forEach(el => el.classList.add('kumonosu-content-hidden'));
document.querySelectorAll('.kumonosu-nav-item').forEach((item, i) => {
item.classList.toggle('kumonosu-active', i === index);
});
setTimeout(() => {
const data = appData[index];
cardBody.innerHTML = getMockHTML(data.id);
titleEl.textContent = data.title;
descEl.textContent = data.description;
lucide.createIcons();
if (!isInitial) els.forEach(el => el.classList.remove('kumonosu-content-hidden'));
}, isInitial ? 0 : 400);
}
function getMockHTML(id) {
const mocks = {
oversight: `<div class="p-10 h-full bg-white flex flex-col"><div class="flex justify-between items-center mb-8"><span class="font-bold text-2xl tracking-tight">進行中の案件</span></div><div class="space-y-4 flex-1">${['生前信託の作成', '遺産分割協議', '法人設立登記', '不動産名義変更'].map((name, i) => `<div class="flex items-center justify-between p-5 bg-slate-50 rounded-3xl border border-slate-100/50"><div class="flex flex-col"><span class="text-[10px] font-bold text-slate-400 uppercase tracking-widest">案件番号 #${i+102}</span><span class="font-bold text-slate-700">${name}</span></div><div class="w-8 h-8 rounded-full bg-white flex items-center justify-center border border-slate-200"><i data-lucide="chevron-right" class="w-4 h-4 text-slate-400"></i></div></div>`).join('')}</div></div>`,
profiles: `<div class="p-10 h-full flex flex-col justify-center items-center text-center"><div class="w-36 h-36 bg-slate-50 rounded-[48px] flex items-center justify-center mb-8 shadow-inner border border-slate-100"><i data-lucide="user-check" class="w-16 h-16 text-slate-300"></i></div><h3 class="text-3xl font-bold text-slate-800">クライアント管理</h3><p class="text-slate-400 mt-3 text-lg">情報を一箇所に統合します。</p></div>`,
portal: `<div class="p-10 h-full bg-slate-50 flex flex-col"><div class="space-y-4 flex-1"><div class="bg-white p-6 rounded-[32px] rounded-bl-none shadow-sm border border-slate-100 max-w-[85%] text-sm leading-relaxed">書類に署名しました。</div><div class="bg-slate-900 text-white p-6 rounded-[32px] rounded-br-none shadow-xl self-end max-w-[85%] text-sm leading-relaxed">ありがとうございます。</div></div><div class="h-24 bg-white rounded-[32px] border border-slate-200 flex items-center px-8 shadow-sm"><i data-lucide="message-circle" class="text-slate-300 mr-4"></i><div class="h-4 bg-slate-100 w-1/2 rounded-full"></div></div></div>`,
drafting: `<div class="p-12 h-full flex flex-col justify-center"><div class="flex items-center space-x-4 mb-8"><div class="w-12 h-12 bg-blue-500 rounded-2xl flex items-center justify-center text-white"><i data-lucide="file-text"></i></div><span class="font-bold text-xl">書類の自動生成</span></div><div class="space-y-6">${[1,2,3].map(i => `<div class="h-2 bg-slate-100 rounded-full w-${i === 2 ? 'full' : '2/3'}"></div>`).join('')}<div class="mt-12 p-8 border-2 border-dashed border-slate-200 rounded-3xl text-center"><p class="text-slate-400 font-bold">PDFを生成中...</p></div></div></div>`,
ai: `<div class="p-10 h-full bg-slate-900 text-white flex flex-col"><div class="flex items-center space-x-3 mb-12"><div class="w-10 h-10 bg-gradient-to-br from-blue-400 to-indigo-600 rounded-xl flex items-center justify-center shadow-lg shadow-blue-500/20"><i data-lucide="zap" class="w-5 h-5 text-white"></i></div><span class="text-lg font-bold tracking-tight">AI アシスタント</span></div><div class="p-8 bg-slate-800/50 rounded-[40px] border border-slate-700/50 flex-1 flex flex-col justify-center"><p class="text-slate-300 leading-relaxed italic text-lg">「最適なプランを提案可能です。」</p></div></div>`,
billing: `<div class="p-12 h-full flex flex-col justify-center text-center"><span class="text-slate-400 text-sm font-bold uppercase tracking-widest mb-4">未請求金額</span><div class="text-6xl font-black text-slate-900 mb-16 tracking-tighter">¥452,000</div><div class="grid grid-cols-2 gap-6"><button class="py-5 bg-slate-100 rounded-3xl font-bold text-slate-600">履歴</button><button class="py-5 bg-slate-900 text-white rounded-3xl font-bold shadow-xl">一括請求</button></div></div>`
};
return mocks[id] || '';
}
window.onload = init;
https://cdn.tailwindcss.com
https://unpkg.com/lucide@latest
このUIは、中央のカードと周囲の馬蹄型ナビゲーション、右側のテキストエリアで構成されるプロダクト紹介コンポーネントです。
主な機能は以下のとおりです。
<path>で半円の軌道ラインを描画し、ナビの位置関係を視覚的に示しています。#0f172a)に反転し、scale(1.2)で拡大表示されます。scale(0.5〜1.0)で縮小されます。appData配列のオブジェクト(title・icon・description)を書き換えると、ナビの内容を変更できます。アイコンはLucideのアイコン名を指定します。getMockHTML関数内の各IDに対応するHTMLを書き換えると、カード内の表示内容を自由にカスタマイズできます。fixedPositions配列のx・y座標を変更すると、各ナビアイコンの配置位置を調整できます。appDataとfixedPositionsに要素を追加すれば、ナビの数を増減できます。SVGの軌道パスもそれに合わせて調整してください。.kumonosu-central-stageのwidth・heightを変更すると、中央カードの大きさが変わります。.kumonosu-content-fadeのtransition時間や、.kumonosu-content-hiddenのblur・translateY値を変更すると、切り替えアニメーションの雰囲気を調整できます。--bg-dark、--clay-baseなど)やTailwindのカラークラスを変更すると、全体の配色を変えられます。stroke属性(デフォルト#f1f5f9)を変更すると、馬蹄型の軌道ラインの色が変わります。非表示にしたい場合はstroke="none"にします。lucide.createIcons()を毎回呼び出してアイコンをSVGに変換しています。頻繁な切り替えではわずかなオーバーヘッドがあります。scale変換と連動しています。scale値を変更した場合、座標の再調整は不要ですが、scaleを外した場合は画面サイズによってナビがはみ出す場合があります。overflow-y: autoが設定されていますが、通常は1画面に収まる設計です。画面が極端に小さい場合(高さ不足)は縦スクロールが必要になります。<div>要素で実装されているため、キーボード操作やスクリーンリーダーでの操作性が限られます。実用する場合は<button>要素への変更とaria-labelの追加を推奨します。