ChatComposer
ChatComposer provides an accessibly named, growing textarea inside a rounded field with a prominent surface, a focus-colored frame and a circular send control. It sends trimmed nonempty text through a callback.
01Preview
02Usage
Load @dg/ui/styles.css once in the root layout.
<script>
import { ChatComposer } from "@dg/ui";
let draft = $state("");
let lastSent = $state("");
</script>
<ChatComposer
id="question"
bind:value={draft}
onsend={(text) => {
lastSent = text;
draft = "";
}}
/>
<p class="feedback" aria-live="polite">{lastSent}</p>
03Notes
- While
pending, the draft stays focusable and read-only; activating stop callsonstopand returns focus to the draft. - The field outline uses
--text-muted, keeping the 3:1 contrast required for a control boundary in both themes. - The field has rounded
1remcorners, unlike square native fields. When the textarea has focus, the entry surface's frame takes--focusand thickens inward to--focus-width, without an outer outline; forced-colors mode shows a system-colored outline. The send control keeps its own focus indicator. - The placeholder and single-line draft are vertically centered within the minimum control height. Additional lines grow the textarea normally, including automatic wrapping.
- The composer keeps its height in a flex column while the thread yields space. The textarea grows to 7.5rem (120px at a 16px root), then scrolls internally; maxlength separately limits the character count.
- A translucent, blurred fade above the composer softens messages scrolling underneath without intercepting pointer input. The field and controls remain opaque.
- The send and stop controls use built-in names in the
Pagelanguage. - Native HTML attributes, DOM callbacks, class and style are forwarded to the form; it owns submission, and
idnames the textarea. - Forwarding uses native attribute types; unknown props are not supported. Pass class as a string.
04Parameters
| Name | Type | Default | Description |
|---|---|---|---|
id | string | Required | Unique textarea ID, also used to connect its hint. |
value | string · bindable | "" | Draft text. Use bind:value; the parent clears it after a successful send. |
onsend | (text: string) => void | undefined | Receives trimmed, nonempty text on Enter or submit. Shift+Enter adds a newline; IME composition does not send. |
label | string | undefined | Accessible textarea name; no visible label is rendered. Defaults to the built-in text in the Page language. |
placeholder | string | undefined | Placeholder shown for an empty draft. Defaults to the built-in text in the Page language. |
hint | string | undefined | Text linked with aria-describedby and set as .feedback; an empty string hides it. Defaults to the built-in keyboard hint. |
disabled | boolean | false | Disable input and sending, for example while a response is loading. |
maxlength | number | 2000 | Native maximum textarea length in characters. |
pending | boolean | false | Make the draft read-only, keeping its focus, and replace sending with a stop action while a request is pending. |
onstop | () => void | undefined | Stop action callback; the parent owns cancellation and pending state. |
05Methods
| Name | Description |
|---|---|
focus() | Focus the textarea without scrolling. Access with bind:this. |
06Accessibility
The label supplies the textarea’s accessible name through aria-label. Use a unique id to connect its hint. Enter sends; Shift+Enter adds a newline; IME composition does not send. Disabled prevents editing and sending. The parent clears the draft and manages request errors.