# Context Cards

> Show the passages an agent retrieved, each with its title, text, and source.

Source: https://ai.honestui.com/docs/components/context-cards

```tsx
import { ContextCards } from "@/components/ai/context-cards"

export function ContextCardsDemo() {
  return (
    <div className="w-full max-w-md min-w-0">
      <ContextCards
        title="Retrieved context"
        chunks={[
          {
            id: "focus",
            title: "Focus ring",
            meta: "212 characters",
            body: "Keyboard focus is shown with a 1px accent ring. A control on an accent fill pushes the ring out by 2px so it clears the fill.",
            source: { label: "Accessibility", href: "https://www.honestui.com/docs/accessibility" },
          },
          {
            id: "tokens",
            title: "Token naming",
            meta: "148 characters",
            body: "Semantic tokens name a role, such as the primary foreground, and resolve to a different value in each theme.",
            source: { label: "tokens-notes.md" },
          },
        ]}
      />
    </div>
  )
}

```

## Overview [#overview]

Context Cards lists what an agent retrieved before it answered. Each card has a title, the passage itself, and where it came from. The heading carries a count, computed from the passages you pass.

Showing the retrieved text lets a person check an answer against its evidence. Pass the passages your retrieval step returned, unedited.

## Anatomy [#anatomy]

<Anatomy title="ContextCards">
  <AnatomyItem name="Heading" description="The title and a count of passages" />

  <AnatomyItem name="Card" description="One passage: title, an optional size or score, and the text" />

  <AnatomyItem name="Source" description="Where the passage came from, as a link when you pass an address" />
</Anatomy>

`context-cards` has no registry dependencies.

## Behavior [#behavior]

### Sources [#sources]

A source with `href` is a link. A source without one is plain text, for files a person cannot open from the page.

### Empty [#empty]

With no passages, the count reads 0 and `emptyMessage` is shown in place of the list. Say what that means for the answer.

## Accessibility [#accessibility]

The component is a section named by its heading, and the cards are a list. Source links are underlined, so they are not marked by color alone. Icons are decorative.

Context Cards has no motion and no keyboard interaction beyond its links.

## Installation [#installation]


  

  
    <CliBlock commands="[&#x22;@honestai/context-cards&#x22;]" />
  

  
    
      
        Copy and paste this file into your project.
      

      ### components/ai/context-cards.tsx

