Skip to content
Brok UI

Loading…

No results

Citation

Open source

Inline citation chip with a numbered badge, truncated excerpt, and source label that links when an href is set.

Version
v2.1.4
Stability
stable
License
MIT
Related
Equation
Kbd
Link Preview

Preview

1 Self-attention scales quadratically with sequence length. arxiv.org 2 Rotary embeddings improve length generalisation. blog.example.com
Variant
previews.components.citation.default.blade.php Blade
<div class="flex w-full flex-wrap justify-center gap-2">
    <x-ui.citation :number="1" excerpt="Self-attention scales quadratically with sequence length." source="arxiv.org" href="#" />
    <x-ui.citation :number="2" excerpt="Rotary embeddings improve length generalisation." source="blog.example.com" />
</div>
Chip Current
Marker Current
Card Current

Installation

terminal
php artisan ui:add citation

Registry contract

php artisan ui:add citation writes only the files below. The CLI validates each file hash before writing and asks before it replaces a local change, unless you pass --force.

  • blade resources/views/components/ui/citation.blade.php
Registry dependencies
None — installs on its own.
Packages
composer: jml/brok:^0.2

Use with AI

A brief for your coding agent: install command, usage, props, guidance and the rules. Copy it, or open a prompt about this component in an assistant.

citation.md
# Brok UI: Citation (`citation`)

Inline citation chip with a numbered badge, truncated excerpt, and source label that links when an href is set.

Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.

## Install

```bash
php artisan ui:add citation
```

## Usage

```blade
<div class="flex w-full flex-wrap justify-center gap-2">
    <x-ui.citation :number="1" excerpt="Self-attention scales quadratically with sequence length." source="arxiv.org" href="#" />
    <x-ui.citation :number="2" excerpt="Rotary embeddings improve length generalisation." source="blog.example.com" />
</div>
```

## Props

- `number` (int, default `1`) — Citation number shown in the marker pill or leading badge.
- `excerpt` (string, default `…relevant quoted text…`) — Quoted or paraphrased text shown in the card or chip.
- `source` (string, default `example.com`) — Source name or domain shown next to the excerpt.
- `href` (mixed|null, default `null`) — Optional link to the source; renders the citation as an anchor.
- `title` (mixed|null, default `null`) — Optional title of the source shown above the excerpt in the marker and card variants.
- `favicon` (mixed|null, default `null`) — Optional favicon URL shown before the source name.
- `variant` (chip|marker|card, default `chip`) — Layout: chip (inline pill), marker (numbered pointer with hover card), or card (sources-list entry).

## Use when

- Use for compact labels, counts, and concise metadata that support scanning.
- Pointing running text or a source list at the origin of a quoted or paraphrased claim, numbered for reference.
- Choosing chip for an inline citation, marker for a superscript-style pointer with a hover or focus card, or card for a dedicated sources list.

## Avoid when

- Do not force badges or short labels to carry complex explanations that need sentence-level context.
- Linking to an unrelated page without a citation number; use a plain link instead. Rendering a mathematical expression rather than a source reference; use equation instead.

## Anti-patterns

- Encoding complex meaning in a badge alone

## Rules

- Use the `<brok:citation>` tag (or `<x-ui.citation>`) in Blade; do not rewrite the component.
- Prefer the documented props and variants over utility-class overrides; when a utility must win, use the `!` important modifier.
- Use semantic design tokens (`bg-primary`, `text-muted-foreground`), never raw colour utilities.
- Keep the `data-slot` attributes; they are the styling and test hooks.

## Links

- Docs: https://brokui.dev/docs/components/citation
- Registry JSON (files, props, contract): https://brokui.dev/r/open/citation.json

Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.

Examples

markers.blade.php Blade
{{-- `variant="marker"`: numbered pills in running text; hover or focus one to see the source. --}}
<p class="max-w-md text-sm leading-6 text-foreground">
    {{ __('Attention cost grows with the square of the sequence length') }}<x-ui.citation variant="marker" :number="1" excerpt="Self-attention scales quadratically with sequence length." title="Attention is all you need" source="arxiv.org" href="#" />{{ __(', which is why long-context models trade it for rotary or sliding-window schemes') }}<x-ui.citation variant="marker" :number="2" excerpt="Rotary embeddings improve length generalisation." title="RoFormer" source="blog.example.com" href="#" />.
</p>
sources.blade.php Blade
{{-- `variant="card"`: a Sources list under an answer. --}}
<div class="flex w-full max-w-md flex-col gap-2">
    <p class="text-2xs font-semibold uppercase tracking-wide text-muted-foreground">{{ __('Sources') }}</p>
    <x-ui.citation variant="card" :number="1" title="Attention is all you need" excerpt="Self-attention scales quadratically with sequence length, so the cost of a 2× longer context is 4× the compute." source="arxiv.org" href="#" />
    <x-ui.citation variant="card" :number="2" title="RoFormer: rotary position embedding" excerpt="Rotary embeddings improve length generalisation." source="blog.example.com" href="#" />
