ClickorKeyboard shortcut Alt + L

Dragto resize

Nord Design System SearchDeveloperDeveloperGetting startedUsing Web ComponentsWebfontsCDNLocalizationCSS Framework Legacy ESLint PluginWorking with AIFAQDesignDesignGetting startedFoundationsAccessibilityColor SystemColor UtilitiesPrinciplesFigma ToolkitGridIconographyNamingNordhealth BrandTypographyThemesBrand assetsToolsComponentsComponentsAccordion New Aside New Autocomplete New AvatarBadgeBannerButtonButton GroupCalendarCard Updated Chart New Chat New Chat AttachmentChat ComposerChat Composer DrawerChat Composer InputChat Dictation ButtonChat Layout Scroll ButtonChat MessageChat Message BubbleChat Message ListChat Message MetadataChat Send ButtonChat System MessageChat TokenChat Tokenized TextChat Tool CallsCheckboxCollapsible New Combobox New Command MenuData Table New Date PickerDate Range Picker New DividerDrawer Updated DropdownEmpty StateField New FieldsetFilter Bar New FooterHeaderIconInputInput Group New Item New Kbd New LayoutMessageMeter New Modal Updated NavigationNotificationNumber Field New Otp Field New Outline New Overflow List New Pagination New PopoutProgressProgress BarQrcodeRadioRangeResizable New Rich Text Editor New Scroll Area New Segmented ControlSelectSkeletonSpinnerStackTabTableTagTextareaTime Picker New Timestamp New ToastToggleTooltipTop BarTruncate New Visually HiddenDesign TokensDesign TokensTailwind CSSTailwind CSSBlocksBlocksTemplatesTemplatesIconsIconsPlaygroundPlaygroundLatest UpdatesLatest UpdatesChangelogChangelogMigration guidesMigration guides
GitHub

Clickto expandKeyboard shortcut Alt + L

Chat New Alpha Light DOM & Tailwind

Open in Storybook

Composes accessible message threads with sender-aware bubbles, metadata, system updates, and streaming scroll behavior.

This component is considered alpha. We're still finalising the API, so it may include a breaking change at any time — use it at your own risk.
Loading...

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.

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 firstlast → 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-label which 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 hidden and inert state 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-chat ownership scope.
  • Reduced motion, forced colors, zoom, RTL, and text resizing retain readable ordering and operable controls.

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

PropertyAttribute Description TypeDefault
densitydensityDensity inherited by the message family.ChatDensitydefaultChatDensity
scrollThresholdscroll-thresholdPixel distance from the bottom which still counts as scroll locked.number24
hideScrollButtonhide-scroll-buttonHides the internal scroll affordance when no consumer override is supplied.booleanfalse

Slots

Slot name Description
Default slotMessage content, normally a Chat Message List.
emptyContent shown while there are no messages.
composerSticky consumer-owned composer.
scrollScroll-to-latest affordance.
EventDetail TypeDescription
nord-scroll-lock-changeChatScrollLockChangeEventReports whether growth follows the bottom edge.
nord-new-messages-changeChatNewMessagesChangeEventReports unseen-message state.
Light DOM & Tailwind. This component renders in the light DOM, so you can style it directly with your own CSS or Tailwind utility classes — there is no shadow boundary, and its default styles are low specificity, so your utilities win.

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

PropertyAttribute Description TypeDefault
namenameAttachment name. Shown to the reader and returned with removal intents.string''
srcsrcImage 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
altaltAlternative text for a meaningful image. Empty by default, because the visible name already identifies the attachment and a duplicate would be announced twice.string''
loadingloadingImage 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'
removableremovableAdds an accessible remove action. Consumers own actual removal.booleanfalse
removeLabelremove-labelOptional complete remove-action label. Defaults to a localized label.string | undefined
EventDetail TypeDescription
nord-attachment-removeChatAttachmentRemoveEventCancellable 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.

PropertyDescriptionDefault
--n-chat-attachment-tile-inline-sizeComposer thumbnail width.5.5rem
--n-chat-attachment-tile-block-sizeComposer thumbnail height.4.5rem
--n-chat-attachment-card-inline-sizeMessage card maximum width.14rem
--n-chat-attachment-card-aspect-ratioMessage 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