```tsx
import * as React from "react"
import { ArrowUpRight as ArrowUpRightIcon, FileText as FileTextIcon } from "honestui/icons"

import { cn } from "@/lib/utils"

export type ContextChunk = {
  id: string
  title: string
  /** The retrieved passage. */
  body: string
  /** A size or score shown beside the title, such as "290 characters". */
  meta?: string
  /** Where the passage came from. With `href`, it is a link. */
  source?: { label: string; href?: string }
}

const smallText =
  "[font-size:var(--hui-font-size-small)] [letter-spacing:var(--hui-letter-spacing-small)] [line-height:var(--hui-line-height-small)]"

export type ContextCardsProps = Omit<React.ComponentProps<"section">, "children" | "title"> & {
  /** The heading, such as "Retrieved context". */
  title: string
  chunks: ContextChunk[]
  /** Shown when `chunks` is empty. */
  emptyMessage?: string
}

/** The passages an agent retrieved, each with its title, text, and source. */
function ContextCards({
  title,
  chunks,
  emptyMessage = "Nothing was retrieved.",
  className,
  ...props
}: ContextCardsProps) {
  const headingId = React.useId()

  return (
    <section
      data-slot="context-cards"
      aria-labelledby={headingId}
      className={cn(
        "flex w-full min-w-0 flex-col gap-[var(--hui-space-3)] text-[var(--hui-color-foreground-base-primary)]",
        smallText,
        className
      )}
      {...props}
    >
      <div className="flex items-center gap-[var(--hui-space-3)]">
        <p id={headingId} className="m-0 [font-weight:var(--hui-font-weight-semibold)]">
          {title}
        </p>
        <span className="rounded-[var(--hui-radius-1)] bg-[var(--hui-color-background-base-secondary)] px-[var(--hui-space-2)] tabular-nums text-[var(--hui-color-foreground-base-secondary)]">
          {chunks.length}
        </span>
      </div>

      {chunks.length === 0 ? (
        <p className="m-0 text-[var(--hui-color-foreground-base-secondary)]">{emptyMessage}</p>
      ) : (
        <ul role="list" className="m-0 flex list-none flex-col gap-[var(--hui-space-3)] p-0">
          {chunks.map((chunk) => (
            <li
              key={chunk.id}
              data-slot="context-card"
              className="min-w-0 overflow-hidden rounded-[var(--hui-radius-4)] border-[0.5px] border-[var(--hui-color-border-base-primary)] bg-[var(--hui-color-background-base-primary)] shadow-[var(--hui-shadow-feather)]"
            >
              <div className="flex min-w-0 items-baseline gap-[var(--hui-space-3)] border-b-[0.5px] border-[var(--hui-color-border-base-primary)] px-[var(--hui-space-4)] py-[var(--hui-space-3)]">
                <span className="min-w-0 truncate [font-weight:var(--hui-font-weight-medium)]">
                  {chunk.title}
                </span>
                {chunk.meta && (
                  <span className="ms-auto shrink-0 text-[var(--hui-color-foreground-base-secondary)]">
                    {chunk.meta}
                  </span>
                )}
              </div>
              <p className="m-0 break-words px-[var(--hui-space-4)] py-[var(--hui-space-3)] text-[var(--hui-color-foreground-base-secondary)] [line-height:var(--hui-line-height-regular)]">
                {chunk.body}
              </p>
              {chunk.source && (
                <div className="px-[var(--hui-space-4)] pb-[var(--hui-space-4)]">
                  <ContextSource source={chunk.source} />
                </div>
              )}
            </li>
          ))}
        </ul>
      )}
    </section>
  )
}

function ContextSource({ source }: { source: NonNullable<ContextChunk["source"]> }) {
  const layout =
    "inline-flex max-w-full items-center gap-[var(--hui-space-2)] rounded-[var(--hui-radius-full)] bg-[var(--hui-color-background-base-secondary)] px-[var(--hui-space-3)] py-[var(--hui-space-1)] text-[var(--hui-color-foreground-base-secondary)]"
  const content = (
    <>
      <FileTextIcon aria-hidden="true" className="size-3.5 shrink-0" />
      <span className="min-w-0 truncate">{source.label}</span>
    </>
  )

  if (!source.href) {
    return <span className={layout}>{content}</span>
  }

  return (
    <a
      href={source.href}
      className={cn(
        layout,
        "underline decoration-[var(--hui-color-border-base-secondary)] underline-offset-2 outline-none motion-safe:[transition:var(--hui-transition-interactive)] hover:text-[var(--hui-color-foreground-base-primary)] focus-visible:[outline:var(--hui-focus-ring)]"
      )}
    >
      {content}
      <ArrowUpRightIcon aria-hidden="true" className="size-3 shrink-0" />
    </a>
  )
}

export { ContextCards }

```

      
        Update the import paths to match your project setup.
      
    
  


## Usage [#usage]

```tsx
import { ContextCards } from "@/components/ai/context-cards"

<ContextCards
  title="Retrieved context"
  chunks={retrieval.chunks.map((chunk) => ({
    id: chunk.id,
    title: chunk.heading,
    body: chunk.text,
    meta: `${chunk.text.length} characters`,
    source: { label: chunk.document.name, href: chunk.document.url },
  }))}
/>
```

## Examples [#examples]

### Nothing retrieved [#nothing-retrieved]

The empty state, with a message written for this case.

```tsx
import { ContextCards } from "@/components/ai/context-cards"

export function ContextCardsEmptyExample() {
  return (
    <div className="w-full max-w-md min-w-0">
      <ContextCards
        title="Context for this answer"
        chunks={[]}
        emptyMessage="No passages matched this question, so the answer uses no retrieved context."
      />
    </div>
  )
}

```

## API reference [#api-reference]

### ContextCards [#contextcards]

Accepts native section props beyond those listed.

| Prop           | Type             | Default  | Description                   |
| -------------- | ---------------- | -------- | ----------------------------- |
| `title`        | string           | Required | The heading.                  |
| `chunks`       | `ContextChunk[]` | Required | The retrieved passages.       |
| `emptyMessage` | string           | English  | Shown when `chunks` is empty. |

### Types [#types]

| Export         | Shape                                                                              |
| -------------- | ---------------------------------------------------------------------------------- |
| `ContextChunk` | `id`, `title`, `body`, and optional `meta`, `source` (`label` and optional `href`) |
