One chat message with sender, time, a bubble, reply quote, preview, attachment, reactions and delivery status.

Last updated

  1. AL
    Ana Lima

    Can you check the launch brief before lunch?

  2. Can you check the launch brief before lunch?
    On it. The copy section needs one more pass.

    Read

Show code
<ol aria-label="Conversation" class="flex flex-col gap-4">
<Message name="Ana Lima" time="9:41" datetime="2026-10-15T09:41">
  <Avatar slot="avatar" name="Ana Lima" size="small" status="online" />
  Can you check the launch brief before lunch?
</Message>
<Message me time="9:43" datetime="2026-10-15T09:43" status="read">
  <span slot="reply">Can you check the launch brief before lunch?</span>
  On it. The copy section needs one more pass.
</Message>
</ol>

Add

npx @ava-ui/apogee add message

Uses @lucide/astro for the status glyphs. Pair with Avatar in the avatar slot.

Usage

---
import Message from '@/components/apogee/Message/Message.astro'
---
<ol aria-label="Conversation">
  <Message name="Ana" time="9:41">Hi!</Message>
  <Message me time="9:42" status="sent">Hello.</Message>
</ol>

Anatomy

  • Row — an <li> (or div with as="div"); your own messages (me) run end to start.
  • avatar slot — beside the bubble, aligned to its bottom.
  • Header — <cite> for the sender and <time> for the time.
  • Bubble — the text; the corner nearest the sender is square. Holds the reply quote above the text, and the preview and attachment below it.
  • reactions slot — a row under the bubble.
  • Status — a glyph with screen-reader text; failures also show the text.

API

@composable — any other attribute passes through to the row.

Prop Type Default Description
name string — Sender, in <cite>.
time string — Shown time.
datetime string — Machine-readable time for <time datetime>.
me boolean false Your own message: end-aligned, primary bubble.
status 'sent' | 'read' | 'failed' — Delivery status under the bubble.
typing boolean false Three animated dots instead of the text, labelled with the sender, e.g. “Ana Lima is typing”.
as 'li' | 'div' 'li' Row element.
class string — Extra classes on the row.

Variants

Every status, a link preview, an attachment and reactions:

  1. Sent, not read yet.

    Sent

  2. Read by everyone.

    Read

  3. This one did not go through.

    Failed to send

  4. MR
    Marco Ricci
    Here is the reference.
    brief-v3.pdf · 240 KB
    👍 2🎉 1
Show code
<Message me status="sent">Sent</Message>
<Message me status="read">Read</Message>
<Message me status="failed">Failed</Message>

States

  • Typing — three dots, labelled for assistive tech; still under reduced motion.
  1. AL
    Ana Lima
Show code
<Message name="Ana Lima" typing><Avatar slot="avatar" name="Ana Lima" size="small" /></Message>

Examples

A conversation with a day marker, and a composer under it. Composers are plain forms: a Textarea and a send Button.

  1. AL
    Ana Lima
    Ready for review?
  2. Ten minutes.

    Sent

Show code
<ol aria-label="Conversation" class="flex flex-col gap-4">
<li><Divider>Today</Divider></li>
<Message …>…</Message>
</ol>
<form class="flex items-end gap-2">
<Textarea label="Message" rows={2} class="flex-1" />
<Button type="submit" iconOnly aria-label="Send"><Send /></Button>
</form>

Accessibility

  • Put Messages in an <ol> with an aria-label; each row is a list item, so screen readers announce position and count.
  • The sender is <cite> and the time is <time datetime>.
  • Status glyphs carry screen-reader text (“Sent”, “Read”, “Failed to send”).
  • The typing bubble is an image labelled with the sender, e.g. “Ana Lima is typing”; its dots stop moving under reduced motion.
  • Bubble colours come from the hue tokens and keep AA in both themes.

Tokens

Token Used for
--background-neutral-faded / --foreground-neutral Others’ bubbles
--background-primary / --on-primary Your bubbles
--border-primary Reply quote rule (others)
--foreground-neutral-faded Time, status
--foreground-critical Failed status

Do / Don’t

  • Do keep one Message per send; split long thoughts into several.
  • Do show status only on your own messages.
  • Don’t colour bubbles per person; the side and the avatar carry identity.
  • Don’t hide actions behind hover only; put them in a menu reachable by keyboard.

Content

  • Times are short (“9:41”, “Yesterday”); the full date belongs in datetime.
  • Day markers in the list (“Today”, “Monday”) use a Divider.