Skip to content

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.

01Preview

Study

Protocol study

Example content with a stable identifier.

Document

Shared archive

Example content with a stable identifier.

Edition

Open edition

Example content with a stable identifier.

02Filter and render

<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.

03Container 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.

04Request states

The complete runnable specimen 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.

05Verification

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.

Related subjects

distributed·gallery