Skip to content

Bubble ​

The message bubble surface for conversations: owns only the bubble itself (background/border/radius/max width + content), while avatar, name, time, and delivery state belong to the oas-message-row composition row. For AI assistants, support chats, IM conversations, etc.

Basic Usage ​

Default bubble

Hi, give me an overview of the OAS-UI conversation components.

Content goes into the default slot; width fits content with an 80% container cap (override chain-wide via --oas-bubble-max-width).

Alignment (align) ​

Start / end sides

Their message: aligned to start by default (logical properties, mirrored automatically in RTL).My message: align="end" sticks to the end side, visually split from theirs.

align="start" | "end" is implemented with logical layout (margin-inline-start: auto + text-align: end) — no physical-direction CSS.

Variants (variant) ​

Six semantic variants

default: neutral soft surface (default).secondary: primary-tinted, typical for my own messages.muted: no fill, secondary text color.outline: filled surface with border.ghost: no fill, no border, width cap lifted.destructive: danger semantics (error echoes etc.).

The tinted surfaces of secondary/destructive mix theme semantic colors via color-mix() (theme-aware) and adapt automatically in dark mode; ghost lifts the 80% width cap for long streaming answers.

Typing (loading) ​

loading typing indicator

Summarize the key points of this document.Simulate replying…

With the boolean loading attribute present, content switches to a three-dot typing indicator (the AI "typing" scenario) and the host element reflects aria-busy; the animation respects prefers-reduced-motion.

Reactions (reactions) ​

Emoji + count (side / align)

I support this plan; let's align on the details.I've confirmed the plan above.

Click a reaction button; the received oas-reaction shows here.

reactions is a JSON array (each item { emoji, count?, active?, label? }, emoji required; an empty array/invalid JSON renders nothing). reactions-side controls top/bottom (default bottom; top places it above the bubble) and reactions-align overrides horizontal alignment (defaults to following the bubble align). Each reaction is a real button (keyboard reachable + focus ring + aria-pressed reflecting the active state): clicking it dispatches oas-reaction (detail { emoji, count, active, index }) — reaction data stays owned by the host, the component only dispatches intent.

Accessibility ​

  • Direction is never conveyed by color/alignment alone: sender name and message text live in oas-message-row's header/content, readable by screen readers.
  • The loading dots carry role="status" with a localized accessible name ("Typing…"); the dots themselves are aria-hidden.
  • Bubbles are purely presentational (no tab stop); rich content (links/buttons) goes into the default slot and keeps native reachability.

API ​

oas-bubble ​

Attributes ​

AttributeDescriptionTypeDefault
alignDirection: start (other party, default) / end (self) — logical-property alignment (align-self, RTL mirrored automatically); when set explicitly it wins over the inherited text-align reflection——
loadingTyping state (boolean presence): content switches to a three-dot typing indicator and the host reflects aria-busy (the AI "typing" scenario)boolean—
reactionsReaction bar as a JSON array; each item { emoji, count?, active?, label? } (emoji required). Empty array / invalid JSON renders nothing——
reactions-alignHorizontal alignment of the reaction bar: start / end; defaults to following the bubble align——
reactions-sideVertical placement of the reaction bar: bottom (default, below the bubble) / top (above the bubble)——

Events ​

EventDescription
oas-reactionFired when a reaction is clicked; detail { emoji, count, active, index } (reaction data stays owned by the host)

Slots ​

NameDescription
defaultBubble content

CSS Variables ​

CSS VariableDefault
--oas-bubble-max-width80%
--oas-bubble-radiusvar(--oas-radius-lg)

Parts: ::part(bubble), ::part(content), ::part(typing), ::part(reactions) reaction strip / ::part(reaction) a single reaction button. align (start default / end) and variant (default / secondary / muted / outline / ghost / destructive) are pure CSS channels.

Released under the MIT OR Apache-2.0 License.