Skip to content
Component installs need the registry setup— the Base UI shadcn project, the @vegastack namespace and the Cloudflare Access service token.
VegaStack Design

Marker

An inline conversation marker — status lines, system notes, and labelled dividers — built on Base UI render, polymorphic to a link or button.

Status
stable
Since
0.1.0
Accessibility pattern
decorative icon + text

Last updated

Pull request merged into main
All checks passed
Synced 12 documents to the workspace

Install

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

pnpm dlx shadcn@latest add @vegastack/marker

Usage

import { Marker, MarkerContent, MarkerIcon } from "@/components/ui/marker";

<Marker>
  <MarkerIcon>
    <CheckIcon />
  </MarkerIcon>
  <MarkerContent>All checks passed</MarkerContent>
</Marker>;

Anatomy

A Marker is a single inline row inside a conversation. MarkerIcon is a decorative leading slot; MarkerContent is the (wrapping) text. The root is a group/marker, so content can react to the chosen variant.

Marker
MarkerContent — data-slot="marker-content"
MarkerIcon — data-slot="marker-icon"
  • Marker — the row (data-slot="marker", data-variant). Built on Base UI useRender, so render turns it into any element (a link, a button).
  • MarkerIcon — the leading icon (data-slot="marker-icon", aria-hidden). Any bare svg child is sized to size-4.
  • MarkerContent — the text (data-slot="marker-content"). Wraps long content; centres itself under the separator variant.

Examples

Status markers

Pull request merged into main
All checks passed
Synced 12 documents to the workspace

Variants

default, separator (a centred label with divider lines), and border (a row with a bottom hairline).

A bordered status row
Today
A plain inline marker

Separators

variant="separator" renders a centred label flanked by divider lines — useful for date dividers and section breaks within a thread.

Today
Worked for 42s
Conversation compacted

Bordered list

variant="border" stacks status rows with a hairline under each.

Switched to release-candidate
Reviewed 8 related files
Opened implementation notes

Status (live region)

Give an in-progress marker role="status" so assistive tech announces it. Pair with a Spinner for the visual cue.

Compacting conversation
Running tests

Streaming text

Pair MarkerContent with the shimmer utility (with or without a spinner) for in-progress, streaming copy.

Generating response…
Reading 4 files

Pass render to make the whole row a link or a button without losing the marker styling.

View the pull request

Motion

Pass animateIn to pop a marker in (motion-pop-in) when its own appearance is the event — a status note that just landed, a divider inserted at the head of a live thread. It's off by default: markers that are part of an already-rendered thread history must not pop on page load.

Waiting for the merge…

For a marker where only part of the text links — not the whole row — nest an <a> inside MarkerContent. It inherits the underlined, hover-brightening link affordance, leaving the rest of the row plain. Use this instead of render when the marker itself isn't the link target.

Merged PR 482 into main
Updated the release notes for this change

Playground

Every Marker prop, auto-generated from its TypeScript types.

Deployed to production

API Reference

Marker

PropTypeDefaultDescription
animateInbooleanfalseOpt-in mount animation (motion-pop-in) for a marker that appears in response to a real event — a status note that just landed, a new "Today" divider inserted at the head of a thread. **Default off**: markers that are part of an already-rendered thread history must not pop on initial page load. Set it only for a marker whose own appearance is the signal (mirrors Badge's animateIn).
renderuseRender.RenderProp<Record<string, unknown>>Render the marker as a different element (e.g. a link or button) via Base UI render composition. Pass a ReactElement or a render function.
variantMarkerVariant'default'Layout style. - default: a plain inline marker (default). - separator: a centred label with divider lines either side. - border: a row with a bottom hairline.

MarkerIcon

The leading icon slot. Accepts the standard <span> props plus className; it is aria-hidden and sizes any bare svg child to size-4.

MarkerIcon

MarkerIcon adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).

Data attributes and CSS variables on MarkerIcon

AttributeValues
data-slot"marker-icon"

MarkerContent

The wrapping text slot. Accepts the standard <span> props plus className; it centres under the separator variant and gives any nested <a> the underlined, hover-brightening link affordance.

MarkerContent

MarkerContent adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).

Data attributes and CSS variables on MarkerContent

AttributeValues
data-slot"marker-content"

Accessibility

  • MarkerIcon is aria-hidden — the adjacent MarkerContent text carries the meaning, so never rely on the icon alone.
  • When rendered as a link/button via render, the element keeps native semantics and the global :focus-visible ring.
  • For an in-progress marker, add role="status" so the row is announced as a live region when its text updates.
  • A streaming shimmer marker honours prefers-reduced-motion (the sweep stops).
ContractStates tested
Behaviourdefault
Accessibilitysemantic-html
Visualdefault, hover

Do / Don't

Do
Use a Marker for inline thread annotations — a status line, a date divider, or a small action row.
Don't
Use it as a standalone status chip outside a conversation — reach for Badge, or Alert for a full banner.

On this page