</div>

API

manifest knowledge + registry-derived coverage

Props

Props accepted by this component: name, type, default value and description.
Prop Type Default Description
number int 1 Citation number shown in the marker pill or leading badge.
excerpt string …relevant quoted text… Quoted or paraphrased text shown in the card or chip.
source string example.com Source name or domain shown next to the excerpt.
href mixed | null null Optional link to the source; renders the citation as an anchor.
title mixed | null null Optional title of the source shown above the excerpt in the marker and card variants.
favicon mixed | null null Optional favicon URL shown before the source name.
variant chip | marker | card chip Layout: chip (inline pill), marker (numbered pointer with hover card), or card (sources-list entry).

Slots

  • default —

Data slots

Stable hooks for CSS overrides and browser tests.

citation citation-card

Behavior

  • Renders as an anchor when href is set and a span otherwise, merging link-only attributes only onto the anchor.
  • The marker variant shows a numbered pill in the text and reveals a source card on hover or keyboard focus.
  • Declares registry capability flags: a11y, responsive, rtl, darkMode, localized.

Guidance

Compact label and metadata

Communicate concise status, count, or metadata.

Use when

  • Use for compact labels, counts, and concise metadata that support scanning.
  • Pointing running text or a source list at the origin of a quoted or paraphrased claim, numbered for reference.
  • Choosing chip for an inline citation, marker for a superscript-style pointer with a hover or focus card, or card for a dedicated sources list.

Avoid when

  • Do not force badges or short labels to carry complex explanations that need sentence-level context.
  • Linking to an unrelated page without a citation number; use a plain link instead. Rendering a mathematical expression rather than a source reference; use equation instead.

Use instead

  • Visible explanatory text

Anti-patterns

  • Encoding complex meaning in a badge alone
Anatomy
root citation-card
Theming hooks
citation citation-card

Accessibility

WCAG 2.2 AA Keyboard focus-visible RTL-ready Localized labels Dark mode
Keyboard
native
Focus
native
  • The marker variant carries an aria-label naming the source number and source, so the pointer is announced even though the card stays visually hidden until hover or focus.
  • Semantic HTML and a stable data-slot attribute for styling and scripting hooks.
  • Focus-visible rings use the ring token, so keyboard focus is always visible.
  • Disabled and invalid states are conveyed to assistive tech, not by color alone.
  • Targets WCAG 2.2 AA; verify contrast in light, dark, admin and customer surfaces in the preview.
  • Labels go through __() and layout uses logical properties (ms-*, text-start), so it mirrors under dir="rtl" — flip the preview to RTL to confirm.
  • Dark mode uses the same semantic tokens under the dark class; high contrast follows forced-color system tokens.

Livewire

Safe

Livewire can update this component through forwarded wire:* attributes.

Validation

Validation support: native. Keep the error message connected with aria-describedby.

livewire-form.blade.php Blade
<form wire:submit="save" class="space-y-2">
    <brok:citation
        wire:model="value"
        :aria-invalid="$errors->has('value') ? 'true' : 'false'"
        aria-describedby="value-error"
    />

    @error('value')
        <p id="value-error" role="alert">{{ $message }}</p>
    @enderror

    <brok:button type="submit" wire:loading.attr="disabled">
        <span wire:loading.remove>Save</span>
        <span wire:loading>Saving…</span>
    </brok:button>
</form>

Source

The exact, editable file ui:add writes into your app. Previews render this same code; there are no preview-only components.

resources/views/components/ui/citation.blade.php Blade
@props([
    'number' => 1,
    'excerpt' => '…relevant quoted text…',
    'source' => 'example.com',
    'href' => null,
    // Optional title of the source (a page, a paper) shown above the excerpt.
    'title' => null,
    // Optional favicon URL; without one the source's first letter marks it.
    'favicon' => null,
    // chip (default): number, excerpt and source on one pill.
    // marker: a small numbered pill for running text; the excerpt and source
    //         appear in a card on hover or focus.
    // card: a source card for a "Sources" list — favicon, title, source, excerpt.
    'variant' => 'chip',
])

