Westy Design Systemv0.2.7
OverviewComponentsExamplesStorybook

Foundations

  • Typography
  • Colors
  • Spacing
  • Radius
  • Shadows
  • Icons

Components

  • Accordion
  • Alert
  • Alert Dialog
  • Aspect Ratio
  • Avatar
  • Badge
  • Breadcrumb
  • Button
  • Button Group
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Collapsible
  • Combobox
  • Command
  • Context Menu
  • Data Table
  • Date Picker
  • Dialog
  • Direction
  • Drawer
  • Dropdown Menu
  • Empty
  • Field
  • Hover Card
  • Input
  • Input Group
  • Input OTP
  • Item
  • Kbd
  • Label
  • Menubar
  • Native Select
  • Navigation Menu
  • Pagination
  • Popover
  • Progress
  • Questionnaire
  • Radio Group
  • Resizable
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Skeleton
  • Slider
  • Sonner
  • Spinner
  • Switch
  • Table
  • Tabs
  • Textarea
  • Toggle
  • Toggle Group
  • Tooltip

Chat

  • Attachment
  • Bubble
  • Marker
  • Message
  • Message Scroller

Chat

Marker

Displays an inline status, system note, bordered row, or labeled separator in a conversation.

Playground

Explored 4 files
—variant
—MarkerIcon
—className
—asChild
—dir
Usage
Marker is the quiet line between messages — a tool call that ran, a date divider, a note the system left behind. It is muted, small-text, and full width by default, so it reads as chrome rather than as conversation. Compose it from MarkerIcon and MarkerContent; both are optional.
Explored 4 files
Today
import {
  Marker,
  MarkerContent,
  MarkerIcon,
} from "@workspace/ui/components/marker"

<Marker>
  <MarkerIcon>
    <MagnifyingGlassIcon />
  </MarkerIcon>
  <MarkerContent>Explored 4 files</MarkerContent>
</Marker>

<Marker variant="separator">
  <MarkerContent>Today</MarkerContent>
</Marker>
Variants
Three layouts for the same muted row. variant is mirrored to data-variant on the root, which is how MarkerContent knows to stop stretching and center itself inside a separator.
A default marker for inline notes.
A separator marker
A border marker for row boundaries.
<Marker>
  <MarkerContent>A default marker for inline notes.</MarkerContent>
</Marker>

<Marker variant="separator">
  <MarkerContent>A separator marker</MarkerContent>
</Marker>

<Marker variant="border">
  <MarkerContent>A border marker for row boundaries.</MarkerContent>
</Marker>
When to use each variant
VariantDescription
defaultAn inline marker for status, notes, and actions.
separatorA centered label with divider lines on each side. Use it for dates and for anything that closes a stretch of thread.
borderA default marker with a bottom border under the row. Use it to stack several markers as a list of boundaries.
With icon
MarkerIcon is a fixed size-4 slot that sizes any unsized SVG inside it, so Phosphor icons drop in without a class. The slot never shrinks — long content wraps around it. Stack the icon above the text with className="flex-col" on the root when the marker stands alone.
Switched to a new branch
Explored 4 files
Syncing completed
<Marker>
  <MarkerIcon>
    <GitBranchIcon />
  </MarkerIcon>
  <MarkerContent>Switched to a new branch</MarkerContent>
</Marker>

<Marker variant="separator">
  <MarkerIcon>
    <MagnifyingGlassIcon />
  </MarkerIcon>
  <MarkerContent>Explored 4 files</MarkerContent>
</Marker>

<Marker className="flex-col">
  <MarkerIcon>
    <BookOpenTextIcon />
  </MarkerIcon>
  <MarkerContent>Syncing completed</MarkerContent>
</Marker>
Status
Drop a Spinner into the icon slot for work still in flight, and set role="status" on the root so the text is announced when it changes. Note that the icon slot is aria-hidden, so the spinner's own label never reaches the accessibility tree — the marker text carries the whole message.
Compacting conversation
Running tests
<Marker role="status">
  <MarkerIcon>
    <Spinner />
  </MarkerIcon>
  <MarkerContent>Compacting conversation</MarkerContent>
</Marker>

<Marker variant="separator" role="status">
  <MarkerIcon>
    <Spinner />
  </MarkerIcon>
  <MarkerContent>Running tests</MarkerContent>
</Marker>
Shimmer
The shimmer utility animates a highlight across the text itself, which reads as streaming without spending an icon slot on it. It clips the background to the glyphs, so put it on MarkerContent — never on the root, where it would swallow the separator lines.
Thinking…
Reading 4 files
<Marker role="status">
  <MarkerContent className="shimmer">Thinking…</MarkerContent>
</Marker>

<Marker variant="separator" role="status">
  <MarkerContent className="shimmer">Reading 4 files</MarkerContent>