PropertyAttribute Description TypeDefault
valuevalueControlled canonical value. Omit for uncontrolled state.string | undefined
placeholderplaceholderPlaceholder forwarded to the registered input.string | undefined
disableddisabledDisables editing and sending.booleanfalse
loadingloadingMarks a send as pending and prevents duplicate sends.booleanfalse
stopShownstop-shownShows stop in place of send.booleanfalse
densitydensityComposer spacing density.ChatComposerDensity'balanced'
statusPositionstatus-positionLogical status placement.ChatComposerStatusPosition'bottom'

Slots

Slot name Description
drawerComposer Drawer or other context content.
header-actionsLeading compact actions.
header-contextTrailing context usage or progress.
inputComposer Input. A default is supplied when omitted.
footer-actionsLeading footer actions.
send-actionsActions immediately before send.
send-buttonSend Button. A default is supplied when omitted.

Methods

Method nameParameters Description
reportValidity() => booleanN/AValidates the registered input, or the containing form when there is one.
focus(options?: FocusOptions) => voidoptions: FocusOptionsFocuses the registered Composer Input.
EventDetail TypeDescription
nord-submitChatComposerSubmitEventCancellable trimmed submission.
nord-stopChatComposerStopEventCancellable 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.

PropertyDescriptionDefault
--n-chat-composer-backgroundComposer surface.var(--n-color-surface)
--n-chat-composer-borderComposer border.var(--n-color-border)
--n-chat-composer-border-radiusOuter radius.var(--n-border-radius-s)
--n-chat-composer-paddingInner 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

PropertyAttribute Description TypeDefault
openopenControlled open state. Leave undefined for uncontrolled state.boolean | undefined
defaultOpendefault-openInitial uncontrolled state.booleanfalse
labellabelDisclosure label.string | undefined
countcountOptional item count. Its presence enables disclosure behavior.number | undefined
disableddisabledPrevents disclosure interaction.booleanfalse

Slots

Slot name Description
Default slotDrawer content.
EventDetail TypeDescription
nord-toggleChatComposerToggleEventReports 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.

PropertyDescriptionDefault
--n-chat-composer-drawer-max-block-sizeMaximum 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

PropertyAttribute Description TypeDefault
namenameForm field name.string | undefined
defaultValuedefault-valueInitial value restored by native form reset.string''
labellabelAccessible label.string | undefined
placeholderplaceholderEmpty-state prompt.string | undefined
maxRowsmax-rowsMaximum visible line count before scrolling.number8
disableddisabledDisables editing and form submission.booleanfalse
requiredrequiredEnables native value-missing validation.booleanfalse
noHistoryno-historyOpts 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.booleanfalse
pasteAsTokenpaste-as-tokenConverts matching long plain-text paste to a token.booleanfalse
pasteTokenThresholdpaste-token-thresholdDefault long-paste threshold.number200
interimTextinterim-textTransient aria-hidden dictation text shown without changing the value.string''
acceptacceptNative accept syntax used to validate file metadata.string | undefined
maxFilesmax-filesMaximum accepted files per paste/drop.unknown
maxFileSizemax-file-sizeMaximum accepted file size in bytes.unknown

Methods

Method nameParameters Description
focus(options?: FocusOptions) => voidoptions: FocusOptions
checkValidity() => voidN/A
reportValidity() => voidN/A
setCustomValidity(message: string) => voidmessage: string
EventDetail TypeDescription
inputEventNative composed input event after user-visible changes.
changeEventNative composed change event on blur after editing.
nord-submitChatComposerSubmitEventStandalone submit intent.
nord-filesChatComposerFilesEventAccepted/rejected file metadata.
nord-history-requestChatComposerHistoryRequestEventCancellable message-history intent at a valid caret boundary.
nord-token-changeChatComposerTokenChangeEventImmutable 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.

PropertyDescriptionDefault
--n-chat-composer-menu-max-block-sizeMaximum 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

PropertyAttribute Description TypeDefault
unsupportedBehaviorunsupported-behaviorUnsupported browsers either hide the control or expose a disabled fallback.ChatDictationUnsupportedBehavior'hide'
disableddisabledDisables dictation even when the recognition API is available.booleanfalse
sizesizeNord small or medium button and equalizer geometry.ChatDictationButtonSize'm'
recognitionLangrecognition-langBCP-47 language sent to speech recognition. Defaults to navigator.language.string | undefined
continuouscontinuousKeep recognition active until explicitly stopped.booleantrue
interimResultsinterim-resultsRequest interim transcript updates.booleantrue
labellabelOptional accessible label override for both idle and listening states.string | undefined

