Skip to content
Component installs need the registry setup
VegaStack Design

Inline Chip

The one inline reference in running text — a person, page, file, task, meeting, customer or project — on the text's baseline, tinted by kind.

Status
stable
Since
0.23.102
Accessibility pattern
link or button; card on focus; ⌘-click opens a tab

Last updated

Asha Rao moved Ship the onboarding checklist to review, linked Q3 plan and spec.pdf, and booked Kick-off with Acme for Acme Corp under Website relaunch.

Install

Add Inline Chip from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.

pnpm dlx shadcn@latest add @vegastack/inline-chip

The same command installs the registry items it composes: @vegastack/file-kind, @vegastack/hover-card, @vegastack/person-avatar.

Usage

import { InlineChip, InlineChipProvider } from "@/components/ui/inline-chip";

<InlineChipProvider
  value={{
    href: (kind, id) => routes[kind]?.(id) ?? null,
    person: (id) => members.get(id),
    onOpen: (target) =>
      target.kind === "file" ? openViewer(target.href) : false,
  }}
>
  <InlineChip kind="task" targetId="T-42" label="Ship the checklist" />
</InlineChipProvider>;

Every @ mention and file link in the system renders as this chip — in TextEdit's editor, in MarkdownView and in Comments — so one InlineChipProvider near the app root tells all of them where a chip links, who a person is, what a hover card previews and what a click opens. A chip's own props win over the provider.

Asha Rao moved Ship the onboarding checklist to review, linked Q3 plan and spec.pdf, and booked Kick-off with Acme for Acme Corp under Website relaunch.

Scope

  • Owns: the chip's look (icon, tint and baseline alignment), its link or button semantics, ⌘-click, and the hover card (a person's avatar, name and email, or the host's preview).
  • Does not own: resolving ids, routing, or the file viewer — the host passes href, person, preview and onOpen, directly or through InlineChipProvider.
  • Compose with: MarkdownView and TextEdit (both render mentions and file links as this chip), FileViewer (open a file chip in it from onOpen).

Anatomy

InlineChip — data-slot="inline-chip-avatar" | "inline-chip-icon" | "inline-chip-label" | "inline-chip-person" | "inline-chip-preview"
InlineChipProvider
InlineChipPreview

Examples

Headings, lists and wrapping

The chip is an inline box, not an inline-flex one: its label is ordinary text on the line's baseline, its icon a 1em glyph, so it takes the size of a heading or a list item and wraps with its line, the tint continuing on each line.

Notes on Website relaunch

A long chip wraps with its line: Customer onboarding handbook, second edition and the text carries on.

Photo and restricted

A person with a known photo shows it in place of the person icon. An id starting with restricted: (or restricted) is a target the reader may not open: muted, never a link.

Asha Rao shared Private page — a page you cannot open.

In Markdown

[@label](mention://kind/id) and a link under fileLinkPrefix both render as this chip.

Review with Asha Rao

Kinds

KindIconGroundInkClick
userphoto, else user--tag-blue-subtle--tag-blue-textnone; hover card
pagefile-text--muted--foregroundonOpen, else href
filethe file's type--muted--foregroundonOpen (viewer), href
taskcircle-check--tag-green-subtle--tag-green-textonOpen, else href
meetingcalendar-days--tag-orange-subtle--tag-orange-textonOpen, else href
customerbuilding-2--tag-purple-subtle--tag-purple-textonOpen, else href
projectfolder-kanban--tag-cyan-subtle--tag-cyan-textonOpen, else href

API Reference

PropTypeDefaultDescription
kind*InlineChipKind—What the chip points at; picks the icon and the tint.
label*string—The name shown.
contentTypestring—A file chip's content type: the icon follows it, not only the name's extension.
hrefstringthe provider's href(kind, targetId)Where the chip links. Overrides the provider's href. null for no link.
imagestring—A user chip's photo, when there is no person.
onOpen((target: InlineChipTarget, event: React.MouseEvent | React.KeyboardEvent) => void | boolean)the provider's onOpenOpens the chip on a plain click or Enter; ⌘/Ctrl-click still opens href in a new tab. Overrides the provider's onOpen.
openOn"click" | "modifier""click"click — a plain click opens. modifier — only ⌘/Ctrl-click opens (in a new tab), so a click inside an editor places the caret instead.
personInlineChipPersonthe provider's person(targetId)A user chip's person: their photo shows in the chip, and the hover card shows the avatar, name and email. Overrides the provider's person.
previewReact.ReactNodethe provider's preview(kind, targetId)The hover card's body for a non-person chip. null turns the card off.
restrictedbooleantargetId starts with "restricted:"A muted chip that never links or opens: a target the reader may not see.
targetIdstring""The target's id: the provider's href, person and preview are asked by it. An id starting with restricted: is a target the reader may not open — a muted chip, never a link.

Data attributes and CSS variables on InlineChip

AttributeValues
data-slot"inline-chip-avatar" | "inline-chip-icon" | "inline-chip-label" | "inline-chip-person" | "inline-chip-preview"
PropTypeDefaultDescription
href((kind: InlineChipKind, id: string) => string | undefined)—Where a chip links, by kind and id. null for no link.
onOpen((target: InlineChipTarget, event: React.MouseEvent | React.KeyboardEvent) => void | boolean)—Opens a chip on a plain click or Enter (a file in the viewer, a task in a sheet). ⌘/Ctrl-click and middle-click still open a linked chip in a new tab. Return false to let the link navigate instead.
person((id: string) => InlineChipPerson | undefined)—The person behind a user chip, by id: the hover card shows them.
preview((kind: InlineChipKind, id: string) => React.ReactNode)—A small preview for a non-person chip's hover card (a task's title and status).
PropTypeDefaultDescription
name*string—The person's name.
emailstring—Their email, under the name in the hover card.
imagestring—Their photo: the chip shows it in place of the person icon.
PropTypeDefaultDescription
title*React.ReactNode—The record's title.
metaReact.ReactNode—A status line under it — a task's status, a meeting's time.

Accessibility

  • A chip with an href is a link; one that only opens (onOpen) is a button (role="button", Enter / Space). A person chip is a tab stop so its hover card opens on focus as well as hover. The icon is hidden from assistive technology — the label names the chip.
  • ⌘/Ctrl-click (and middle-click) on a linked chip opens it in a new tab. Inside an editor (openOn="modifier") a plain click places the caret and only ⌘/Ctrl-click opens.
  • The hit area reaches 24px tall without changing the line's height.
KeyAction
TabFocus the chip; opens its card
Enter / SpaceOpen the chip's target
ContractStates tested
Behaviourdefault, link, open, restricted, hover-card
Accessibilitybrowser-accessibility-test, keyboard-open, focus-preview
Visualdefault, hover, focus, restricted

Do / Don't

Do
Wrap the app in one InlineChipProvider so editor, Markdown and comments chips all link and preview the same way.
Don't
Draw a local mention or file pill in app code — every inline reference is this chip.

On this page