# Collection

Collection headings, search controls, featured records and empty states.

- Group: Website
- Import: `import { ContentBand } from '@misoto22/folio/website'`
- Page: https://ui.misoto22.com/components/collection/

## When to reach for it

Display a searchable archive without moving filtering or data access into the package.

## Best practices

### Do

- Supply localized labels, descriptive links and meaningful image alternatives.

### Don’t

- Do not put service credentials, routing logic or backend models inside presentation components.

## ContentBand

### Props

- `as` — `ElementType`.
- `tone` — `'base' | 'muted' | 'feature'` default `'base'`.
- `bleed` — `boolean` default `false`.
- `spacing` — `'none' | 'content' | 'section'` default `'none'`.

Also accepts: `HTMLAttributes<HTMLElement>`.

## CollectionIntro

### Props

- `title` (required) — `ReactNode`.
- `description` — `ReactNode`.
- `context` — `ReactNode`.
- `media` — `ReactNode`.
- `actions` — `ReactNode`.
- `compact` — `boolean` default `false`.

## CollectionMetadata

### Props

- `items` (required) — `CollectionMetadataItem[]`.

## CollectionGroup

### Props

- `title` — `ReactNode`.
- `caption` — `ReactNode`.
- `headingId` — `string`.
- `compact` — `boolean`.

Also accepts: `Omit<HTMLAttributes<HTMLElement>, 'title'>`.

## CollectionSearch

### Props

- `value` (required) — `string`.
- `onChange` (required) — `(value: string) => void`.
- `label` (required) — `string`.
- `placeholder` — `string`.
- `clearLabel` (required) — `string`.
- `focusShortcut` — `boolean` default `false`.

## CollectionControls

### Props

- `items` (required) — `CollectionFilter[]`.
- `selected` (required) — `string | null`.
- `onSelect` (required) — `(value: string | null) => void`.
- `filterLabel` — `string`.
- `search` — `CollectionSearchProps`.
- `quiet` — `boolean`.
- `showCounts` — `boolean` default `true`.

## HighlightedText

### Props

- `text` (required) — `string`.
- `query` (required) — `string`.
- `className` — `string`.

## RecordRow

### Props

- `link` — `CollectionLink`.
- `title` (required) — `ReactNode`.
- `summary` — `ReactNode`.
- `context` — `ReactNode`.
- `media` — `ReactNode`.
- `index` — `ReactNode`.
- `metadata` — `ReactNode`.
- `trailing` — `ReactNode`.
- `subtitle` — `ReactNode`.
- `headingLevel` — `2 | 3` default `2`.
- `variant` — `'editorial' | 'media' | 'compact'` default `'editorial'`.

## FeaturedRecord

### Props

- `media` (required) — `ReactNode`.
- `title` (required) — `ReactNode`.
- `context` — `ReactNode`.
- `summary` — `ReactNode`.
- `action` — `ReactNode`.

## CollectionEmpty

### Props

- `message` (required) — `string`.
- `hint` — `string`.
- `action` — `ReactNode`.

## CollectionFooter

### Props

- `children` — `ReactNode`.
- `feedback` — `ReactNode`.

## TaxonomyLinks

### Props

- `children` (required) — `ReactNode`.
- `label` — `string`.

## ReferenceLink

A quiet resource link for references, separate from action buttons.

## ReferenceRows

### Props

- `items` (required) — `{ id?: string; label: ReactNode; hint?: ReactNode; content: ReactNode }[]`.

## CollectionTally

### Props

- `children` (required) — `ReactNode`.

## CollectionList

### Props

- `children` (required) — `ReactNode`.

Also accepts: `HTMLAttributes<HTMLUListElement>`.

## Example — searchable records

```tsx
import { CollectionControls, CollectionEmpty, CollectionIntro, RecordRow } from '@misoto22/folio/website'

<div className="w-full">
  <CollectionIntro title="Library" description="Essays and projects from the studio." />
  <CollectionControls filterLabel="Category" selected={category} onSelect={setCategory}
    items={[{ value: null, label: 'All' }, { value: 'essays', label: 'Essays' }, { value: 'projects', label: 'Projects' }]}
    search={{ value: query, onChange: setQuery, label: 'Search the library', placeholder: 'Search titles', clearLabel: 'Clear search' }} />
  {records.map((record) => <RecordRow key={record.id} variant="compact"
    link={<a href={`#${record.id}`} />} title={record.title} summary={record.summary} context={record.category} />)}
  {records.length === 0 && <CollectionEmpty message="No matching records" hint="Try another title or category." />}
</div>
```
