F-005 Form medium

How to create a search with suggestions サジェスト検索の作り方

Autocomplete search box that displays a suggestion list while typing. 入力中に候補リストを表示するオートコンプリート検索ボックス。

ライブデモ Live Demo

🔍

概要・用途・特徴Overview, Usage & Features

何ができるかWhat it does

Autocomplete search box that displays a suggestion list while typing.

入力中に候補リストを表示するオートコンプリート検索ボックス。

どこで使うかWhere to use

contact form, signup flow, checkout form, search interface

検索バー、タグ入力、オートコンプリートフォームフィールド、商品検索

特徴Key features

Search input with real-time dropdown suggestions. Debounced input handler for performance. Keyboard navigable suggestion list (arrow keys, Enter, Escape). Highlights matching text in suggestions. Accessible combobox pattern.

リアルタイムドロップダウン候補付き検索入力。パフォーマンスのためのデバウンス入力ハンドラ。キーボードナビゲーション可能な候補リスト(矢印キー・Enter・Escape)。候補内のマッチングテキストをハイライト。アクセシブルコンボボックスパターン。

調整可能パラメータ Adjustable Parameters

Parameter Default Description
--suggestion-max-heightMax height of suggestion list
--highlight-colorMatched text highlight color

実装コード Implementation Code

// react/F-005.jsx
import { useState } from 'react';
import './F-005.css';

export default function SearchWithSuggestions({ items = [], placeholder = '検索...' }) {
  const [query, setQuery] = useState('');
  const matches = query ? items.filter(i => i.toLowerCase().includes(query.toLowerCase())) : [];

  return (
    <div className="search-wrapper">
      <span className="search-icon">🔍</span>
      <input className="search-input" value={query} onChange={e => setQuery(e.target.value)} placeholder={placeholder} />
      <div className={`suggestions ${matches.length ? 'open' : ''}`}>
        {matches.map(m => <div key={m} className="suggestion-item" onClick={() => setQuery(m)}>{m}</div>)}
      </div>
    </div>
  );
}
/* react/F-005.css */
/* F-005: Search with Suggestions */
.search-wrapper {
  position: relative;
}

.search-input {
  width: 100%;
  padding: 14px 16px 14px 44px;
  font-size: 16px;
  border: 2px solid #e0e4f0;
  border-radius: 12px;
  outline: none;
  transition: border-color 0.3s ease;
  font-family: inherit;
}

.search-input:focus {
  border-color: #5c6ac4;
  box-shadow: 0 0 0 3px rgba(92, 106, 196, 0.15);
}

.search-icon {
  position: absolute;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 18px;
  color: #999;
  pointer-events: none;
}

.suggestions {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  border: 1px solid #e0e4f0;
  border-radius: 12px;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease, opacity 0.3s ease;
  opacity: 0;
  z-index: 10;
  background: #fff;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
}

.suggestions.open {
  max-height: 300px;
  opacity: 1;
}

.suggestion-item {
  padding: 12px 16px;
  cursor: pointer;
  transition: background 0.2s ease;
  display: flex;
  align-items: center;
  gap: 10px;
}

.suggestion-item:hover {
  background: #f5f7ff;
}

.suggestion-item mark {
  background: rgba(92, 106, 196, 0.2);
  color: #5c6ac4;
  font-weight: 600;
  border-radius: 2px;
  padding: 0 2px;
}
import { useState } from 'react';
import './F-005.css';

export default function SearchWithSuggestions({ items = [], placeholder = '検索...' }) {
  const [query, setQuery] = useState('');
  const matches = query ? items.filter(i => i.toLowerCase().includes(query.toLowerCase())) : [];

  return (
    <div className="search-wrapper">
      <span className="search-icon">🔍</span>
      <input className="search-input" value={query} onChange={e => setQuery(e.target.value)} placeholder={placeholder} />
      <div className={`suggestions ${matches.length ? 'open' : ''}`}>
        {matches.map(m => <div key={m} className="suggestion-item" onClick={() => setQuery(m)}>{m}</div>)}
      </div>
    </div>
  );
}
/* F-005: Search with Suggestions */
.search-wrapper {
  position: relative;
}

.search-input {
  width: 100%;
  padding: 14px 16px 14px 44px;
  font-size: 16px;
  border: 2px solid #e0e4f0;
  border-radius: 12px;
  outline: none;
  transition: border-color 0.3s ease;
  font-family: inherit;
}

