# Collection recipe

Compose a collection with named filters, stable records, results and recoverable states. The application owns filtering and requests; `Grid` and `Card` supply layout and presentation. The live example uses local records and explicit completion controls, not a backend.

## Filter and render

```svelte
<script>
  import { Button, Card, Grid, Section } from "@dg/ui";
  const records = [
    { id: "protocol", title: "Protocol study" },
    { id: "archive", title: "Shared archive" },
  ];
  let query = $state("");
  const results = $derived(records.filter((record) => record.title.toLowerCase().includes(query.trim().toLowerCase())));
</script>

<Section>
  <label for="collection-filter">Search the collection</label>
  <input type="search" id="collection-filter" bind:value={query} />
  <p class="feedback" role="status">{results.length} results</p>
</Section>
{#if results.length}
  <Section padding="none" gap="none">
    <Grid divided style="grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr))">
      {#each results as record (record.id)}
        <Card title={record.title}><p>Example record.</p></Card>
      {/each}
    </Grid>
  </Section>
{:else}
  <Section>
    <p>No results match these filters.</p>
    <Button label="Clear filters" onclick={() => (query = "")} />
  </Section>
{/if}
```

The results grid has its own unpadded `Section`, so its dividers reach the frame. `Card` cells of a divided `Grid` take the layout inset and align with the filters above.

## Container sizing

`Grid` switches to one column when its own width is 40rem or narrower. This recipe overrides the forwarded style with native CSS auto-fit tracks because each card needs a readable minimum width within its actual container. It does not depend on private `Grid` variables or introduce a new public sizing option. Jointive dividers remain gapless.

Use ordinary `Grid` columns for intentional equal divisions. Choose container-driven tracks only when the content constraint requires them; verify a narrow desktop container separately from a narrow viewport.

## Request states

The [complete runnable specimen](../../../../apps/ds/src/lib/docs/guides/CollectionRecipe.svelte) includes type filtering, pending, cancellation, simulated failure and retry. Keep filters when a request fails. A request controller belongs in the consuming app: cancel superseded requests or ignore stale responses, prevent duplicate submissions and make status text available outside the busy results region.

Distinguish an empty collection from no matching results and from a failed request. A failed refresh keeps the current results and filters visible, explains the failure and offers a retry beside it. Only show Clear filters when filters explain the empty view. Link a `Card` only when a real destination exists; use an article `Card` when it contains separate controls.

## Verification

Test matching and unmatched filters, repeated refresh, cancellation and recovery without lost input. Check long card titles, keyboard focus along divider edges, text enlargement, 320px and a 320px-wide container on desktop. The demo's completion buttons are development controls and must not appear in an audience-facing application.
