弾むチェック
クリックでチェックマークが跳ねながら描かれ、ボックスが弾性スケールし背景色がふわっと波及するチェックボックス。同意フォームなどに使えます。
ライブデモ
コード
HTML
<!-- 弾むチェック: チェックマークが跳ねながら描かれ、ボックスが弾性スケールし波紋が広がる -->
<div class="stage">
<label class="elastic-check">
<input type="checkbox" class="elastic-check__input" />
<span class="elastic-check__box" aria-hidden="true">
<svg class="elastic-check__icon" viewBox="0 0 24 24">
<rect class="elastic-check__rect" x="2" y="2" width="20" height="20" rx="6" />
<path class="elastic-check__mark" d="M6 12.5l4 4 8-9" />
</svg>
</span>
<span class="elastic-check__label">通知を受け取る</span>
</label>
<p class="hint">クリックしてON/OFF</p>
</div>
CSS
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
overflow: hidden;
display: grid;
place-items: center;
font-family: "Segoe UI", system-ui, sans-serif;
background: radial-gradient(circle at 50% 30%, #151b2e 0%, #0d0e1a 72%);
color: #f4f5fb;
}
.stage { display: grid; place-items: center; gap: 16px; }
.elastic-check {
display: inline-flex;
align-items: center;
gap: 14px;
padding: 12px 20px;
border-radius: 16px;
cursor: pointer;
background: rgba(255, 255, 255, .04);
-webkit-tap-highlight-color: transparent;
}
/* ネイティブinputは視覚的に隠すがフォーカス・操作性は維持する */
.elastic-check__input {
position: absolute;
width: 1px;
height: 1px;
opacity: 0;
pointer-events: none;
}
.elastic-check__box {
position: relative;
width: 30px;
height: 30px;
flex: none;
animation: check-idle 3.4s ease-in-out infinite;
}
.elastic-check__input:checked ~ .elastic-check__box { animation: none; }
.elastic-check__input:focus-visible ~ .elastic-check__box {
outline: 2px solid #34d399;
outline-offset: 3px;
border-radius: 8px;
}
@keyframes check-idle {
0%, 100% { filter: drop-shadow(0 0 0 rgba(52, 211, 153, 0)); }
50% { filter: drop-shadow(0 0 5px rgba(52, 211, 153, .35)); }
}
.elastic-check__icon { width: 100%; height: 100%; overflow: visible; }
.elastic-check__rect {
fill: transparent;
stroke: rgba(255, 255, 255, .35);
stroke-width: 1.6;
transition: fill .35s ease, stroke .35s ease;
}
.elastic-check__input:checked ~ .elastic-check__box .elastic-check__rect {
fill: #1c8f6b;
stroke: #34d399;
}
.elastic-check__mark {
fill: none;
stroke: #f4f5fb;
stroke-width: 2.4;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 22;
stroke-dashoffset: 22;
transition: stroke-dashoffset .5s cubic-bezier(.34, 1.56, .64, 1);
}
.elastic-check__input:checked ~ .elastic-check__box .elastic-check__mark {
stroke-dashoffset: 0;
}
/* 波紋(::after)。チェックが入るたびJSでクラスを再付与して背景色を波及させる */
.elastic-check__box::after {
content: "";
position: absolute;
inset: 0;
border-radius: 8px;
background: #34d399;
opacity: 0;
transform: scale(.4);
pointer-events: none;
z-index: -1;
}
.elastic-check__box.is-wave::after {
animation: wave-ripple .55s ease-out;
}
@keyframes wave-ripple {
0% { opacity: .5; transform: scale(.4); }
100% { opacity: 0; transform: scale(2.1); }
}
/* ボックス自体の弾性スケール(チェック・解除どちらでも再生) */
.elastic-check__box.is-pop { animation: elastic-pop .5s cubic-bezier(.34, 1.56, .64, 1); }
@keyframes elastic-pop {
0% { transform: scale(1); }
40% { transform: scale(1.22); }
70% { transform: scale(.92); }
100% { transform: scale(1); }
}
.elastic-check__label {
font-size: 15px;
font-weight: 600;
color: #f4f5fb;
}
.hint { margin: 0; font-size: 13px; color: rgba(244, 245, 251, .5); }
@media (prefers-reduced-motion: reduce) {
.elastic-check__box { animation: none; }
.elastic-check__mark { transition-duration: .15s; }
.elastic-check__box.is-wave::after,
.elastic-check__box.is-pop { animation: none; }
}
JavaScript
// 弾むチェック: state変化のたびにボックスを弾性スケールさせ、ONの時だけ波紋を広げる
(() => {
const input = document.querySelector('.elastic-check__input');
const box = document.querySelector('.elastic-check__box');
const hint = document.querySelector('.hint');
if (!input || !box) return; // null安全
input.addEventListener('change', () => {
// 弾性スケールはON/OFFどちらでも再生。連続操作でも途切れないようreflowで強制的に再起動する
box.classList.remove('is-pop');
void box.offsetWidth; // reflow
box.classList.add('is-pop');
if (input.checked) {
box.classList.remove('is-wave');
void box.offsetWidth; // reflow
box.classList.add('is-wave');
}
if (hint) {
hint.textContent = input.checked ? 'もう一度クリックでOFFに戻せます' : 'クリックしてON/OFF';
}
window.setTimeout(() => {
box.classList.remove('is-pop', 'is-wave');
}, 560);
});
})();
🤖 AIエージェント用プロンプト
このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「弾むチェック」の効果を追加してください。
# 追加してほしい効果
弾むチェック(遊べるUI)
クリックでチェックマークが跳ねながら描かれ、ボックスが弾性スケールし背景色がふわっと波及するチェックボックス。同意フォームなどに使えます。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- 弾むチェック: チェックマークが跳ねながら描かれ、ボックスが弾性スケールし波紋が広がる -->
<div class="stage">
<label class="elastic-check">
<input type="checkbox" class="elastic-check__input" />
<span class="elastic-check__box" aria-hidden="true">
<svg class="elastic-check__icon" viewBox="0 0 24 24">
<rect class="elastic-check__rect" x="2" y="2" width="20" height="20" rx="6" />
<path class="elastic-check__mark" d="M6 12.5l4 4 8-9" />
</svg>
</span>
<span class="elastic-check__label">通知を受け取る</span>
</label>
<p class="hint">クリックしてON/OFF</p>
</div>
【CSS】
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
overflow: hidden;
display: grid;
place-items: center;
font-family: "Segoe UI", system-ui, sans-serif;
background: radial-gradient(circle at 50% 30%, #151b2e 0%, #0d0e1a 72%);
color: #f4f5fb;
}
.stage { display: grid; place-items: center; gap: 16px; }
.elastic-check {
display: inline-flex;
align-items: center;
gap: 14px;
padding: 12px 20px;
border-radius: 16px;
cursor: pointer;
background: rgba(255, 255, 255, .04);
-webkit-tap-highlight-color: transparent;
}
/* ネイティブinputは視覚的に隠すがフォーカス・操作性は維持する */
.elastic-check__input {
position: absolute;
width: 1px;
height: 1px;
opacity: 0;
pointer-events: none;
}
.elastic-check__box {
position: relative;
width: 30px;
height: 30px;
flex: none;
animation: check-idle 3.4s ease-in-out infinite;
}
.elastic-check__input:checked ~ .elastic-check__box { animation: none; }
.elastic-check__input:focus-visible ~ .elastic-check__box {
outline: 2px solid #34d399;
outline-offset: 3px;
border-radius: 8px;
}
@keyframes check-idle {
0%, 100% { filter: drop-shadow(0 0 0 rgba(52, 211, 153, 0)); }
50% { filter: drop-shadow(0 0 5px rgba(52, 211, 153, .35)); }
}
.elastic-check__icon { width: 100%; height: 100%; overflow: visible; }
.elastic-check__rect {
fill: transparent;
stroke: rgba(255, 255, 255, .35);
stroke-width: 1.6;
transition: fill .35s ease, stroke .35s ease;
}
.elastic-check__input:checked ~ .elastic-check__box .elastic-check__rect {
fill: #1c8f6b;
stroke: #34d399;
}
.elastic-check__mark {
fill: none;
stroke: #f4f5fb;
stroke-width: 2.4;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 22;
stroke-dashoffset: 22;
transition: stroke-dashoffset .5s cubic-bezier(.34, 1.56, .64, 1);
}
.elastic-check__input:checked ~ .elastic-check__box .elastic-check__mark {
stroke-dashoffset: 0;
}
/* 波紋(::after)。チェックが入るたびJSでクラスを再付与して背景色を波及させる */
.elastic-check__box::after {
content: "";
position: absolute;
inset: 0;
border-radius: 8px;
background: #34d399;
opacity: 0;
transform: scale(.4);
pointer-events: none;
z-index: -1;
}
.elastic-check__box.is-wave::after {
animation: wave-ripple .55s ease-out;
}
@keyframes wave-ripple {
0% { opacity: .5; transform: scale(.4); }
100% { opacity: 0; transform: scale(2.1); }
}
/* ボックス自体の弾性スケール(チェック・解除どちらでも再生) */
.elastic-check__box.is-pop { animation: elastic-pop .5s cubic-bezier(.34, 1.56, .64, 1); }
@keyframes elastic-pop {
0% { transform: scale(1); }
40% { transform: scale(1.22); }
70% { transform: scale(.92); }
100% { transform: scale(1); }
}
.elastic-check__label {
font-size: 15px;
font-weight: 600;
color: #f4f5fb;
}
.hint { margin: 0; font-size: 13px; color: rgba(244, 245, 251, .5); }
@media (prefers-reduced-motion: reduce) {
.elastic-check__box { animation: none; }
.elastic-check__mark { transition-duration: .15s; }
.elastic-check__box.is-wave::after,
.elastic-check__box.is-pop { animation: none; }
}
【JavaScript】
// 弾むチェック: state変化のたびにボックスを弾性スケールさせ、ONの時だけ波紋を広げる
(() => {
const input = document.querySelector('.elastic-check__input');
const box = document.querySelector('.elastic-check__box');
const hint = document.querySelector('.hint');
if (!input || !box) return; // null安全
input.addEventListener('change', () => {
// 弾性スケールはON/OFFどちらでも再生。連続操作でも途切れないようreflowで強制的に再起動する
box.classList.remove('is-pop');
void box.offsetWidth; // reflow
box.classList.add('is-pop');
if (input.checked) {
box.classList.remove('is-wave');
void box.offsetWidth; // reflow
box.classList.add('is-wave');
}
if (hint) {
hint.textContent = input.checked ? 'もう一度クリックでOFFに戻せます' : 'クリックしてON/OFF';
}
window.setTimeout(() => {
box.classList.remove('is-pop', 'is-wave');
}, 560);
});
})();
# 外部ライブラリ
なし(追加ライブラリ不要)
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。