展開する検索
丸い検索アイコンがクリックで横長バーへ弾性展開し、入力欄がスライドイン。もう一度押すかフォーカスを外すと丸に戻ります。ヘッダーの検索UIに。
ライブデモ
コード
HTML
<!-- 展開する検索: 丸い検索アイコンがクリックで横長バーへ弾性展開する -->
<div class="stage">
<form class="expand-search" role="search" autocomplete="off">
<button type="button" class="expand-search__toggle" aria-label="検索を開く" aria-expanded="false">
<svg class="expand-search__icon" viewBox="0 0 24 24" aria-hidden="true">
<circle cx="10.5" cy="10.5" r="6.5" />
<line x1="15.3" y1="15.3" x2="21" y2="21" />
</svg>
</button>
<input type="search" class="expand-search__input" placeholder="サイト内を検索…" aria-label="検索キーワード" />
</form>
<p class="hint">アイコンをクリックして検索窓を開く</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%, #14202b 0%, #0d0e1a 72%);
color: #f4f5fb;
}
.stage { display: grid; place-items: center; gap: 18px; }
.expand-search {
--expanded: min(300px, 76vw);
position: relative;
display: flex;
align-items: center;
width: 52px;
height: 52px;
border-radius: 999px;
background: rgba(255, 255, 255, .05);
border: 1px solid rgba(255, 255, 255, .08);
box-shadow: 0 10px 26px rgba(0, 0, 0, .4);
overflow: hidden;
transition: width .5s cubic-bezier(.34, 1.56, .64, 1), background-color .3s ease, border-color .3s ease;
margin: 0;
}
.expand-search.is-open {
width: var(--expanded);
background: rgba(255, 255, 255, .08);
border-color: rgba(103, 232, 249, .35);
}
.expand-search__toggle {
flex: none;
width: 52px;
height: 52px;
border: none;
border-radius: 50%;
background: transparent;
cursor: pointer;
display: grid;
place-items: center;
color: #cfd4e6;
animation: search-idle 3.4s ease-in-out infinite;
}
.expand-search.is-open .expand-search__toggle { animation: none; color: #67e8f9; }
.expand-search__toggle:focus-visible { outline: 2px solid #67e8f9; outline-offset: -3px; border-radius: 50%; }
@keyframes search-idle {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.08); }
}
.expand-search__icon {
width: 22px;
height: 22px;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
}
.expand-search__input {
flex: 1 1 auto;
min-width: 0;
height: 100%;
border: none;
background: transparent;
color: #f4f5fb;
font-size: 15px;
padding: 0;
opacity: 0;
transform: translateX(8px);
transition: opacity .25s ease, transform .3s ease;
}
.expand-search.is-open .expand-search__input {
opacity: 1;
transform: translateX(0);
padding-right: 18px;
transition-delay: .15s;
}
.expand-search__input::placeholder { color: rgba(244, 245, 251, .4); }
.expand-search__input:focus { outline: none; }
/* ネイティブの検索フィールドに付くクリアボタン(x)を装飾なしに */
.expand-search__input::-webkit-search-cancel-button { -webkit-appearance: none; }
.hint { margin: 0; font-size: 13px; color: rgba(244, 245, 251, .5); }
@media (prefers-reduced-motion: reduce) {
.expand-search__toggle { animation: none; }
.expand-search,
.expand-search__input { transition-duration: .12s; transition-delay: 0s !important; }
}
JavaScript
// 展開する検索: クリックで丸ボタンが横長バーへ展開し、inputがスライドイン。
// もう一度クリック・Escape・外側へのblurで丸に戻る
(() => {
const form = document.querySelector('.expand-search');
const toggle = document.querySelector('.expand-search__toggle');
const input = document.querySelector('.expand-search__input');
const hint = document.querySelector('.hint');
if (!form || !toggle || !input) return; // null安全
const setOpen = (open) => {
form.classList.toggle('is-open', open);
toggle.setAttribute('aria-expanded', String(open));
toggle.setAttribute('aria-label', open ? '検索を閉じる' : '検索を開く');
if (open) {
window.requestAnimationFrame(() => input.focus());
} else {
input.value = '';
}
if (hint) {
hint.textContent = open ? 'Escかフォーカスを外すと閉じます' : 'アイコンをクリックして検索窓を開く';
}
};
toggle.addEventListener('click', () => setOpen(!form.classList.contains('is-open')));
input.addEventListener('blur', () => {
// トグルボタンへフォーカスが移った場合はクリックハンドラに開閉を任せる
window.setTimeout(() => {
if (!form.contains(document.activeElement)) setOpen(false);
}, 0);
});
input.addEventListener('keydown', (e) => {
if (e.key === 'Escape') { setOpen(false); toggle.focus(); }
});
form.addEventListener('submit', (e) => e.preventDefault());
})();
🤖 AIエージェント用プロンプト
このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「展開する検索」の効果を追加してください。
# 追加してほしい効果
展開する検索(遊べるUI)
丸い検索アイコンがクリックで横長バーへ弾性展開し、入力欄がスライドイン。もう一度押すかフォーカスを外すと丸に戻ります。ヘッダーの検索UIに。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- 展開する検索: 丸い検索アイコンがクリックで横長バーへ弾性展開する -->
<div class="stage">
<form class="expand-search" role="search" autocomplete="off">
<button type="button" class="expand-search__toggle" aria-label="検索を開く" aria-expanded="false">
<svg class="expand-search__icon" viewBox="0 0 24 24" aria-hidden="true">
<circle cx="10.5" cy="10.5" r="6.5" />
<line x1="15.3" y1="15.3" x2="21" y2="21" />
</svg>
</button>
<input type="search" class="expand-search__input" placeholder="サイト内を検索…" aria-label="検索キーワード" />
</form>
<p class="hint">アイコンをクリックして検索窓を開く</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%, #14202b 0%, #0d0e1a 72%);
color: #f4f5fb;
}
.stage { display: grid; place-items: center; gap: 18px; }
.expand-search {
--expanded: min(300px, 76vw);
position: relative;
display: flex;
align-items: center;
width: 52px;
height: 52px;
border-radius: 999px;
background: rgba(255, 255, 255, .05);
border: 1px solid rgba(255, 255, 255, .08);
box-shadow: 0 10px 26px rgba(0, 0, 0, .4);
overflow: hidden;
transition: width .5s cubic-bezier(.34, 1.56, .64, 1), background-color .3s ease, border-color .3s ease;
margin: 0;
}
.expand-search.is-open {
width: var(--expanded);
background: rgba(255, 255, 255, .08);
border-color: rgba(103, 232, 249, .35);
}
.expand-search__toggle {
flex: none;
width: 52px;
height: 52px;
border: none;
border-radius: 50%;
background: transparent;
cursor: pointer;
display: grid;
place-items: center;
color: #cfd4e6;
animation: search-idle 3.4s ease-in-out infinite;
}
.expand-search.is-open .expand-search__toggle { animation: none; color: #67e8f9; }
.expand-search__toggle:focus-visible { outline: 2px solid #67e8f9; outline-offset: -3px; border-radius: 50%; }
@keyframes search-idle {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.08); }
}
.expand-search__icon {
width: 22px;
height: 22px;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
}
.expand-search__input {
flex: 1 1 auto;
min-width: 0;
height: 100%;
border: none;
background: transparent;
color: #f4f5fb;
font-size: 15px;
padding: 0;
opacity: 0;
transform: translateX(8px);
transition: opacity .25s ease, transform .3s ease;
}
.expand-search.is-open .expand-search__input {
opacity: 1;
transform: translateX(0);
padding-right: 18px;
transition-delay: .15s;
}
.expand-search__input::placeholder { color: rgba(244, 245, 251, .4); }
.expand-search__input:focus { outline: none; }
/* ネイティブの検索フィールドに付くクリアボタン(x)を装飾なしに */
.expand-search__input::-webkit-search-cancel-button { -webkit-appearance: none; }
.hint { margin: 0; font-size: 13px; color: rgba(244, 245, 251, .5); }
@media (prefers-reduced-motion: reduce) {
.expand-search__toggle { animation: none; }
.expand-search,
.expand-search__input { transition-duration: .12s; transition-delay: 0s !important; }
}
【JavaScript】
// 展開する検索: クリックで丸ボタンが横長バーへ展開し、inputがスライドイン。
// もう一度クリック・Escape・外側へのblurで丸に戻る
(() => {
const form = document.querySelector('.expand-search');
const toggle = document.querySelector('.expand-search__toggle');
const input = document.querySelector('.expand-search__input');
const hint = document.querySelector('.hint');
if (!form || !toggle || !input) return; // null安全
const setOpen = (open) => {
form.classList.toggle('is-open', open);
toggle.setAttribute('aria-expanded', String(open));
toggle.setAttribute('aria-label', open ? '検索を閉じる' : '検索を開く');
if (open) {
window.requestAnimationFrame(() => input.focus());
} else {
input.value = '';
}
if (hint) {
hint.textContent = open ? 'Escかフォーカスを外すと閉じます' : 'アイコンをクリックして検索窓を開く';
}
};
toggle.addEventListener('click', () => setOpen(!form.classList.contains('is-open')));
input.addEventListener('blur', () => {
// トグルボタンへフォーカスが移った場合はクリックハンドラに開閉を任せる
window.setTimeout(() => {
if (!form.contains(document.activeElement)) setOpen(false);
}, 0);
});
input.addEventListener('keydown', (e) => {
if (e.key === 'Escape') { setOpen(false); toggle.focus(); }
});
form.addEventListener('submit', (e) => e.preventDefault());
})();
# 外部ライブラリ
なし(追加ライブラリ不要)
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。