.search-input:focus {
  border-color: #5c6ac4;
  box-shadow: 0 0 0 3px rgba(92, 106, 196, 0.15);
}

.search-icon {
  position: absolute;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 18px;
  color: #999;
  pointer-events: none;
}

.suggestions {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  border: 1px solid #e0e4f0;
  border-radius: 12px;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease, opacity 0.3s ease;
  opacity: 0;
  z-index: 10;
  background: #fff;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
}

.suggestions.open {
  max-height: 300px;
  opacity: 1;
}

.suggestion-item {
  padding: 12px 16px;
  cursor: pointer;
  transition: background 0.2s ease;
  display: flex;
  align-items: center;
  gap: 10px;
}

.suggestion-item:hover {
  background: #f5f7ff;
}

.suggestion-item mark {
  background: rgba(92, 106, 196, 0.2);
  color: #5c6ac4;
  font-weight: 600;
  border-radius: 2px;
  padding: 0 2px;
}

仕組みとカスタマイズHow It Works & Customization

仕組みHow it works

An input event listener with debounce (200-300ms) fires after typing stops. It filters a local dataset or calls an API with the current query. Results are rendered into a dropdown listbox. JavaScript arrow key handlers update the aria-activedescendant and visually highlighted item. Pressing Enter selects the highlighted option and fills the input.

タイピング停止後にデバウンス(200〜300ms)付きinputイベントリスナーが発火。ローカルデータセットをフィルタリングまたは現在のクエリでAPIを呼び出し。結果がドロップダウンリストボックスにレンダリング。JavaScript矢印キーハンドラーがaria-activedescendantと視覚的にハイライトされたアイテムを更新。Enterキーでハイライトされたオプションを選択して入力を埋めます。

カスタマイズ方法Customization

Add an icon prefix (search icon) to the input. Group suggestions by category with headings. Show a "No results" empty state when the query matches nothing. Add recent searches from localStorage above API results.

入力にアイコンプレフィックス(検索アイコン)を追加。見出し付きでカテゴリー別に候補をグループ化。クエリが何もマッチしない場合に"結果なし"の空の状態を表示。API結果の上にlocalStorageからの最近の検索を追加。

注意点Caveats

Follow the ARIA combobox pattern: add role="combobox" to the input, aria-expanded, aria-controls pointing to the listbox, and aria-activedescendant tracking the focused option. Never trap focus in the suggestion list — Escape should close it and return focus to the input.

ARIAコンボボックスパターンに従ってください:入力にrole="combobox"・aria-expanded・リストボックスを指すaria-controls・フォーカスされたオプションを追跡するaria-activedescendantを追加してください。候補リストにフォーカスをトラップしないでください — Escapeで閉じて入力にフォーカスを戻すべきです。

よくある質問 Frequently Asked Questions

How to customize the search with suggestions? Search with Suggestionsをカスタマイズするには?

Modify the CSS custom properties and class styles defined in the code section. Key adjustable values include colors, sizes, durations, and spacing. See the Adjustable Parameters section for specific variables.

コードセクションで定義されているCSSカスタムプロパティとクラススタイルを変更してください。色、サイズ、時間、間隔が主な調整可能値です。具体的な変数は調整可能パラメータセクションを参照してください。

How to use the search with suggestions in React? ReactでSearch with Suggestionsを使うには?

Import the provided React component and its CSS file. The component accepts props for customization. Check the React code section for the full implementation and available props.

提供されているReactコンポーネントとCSSファイルをインポートしてください。コンポーネントのpropsでカスタマイズできます。完全な実装と利用可能なpropsはReactコードセクションを参照してください。

What are the performance implications of search with suggestions? Search with Suggestionsのパフォーマンスへの影響は?

This implementation uses CSS transforms and opacity for animations, which are GPU-accelerated. It's lightweight and doesn't cause layout thrashing. Consider using prefers-reduced-motion for accessibility.

この実装はCSSのtransformとopacityを使用しており、GPUアクセラレーションされます。軽量でレイアウトスラッシングを引き起こしません。アクセシビリティのためにprefers-reduced-motionの使用を検討してください。

AIへの指示テンプレート AI Prompt Template

以下をAIに貼り付けると実装を依頼できます。 Paste the following into your AI assistant to request implementation.