howdy/ui/chat

Chat: a conversation that keeps to its newest message, messages with an avatar and a header, speech bubbles, reactions and notes.

chat.conversation(words.english(), [attribute.aria_label("Messages")], [
  chat.note([text("Today")]),
  chat.message(chat.Incoming, avatar: avatar.initials("GH"), header: [text("Grace · 09:41")], content: [
    chat.bubble(chat.Incoming, [text("Did the deploy go out?")]),
  ]),
  chat.message(chat.Outgoing, avatar: element.none(), header: [], content: [
    chat.bubble(chat.Outgoing, [text("Ten minutes ago.")]),
    chat.reactions([chat.reaction("👍", 2, [])]),
  ]),
])

A conversation stays at its newest message as messages arrive or a reply streams in, and keeps its place when you scroll back to read. It needs no script: it is laid out from the bottom, and the browser anchors the scroll position. It is a log for screen readers, so they announce new messages. Give each message an id to link to it.

Older history can be added above the first message, such as when on_older asks for it, and the reader’s place stays put. remember brings a reader back to where they were after a reload, and start_at opens the conversation at a message, such as the first unread one.

Types

Who wrote a message: someone else, or the person reading.

pub type Side {
  Incoming
  Outgoing
}

Constructors

  • Incoming
  • Outgoing

How a note or status line stands out.

pub type Tone {
  Neutral
  Accent
  Critical
}

Constructors

  • Neutral
  • Accent

    The primary colour, such as for “New messages”.

  • Critical

    The danger colour, such as for “Deploy failed”.

A bubble’s surface.

pub type Variant {
  Filled
  Muted
  Outline
  Ghost
  Danger
}

Constructors

  • Filled

    The primary colour: the reader’s own messages, by default.

  • Muted

    The muted colour: everyone else’s messages, by default.

  • Outline
  • Ghost

    No surface, for long replies that read better as plain text.

  • Danger

    The danger colour, such as for a message that failed to send.

Values

pub fn body_class(side: Side) -> css.Class
pub fn bubble(
  side: Side,
  children: List(element.Element(msg)),
) -> element.Element(msg)

A speech bubble: filled with the primary colour for your own messages, the muted colour for everyone else’s.

pub fn bubble_class(side: Side, variant: Variant) -> css.Class
pub fn classes() -> List(css.Class)

Every class this module uses, for howdy/ui/export.

pub fn conversation(
  words: words.Words,
  attributes: List(attribute.Attribute(msg)),
  children: List(element.Element(msg)),
) -> element.Element(msg)

The scrolling list of messages. Size it with a height or by placing it in a flex column; label it with aria-label. When it is scrolled back from the newest message, a button saying the words’ jump_to_newest appears.

pub fn footer_class() -> css.Class
pub fn frame_class() -> css.Class
pub fn header_class() -> css.Class
pub fn jump(
  id: String,
  children: List(element.Element(msg)),
) -> element.Element(msg)

A button that scrolls the conversation to the message with this id and highlights it for a moment, such as a reply’s quote of what it answers.

pub fn jump_class() -> css.Class
pub fn latest_class() -> css.Class
pub fn log_class() -> css.Class
pub fn message(
  side: Side,
  attributes attributes: List(attribute.Attribute(msg)),
  avatar avatar: element.Element(msg),
  header header: List(element.Element(msg)),
  content content: List(element.Element(msg)),
  footer footer: List(element.Element(msg)),
) -> element.Element(msg)

One message, or a run of messages from one person: an avatar beside a header, the content and a footer. Several bubbles in the content join into one group. Pass element.none() for no avatar, and [] for no header or footer. Give it an id in attributes to link to it with jump.

pub fn message_class(side: Side) -> css.Class
pub fn note(
  children: List(element.Element(msg)),
) -> element.Element(msg)

A line between messages, such as a date or “Grace joined”.

pub fn note_class(tone: Tone) -> css.Class
pub fn on_older(message: msg) -> attribute.Attribute(msg)

Hear when the reader has scrolled back near the oldest message shown, to load older ones and add them above it. It is sent once until more messages arrive. Put it in the conversation’s attributes.

pub fn reaction(
  emoji: String,
  count: Int,
  attributes: List(attribute.Attribute(msg)),
) -> element.Element(msg)

A reaction and how many people chose it. Mark the reader’s own with attribute.aria_pressed("true"), and add a click handler to toggle it.

pub fn reaction_class() -> css.Class
pub fn reactions(
  children: List(element.Element(msg)),
) -> element.Element(msg)

A row of reactions under a message.

pub fn reactions_class() -> css.Class
pub fn remember(key: String) -> attribute.Attribute(msg)

Keep the reader’s place in this conversation, named key, for the rest of the browser session, and go back to it when the page is reloaded. Put it in the conversation’s attributes.

pub fn scroller_class() -> css.Class
pub fn start_at(id: String) -> attribute.Attribute(msg)

Open the conversation with the message with this id at the top, such as the first unread one, instead of at the newest. A remembered place wins.

pub fn status(
  tone: Tone,
  icon: element.Element(msg),
  children: List(element.Element(msg)),
) -> element.Element(msg)

A line of news from the system rather than a person, with an icon: “Deploy finished”, “Grace changed the topic”.

pub fn status_class(tone: Tone) -> css.Class
pub fn status_icon_class() -> css.Class
pub fn styled_bubble(
  side: Side,
  variant: Variant,
  children: List(element.Element(msg)),
) -> element.Element(msg)

A bubble with another surface. side still decides which corner points at its sender.

pub fn tinted_note(
  tone: Tone,
  children: List(element.Element(msg)),
) -> element.Element(msg)

A line between messages in a tone, such as “New messages” in the accent colour.

✨ Search Document