CitationChipexperimental
A compact chip that marks a citation. Clicking opens a popover with the source title, snippet, and a policy-checked link. StreamingMarkdown creates these chips from its sources prop, or use CitationChip on its own for a reference list.
Playground
<script lang="ts">
import { CitationChip } from '@urbicon-ui/blocks';
const source = { id: '1', title: 'Attention Is All You Need', url: 'https://arxiv.org/abs/1706.03762', snippet: 'We propose the Transformer, a network architecture based solely on attention mechanisms.' };
</script>
<CitationChip
{source}
index={1}
/>01 Examples
Standalone source footer
<script lang="ts">
import { CitationChip, type CitationSource } from '@urbicon-ui/blocks';
// Standalone reference list — no streamed message required. Each chip opens a
// popover with the title, snippet, and a policy-checked outbound link.
const sources: CitationSource[] = [
{
id: '1',
title: 'Attention Is All You Need',
url: 'https://arxiv.org/abs/1706.03762',
snippet: 'The Transformer, based solely on attention mechanisms.'
},
{
id: '2',
title: 'Scaling Laws for Neural Language Models',
url: 'https://arxiv.org/abs/2001.08361',
snippet: 'Quality improves smoothly with model size, dataset size, and compute.'
},
{
id: '3',
title: 'Training language models to follow instructions',
url: 'https://arxiv.org/abs/2203.02155',
snippet: 'Human feedback aligns models with user intent.'
}
];
</script>
<div class="flex flex-wrap items-center gap-2">
<span class="text-text-tertiary text-sm">Sources:</span>
{#each sources as source, i (source.id)}
<CitationChip {source} index={i + 1} />
{/each}
</div>
Numeric vs. label
<script lang="ts">
import { CitationChip, type CitationSource } from '@urbicon-ui/blocks';
const source: CitationSource = {
id: '1',
title: 'Attention Is All You Need',
url: 'https://arxiv.org/abs/1706.03762',
snippet: 'The Transformer, based solely on attention mechanisms.'
};
</script>
<div class="flex flex-wrap items-center gap-3">
<!-- numeric: a compact footnote pill for dense inline citations. -->
<CitationChip {source} index={1} citationStyle="numeric" />
<!-- label: the (truncated) title, for named sources in a footer or sidebar. -->
<CitationChip {source} index={1} citationStyle="label" />
</div>
From StreamingMarkdown
Self-attention replaced recurrence in sequence models .
<script lang="ts">
import { StreamingMarkdown, type CitationSource } from '@urbicon-ui/blocks';
// In a streamed answer you rarely construct chips yourself: StreamingMarkdown
// wires them up from its `sources` prop. Each in-text [id] marker whose id
// matches a source becomes a CitationChip automatically.
const sources: CitationSource[] = [
{
id: '1',
title: 'Attention Is All You Need',
url: 'https://arxiv.org/abs/1706.03762',
snippet: 'The Transformer, based solely on attention mechanisms.'
}
];
const content = 'Self-attention replaced recurrence in sequence models [1].';
</script>
<StreamingMarkdown {content} {sources} headingLevelStart={3} />
02 Accessibility
Descriptive trigger label
A bare "[1]" tells a screen reader nothing, so the trigger's aria-label defaults to Source {index}: {title} (or Source: {title} with no index). Override it with
the label prop when you need different wording.
Named popover
The chip opens a Popover; the same aria-label lands on the panel, so the opened panel
carries a name. Keyboard and focus behaviour (open, close on Escape, focus return) come from the underlying
Popover primitive.
Policy-checked link
The outbound link follows the same strict urlPolicy as StreamingMarkdown. If the URL is blocked or
absent, the popover shows just the title and snippet with no link, so an untrusted source URL
cannot introduce a dangerous scheme.
03 Related
For the streaming flow that produces these chips automatically, see StreamingMarkdown. Its sources prop resolves the markers and applies the same urlPolicy to every chip.
04 API Reference
11 propsProp | Type | Default | Description | |
|---|---|---|---|---|
source required | CitationSource | — | The cited source. Required. | |
citationStyle | numericlabel | 'numeric' | What the chip shows: numeric renders index (or source.id as a
fallback) as a compact numeric pill; label renders the (truncated)
source.title. | |
class | string | — | Extra classes merged onto the trigger chip (the root slot). | |
index | number | — | 1-based ordinal shown as the chip label under citationStyle="numeric". Falls back to source.id when omitted. | |
label | string | — | Override the trigger's aria-label. Defaults to
Source {index}: {title} (or Source: {title} without an index). | |
openLabel | string | 'Open source' | Text of the outbound link in the popover. | |
preset | string | — | Apply a named preset registered via <BlocksProvider presets={{ CitationChip: {...} }}>.
Prefer this over class overrides when the requested look falls outside the
semantic intent palette. | |
slotClasses | Partial<Record<CitationChipSlots, string>> | — | Per-slot class overrides. Slots: trigger (root chip), popover (content wrapper), title, snippet, link, linkIcon. | |
unstyled | boolean | — | Strip all default tv() classes, the surrounding Popover's included; combine with class / slotClasses for a custom look. | |
urlPolicy | MarkdownUrlPolicy | — | URL policy applied to source.url before it becomes a link (same strict
default as the streaming-markdown engine — untrusted LLM output). A blocked
URL yields no link in the popover, only title/snippet. | |
...HTMLButtonAttributes inherited | HTMLAttributes | — | HTML attributes (excluding: 'class' | 'children') |
05 Types
Local type definitions used by this component.
Name | Kind | Category | Used by | Description | |
|---|---|---|---|---|---|
CitationSource | interface | helper | 1 | A cited source surfaced behind a [id] citation marker. id keys the
source to its marker; title is always shown, url / snippet are optional
and only render when present (and, for url, when the URL policy allows it). | |
CitationChipProps | interface | props | 0 | — | |
MarkdownUrlPolicy | interface | helper | 1 | — | |
CitationChipSlots | type | variant | 0 | Slot names derived from the tv() config — single source of truth for slotClasses. | |
SlotNames | type | helper | 0 | Extracts the slot-name union from a slotted tv() config function — the
companion to VariantProps. The slot-mode overload returns
(props?) => { [K in keyof S]: SlotFn }, so keyof ReturnType<T> is exactly
the set of slot names a component declares in tv({ slots: … }).
Use it to type a component's slotClasses prop from the single source of
truth (its *.variants.ts) instead of hand-maintaining a parallel union
that silently drifts when a slot is added or renamed: |
06 Installation
Import
import { CitationChip } from '@urbicon-ui/blocks';
import type { CitationChipProps, CitationSource } from '@urbicon-ui/blocks';