Usage
Chat’s message and layout primitives structure a conversation without owning application data. Messages, actions, empty content, and the composer stay as the original consumer nodes; your product remains responsible for persistence, loading, authorization, and submission.
Import the elements used by a composition:
import "@nordhealth/components/lib/Chat"
import "@nordhealth/components/lib/ChatLayoutScrollButton"
import "@nordhealth/components/lib/ChatMessageList"
import "@nordhealth/components/lib/ChatMessage"
import "@nordhealth/components/lib/ChatMessageBubble"
import "@nordhealth/components/lib/ChatMessageMetadata"
import "@nordhealth/components/lib/ChatSystemMessage"
import "@nordhealth/components/lib/ChatComposer"
import "@nordhealth/components/lib/ChatAttachment"
import "@nordhealth/components/lib/ChatComposerDrawer"
import "@nordhealth/components/lib/ChatComposerInput"
import "@nordhealth/components/lib/ChatToken"
import "@nordhealth/components/lib/ChatSendButton"
import "@nordhealth/components/lib/ChatTokenizedText"
import "@nordhealth/components/lib/ChatToolCalls"
import "@nordhealth/components/lib/ChatDictationButton"
The panel example combines a named conversation log, assistant and user turns, a date divider, consumer-owned composer, empty state, and scroll affordance.
Composition
Chat is the scroll container for a Message List and consumer-owned Composer. Message, Bubble, Metadata, and System Message structure each turn, while the composer family covers input, drawers, tokens, send actions, tool activity, and optional dictation. Import only the elements used by the product composition.
- Chat Attachment — an image or file attached to a message, pending or sent.
- Chat Layout Scroll Button — the jump-to-latest button that tracks new messages.
- Chat Message List — the accessible, scrollable conversation log.
- Chat Message — groups one turn's avatar, name, bubbles, and metadata.
- Chat Message Bubble — styles a single message fragment (solid or ghost).
- Chat Message Metadata — optional timestamps, delivery status, and footer actions.
- Chat System Message — dividers and status updates between turns.
- Chat Composer — the message-composition shell holding the input and its actions.
- Chat Composer Drawer — a collapsible region for attachments, feedback, or context.
- Chat Composer Input — the rich plain-text input with tokens and triggers.
- Chat Send Button — the send/stop action for the composer.
- Chat Token — an atomic inline token for a mention, entity, or tag.
- Chat Tokenized Text — safely rebuilds tokens from a serialized string.
- Chat Tool Calls — shows LLM tool or function activity with status.
- Chat Dictation Button — speech-to-text capture for the composer.
Examples
Messages and grouping
Set Message sender to assistant or user. The private message context aligns its Bubble and Metadata descendants at the logical edge, including RTL, without reversing semantic or keyboard order. Optional avatar and name properties provide a simple sender treatment; use the corresponding slots when the product needs custom content.
For consecutive fragments from one sender, mark bubbles first, middle, and last. Only the sender-side corners tighten. Filled, ghost, and error variants share Nord semantic color and spacing roles.
Choose compact, balanced, or spacious density on Chat, Message List, or a specific Message override. Message List’s independent gap property accepts Nord spacing values from none through xxl.
Metadata and system updates
Metadata renders only when it has a string timestamp, slotted timestamp, footer, actions, or status. Use slot="timestamp" for localized content such as Nord Timestamp. Delivery statuses are sending, sent, delivered, read, and error; visible text and symbols communicate meaning without color alone.
System Message separates conversation sections or announces status. Use variant="divider" announce="off" for decorative date headings. Use polite for ordinary background changes and reserve assertive for genuinely urgent updates.
Full conversation content
Message List is a named polite role="log". It announces additions, not every text mutation in a streamed response, and exposes aria-busy only while streaming is true. Its empty slot is shown until message rows arrive. The full-featured story includes document tokens, a date divider, grouped assistant bubbles, a ghost bubble with timestamp metadata, a system update, and a delivered user reply. Its deliberate first → last → ghost middle group order exercises non-sequential grouping without adding a disclosure or source-content panel. Code-oriented content is intentionally excluded from Nord’s product-domain Chat examples.
Streaming and older messages
Chat owns the scroll container. It positions the first asynchronous content fill instantly, follows later growth while locked, and respects reduced-motion preferences. If the reader scrolls upward, automatic following stops; new content sets hasNewMessages and emits nord-new-messages-change. Calling scrollToBottom() or naturally reaching the bottom clears that state.
With load-older enabled, Message List’s leading sentinel emits one nord-load-more event when eligible. Prepend the original older nodes in response. The surrounding Chat preserves the reader’s visual anchor from the previous scrollHeight and scrollTop. requestOlder() provides the same intent when a product uses its own trigger.
The scroll affordance emits cancelable nord-scroll-to-latest. Its visible attribute is an explicit override; remove the attribute to return control to the surrounding Chat. Supply label for visible wording such as “3 new messages”; the icon-only state retains a localized accessible name.
Composing messages
Composer is a framework-neutral form shell. It supplies a rich input and send button when their named regions are omitted. Set slot="drawer", header-actions, header-context, input, footer-actions, send-actions, or send-button on direct children to document their role while preserving source and keyboard order. Listen for cancelable nord-submit and nord-stop; the submit event exposes the trimmed plain-string value.
Set the value JavaScript property for controlled usage and echo native input changes back from any framework. Omit it for uncontrolled usage. disabled, loading, and stop-shown coordinate the input and send action. Error and warning status records can appear at the logical top or bottom.
Drawer
Drawer preserves consumer nodes while presenting attachments, progress, or contextual feedback. Omit open for uncontrolled disclosure and use default-open only for its initial state; set open and echo the proposed nord-toggle.open value for controlled usage. count is displayed with the localized label.
Rich input
Composer Input is a form-associated Shadow-DOM plain-text control. Tokens remain atomic during selection, deletion, copy, cut, paste, undo, redo, and form serialization. Configure mentions or commands by setting the triggers JavaScript property to framework-neutral records containing a trigger character, sync or async source, and onSelect mapper. Async searches receive an AbortSignal and are canceled on replacement or disconnect.
insertText(), insertToken(), setSelectionRange(), and setValue() provide imperative integration points without exposing the editable Shadow DOM. nord-files, nord-token-change, and native composed input/change events carry serializable data for every framework. File events expose metadata only; products remain responsible for reading and uploading accepted files.
Send actions and tokenized output
Send Button works independently or reads Composer state through private context. In stop mode it emits nord-stop instead of submitting. Tokenized Text safely turns exact, boundary-matched serialized values into semantic Nord tokens and leaves unknown or hostile strings as text.
Composer, Drawer, Token, Send Button, and Tokenized Text use strict scoped light-DOM styles so their CSS does not become a global selector surface. The shared stylesheet uses :deep() only for intentional compound-child styling; it never stamps consumer content. Composer Input retains Shadow DOM because it captures form and editing data.
Tool calls
Tool Calls presents an ordered immutable snapshot of tool activity. One call stays inline; multiple calls use an accessible disclosure which surfaces the latest activity. Supported statuses are pending, running, complete, error, and unknown. Error text remains visible alongside the status icon, and durations appear only for completed calls.
Set the calls JavaScript property to an array of records and use stable key values while streaming. The renderable stats and resultDetail fields accept an Element, Text, string, or number, never framework-owned render values. Consumer nodes remain unscoped and retain their identity, listeners, state, order, and text across updates.
Use default-open for initial uncontrolled expansion or set the open property for controlled expansion. nord-toggle bubbles with read-only open and optional callKey fields; controlled consumers echo event.open back to the property.
Dictation
Dictation Button is a Shadow-DOM speech data-capture control. It feature-detects standard and prefixed Web Speech recognition and exposes framework-neutral start(), stop(), abort(), and toggle() methods. Repeated starts are ignored; disconnecting, aborting, or disabling during capture releases recognition listeners immediately.
The default unsupported-behavior="hide" renders no control when speech recognition is unavailable. Use unsupported-behavior="disable" to provide a labelled disabled fallback, and always retain keyboard text entry.
Every interim and final update emits nord-transcript; final text additionally emits nord-dictation-result. Integrate with Composer Input through its public insertText() method rather than reaching through its Shadow DOM. Speech recognition may send audio to a browser, operating-system, or remote provider, so request microphone access only after explicit user activation and disclose the applicable privacy behavior at the point of use.
Accessibility
- Give every Message List a concise
aria-labelwhich distinguishes that log from other live regions. - Keep messages and actions in chronological source order. Sender alignment uses logical grid placement and never changes tab order.
- Use System Message announcements for concise state changes instead of expanding the log’s live-region relevance to streamed text.
- Delivery statuses include wording and symbols; error and progress are not color-only.
- Empty, loading, and scroll controls restore only
hiddenandinertstate the component added. Consumer-owned state is preserved. - Scoped light-DOM styles use exact hosts and reviewed named-slot targets. Consumer nodes are never stamped with the
nord-chatownership scope. - Reduced motion, forced colors, zoom, RTL, and text resizing retain readable ordering and operable controls.
Templates
See this component composed into complete application views:
API reference
Chat
Chat owns scroll behavior and positioning while leaving messages, empty content, composer, and scroll affordance in consumer DOM order.
<nord-chat></nord-chat>Props
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
density | density | Density inherited by the message family. | ChatDensity | defaultChatDensity |
scrollThreshold | scroll-threshold | Pixel distance from the bottom which still counts as scroll locked. | number | 24 |
hideScrollButton | hide-scroll-button | Hides the internal scroll affordance when no consumer override is supplied. | boolean | false |
Slots
| Slot name | Description |
|---|---|
Default slot | Message content, normally a Chat Message List. |
empty | Content shown while there are no messages. |
composer | Sticky consumer-owned composer. |
scroll | Scroll-to-latest affordance. |
| Event | Detail Type | Description |
|---|---|---|
nord-scroll-lock-change | ChatScrollLockChangeEvent | Reports whether growth follows the bottom edge. |
nord-new-messages-change | ChatNewMessagesChangeEvent | Reports unseen-message state. |
Parts
This component is made up of the following parts.
Chat Attachment
An image or file attached to a message, in the composer before sending and in
the message log after. Files render as a compact pill in both places; images
render as a small thumbnail in the composer and a captioned card in a
message, because a pending upload and a delivered one are read differently.
Presentation follows placement, so the same markup works in either position.
removable adds the remove action; a sent attachment is a record, so it is
normally left off in the message log.
<nord-chat-attachment></nord-chat-attachment>Props
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
name | name | Attachment name. Shown to the reader and returned with removal intents. | string | '' |
src | src | Image source. Its presence is what makes this an image: without one there is nothing to preview, so the attachment renders as a file. | string | undefined | — |
alt | alt | Alternative text for a meaningful image. Empty by default, because the visible name already identifies the attachment and a duplicate would be announced twice. | string | '' |
loading | loading | Image loading strategy. Eager by default: a message log is anchored to its
newest message, so an image resolving after that scroll shifts what the
reader is looking at. Use lazy for long histories. | 'eager' | 'lazy' | 'eager' |
removable | removable | Adds an accessible remove action. Consumers own actual removal. | boolean | false |
removeLabel | remove-label | Optional complete remove-action label. Defaults to a localized label. | string | undefined | — |
| Event | Detail Type | Description |
|---|---|---|
nord-attachment-remove | ChatAttachmentRemoveEvent | Cancellable removal intent. |
CSS Properties
CSS Custom Properties provide more fine grain control over component presentation. We advise utilizing existing properties on the component before using these.
| Property | Description | Default |
|---|---|---|
--n-chat-attachment-tile-inline-size | Composer thumbnail width. | 5.5rem |
--n-chat-attachment-tile-block-size | Composer thumbnail height. | 4.5rem |
--n-chat-attachment-card-inline-size | Message card maximum width. | 14rem |
--n-chat-attachment-card-aspect-ratio | Message card crop ratio. | 4 / 3 |
Chat Composer
Framework-neutral chat composition shell with controlled/uncontrolled value,
form submission, send/stop state, and named light-DOM composition regions.
The component never re-renders consumer nodes. Use slot values drawer,
header-actions, header-context, input, footer-actions, send-actions,
and send-button to document each direct child's role. Source order remains
the semantic and keyboard order in every framework.
<nord-chat-composer></nord-chat-composer>Props
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
value | value | Controlled canonical value. Omit for uncontrolled state. | string | undefined | — |
placeholder | placeholder | Placeholder forwarded to the registered input. | string | undefined | — |
disabled | disabled | Disables editing and sending. | boolean | false |
loading | loading | Marks a send as pending and prevents duplicate sends. | boolean | false |
stopShown | stop-shown | Shows stop in place of send. | boolean | false |
density | density | Composer spacing density. | ChatComposerDensity | 'balanced' |
statusPosition | status-position | Logical status placement. | ChatComposerStatusPosition | 'bottom' |
Slots
| Slot name | Description |
|---|---|
drawer | Composer Drawer or other context content. |
header-actions | Leading compact actions. |
header-context | Trailing context usage or progress. |
input | Composer Input. A default is supplied when omitted. |
footer-actions | Leading footer actions. |
send-actions | Actions immediately before send. |
send-button | Send Button. A default is supplied when omitted. |
Methods
| Method name | Parameters | Description |
|---|---|---|
reportValidity() => boolean | N/A | Validates the registered input, or the containing form when there is one. |
focus(options?: FocusOptions) => void | options: FocusOptions | Focuses the registered Composer Input. |
| Event | Detail Type | Description |
|---|---|---|
nord-submit | ChatComposerSubmitEvent | Cancellable trimmed submission. |
nord-stop | ChatComposerStopEvent | Cancellable stop intent. |
CSS Properties
CSS Custom Properties provide more fine grain control over component presentation. We advise utilizing existing properties on the component before using these.
| Property | Description | Default |
|---|---|---|
--n-chat-composer-background | Composer surface. | var(--n-color-surface) |
--n-chat-composer-border | Composer border. | var(--n-color-border) |
--n-chat-composer-border-radius | Outer radius. | var(--n-border-radius-s) |
--n-chat-composer-padding | Inner spacing. | calc(var(--n-space-s)+var(--n-space-xs)) |
Chat Composer Drawer
Collapsible Composer region for attachments, feedback, progress, or context.
Consumer nodes remain direct children so framework renderers retain ownership
of their DOM relationships. The host itself is the disclosure region.
The disclosure is intentionally a native button: Nord Button does not expose
aria-controls on its shadow-owned control, which this composite requires.
<nord-chat-composer-drawer></nord-chat-composer-drawer>Props
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
open | open | Controlled open state. Leave undefined for uncontrolled state. | boolean | undefined | — |
defaultOpen | default-open | Initial uncontrolled state. | boolean | false |
label | label | Disclosure label. | string | undefined | — |
count | count | Optional item count. Its presence enables disclosure behavior. | number | undefined | — |
disabled | disabled | Prevents disclosure interaction. | boolean | false |
Slots
| Slot name | Description |
|---|---|
Default slot | Drawer content. |
| Event | Detail Type | Description |
|---|---|---|
nord-toggle | ChatComposerToggleEvent | Reports an open-state proposal. |
CSS Properties
CSS Custom Properties provide more fine grain control over component presentation. We advise utilizing existing properties on the component before using these.
| Property | Description | Default |
|---|---|---|
--n-chat-composer-drawer-max-block-size | Maximum expanded drawer size. | 20rem |
Chat Composer Input
Form-associated, framework-neutral rich plain-text input. Tokens are atomic,
trigger sources may be synchronous or asynchronous, and consumer strings are
never parsed as HTML. Native input bubbles for every user-visible change;
change commits on blur.
<nord-chat-composer-input></nord-chat-composer-input>Props
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
name | name | Form field name. | string | undefined | — |
defaultValue | default-value | Initial value restored by native form reset. | string | '' |
label | label | Accessible label. | string | undefined | — |
placeholder | placeholder | Empty-state prompt. | string | undefined | — |
maxRows | max-rows | Maximum visible line count before scrolling. | number | 8 |
disabled | disabled | Disables editing and form submission. | boolean | false |
required | required | Enables native value-missing validation. | boolean | false |
noHistory | no-history | Opts out of Up/Down message history recall. History is on by default: a
type: Boolean property defaulting to true cannot be switched off from
markup (an absent attribute never reaches the converter), so the opt-out is
the inverted attribute, matching Nord's hide-*/no-* convention. | boolean | false |
pasteAsToken | paste-as-token | Converts matching long plain-text paste to a token. | boolean | false |
pasteTokenThreshold | paste-token-threshold | Default long-paste threshold. | number | 200 |
interimText | interim-text | Transient aria-hidden dictation text shown without changing the value. | string | '' |
accept | accept | Native accept syntax used to validate file metadata. | string | undefined | — |
maxFiles | max-files | Maximum accepted files per paste/drop. | unknown | — |
maxFileSize | max-file-size | Maximum accepted file size in bytes. | unknown | — |
Methods
| Method name | Parameters | Description |
|---|---|---|
focus(options?: FocusOptions) => void | options: FocusOptions | |
checkValidity() => void | N/A | |
reportValidity() => void | N/A | |
setCustomValidity(message: string) => void | message: string |
| Event | Detail Type | Description |
|---|---|---|
input | Event | Native composed input event after user-visible changes. |
change | Event | Native composed change event on blur after editing. |
nord-submit | ChatComposerSubmitEvent | Standalone submit intent. |
nord-files | ChatComposerFilesEvent | Accepted/rejected file metadata. |
nord-history-request | ChatComposerHistoryRequestEvent | Cancellable message-history intent at a valid caret boundary. |
nord-token-change | ChatComposerTokenChangeEvent | Immutable token snapshot. |
CSS Properties
CSS Custom Properties provide more fine grain control over component presentation. We advise utilizing existing properties on the component before using these.
| Property | Description | Default |
|---|---|---|
--n-chat-composer-menu-max-block-size | Maximum suggestion menu block size. | 16rem |
Chat Dictation Button
Starts and stops speech-to-text capture for a chat composer. Speech can be
sent to a browser or platform recognition service: disclose that privacy
behavior, request microphone access only after the user's activation, and do
not start capture automatically. Always retain keyboard text entry as the
fallback when Web Speech is unsupported or permission is denied.
Every final transcript emits nord-dictation-result. Composer integration
should listen for that event and call Composer Input's public insertText()
method; this component never reaches through another element's Shadow DOM.
The same methods and bubbling events work from plain HTML, React, Vue, Svelte,
Angular, or any other DOM-capable framework.
<nord-chat-dictation-button></nord-chat-dictation-button>Props
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
unsupportedBehavior | unsupported-behavior | Unsupported browsers either hide the control or expose a disabled fallback. | ChatDictationUnsupportedBehavior | 'hide' |
disabled | disabled | Disables dictation even when the recognition API is available. | boolean | false |
size | size | Nord small or medium button and equalizer geometry. | ChatDictationButtonSize | 'm' |
recognitionLang | recognition-lang | BCP-47 language sent to speech recognition. Defaults to navigator.language. | string | undefined | — |
continuous | continuous | Keep recognition active until explicitly stopped. | boolean | true |
interimResults | interim-results | Request interim transcript updates. | boolean | true |
label | label | Optional accessible label override for both idle and listening states. | string | undefined | — |
Methods
| Method name | Parameters | Description |
|---|---|---|
focus(options?: FocusOptions) => void | options: FocusOptions |
CSS Properties
CSS Custom Properties provide more fine grain control over component presentation. We advise utilizing existing properties on the component before using these.
| Property | Description | Default |
|---|---|---|
--n-chat-dictation-button-size | Overrides the Nord Button square size. | — |
Chat Layout Scroll Button
Chat Layout Scroll Button composes the existing Nord Button and Nord Icon to return the reader to the newest content. It can inherit visibility and new-message state from Chat.
<nord-chat-layout-scroll-button></nord-chat-layout-scroll-button>Props
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
visibility | visibility | Anything that is not an explicit override resolves to auto, including the
null Lit hands back when the attribute is removed. The property is then
always one of the three documented values rather than sometimes null. | ChatLayoutScrollButtonVisibility | — |
label | label | Visible button label. Omit it for a localized icon-only control. | string | undefined | — |
Chat Message
Chat Message groups one speaker's avatar, name, bubbles, and metadata without taking ownership of consumer-authored message content.
<nord-chat-message></nord-chat-message>Props
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
sender | sender | The semantic sender of the message. | ChatMessageSender | 'assistant' |
density | density | Optional density override. Otherwise inherits the surrounding chat density. | ChatDensity | undefined | — |
name | name | Sender name used for the visible label and accessible message name. | string | undefined | — |
avatar | avatar | URL for an owned Nord Avatar. Use the avatar slot for custom content. | string | undefined | — |
Slots
| Slot name | Description |
|---|---|
Default slot | Message bubbles and other consumer-owned message content. |
avatar | Consumer-provided avatar content, replacing the avatar URL. |
name | Consumer-provided sender name. |
metadata | Consumer-provided message metadata. |
Chat Message Bubble
Chat Message Bubble styles one consumer-owned message fragment using sender and density values inherited privately from Chat Message.
<nord-chat-message-bubble></nord-chat-message-bubble>Props
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
variant | variant | Visual treatment for the bubble. | ChatMessageBubbleVariant | 'filled' |
group | group | Position within a consecutive group of bubbles from the same sender. | ChatMessageBubbleGroup | undefined | — |
Slots
| Slot name | Description |
|---|---|
Default slot | Message content. |
CSS Properties
CSS Custom Properties provide more fine grain control over component presentation. We advise utilizing existing properties on the component before using these.
| Property | Description | Default |
|---|---|---|
--n-chat-message-bubble-max-inline-size | Maximum message measure. | 70ch |
--n-chat-message-bubble-padding | Inner message spacing. | var(--n-space-s) var(--n-space-m) |
--n-chat-message-bubble-border-width | Message border width. | 1px |
--n-chat-message-bubble-border-style | Message border style. | solid |
--n-chat-message-bubble-border-color | Message border color. | var(--n-color-border-strong) |
--n-chat-message-bubble-border-radius | Message corner radius. | var(--n-border-radius) |
--n-chat-message-bubble-connected-border-radius | Corner radius where grouped messages connect. | var(--n-border-radius-sharp) |
--n-chat-message-bubble-background | Message surface color. | var(--n-color-surface) |
--n-chat-message-bubble-color | Message text color. | var(--n-color-text) |
Chat Message List
Chat Message List is an accessible conversation log which preserves direct consumer children and coordinates loading older messages with Chat.
<nord-chat-message-list></nord-chat-message-list>Props
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
density | density | Optional density override. Otherwise inherits the surrounding chat density. | ChatDensity | undefined | — |
gap | gap | Optional Nord spacing token between message rows. | ChatMessageListGap | undefined | — |
loading | loading | Whether older messages are currently loading. | boolean | false |
streaming | streaming | Whether assistant content is currently streaming. | boolean | false |
loadOlder | load-older | Enables the top-of-list load intent. | boolean | false |
Slots
| Slot name | Description |
|---|---|
Default slot | Chat messages and system messages in chronological DOM order. |
empty | Content shown only when the log has no messages. |
| Event | Detail Type | Description |
|---|---|---|
nord-load-more | ChatLoadMoreEvent | Requests messages older than the current first item. |
Chat Message Metadata
Chat Message Metadata presents optional timestamps, delivery state, footer content, and actions without recreating consumer-owned controls.
<nord-chat-message-metadata></nord-chat-message-metadata>Props
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
timestamp | timestamp | Display-ready timestamp text. | string | undefined | — |
status | status | Current delivery status. | ChatMessageStatus | undefined | — |
Slots
| Slot name | Description |
|---|---|
timestamp | Consumer-formatted timestamp content, taking precedence over the string property. |
footer | Additional message details such as an edited marker. |
actions | Consumer-owned message actions. |
Chat Send Button
Nord Button-based send/stop action for standalone use or Composer coordination.
<nord-chat-send-button></nord-chat-send-button>Props
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
stopShown | stop-shown | Shows stop rather than send behavior. | boolean | false |
disabled | disabled | Disables send behavior. Stop remains available unless explicitly disabled. | boolean | false |
loading | loading | Exposes pending send state and disables sending. | boolean | false |
sendLabel | send-label | Accessible label in send state. | string | undefined | — |
stopLabel | stop-label | Accessible label in stop state. | string | undefined | — |
type | type | Native button type when sending. | 'submit' | 'button' | 'submit' |
Slots
| Slot name | Description |
|---|---|
send-icon | Custom send-state icon. |
stop-icon | Custom stop-state icon. |
CSS Properties
CSS Custom Properties provide more fine grain control over component presentation. We advise utilizing existing properties on the component before using these.
| Property | Description | Default |
|---|---|---|
--n-chat-send-button-size | Square action size. | var(--n-space-xl) |
Chat System Message
Chat System Message separates or announces conversation status updates.
<nord-chat-system-message></nord-chat-system-message>Props
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
variant | variant | Visual treatment for the system message. | ChatSystemMessageVariant | 'default' |
announce | announce | Live-region priority. Use off for decorative date dividers. | ChatSystemMessageAnnouncement | 'polite' |
Slots
| Slot name | Description |
|---|---|
Default slot | Status or divider text. |
icon | Optional leading icon. |
Chat Token
An atomic visual token used by Composer Input and Tokenized Text. It is named for what it is rather than where it started: the composer is one host, not the only one. Consumer children remain in the light DOM; the component only owns its optional generated label and remove button. The remove action stays native because this atomic control is embedded inside a contenteditable composite; Nord Button's form proxy and shadow control are inappropriate in that editing surface. Its glyph reuses Nord Icon.
<nord-chat-token></nord-chat-token>Props
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
value | value | Canonical plain-text serialization for this token. | string | '' |
label | label | Fallback accessible and visible label when no consumer content exists. | string | undefined | — |
variant | variant | Nord semantic color treatment. | ChatTokenVariant | 'neutral' |
removable | removable | Adds an accessible remove action. Composer Input owns actual removal. | boolean | false |
expandable | expandable | Adds an accessible expand action which turns the token back into plain text. Composer Input owns the actual expansion. | boolean | false |
expandLabel | expand-label | Optional complete expand-action label. Defaults to a localized label. | string | undefined | — |
removeLabel | remove-label | Optional complete remove-action label. Defaults to a localized label. | string | undefined | — |
Slots
| Slot name | Description |
|---|---|
Default slot | Consumer-owned token label or richer presentation. |
| Event | Detail Type | Description |
|---|---|---|
nord-token-remove | ChatTokenRemoveEvent | Cancellable removal intent. |
nord-token-expand | ChatTokenExpandEvent | Cancellable intent to become plain text again. |
CSS Properties
CSS Custom Properties provide more fine grain control over component presentation. We advise utilizing existing properties on the component before using these.
| Property | Description | Default |
|---|---|---|
--n-chat-token-remove-target-size | Pointer target size around the remove glyph. | calc(var(--n-line-height-form)+var(--n-space-xs)) |
Chat Tokenized Text
Safely reconstructs known Composer Tokens from a canonical serialized string.
It creates only text nodes and token elements and never parses consumer HTML.
Consumer children are left untouched. When both meaningful consumer content
and value exist, consumer content wins so output is never duplicated.
Token values match only at word/marker boundaries, never inside larger words.
<nord-chat-tokenized-text></nord-chat-tokenized-text>Props
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
value | value | Canonical plain-text message to render. | string | undefined | — |
Chat Tool Calls
Displays ordered LLM tool or function calls. One call stays inline; multiple
calls use a summary disclosure whose collapsed surface includes the latest
activity. Result nodes remain consumer-owned and are never scoped or cloned.
Prefer stable key values during streaming, provide a useful target, show
durations only after completion, and include visible errorMessage text.
Consumers localize fixed status/summary strings through Nord translations.
<nord-chat-tool-calls></nord-chat-tool-calls>Props
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
open | open | Controlled expansion state for the group, or for a single call's detail. | boolean | undefined | — |
defaultOpen | default-open | Initial expansion state when open is not controlled. | boolean | false |
label | label | Optional localized summary label for a group. | string | undefined | — |
| Event | Detail Type | Description |
|---|---|---|
nord-toggle | ChatToolCallsToggleEvent | Reports the next expansion state. In controlled mode the consumer must echo open back. |
CSS Properties
CSS Custom Properties provide more fine grain control over component presentation. We advise utilizing existing properties on the component before using these.
| Property | Description | Default |
|---|---|---|
--n-chat-tool-calls-surface | Disclosure surface color. | var(--n-color-surface-raised) |
--n-chat-tool-calls-border-color | Disclosure border color. | var(--n-color-border) |
Dependencies
This component is internally dependent on the following components:
Design guidelinesFor designers
Usage
This section covers the message and layout primitives in the Chat family. They provide semantic conversation structure, sender-aware presentation, and streaming scroll behavior while applications retain ownership of message data, loading, persistence, and submission.
Do
- Put messages in chronological DOM order inside Chat Message List.
- Give each Message List a concise accessible name which distinguishes it from other logs on the page.
- Let Chat own the scrolling surface when you need streaming lock, new-message awareness, or prepend anchoring.
- Prepend the original older-message nodes after
nord-load-more; Chat preserves the reader’s visual position. - Use System Message with
announce="off"for decorative date dividers and a live announcement mode only for meaningful status changes.
Don’t
- Don’t reverse message DOM order to align user messages. Sender styling uses logical grid placement and keeps reading and keyboard order intact.
- Don’t announce every streamed token. Message List uses
aria-relevant="additions"; use the dedicated system and new-message status regions for concise updates. - Don’t recreate messages, composer controls, or slotted actions during updates. Consumer node identity, listeners, values, and focus are intentionally preserved.
- Don’t expose the private density/message/chat contexts or stream controllers as application APIs.
Composition
nord-chat is the scrolling owner. Its default content is normally one nord-chat-message-list; slot="empty", slot="composer", and slot="scroll" identify the empty state, sticky composer, and scroll-to-latest affordance. Chat never reparents those nodes.
nord-chat-message-list is a named role="log" with polite additions-only announcements. Its direct chronological children are normally nord-chat-message and nord-chat-system-message. streaming adds aria-busy="true"; ordinary loading does not. density accepts compact, balanced, or spacious, and gap can independently use Nord spacing values from none through xxl.
nord-chat-message supplies private sender and density context to Bubble and Metadata. Set sender="user" or sender="assistant"; use the avatar, name, and metadata slots for consumer-formatted content, or the avatar URL and name convenience properties. User content moves to the logical end without changing DOM order, including in RTL.
Bubble supports filled, ghost, and error variants. Ghost keeps the same density-aware inline padding as a filled bubble while removing block padding, background, and border. group="first|middle|last" uses the sharp Nord radius only on sender-side connecting corners, so consecutive bubbles read as one turn without changing the outer silhouette. Metadata renders only when a string timestamp, slotted timestamp, footer, actions, or delivery status is present. Supported statuses are sending, sent, delivered, read, and error; each includes visible text rather than relying on color alone.
System Message supports default and divider variants, an icon slot, and announce="off|polite|assertive". Dividers should normally use announce="off" so dates are not presented as conversational messages.
Composing messages
nord-chat-composer is a framework-neutral form shell. Its direct named regions form three visual rows without wrapping or replacing consumer nodes: header-actions and header-context, the full-width input, then footer-actions, send-actions, and send-button. drawer is a full-width region above them. A Composer Input and Send Button are supplied when those two regions are omitted.
Consumer properties remain consumer-owned. Composer communicates effective value, disabled/loading state, stop state, and whether submission is available through private context; it does not overwrite a supplied Input’s placeholder or a supplied Send Button’s public properties. Set placeholder on Composer only when using its generated Input.
Composer Input is a form-associated plain-text control with a shadow-owned contenteditable editor. It exposes a canonical string value, native validity methods, serialized selection offsets, atomic tokens, message history, plain-text clipboard/drop behavior, and framework-neutral trigger definitions. Assignment to value opts into controlled behavior; echo the new value from the bubbling input event. Synchronous, microtask, and next-task echoes retain the live DOM and caret. At valid Up/Down multiline boundaries, the input emits cancellable nord-history-request before its local history default action; cancel it when the application resolves history itself.
Array trigger sources filter immediately. Function and search-object sources debounce by 150ms unless debounce is set explicitly, receive an AbortSignal, and discard stale results after value, selection, trigger, history, token, or disabled-state changes. The suggestion menu grows to 16rem by default; set --n-chat-composer-menu-max-block-size to change that constraint.
Composer Drawer enables disclosure only when count is present. Without a count, its control is omitted from interaction and content remains visible. The host is the labelled disclosure region, and consumer nodes always remain its direct children so framework renderers retain ownership of their DOM relationships. Send Button composes the public Nord Button and Nord Icon primitives while retaining send/stop events and Composer coordination.
Scroll ownership and older messages
Chat begins locked to the newest content. The first asynchronous fill is positioned instantly. Later streaming growth follows with reduced-motion-aware scrolling while locked. Scrolling away from the bottom unlocks Chat; subsequent growth sets read-only hasNewMessages and emits nord-new-messages-change. Returning naturally to the bottom or calling scrollToBottom() clears unseen state and re-locks growth.
When load-older is enabled, Message List observes its leading sentinel and emits one nord-load-more intent when eligible. requestOlder() is the framework-neutral fallback for products which own a different eligibility trigger. Duplicate requests are suppressed until older content is prepended. Chat captures the previous scrollHeight and scrollTop, then restores the same visual anchor after the prepend.
The scroll button emits cancelable nord-scroll-to-latest before calling the surrounding Chat. An explicit visible attribute overrides context; removing the attribute returns visibility to Chat control. label adds visible text, while the icon-only state retains a localized accessible name.
Slots
| Element | Slot | Purpose |
|---|---|---|
| Chat | default | Message region, normally Chat Message List. |
| Chat | empty | Shown only while no message content exists. |
| Chat | composer | Consumer-owned sticky composer. |
| Chat | scroll | Scroll-to-latest affordance. |
| Chat Message List | default | Chronological messages and system messages. |
| Chat Message List | empty | Empty conversation content. |
| Chat Message | default | Bubbles and other message content. |
| Chat Message | avatar, name, metadata | Consumer-formatted sender details. |
| Chat Message Metadata | timestamp, footer, actions | Formatted time, supporting details, and controls. |
| Chat System Message | icon | Optional leading icon. |
| Chat Composer | drawer | Full-width attachments, progress, feedback, or context. |
| Chat Composer | header-actions, header-context | Leading header actions and trailing context state. |
| Chat Composer | input | Composer Input; generated when omitted. |
| Chat Composer | footer-actions, send-actions | Leading footer actions and actions adjacent to send. |
| Chat Composer | send-button | Send Button; generated when omitted. |
| Chat Send Button | send-icon, stop-icon | Consumer-owned icons for each action state. |
Events
nord-load-morebubbles and is composed when older messages should be loaded.nord-scroll-to-latestbubbles, is composed, and is cancelable. Cancel it to take over the scroll action.nord-scroll-lock-changeexposes a read-onlylockedboolean.nord-new-messages-changeexposes a read-onlyhasNewMessagesboolean.nord-submitis a cancelable Composer or standalone Input/Send Button submission intent.nord-stopis a cancelable stop intent while stop mode is shown.nord-toggleproposes Drawer disclosure state; controlled Drawers do not mutateopen.nord-filesreports accepted and rejected file metadata without uploading files.nord-token-changereports an immutable token snapshot after token edits.nord-history-requestexposes read-only direction, current value, and selection offsets; it is cancelable for application-owned history.
Accessibility
Message List supplies role="log", aria-live="polite", and additions-only relevance. Name each log with aria-label; generated fallback labels never overwrite a label added by the consumer after connection. Message uses role="article" and a generated “Message from …” name when no consumer label exists. Delivery state lives in a polite status region with visible wording and symbols.
Keep action controls in semantic source order. Logical placement handles LTR and RTL without order or reversed flex direction. Reduced motion removes nonessential transitions and changes smooth stream following to instant movement. Forced-colors treatment retains bubble boundaries and system dividers. At 200% text size, let the token-based spacing and fluid bubble measure wrap naturally.
Presentational Chat primitives use scoped light DOM. Each light-DOM host and component-owned node carries a stable component-family scope, while consumer nodes are never stamped by their parent. Styles are compiled like Vue scoped styles: ordinary selectors require the scope token, and the internal :deep() escape is reserved for reviewed, exact named-slot selectors when a parent intentionally lays out a consumer child. Broad element, class, and data-marker deep selectors are not used.
Composer Input is the deliberate exception: as a data-entry control it keeps its editor, suggestion list, and form/ARIA implementation in Shadow DOM. This light-presentation/shadow-input split preserves the same HTML, properties, methods, form behavior, and bubbling DOM events in plain HTML, React, Vue, and other standards-based frameworks without publishing framework-only hooks.