Message Bubble

Individual conversation message.

assistant

Here is the revised itinerary with two extra hours in the museum district.

10:42

html

<div class="msg-bubble-row msg-bubble-row--assistant">
  <span class="msg-bubble__who">assistant</span>
  <div class="msg-bubble msg-bubble--assistant msg-bubble--tail" style="--accent:#A8E6C1;">
    <p class="msg-bubble__text">Here is the revised itinerary with two extra hours in the museum district.</p>
    <span class="msg-bubble__time">10:42</span>
  </div>
</div>
<style>
.msg-bubble-row { max-width: 420px; display: flex; flex-direction: column; font: 15px/1.4 system-ui, sans-serif; margin-bottom: 4px; }
.msg-bubble-row--user { align-items: flex-end; }
.msg-bubble-row--assistant, .msg-bubble-row--system { align-items: flex-start; }
.msg-bubble__who { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: #24143080; margin-bottom: 4px; }
.msg-bubble { position: relative; max-width: 320px; padding: 10px 14px; border-radius: 16px; color: #241430; }
.msg-bubble--user { background: var(--accent); border-bottom-right-radius: 4px; }
.msg-bubble--assistant { background: #24143010; border-bottom-left-radius: 4px; }
.msg-bubble--system { background: transparent; border: 1.5px dashed #24143040; font-style: italic; }
.msg-bubble__text { margin: 0; }
.msg-bubble__time { display: block; margin-top: 4px; font-size: 11px; color: #24143080; }
.msg-bubble--tail.msg-bubble--user::after { content: ""; position: absolute; right: -6px; bottom: 0; width: 12px; height: 12px; background: var(--accent); clip-path: polygon(0 0, 0% 100%, 100% 100%); }
.msg-bubble--tail.msg-bubble--assistant::after { content: ""; position: absolute; left: -6px; bottom: 0; width: 12px; height: 12px; background: #24143010; clip-path: polygon(100% 0, 0 100%, 100% 100%); }
</style>

tailwind

<div class="msg-bubble-tw-row msg-bubble-tw-row--assistant max-w-md flex flex-col font-sans text-[15px] mb-1">
  <span class="text-[11px] font-semibold uppercase tracking-wide text-[#24143080] mb-1">assistant</span>
  <div class="msg-bubble-tw msg-bubble-tw--assistant msg-bubble-tw--tail relative max-w-xs px-3.5 py-2.5 rounded-2xl text-[#241430]" style="--accent:#A8E6C1;">
    <p class="m-0">Here is the revised itinerary with two extra hours in the museum district.</p>
    <span class="block mt-1 text-[11px] text-[#24143080]">10:42</span>
  </div>
</div>
<style>
.msg-bubble-tw-row--user { align-items: flex-end; }
.msg-bubble-tw-row--assistant, .msg-bubble-tw-row--system { align-items: flex-start; }
.msg-bubble-tw--user { background: var(--accent); border-bottom-right-radius: 4px; }
.msg-bubble-tw--assistant { background: #24143010; border-bottom-left-radius: 4px; }
.msg-bubble-tw--system { background: transparent; border: 1.5px dashed #24143040; font-style: italic; }
.msg-bubble-tw--tail.msg-bubble-tw--user::after { content: ""; position: absolute; right: -6px; bottom: 0; width: 12px; height: 12px; background: var(--accent); clip-path: polygon(0 0, 0% 100%, 100% 100%); }
.msg-bubble-tw--tail.msg-bubble-tw--assistant::after { content: ""; position: absolute; left: -6px; bottom: 0; width: 12px; height: 12px; background: #24143010; clip-path: polygon(100% 0, 0 100%, 100% 100%); }
</style>

react

export function MessageBubble({
  text = "Here is the revised itinerary with two extra hours in the museum district.",
  sender = "assistant",
  accent = "#A8E6C1",
  timestamp = "10:42",
  tail = true,
}) {
  const align = sender === "user" ? "flex-end" : "flex-start";
  const bubbleStyle = {
    user: { background: accent, borderBottomRightRadius: 4 },
    assistant: { background: "#24143010", borderBottomLeftRadius: 4 },
    system: { background: "transparent", border: "1.5px dashed #24143040", fontStyle: "italic" },
  }[sender] ?? {};
  const tailStyle = tail
    ? sender === "user"
      ? { position: "absolute", right: -6, bottom: 0, width: 12, height: 12, background: accent, clipPath: "polygon(0 0, 0% 100%, 100% 100%)" }
      : sender === "assistant"
        ? { position: "absolute", left: -6, bottom: 0, width: 12, height: 12, background: "#24143010", clipPath: "polygon(100% 0, 0 100%, 100% 100%)" }
        : null
    : null;
  return (
    <div style={{ maxWidth: 420, display: "flex", flexDirection: "column", alignItems: align, font: "15px/1.4 system-ui, sans-serif", marginBottom: 4 }}>
      <span style={{ fontSize: 11, fontWeight: 600, textTransform: "uppercase", letterSpacing: ".04em", color: "#24143080", marginBottom: 4 }}>{sender}</span>
      <div style={{ position: "relative", maxWidth: 320, padding: "10px 14px", borderRadius: 16, color: "#241430", ...bubbleStyle }}>
        <p style={{ margin: 0 }}>{text}</p>
        <span style={{ display: "block", marginTop: 4, fontSize: 11, color: "#24143080" }}>{timestamp}</span>
        {tailStyle && <span aria-hidden="true" style={tailStyle} />}
      </div>
    </div>
  );
}