@php
    // Accept a string, a backed enum or a Stringable for `variant`.
    $styles = require base_path(config('ui.component_path', 'resources/views/components/ui').'/_styles.php');
    $variant = $styles['normalizeVariant']($variant);
    $number = (int) $number;
    $variant = in_array($variant, ['chip', 'marker', 'card'], true) ? $variant : 'chip';
    $tag = $href ? 'a' : 'span';
    $linkAttrs = $href ? ['href' => $href, 'rel' => 'noopener'] : [];
    $ring = 'focus-visible:outline-none focus-visible:ring-[length:var(--ring-width)] focus-visible:ring-ring focus-visible:ring-offset-[length:var(--ring-offset-width)] focus-visible:ring-offset-background';
    $classes = match ($variant) {
        // A hair of space before the pill so it does not sit on the word.
        // Raised a little above the baseline (not a full superscript), so the
        // pill sits in the line instead of floating over it.
        'marker' => 'group/citation relative ms-1 inline-flex align-[0.2em]',
        'card' => 'flex w-full min-w-0 gap-3 rounded-md border border-border bg-card p-4 text-start text-card-foreground transition-colors'.($href ? ' hover:bg-muted/50 '.$ring : ''),
        default => 'group/citation inline-flex max-w-full items-center gap-2 rounded-full border border-border bg-muted ps-1 pe-3 py-1 align-middle text-sm text-foreground transition-colors'.($href ? ' hover:bg-accent '.$ring : ''),
    };
    $classes .= ' motion-reduce:transition-none';
@endphp

{{--
    Citation — a numbered pointer to a source. The whole thing is an <a> when
    `href` is set and a <span> otherwise; the tag is resolved in PHP (never an
    @if inside the attribute list), and link-only attributes are merged in
    only on the anchor variant. The file ends on the closing tag with no
    newline: a marker sits inside running text, and a trailing newline would
    render as a space between the pill and the punctuation after it.
--}}
<{{ $tag }}
    data-slot="citation"
    data-variant="{{ $variant }}"
    @foreach ($linkAttrs as $name => $val) {{ $name }}="{{ $val }}" @endforeach
    @if ($variant === 'marker') aria-label="{{ __('Source :number: :source', ['number' => $number, 'source' => $source]) }}" @endif
    {{ $attributes->merge(['class' => $classes]) }}
>
    @if ($variant === 'marker')
        {{-- The pill in the text; the card unfolds below it on hover/focus. --}}
        <span class="inline-flex h-4 min-w-4 items-center justify-center rounded-full border border-border bg-muted px-1 text-2xs font-semibold leading-none tabular-nums text-muted-foreground transition-colors group-hover/citation:border-primary group-hover/citation:bg-primary group-hover/citation:text-primary-foreground group-focus-visible/citation:border-primary group-focus-visible/citation:bg-primary group-focus-visible/citation:text-primary-foreground motion-reduce:transition-none {{ $ring }}">{{ $number }}</span>
        <span
            data-slot="citation-card"
            role="tooltip"
            class="pointer-events-none invisible absolute start-0 top-full z-popover mt-1 w-64 rounded-md border border-border bg-popover p-3 text-start text-xs text-popover-foreground opacity-0 shadow-md transition-opacity group-hover/citation:visible group-hover/citation:opacity-100 group-focus-visible/citation:visible group-focus-visible/citation:opacity-100 motion-reduce:transition-none"
        >
            <span class="flex items-center gap-2 text-muted-foreground">
                @if ($favicon)<img src="{{ $favicon }}" alt="" class="size-4 rounded-sm" loading="lazy" />@endif
                <span class="truncate">{{ $source }}</span>
            </span>
            @if ($title)<span class="mt-1 block truncate font-medium text-foreground">{{ $title }}</span>@endif
            <span class="mt-1 block text-muted-foreground line-clamp-3">{{ $excerpt }}</span>
        </span>
    @elseif ($variant === 'card')
        <span class="inline-flex size-6 shrink-0 items-center justify-center rounded-full border border-border bg-muted text-xs font-semibold tabular-nums text-muted-foreground">{{ $number }}</span>
        <span class="min-w-0 flex-1">
            <span class="flex h-6 items-center gap-2 text-xs text-muted-foreground">
                @if ($favicon)<img src="{{ $favicon }}" alt="" class="size-4 rounded-sm" loading="lazy" />@endif
                <span class="truncate">{{ $source }}</span>
                @if ($href)
                    <svg class="size-3 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 3h6v6M10 14 21 3M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /></svg>
                @endif
            </span>
            @if ($title)<span class="mt-1 block text-sm font-medium text-foreground">{{ $title }}</span>@endif
            <span class="mt-1 block text-sm text-muted-foreground line-clamp-2">{{ $excerpt }}</span>
        </span>
    @else
        <span class="inline-flex size-5 shrink-0 items-center justify-center rounded-full border border-border bg-background text-xs font-semibold tabular-nums text-foreground">{{ $number }}</span>
        <span class="truncate">{{ $excerpt }}</span>
        <span class="inline-flex shrink-0 items-center gap-1 text-xs text-muted-foreground">
            @if ($favicon)<img src="{{ $favicon }}" alt="" class="size-3 rounded-sm" loading="lazy" />@endif
            {{ $source }}
        </span>
    @endif
</{{ $tag }}>

Ownership & lifecycle

Owner, release state, review evidence and adoption for this item.
Owner
Platform UI (@JoshJML)
Current version
2.1.4
Status
Stable
License
open
Accessibility reviewed
No review date recorded
Last breaking change
No date recorded
Deprecation
Not deprecated
Contract
v6
Foundation
≥ 1.0.0