セグメント切替
週・月・年のようなピル状の3択切替。クリックすると白い選択インジケータが滑らかにスライドして移動し、aria-pressedも排他的に更新されます。矢印キーでも操作できます。
ライブデモ
コード
HTML
<!-- セグメント切替ボタン: ピル状の3択でインジケータが選択位置へ滑らかに移動する -->
<div class="stage">
<div class="btn-segment" role="group" aria-label="表示期間の切り替え">
<span class="btn-segment__thumb" aria-hidden="true"></span>
<button class="btn-segment__item" type="button" data-index="0" aria-pressed="true">週間</button>
<button class="btn-segment__item" type="button" data-index="1" aria-pressed="false">月間</button>
<button class="btn-segment__item" type="button" data-index="2" aria-pressed="false">年間</button>
</div>
<p class="hint">クリックで選択が切り替わり、インジケータが滑らかに移動します(矢印キーでも操作可)</p>
</div>
CSS
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
overflow: hidden;
font-family: "Segoe UI", system-ui, sans-serif;
background: radial-gradient(circle at 50% 20%, #f3f6fb 0%, #e9edf7 50%, #dde3f2 100%);
}
.stage {
display: grid;
place-items: center;
gap: 22px;
padding: 24px;
}
/* --i: 選択中インデックス(0始まり)。JSがクリック/矢印キーで書き換える */
.btn-segment {
--i: 0;
position: relative;
display: inline-flex;
padding: 5px;
border-radius: 999px;
background: rgba(30, 41, 71, .08);
box-shadow: inset 0 1px 3px rgba(30, 41, 71, .12);
/* 待機時のアイドル: 外側にごく淡い影が呼吸してクリック可能なことを伝える */
animation: segment-idle-breathe 3.4s ease-in-out infinite;
}
@keyframes segment-idle-breathe {
0%, 100% { box-shadow: inset 0 1px 3px rgba(30, 41, 71, .12); }
50% { box-shadow: inset 0 1px 3px rgba(30, 41, 71, .12), 0 6px 16px rgba(59, 91, 219, .16); }
}
.btn-segment__thumb {
position: absolute;
top: 5px;
left: 5px;
bottom: 5px;
width: calc((100% - 10px) / 3);
border-radius: 999px;
background: #fff;
box-shadow: 0 3px 10px rgba(30, 41, 71, .22);
transform: translateX(calc(var(--i) * 100%));
transition: transform .4s cubic-bezier(.22, 1, .36, 1);
}
.btn-segment__item {
position: relative;
z-index: 1;
min-width: 84px;
border: none;
background: transparent;
padding: 11px 20px;
font-size: 15px;
font-weight: 700;
letter-spacing: .02em;
color: #5b6478;
cursor: pointer;
border-radius: 999px;
-webkit-tap-highlight-color: transparent;
transition: color .3s ease;
}
.btn-segment__item:hover {
color: #34405c;
}
.btn-segment__item[aria-pressed="true"] {
color: #1f2a4d;
}
.btn-segment__item:focus-visible {
outline: 3px solid rgba(59, 91, 219, .5);
outline-offset: 3px;
}
.hint {
margin: 0;
font-size: 13px;
color: #5b6478;
text-align: center;
max-width: 280px;
}
@media (prefers-reduced-motion: reduce) {
.btn-segment { animation: none; }
.btn-segment__thumb { transition-duration: .01ms; }
}
JavaScript
// セグメント切替ボタン: クリックで選択インデックスを --i に反映し、aria-pressedを排他的に更新する
(() => {
const group = document.querySelector('.btn-segment');
if (!group) return; // null安全
const items = Array.from(group.querySelectorAll('.btn-segment__item'));
if (!items.length) return;
const select = (index) => {
group.style.setProperty('--i', String(index));
items.forEach((el, i) => {
el.setAttribute('aria-pressed', String(i === index));
});
};
items.forEach((el) => {
el.addEventListener('click', () => {
const index = Number(el.dataset.index || 0);
select(index);
});
});
// 左右矢印キーでの選択移動もサポート
group.addEventListener('keydown', (e) => {
if (e.key !== 'ArrowRight' && e.key !== 'ArrowLeft') return;
const current = items.findIndex((el) => el.getAttribute('aria-pressed') === 'true');
if (current === -1) return;
const dir = e.key === 'ArrowRight' ? 1 : -1;
const next = (current + dir + items.length) % items.length;
select(next);
items[next].focus();
e.preventDefault();
});
})();
🤖 AIエージェント用プロンプト
このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「セグメント切替」の効果を追加してください。
# 追加してほしい効果
セグメント切替(ボタン)
週・月・年のようなピル状の3択切替。クリックすると白い選択インジケータが滑らかにスライドして移動し、aria-pressedも排他的に更新されます。矢印キーでも操作できます。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- セグメント切替ボタン: ピル状の3択でインジケータが選択位置へ滑らかに移動する -->
<div class="stage">
<div class="btn-segment" role="group" aria-label="表示期間の切り替え">
<span class="btn-segment__thumb" aria-hidden="true"></span>
<button class="btn-segment__item" type="button" data-index="0" aria-pressed="true">週間</button>
<button class="btn-segment__item" type="button" data-index="1" aria-pressed="false">月間</button>
<button class="btn-segment__item" type="button" data-index="2" aria-pressed="false">年間</button>
</div>
<p class="hint">クリックで選択が切り替わり、インジケータが滑らかに移動します(矢印キーでも操作可)</p>
</div>
【CSS】
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
overflow: hidden;
font-family: "Segoe UI", system-ui, sans-serif;
background: radial-gradient(circle at 50% 20%, #f3f6fb 0%, #e9edf7 50%, #dde3f2 100%);
}
.stage {
display: grid;
place-items: center;
gap: 22px;
padding: 24px;
}
/* --i: 選択中インデックス(0始まり)。JSがクリック/矢印キーで書き換える */
.btn-segment {
--i: 0;
position: relative;
display: inline-flex;
padding: 5px;
border-radius: 999px;
background: rgba(30, 41, 71, .08);
box-shadow: inset 0 1px 3px rgba(30, 41, 71, .12);
/* 待機時のアイドル: 外側にごく淡い影が呼吸してクリック可能なことを伝える */
animation: segment-idle-breathe 3.4s ease-in-out infinite;
}
@keyframes segment-idle-breathe {
0%, 100% { box-shadow: inset 0 1px 3px rgba(30, 41, 71, .12); }
50% { box-shadow: inset 0 1px 3px rgba(30, 41, 71, .12), 0 6px 16px rgba(59, 91, 219, .16); }
}
.btn-segment__thumb {
position: absolute;
top: 5px;
left: 5px;
bottom: 5px;
width: calc((100% - 10px) / 3);
border-radius: 999px;
background: #fff;
box-shadow: 0 3px 10px rgba(30, 41, 71, .22);
transform: translateX(calc(var(--i) * 100%));
transition: transform .4s cubic-bezier(.22, 1, .36, 1);
}
.btn-segment__item {
position: relative;
z-index: 1;
min-width: 84px;
border: none;
background: transparent;
padding: 11px 20px;
font-size: 15px;
font-weight: 700;
letter-spacing: .02em;
color: #5b6478;
cursor: pointer;
border-radius: 999px;
-webkit-tap-highlight-color: transparent;
transition: color .3s ease;
}
.btn-segment__item:hover {
color: #34405c;
}
.btn-segment__item[aria-pressed="true"] {
color: #1f2a4d;
}
.btn-segment__item:focus-visible {
outline: 3px solid rgba(59, 91, 219, .5);
outline-offset: 3px;
}
.hint {
margin: 0;
font-size: 13px;
color: #5b6478;
text-align: center;
max-width: 280px;
}
@media (prefers-reduced-motion: reduce) {
.btn-segment { animation: none; }
.btn-segment__thumb { transition-duration: .01ms; }
}
【JavaScript】
// セグメント切替ボタン: クリックで選択インデックスを --i に反映し、aria-pressedを排他的に更新する
(() => {
const group = document.querySelector('.btn-segment');
if (!group) return; // null安全
const items = Array.from(group.querySelectorAll('.btn-segment__item'));
if (!items.length) return;
const select = (index) => {
group.style.setProperty('--i', String(index));
items.forEach((el, i) => {
el.setAttribute('aria-pressed', String(i === index));
});
};
items.forEach((el) => {
el.addEventListener('click', () => {
const index = Number(el.dataset.index || 0);
select(index);
});
});
// 左右矢印キーでの選択移動もサポート
group.addEventListener('keydown', (e) => {
if (e.key !== 'ArrowRight' && e.key !== 'ArrowLeft') return;
const current = items.findIndex((el) => el.getAttribute('aria-pressed') === 'true');
if (current === -1) return;
const dir = e.key === 'ArrowRight' ? 1 : -1;
const next = (current + dir + items.length) % items.length;
select(next);
items[next].focus();
e.preventDefault();
});
})();
# 外部ライブラリ
なし(追加ライブラリ不要)
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。