</Marker>
Separator
The divider lines are ::before and ::after pseudo-elements that split the leftover width between them, so the label stays centered at any content length. They use logical margins and mirror under dir="rtl".
Today
Worked for 42s
Conversation compacted
<Marker variant="separator">
  <MarkerContent>Today</MarkerContent>
</Marker>

<Marker variant="separator">
  <MarkerContent>Worked for 42s</MarkerContent>
</Marker>

<Marker variant="separator">
  <MarkerContent>Conversation compacted</MarkerContent>
</Marker>
Border
border adds a rule under the row and the padding to clear it. Stacked with a tight gap it turns a run of markers into a log — a list of steps that already happened.
Switched to release-candidate
Reviewed 8 related files
Opened implementation notes
<Marker variant="border">
  <MarkerIcon>
    <GitBranchIcon />
  </MarkerIcon>
  <MarkerContent>Switched to release-candidate</MarkerContent>
</Marker>

<Marker variant="border">
  <MarkerIcon>
    <MagnifyingGlassIcon />
  </MarkerIcon>
  <MarkerContent>Reviewed 8 related files</MarkerContent>
</Marker>

<Marker variant="border">
  <MarkerIcon>
    <FileTextIcon />
  </MarkerIcon>
  <MarkerContent>Opened implementation notes</MarkerContent>
</Marker>
Links and buttons
asChild swaps the root div for whatever element you pass, which is how a marker becomes a real link or button. An a child picks up an underline and a foreground hover automatically; a button is unstyled, so add the hover treatment yourself.
View the pull request
<Marker asChild>
  <a href="#links-and-buttons">
    <MarkerIcon>
      <GitPullRequestIcon />
    </MarkerIcon>
    <MarkerContent>View the pull request</MarkerContent>
  </a>
</Marker>

<Marker asChild>
  <button
    type="button"
    className="transition-colors hover:text-foreground"
    onClick={() => toast("You clicked the revert button")}
  >
    <MarkerIcon>
      <ArrowCounterClockwiseIcon />
    </MarkerIcon>
    <MarkerContent>Revert this change</MarkerContent>
  </button>
</Marker>
In a conversation
In a thread, markers are the connective tissue between Bubble rows: a separator opens the day, an inline status covers the work between two turns. Keep them at the same width as the bubbles so the thread keeps one left edge.
Today
Can you check why the install is failing?
Explored 4 files
The workspace package declares peers that Yarn 1 never installs.
Added the missing peers
<Marker variant="separator">
  <MarkerContent>Today</MarkerContent>
</Marker>

<Bubble variant="muted">
  <BubbleContent>Can you check why the install is failing?</BubbleContent>
</Bubble>

<Marker role="status">
  <MarkerIcon>
    <MagnifyingGlassIcon />
  </MarkerIcon>
  <MarkerContent>Explored 4 files</MarkerContent>
</Marker>

<Bubble align="end">
  <BubbleContent>
    The workspace package declares peers that Yarn 1 never installs.
  </BubbleContent>
</Bubble>
Accessibility
A marker is a plain div with no implicit role, which is right for a note that is already in reading order. Add role="status" only when the text updates in place — a streaming status or a progress line — so the change is announced politely rather than re-read on every token. A separator carrying a label needs no role at all: the divider lines are decorative pseudo-elements and are never exposed. MarkerIcon is aria-hidden, so an icon-only marker has no accessible name — give the root an aria-label in that case. Finally, a marker that does something must be a real button or a through asChild; the muted text is small, so never rely on hover alone to signal that it is interactive.
<Marker role="status">
  <MarkerIcon>
    <Spinner />
  </MarkerIcon>
  <MarkerContent>Compacting conversation</MarkerContent>
</Marker>

<Marker aria-label="Synced">
  <MarkerIcon>
    <CheckIcon />
  </MarkerIcon>
</Marker>

<Marker asChild>
  <a href="/files">
    <MarkerIcon>
      <FileTextIcon />
    </MarkerIcon>
    <MarkerContent>Explored 4 files</MarkerContent>
  </a>
</Marker>
API Reference
Three elements: a div root and two span slots. Marker mirrors variant to data-variant, which MarkerContent reads to lay itself out. The package also exports markerVariants for composing the root styles into a custom component. All native props pass through.
PartPropDefault
Markervariant?: "default" | "separator" | "border""default"
MarkerasChild?: booleanfalse
MarkerclassName?: string—
MarkerIconclassName?: string—
MarkerContentclassName?: string—

Tokens used

5 design tokens consumed by the Marker component.

  • --muted-foreground

    Muted Foreground

    text-muted-foreground
  • --foreground

    Foreground

    [a]:hover:text-foreground*:[a]:hover:text-foreground
  • --border

    Border

    before:bg-borderafter:bg-borderborder-border
  • --text-sm

    Text Small (14px)

    text-sm
  • --spacing

    Spacing unit (4px)

    gap-2min-h-4size-4pb-2before:me-1after:ms-1underline-offset-3