Skip to content

Marker

A divider that labels a moment inside a conversation or feed: a centered label between two lines. Use date for a new day, unread for where new messages start, and event for something that happened, like someone joining.

AIv0.1.0experimentalWCAG 2.2 AAView spec

Bring your own model.

These components only show what you give them, in one message shape, and report what the person does. They never call a model or a server, so they work with any backend. They are built for how assistive technology handles streaming text, tool steps and approvals.

See you tomorrow.
Morning! Ready when you are.
import { Bubble, BubbleList, Marker } from "@rdloom/react";

export default function MarkerDatesExample() {
  return (
    <div className="flex w-full justify-center">
      <BubbleList className="w-full max-w-md">
        <Marker label="Yesterday" dateTime="2026-10-07" />
        <Bubble from="user">See you tomorrow.</Bubble>
        <Marker label="Today" dateTime="2026-10-08" />
        <Bubble>Morning! Ready when you are.</Bubble>
      </BubbleList>
    </div>
  );
}

Installation

npx rdloom add marker

Copies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs react-aria-components; add --install to install them.

Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/marker.json

Works, but without upgrade tracking.

Usage

import { Bubble, BubbleList, Marker } from "@rdloom/react";

<div className="flex w-full justify-center">
  <BubbleList className="w-full max-w-md">
    <Marker label="Yesterday" dateTime="2026-10-07" />
    <Bubble from="user">See you tomorrow.</Bubble>
    <Marker label="Today" dateTime="2026-10-08" />
    <Bubble>Morning! Ready when you are.</Bubble>
  </BubbleList>
</div>

Unread

Thanks for the update.
Can you review the draft?
import { useState } from "react";
import { Bubble, BubbleList, Marker } from "@rdloom/react";

export default function MarkerUnreadExample() {
  const [jumped, setJumped] = useState(false);
  return (
    <div className="flex w-full justify-center">
      <BubbleList className="w-full max-w-md">
        <Bubble>Thanks for the update.</Bubble>
        <Marker variant="unread" label="New messages" jumpLabel="Jump to first new message" onJump={() => setJumped(true)} />
        <Bubble>Can you review the draft?</Bubble>
        {jumped && (
          <p role="status" className="text-center text-xs text-[var(--rd-color-text-muted)]">
            Jumped to the first new message
          </p>
        )}
      </BubbleList>
    </div>
  );
}

Events

import { Marker } from "@rdloom/react";

export default function MarkerEventsExample() {
  return (
    <div className="flex w-full justify-center">
      <div className="flex w-full max-w-md flex-col gap-2">
        <Marker variant="event" label="Alex joined" />
        <Marker variant="event" label="Topic changed to Launch plan" />
        <Marker variant="event" label="Sam left" />
      </div>
    </div>
  );
}

API Reference

Defined by the spec. Components also accept the props of the React Aria component they wrap.

PropTypeDefault
labelrequired

The text, e.g. "Today", "New messages" or "Alex joined".

stringnone
variant

date is neutral. unread uses the action colour for the label and lines. event is a smaller, muted note.

"date" | "unread" | "event""date"
dateTime

A machine readable date for a date marker, e.g. "2026-10-08". The label becomes a time element.

stringnone
onJump

Turns the label of an unread marker into a button, e.g. to scroll to the first new message.

() => voidnone
jumpLabel

Accessible name of the jump button when it needs more than the label, e.g. "Jump to first new message".

stringnone

Accessibility

Role separator, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.

Keyboard

  • Tab: moves to the jump button when onJump is given
  • Enter or Space: runs onJump

Screen readers announce

  • "Today, separator"
  • "New messages, button" when it can be jumped to

What your code must do

  • Without onJump the marker is a separator named by its label, so the change of moment is announced once
  • With onJump the marker is a labelled group holding a real button, because a separator cannot contain controls
  • The lines are decorative and hidden from assistive technology
  • The unread variant also says its meaning in words ("New messages"), not only in colour
  • The label meets 4.5:1 on the background

Guidelines

Use it when

  • Starting a new day in a long conversation
  • Showing where unread messages begin
  • Noting an event such as a person joining or a topic change

Avoid it when

  • A plain line between unrelated content: use Separator
  • A section title that structures a page: use a heading

Don't

  • A marker for every message
  • A long sentence as the label: keep it to a few words
  • Using colour alone to mean unread

Design tokens

The semantic tokens this component uses. Change them once and every component follows; see Design tokens.

  • --rd-color-border-default
  • --rd-color-text-muted
  • --rd-color-action-primary
  • --rd-color-focus-ring
  • --rd-radius-control