meowerse_
docs menu

Prompt

The chat composer: a › glyph before a normal, auto-growing textarea, with a visible send button.

import { Prompt } from "@meowerse/ui"; · source packages/ui/src/components/Prompt.tsx

variants and states

empty — send is aria-disabled
with text
busy — still editable; sends queue
floating label — labelPlacement="float": the label shows while empty
floating label, with text — the label stays visible on the border

try it

  1. are we still on for tonight?

Nothing leaves this page: messages stay in this demo.

demo source
tsx
import { useRef, useState } from "react";
import { Prompt } from "@meowerse/ui";

type Msg = { id: number; text: string; own: boolean };

// A tiny composer: sends land in the log below, exactly as typed. Nothing leaves the page.
export default function Demo() {
  const [value, setValue] = useState("");
  const [msgs, setMsgs] = useState<Msg[]>([{ id: 1, text: "are we still on for tonight?", own: false }]);
  const seq = useRef(1);
  return (
    <div className="demo chat">
      <ol className="chat__log" aria-label="messages" aria-live="polite">
        {msgs.map((m) => <li key={m.id} className={m.own ? "bubble bubble--own" : "bubble"}>{m.text}</li>)}
      </ol>
      <Prompt label="message" value={value} onChange={setValue}
        onSubmit={(text) => { seq.current += 1; const id = seq.current; setMsgs((ms) => [...ms, { id, text, own: true }]); setValue(""); }} />
      <p className="mw-muted">Nothing leaves this page: messages stay in this demo.</p>
    </div>
  );
}

props

proptypedefaultrequired
labelstring the label: visually hidden and repeated as the placeholder, unless labelPlacement is float.—yes
labelPlacement"hidden" | "float" where the label sits: hidden (the placeholder repeats it), or float — shown inside the empty field and moved up onto its top border once it is focused or filled, so it stays visible (CSS only)."hidden"no
placeholderstring overrides the placeholder text shown while empty (with a floating label, only while focused).—no
valuestring the textarea's current value.—yes
onChange(v: string) => void called with the new value on every keystroke.—yes
onSubmit(v: string) => void called with the trimmed value when it is sent.—yes
sendLabelstring the send button's accessible name; a blank one falls back to "send"."send"no
maxRowsnumber the tallest the textarea grows before it scrolls.6no
busyboolean shows the composer as busy; the field stays editable so sends can queue.falseno
enterSends"auto" | "always" | "never" whether Enter sends: auto only on non-touch pointers, always, or never (the button always sends)."auto"no
classNamestring extra class names to append.—no

css custom properties

variabledarklight
--c-accent#00ff82#0a6e3c
--c-accent-fill#00ff82same
--c-bg-elev#111113#f1f0ec
--c-fg#ededeb#1a1a1c
--c-fg-muted#b3b3af#46464a
--c-fg-subtle#8c8c88#5e5e5b
--c-line-input#6a6a70#7a7973
--c-on-accent#06170dsame
--control-height44pxsame
--d-fast120mssame
--disabled-opacity0.5same
--easecubic-bezier(.16,1,.3,1)same
--fs-20.8125remsame
--fs-41remsame
--r-m4pxsame
--sp-14pxsame
--sp-28pxsame

Classes: .mw-prompt. Set any of these variables on an ancestor (or wrap a subtree in .mw-theme--dark / .mw-theme--light) to restyle just that part of a page.

usage

empty

tsx
import { Prompt } from "@meowerse/ui";

<Prompt label="message" value="" onChange={handleChange} onSubmit={handleSubmit} />

with text

tsx
import { Prompt } from "@meowerse/ui";

<Prompt label="message" value="see you at 7" onChange={handleChange} onSubmit={handleSubmit} />

busy

tsx
import { Prompt } from "@meowerse/ui";

<Prompt label="message" value="and one more thing" busy onChange={handleChange} onSubmit={handleSubmit} />

floating label

tsx
import { Prompt } from "@meowerse/ui";

<Prompt label="message" labelPlacement="float" value="" onChange={handleChange} onSubmit={handleSubmit} />

floating label, with text

tsx
import { Prompt } from "@meowerse/ui";

<Prompt label="message" labelPlacement="float" value="see you at 7" onChange={handleChange} onSubmit={handleSubmit} />

accessibility

  • A labelled textarea: the label is visually hidden and the placeholder repeats it. With labelPlacement="float" the same label is shown instead, inside the empty field and then on its border, so it never disappears while typing.
  • On desktop, Enter sends and Shift+Enter adds a line; on phones, Enter adds a line and the button sends. Nothing is sent while an input method is composing.
  • The field is never disabled; the send button is 44×44 px and named "send".
keydoes
Entersends (desktop)
Shift+Enteradds a line
Tabmoves to the send button

do and don't

do

  • Queue sends while offline instead of disabling the field.

don't

  • Don't make it a borderless command line or add a blinking block cursor (spec §7).