Skip to content

MessageBubble

<caos-message-bubble>

one message, with its author, its time and its body

May be placed on a record page, an app’s home page and a page inside an app.

Name Attribute Description Type Default Required
author author, markup only Who said it. Also the source of the initials in the avatar when no avatar text is given — the first letter of each of the first two words. text none declared Optional
at at, markup only When it was said, as an ISO-8601 instant. Drawn as a time of day in the reader’s own locale, because the day is said by the divider above rather than on every message. A value the runtime cannot read as a date draws no time rather than drawing a wrong one. text none declared Optional
body body, markup only What they said, as markdown. Settable as a property too, and the property wins — a message carries newlines and can run long, and an attribute holding one goes through the browser’s own attribute rules before this component sees it. text none declared Optional
avatar avatar, markup only What the avatar shows, when the initials taken from the author are not what you want. One or two characters; it is decorative and is hidden from screen readers, because the author’s name is already read out beside it. text none declared Optional
grouped grouped, markup only This message continues the one above it: the avatar, name and time are not drawn, and only the body appears. Decided by whatever draws the list — see the notes above. boolean false Optional
highlighted highlighted, markup only Mark this as the message that was navigated to — from a search result, or a link into the conversation. Held by the attribute rather than by an animation that ends, so a reader who looked away can still see which one it was. boolean false Optional

This piece raises no events.

Name What goes in it
footer Anything offered ABOUT this message — a reaction, a reply count, an action a surface knows how to run. Deliberately a slot and not a set of inputs: what can be done about a message is the application’s vocabulary, not the library’s, and a component that shipped its own action schema would be shipping one application’s.
Design token What it controls
--caos-color-text The message body and the author’s name.
--caos-color-text-subtle The time beside the name.
--caos-color-text-muted The initials in the avatar.
--caos-color-surface-2 The avatar behind the initials, the tint on a highlighted message, and behind a mention.
--caos-color-border The avatar’s edge, and the rules in a table or a code block.
--caos-color-accent A link in the body, a mention, and the control that opens a folded message.
--caos-font The typeface the message is set in.
--caos-font-mono Code, inline and in a block.
--caos-text-body The size of the message body.
--caos-text-label The time, the initials and the fold control.
--caos-font-weight-label The weight of the author’s name, and of a mention in the body.
--caos-space-1 The space above and below one message.
--caos-space-2 The gap between the avatar and the message.
--caos-space-3 The inset from the edge of the conversation.
--caos-space-4 The indent of a list in the body.
--caos-radius-pill The roundness of the avatar.
--caos-radius-sm The roundness of a code block, an inline code pill and a mention.
--caos-focus-ring The ring on the fold control and on a link in the body.

Nothing else drives this piece by calling it.

Nothing on this piece can be read back.

Part Which piece of it When it is there
::part(message) the whole row Always
::part(avatar) the initials disc Always
::part(header) the name and time line Always
::part(author) the name Always
::part(time) the time Always
::part(body) the rendered message Always
::part(footer) whatever was slotted under the message Always

One thing somebody said, in a conversation with other things somebody said. A single message standing alone with nothing before or after it is a piece of text, and wants whatever the surrounding page uses for text.

  • KeyValueRow — The subject is a field and its value rather than a person and what they said.
  • NoticeBanner — The platform is telling the reader something, rather than a person telling another person. A message has an author; a notice does not.
  • DetailSection — The content is a record’s own long text. It is one field of one record, not an entry in a conversation, and nothing about it is grouped or timed.

Each message is a role="article", so a screen reader can move message by message instead of paragraph by paragraph through a whole conversation. The time is a <time> with a machine-readable datetime, so “9:14 AM” is not the only form of it available. The avatar is aria-hidden: it repeats the author’s name, and reading both says the same person twice before every message. The fold control carries aria-expanded. Left to the author: the AUTHOR name. A grouped message draws no name by design, so a reader moving article by article through a run of grouped messages hears the bodies without the name — which is correct, because the name did not change, and is the reason grouping must only be set when it genuinely did not.

The first message from somebody: avatar, name, time, body.

{
"id": "example",
"section": "A message with its header",
"columns": 1,
"items": [
{
"id": "message_bubble_1",
"type": "component",
"key": "message_bubble",
"inputs": {
"author": "Ada Lovelace",
"at": "2026-09-13T09:14:00.000Z",
"body": "Pushed the fix — it was **the index**, not the query."
}
}
]
}
<caos-message-bubble author="Ada Lovelace" at="2026-09-13T09:14:00.000Z" body="Pushed the fix — it was **the index**, not the query."></caos-message-bubble>

Grouped: no avatar, no name, no time. The message above said all three a moment ago, and repeating them is what makes a conversation read as a log file.

{
"id": "example",
"section": "The next message from the same person",
"columns": 1,
"items": [
{
"id": "message_bubble_1",
"type": "component",
"key": "message_bubble",
"inputs": {
"author": "Ada Lovelace",
"at": "2026-09-13T09:14:00.000Z",
"body": "Pushed the fix — it was **the index**, not the query."
}
},
{
"id": "message_bubble_2",
"type": "component",
"key": "message_bubble",
"inputs": {
"author": "Ada Lovelace",
"at": "2026-09-13T09:15:00.000Z",
"body": "Plan reads back clean now.",
"grouped": true
}
}
]
}
<caos-message-bubble author="Ada Lovelace" at="2026-09-13T09:14:00.000Z" body="Pushed the fix — it was **the index**, not the query."></caos-message-bubble>
<caos-message-bubble author="Ada Lovelace" at="2026-09-13T09:15:00.000Z" body="Plan reads back clean now." grouped></caos-message-bubble>

The small markdown a message may carry, drawn as nodes rather than as HTML.

{
"id": "example",
"section": "A body with marks in it",
"columns": 1,
"items": [
{
"id": "message_bubble_1",
"type": "component",
"key": "message_bubble",
"inputs": {
"author": "Grace Hopper",
"at": "2026-09-13T10:02:00.000Z",
"body": "Three things to check:\n\n- the `at` bound is inclusive\n- the filter reaches the index\n- an empty page says *why*\n\nSee https://example.test/notes for the rest."
}
}
]
}
<caos-message-bubble author="Grace Hopper" at="2026-09-13T10:02:00.000Z" body="Three things to check:
- the `at` bound is inclusive
- the filter reaches the index
- an empty page says *why*
See https://example.test/notes for the rest."></caos-message-bubble>