Methods

Method nameParameters Description
focus(options?: FocusOptions) => voidoptions: FocusOptions
EventDetail TypeDescription
nord-transcriptChatTranscriptEventEvery interim and final transformed transcript.
nord-dictation-resultChatDictationResultEventFinal transformed transcript for application/composer integration.
nord-dictation-startChatDictationStartEventRecognition began listening.
nord-dictation-endChatDictationEndEventRecognition stopped, aborted, or ended.
nord-dictation-errorChatDictationErrorEventPermission or recognition failure.

CSS Properties

CSS Custom Properties provide more fine grain control over component presentation. We advise utilizing existing properties on the component before using these.

PropertyDescriptionDefault
--n-chat-dictation-button-sizeOverrides 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

PropertyAttribute Description TypeDefault
visibilityvisibilityAnything 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
labellabelVisible button label. Omit it for a localized icon-only control.string | undefined
EventDetail TypeDescription
nord-scroll-to-latestChatScrollToLatestEventCancelable scroll-to-latest intent.

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

PropertyAttribute Description TypeDefault
sendersenderThe semantic sender of the message.ChatMessageSender'assistant'
densitydensityOptional density override. Otherwise inherits the surrounding chat density.ChatDensity | undefined
namenameSender name used for the visible label and accessible message name.string | undefined
avataravatarURL for an owned Nord Avatar. Use the avatar slot for custom content.string | undefined

Slots

Slot name Description
Default slotMessage bubbles and other consumer-owned message content.
avatarConsumer-provided avatar content, replacing the avatar URL.
nameConsumer-provided sender name.
metadataConsumer-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

PropertyAttribute Description TypeDefault
variantvariantVisual treatment for the bubble.ChatMessageBubbleVariant'filled'
groupgroupPosition within a consecutive group of bubbles from the same sender.ChatMessageBubbleGroup | undefined

Slots

Slot name Description
Default slotMessage 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.

PropertyDescriptionDefault
--n-chat-message-bubble-max-inline-sizeMaximum message measure.70ch
--n-chat-message-bubble-paddingInner message spacing.var(--n-space-s) var(--n-space-m)
--n-chat-message-bubble-border-widthMessage border width.1px
--n-chat-message-bubble-border-styleMessage border style.solid
--n-chat-message-bubble-border-colorMessage border color.var(--n-color-border-strong)
--n-chat-message-bubble-border-radiusMessage corner radius.var(--n-border-radius)
--n-chat-message-bubble-connected-border-radiusCorner radius where grouped messages connect.var(--n-border-radius-sharp)
--n-chat-message-bubble-backgroundMessage surface color.var(--n-color-surface)
--n-chat-message-bubble-colorMessage 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

PropertyAttribute Description TypeDefault
densitydensityOptional density override. Otherwise inherits the surrounding chat density.ChatDensity | undefined
gapgapOptional Nord spacing token between message rows.ChatMessageListGap | undefined
loadingloadingWhether older messages are currently loading.booleanfalse
streamingstreamingWhether assistant content is currently streaming.booleanfalse
loadOlderload-olderEnables the top-of-list load intent.booleanfalse

Slots

Slot name Description
Default slotChat messages and system messages in chronological DOM order.
emptyContent shown only when the log has no messages.
EventDetail TypeDescription
nord-load-moreChatLoadMoreEventRequests 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

PropertyAttribute Description TypeDefault
timestamptimestampDisplay-ready timestamp text.string | undefined
statusstatusCurrent delivery status.ChatMessageStatus | undefined

Slots

Slot name Description
timestampConsumer-formatted timestamp content, taking precedence over the string property.
footerAdditional message details such as an edited marker.
actionsConsumer-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

PropertyAttribute Description TypeDefault
stopShownstop-shownShows stop rather than send behavior.booleanfalse
disableddisabledDisables send behavior. Stop remains available unless explicitly disabled.booleanfalse
loadingloadingExposes pending send state and disables sending.booleanfalse
sendLabelsend-labelAccessible label in send state.string | undefined
stopLabelstop-labelAccessible label in stop state.string | undefined
typetypeNative button type when sending.'submit' | 'button''submit'

