ナビゲーション
ビュー分割切替
一覧、カード、コンパクトの3表現を切り替えるビュー切替の挙動を確認するレシピです。
動きを試す
プレビュー
必要なファイルを選ぶ
コード
index.html
利用前に変更する箇所
- データ項目名と表示密度の閾値を運用に合わせる
<!doctype html>
<html lang="ja" data-theme="light">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>ビュー分割切替</title>
<style>
:root { --bg:#f7f8fa; --surface:#ffffff; --text:#18212f; --muted:#5d6b7a; --border:#d7dee8; --focus:#0077cc; --accent:#245fd6; }
:root[data-theme="dark"] { --bg:#0f141a; --surface:#171d26; --text:#f2f5f8; --muted:#aab4c0; --border:#354152; --focus:#65c7ff; --accent:#7ea7ff; }
* { box-sizing: border-box; }
body { margin: 0; padding: 16px; background: var(--bg); color: var(--text); font-family: Arial, sans-serif; }
.card { max-width: 760px; margin: 0 auto; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 12px; }
.controls { display: flex; gap: 8px; flex-wrap: wrap; }
button {
min-height: 44px;
padding: 8px 12px;
border: 1px solid var(--border);
border-radius: 8px;
background: var(--surface);
color: var(--text);
}
button:focus-visible { outline: 3px solid var(--focus); }
button[aria-pressed="true"] { border-color: var(--accent); font-weight: 700; }
[data-state-panel][hidden] { display: none; }
.list-item, .card-item, .compact-item { list-style: none; margin: 0; padding: 8px; border-bottom: 1px solid var(--border); }
.card-view {
display: grid;
gap: 8px;
margin: 1em 0;
padding: 0;
}
.card-view li { list-style: none; padding: 10px; border: 1px solid var(--border); border-radius: 8px; }
.compact-view .compact-item { font-size: 0.9rem; }
.status { min-height: 1.2em; margin-top: 8px; }
@media (max-width: 360px) { body { padding: 10px; } }
@media (prefers-reduced-motion: reduce) { * { transition: none !important; } }
</style>
</head>
<body>
<main class="card">
<h1>ビュー表示切替</h1>
<div class="controls">
<button type="button" data-set-state="list" aria-pressed="true">一覧</button>
<button type="button" data-set-state="card" aria-pressed="false">カード</button>
<button type="button" data-set-state="compact" aria-pressed="false">コンパクト</button>
</div>
<section data-state-panel="list">
<h2>リストビュー</h2>
<ul>
<li class="list-item" data-record-id="record-a">受付A / 完了</li>
<li class="list-item" data-record-id="record-b">受付B / 確認中</li>
<li class="list-item" data-record-id="record-c">受付C / 未着手</li>
</ul>
</section>
<section data-state-panel="card" hidden>
<h2>カードビュー</h2>
<ul class="card-view">
<li data-record-id="record-a">受付A / 完了</li>
<li data-record-id="record-b">受付B / 確認中</li>
<li data-record-id="record-c">受付C / 未着手</li>
</ul>
</section>
<section data-state-panel="compact" hidden>
<h2>コンパクトビュー</h2>
<ul>
<li class="compact-item" data-record-id="record-a">受付A / 完了</li>
<li class="compact-item" data-record-id="record-b">受付B / 確認中</li>
<li class="compact-item" data-record-id="record-c">受付C / 未着手</li>
</ul>
</section>
<p role="status" data-status class="status"></p>
</main>
<script>
(() => {
const params = new URLSearchParams(window.location.search);
const instance = params.get('instance') ?? 'missing-instance';
const statePanels = Array.from(document.querySelectorAll('[data-state-panel]'));
const status = document.querySelector('[data-status]');
const buttons = Array.from(document.querySelectorAll('[data-set-state]'));
const validStates = new Set(['list', 'card', 'compact']);
const setTheme = (theme) => {
if (theme === 'dark' || theme === 'light') document.documentElement.dataset.theme = theme;
};
const setState = (nextState) => {
if (!validStates.has(nextState)) return;
document.documentElement.dataset.state = nextState;
for (const panel of statePanels) panel.hidden = panel.dataset.statePanel !== nextState;
for (const button of buttons) {
button.setAttribute('aria-pressed', String(button.dataset.setState === nextState));
}
const selectedButton = buttons.find((button) => button.dataset.setState === nextState);
if (status && selectedButton) status.textContent = `${selectedButton.textContent?.trim()}表示に切り替えました。`;
};
for (const button of buttons) {
button.addEventListener('click', () => setState(button.dataset.setState ?? 'list'));
}
window.addEventListener('message', (event) => {
if (event.source !== window.parent || typeof event.data !== 'object' || event.data === null) return;
if (event.data.source !== 'wpl-host' || event.data.instance !== instance) return;
if (event.data.type === 'wpl-preview-state') setState(event.data.value);
if (event.data.type === 'wpl-preview-theme') setTheme(event.data.value);
});
setTheme(params.get('theme') === 'dark' ? 'dark' : 'light');
setState(validStates.has(params.get('state')) ? (params.get('state') ?? 'list') : 'list');
window.parent.postMessage({ source: 'wpl-preview', type: 'wpl-preview-ready', instance }, '*');
})();
</script>
</body>
</html>
プレビューで試せる動き
- リスト: 情報を行単位で見せるベース表示です。
- カード: 項目を情報量付きカードで見やすく表示します。
- コンパクト: 折りたたみ情報で縦幅を節約します。
使い分けを確認する
このレシピが合う場面
適する場面
- 同一データを一覧/カード/コンパクトで見せ分ける
- 画面幅と用途で表示型を切り替える
- 切替時の状態保持と選択復元を確認する
適さない場面
- 表示型ごとの差分が巨大で再構築が重い場合
- 一度の操作で複数データを同時編集する画面
