SearchInput

ui forminputsearch

Suchfeld mit führendem Lupen-Icon und type="search". Kontrolliert über value/onChange(string). Einheitliche Control-Höhe.

Abhängigkeiten

Barrierefreiheit

  • type="search"; bei eigenständigem Einsatz ein aria-label/Placeholder vergeben.

Beispiele

Kontrolliert
const [q, setQ] = useState("");
<SearchInput value={q} onChange={setQ} placeholder="Suchen…" />

Genutzte Tokens · 11

--bg--border--control-height--fg--field-font-size--field-pad-x--field-radius--font-mono--muted--surface--text-xs

Quelldateien

components/ui/SearchInput.tsx tsx
import type { ReactNode, Ref } from 'react';
import { SearchIcon } from './Icon.tsx';
import styles from './SearchInput.module.css';

/**
 * Suchfeld mit vorangestelltem Icon (1:1 aus media-presenter
 * `components/SearchInput.tsx`). Store-frei: `value`/`onChange` sind die
 * Softkey-Hooks, die Konsumenten verdrahten (presenter: Global-Search-Store;
 * worship: Lied-Suche gegen den DataProvider).
 */
export interface SearchInputProps {
  value: string;
  onChange: (value: string) => void;
  placeholder?: string;
  iconSize?: number;
  autoFocus?: boolean;
  className?: string;
  /** Ref auf das Eingabefeld — z. B. für einen globalen ⌘K-Fokus-Shortcut. */
  inputRef?: Ref<HTMLInputElement>;
  /** Rechts im Feld eingeblendet, z. B. der Shortcut-Hinweis „⌘ K". */
  trailing?: ReactNode;
}

export function SearchInput({
  value,
  onChange,
  placeholder,
  iconSize = 14,
  autoFocus,
  className,
  inputRef,
  trailing,
}: SearchInputProps) {
  const wrapperClass = [styles.wrap, className].filter(Boolean).join(' ');
  const inputClass = [styles.input, trailing ? styles.hasTrailing : ''].filter(Boolean).join(' ');
  return (
    <div className={wrapperClass}>
      <span className={styles.icon}>
        <SearchIcon size={iconSize} />
      </span>
      <input
        ref={inputRef}
        type="search"
        className={inputClass}
        placeholder={placeholder}
        value={value}
        onChange={(e) => onChange(e.target.value)}
        autoFocus={autoFocus}
      />
      {trailing && <span className={styles.trailing}>{trailing}</span>}
    </div>
  );
}
components/ui/SearchInput.module.css css
.wrap {
  position: relative;
}

.input {
  width: 100%;
  height: var(--control-height);
  padding: 0 var(--field-pad-x) 0 32px;
  border-radius: var(--field-radius);
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--fg);
  font-size: var(--field-font-size);
  line-height: 1;
  box-sizing: border-box;
}

.icon {
  position: absolute;
  left: 8px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--muted);
  display: inline-flex;
}

/* Platz für den Trailing-Slot (Shortcut-Hinweis) freihalten. */
.hasTrailing {
  padding-right: 52px;
}

/* Trailing-Slot rechts im Feld — Optik des presenter-⌘K-Hinweises. */
.trailing {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--muted);
  padding: 2px 6px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--surface);
  pointer-events: none;
}

Holen

# MCP (Claude Code / Cursor)
get_component({ name: "search-input" })

# Registry direkt
https://www.ekklesi.tools/design/registry/components/search-input.json