Slots

Slot name Description
send-iconCustom send-state icon.
stop-iconCustom stop-state icon.
EventDetail TypeDescription
nord-submitChatComposerSubmitEventStandalone send intent.
nord-stopChatComposerStopEventStandalone 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.

PropertyDescriptionDefault
--n-chat-send-button-sizeSquare 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

PropertyAttribute Description TypeDefault
variantvariantVisual treatment for the system message.ChatSystemMessageVariant'default'
announceannounceLive-region priority. Use off for decorative date dividers.ChatSystemMessageAnnouncement'polite'

Slots

Slot name Description
Default slotStatus or divider text.
iconOptional 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

PropertyAttribute Description TypeDefault
valuevalueCanonical plain-text serialization for this token.string''
labellabelFallback accessible and visible label when no consumer content exists.string | undefined
variantvariantNord semantic color treatment.ChatTokenVariant'neutral'
removableremovableAdds an accessible remove action. Composer Input owns actual removal.booleanfalse
expandableexpandableAdds an accessible expand action which turns the token back into plain text. Composer Input owns the actual expansion.booleanfalse
expandLabelexpand-labelOptional complete expand-action label. Defaults to a localized label.string | undefined
removeLabelremove-labelOptional complete remove-action label. Defaults to a localized label.string | undefined

Slots

Slot name Description
Default slotConsumer-owned token label or richer presentation.
EventDetail TypeDescription
nord-token-removeChatTokenRemoveEventCancellable removal intent.
nord-token-expandChatTokenExpandEventCancellable 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.

PropertyDescriptionDefault
--n-chat-token-remove-target-sizePointer 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

PropertyAttribute Description TypeDefault
valuevalueCanonical 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

PropertyAttribute Description TypeDefault
openopenControlled expansion state for the group, or for a single call's detail.boolean | undefined
defaultOpendefault-openInitial expansion state when open is not controlled.booleanfalse
labellabelOptional localized summary label for a group.string | undefined
EventDetail TypeDescription
nord-toggleChatToolCallsToggleEventReports 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.

PropertyDescriptionDefault
--n-chat-tool-calls-surfaceDisclosure surface color.var(--n-color-surface-raised)
--n-chat-tool-calls-border-colorDisclosure border color.var(--n-color-border)
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

ElementSlotPurpose
ChatdefaultMessage region, normally Chat Message List.
ChatemptyShown only while no message content exists.
ChatcomposerConsumer-owned sticky composer.
ChatscrollScroll-to-latest affordance.
Chat Message ListdefaultChronological messages and system messages.
Chat Message ListemptyEmpty conversation content.
Chat MessagedefaultBubbles and other message content.
Chat Messageavatar, name, metadataConsumer-formatted sender details.
Chat Message Metadatatimestamp, footer, actionsFormatted time, supporting details, and controls.
Chat System MessageiconOptional leading icon.
Chat ComposerdrawerFull-width attachments, progress, feedback, or context.
Chat Composerheader-actions, header-contextLeading header actions and trailing context state.
Chat ComposerinputComposer Input; generated when omitted.
Chat Composerfooter-actions, send-actionsLeading footer actions and actions adjacent to send.
Chat Composersend-buttonSend Button; generated when omitted.
Chat Send Buttonsend-icon, stop-iconConsumer-owned icons for each action state.

Events

  • nord-load-more bubbles and is composed when older messages should be loaded.
  • nord-scroll-to-latest bubbles, is composed, and is cancelable. Cancel it to take over the scroll action.
  • nord-scroll-lock-change exposes a read-only locked boolean.
  • nord-new-messages-change exposes a read-only hasNewMessages boolean.
  • nord-submit is a cancelable Composer or standalone Input/Send Button submission intent.
  • nord-stop is a cancelable stop intent while stop mode is shown.
  • nord-toggle proposes Drawer disclosure state; controlled Drawers do not mutate open.
  • nord-files reports accepted and rejected file metadata without uploading files.
  • nord-token-change reports an immutable token snapshot after token edits.
  • nord-history-request exposes 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.