Chat thread
betaThe canonical message bubble: incoming/outgoing, attachments inside, meta (avatar, name, time, delivery state, actions) below. Used by chats and the e-mail conversation view.
Canon 17 Oct 2026 (owner decision): bubble from prototype proto/mail-konverzace@6355a0399 (.kvb-*) replaces the K7-4 chat bubble (accent-tint, meta inside). Incoming --line-100, outgoing solid --accent; --radius-xl with --radius-xs sender-side corner; padding --sp-3 --sp-5; attachments inside under the text; meta below; max 80% (phone 85%). Voice/video outgoing stays on --accent-tint (follow-up). History: K7-4 (acrm !4447), AD review 2 Oct 2026, sending/failed states (!4514), published = meta text only (13 Oct 2026). Registry repaired 17 Oct 2026 (review F2): removed classes with no CSS definition and no TSX use on main: .cht-bubble, .cht-atts, .cht-att, .cht-who, .cht-link. Registry repaired 17 Oct 2026 (review F3): mapped existing Ladle stories.
Used in 2 files · 2 areas · 3× — components/mail, pages/komunikace
Overview
ChatThread + ChatBubble are the canonical message bubble of the design system (canon since 17 Oct 2026, from prototype proto/mail-konverzace@6355a0399). The same bubble renders a 1:1 chat (Communication › Chats: Telegram, WhatsApp, SMS) and an e-mail conversation view.
Anatomy of one message row (.cht-msg.in|out):
- Bubble (
.cht-bubble) — a box, not a line: incoming--line-100, outgoing solid--accentwith--fg-on-accent;--radius-xlwith a sharp--radius-xscorner at the bottom on the sender's side; padding--sp-3 --sp-5. Inside: the text, then the attachments. - Attachments (
.cht-atts) — INSIDE the bubble, UNDER the text. - Meta row (
.cht-msg-m) — BELOW the bubble: avatar (other party), name, time, delivery state (outgoing), host actions ("Original"). The clamp toggle is not a meta action: it sits inside the bubble (more).
Max width 80% of the thread (phone 85%). Axes (ACRM-527): the bubble is a box — its edge sits on the box axis; the component carries no negative margin, a host that needs the bubble to hang onto its page's box axis shifts it itself.
This is not the Discussion candidate (project comment threads: flat rows, no bubbles).
Components
ChatThread— scrolling log container (role="log", flex column,--sp-4gap). Day separators are plain.cht-daytext the host renders between rows (noChatDaycomponent). The e-mail conversation view may use its own flex-column container (role="list") withChatBubblerows — the row only needs a flex-column parent.ChatBubble— one message row. Props:text—ReactNode. A plain string keeps line breaks (pre-wrap); formatted content (paragraphs, bold, sanitised HTML viadangerouslySetInnerHTMLon a child) goes withrich. Empty /nulltext draws no text block (attachment-only bubble).rich— formatted content: nopre-wrap,--sp-3between<p>.clampLines+expanded+textRef— clamp long text to N lines; the host readstextRef.current.scrollHeight > clientHeightto decide whether to offer "Show all" (viamore) and flipsexpanded. Attachments are never clamped.more— the "Show all" / "Show less" toggle INSIDE the bubble, directly under the text (before attachments), as a text button.cht-morewitharia-expanded(white underlined in the accent bubble). Chat-app convention; short messages render none.attachments— node rendered inside the bubble under the text. Chat uses DSAttachment; e-mail uses.cht-attpills (22 px pill,--surface-100).time(pre-formatted),out,fromLabel(pre-translated sender, required).senderName+avatar— visible sender in the meta row (group threads, e-mail; usually only the other party gets an avatar).senderNameisaria-hidden: the screen reader already hasfromLabel.state('sending' | 'sent' | 'delivered' | 'read' | 'failed'),stateLabel,errorText— delivery state of an outgoing message, shown in the meta row.metaExtra— small meta after the time (e.g. publish tag);actions— host buttons at the end of the meta row (use.cht-link:--small,--fg-muted, underline on hover/focus).published,className.
I18n
The package does not format dates, translate labels, or know about locale. The host supplies time, fromLabel, senderName, stateLabel and action labels already formatted/translated.
Delivery status
state | icon | tone |
|---|---|---|
sending | clock | default |
sent | check (single tick) | default |
delivered | checks (double tick) | default |
read | checks (double tick) | accent |
failed | alert-circle | critical |
The icon renders only when out is true and state is set. Always pass stateLabel with state. The state lives in the bubble's meta row — never a separate status line.
Voice and video messages
VoiceMessage / VideoNote / VideoMessage use the same row (.cht-msg) with their own bubble content. Outgoing voice/video is solid --accent like every outgoing bubble (AD 17 Oct 2026, proto/bublina-hlas-ajda@1740bc21d); the player carries --fg-on-accent on it (see Voice message). An attachment inside an outgoing bubble is transparent with a white (--fg-on-accent) frame, text and icons; its hover is a 15 % white veil. A bubble that ends with attachments gets padding-bottom: calc(var(--sp-3) + var(--sp-2)) so the gap above and below looks equal.
Do / Don't
- Do pass
fromLabelfor every bubble, even your own ("Já"/"You"). - Do keep
textas real DOM content (never anaria-label) so it stays findable/translatable. - Do put attachments in
attachments(inside the bubble) and message actions inactions(meta row) — never a button inside the bubble text. - Don't draw an extra frame around the text inside the bubble (the bubble is the one box).
- Don't use it for internal comment threads — that is
Discussion. - Don't invent a "visually hidden" class; use
.acrm-sr.
Source: docs/discussion/chat-thread.usage.md · GitLab