Search result row
The row pattern for a search results page. Title + breadcrumb + snippet + matched-term highlighting.
'use client';
import type { ReactNode } from 'react';import { Stack, Text } from '@helixui/core';
export interface SearchResult { id: string; title: string; href: string; breadcrumb: string[]; // ['Docs', 'Components', 'Button'] snippet: string; // raw text — highlightTerms wraps matches matchedTerms?: string[];}
function highlight(text: string, terms: string[] | undefined): ReactNode { if (!terms || !terms.length) return text; const re = new RegExp('(' + terms.map(escape).join('|') + ')', 'gi'); return text.split(re).map((part, i) => re.test(part) ? <mark key={i} style={{ background: 'var(--helixui-color-bg-action-brand-subtle)', borderRadius: 2 }}>{part}</mark> : part, );}function escape(s: string) { return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }
export function SearchResultRow({ result }: { result: SearchResult }) { return ( <a href={result.href} style={{ display: 'block', padding: 'var(--helixui-space-3) var(--helixui-space-4)', borderRadius: 'var(--helixui-radius-md)', textDecoration: 'none', color: 'inherit', }} className="search-result-row" > <Stack gap={1}> <Text size="xs" tone="muted">{result.breadcrumb.join(' / ')}</Text> <Text weight="semibold" tone="primary">{highlight(result.title, result.matchedTerms)}</Text> <Text size="sm" tone="secondary">{highlight(result.snippet, result.matchedTerms)}</Text> </Stack> </a> );}Hover state
Add this once to your global stylesheet:
.search-result-row:hover { background: var(--helixui-color-bg-surface-subtle);}Cross-cutting hover styles like this belong in CSS, not inline — they
don’t compose well via style props.