Project note

The product case: a note with templates, dictation and send. Enter stays a newline; sending goes through Ctrl/Cmd+Enter or the send button.

Compact chat input

The same recipe tuned down: one row, smaller floor. The admini chat page uses this composition.

States

The surface reflects the control: aria-invalid turns the border danger through --form-invalid-border, and a disabled control quiets the whole surface.

Enter contract

The only JavaScript the composer needs. Plain Enter is a newline; composition sessions (IME) never send.

input.addEventListener("keydown", (event) => {
  if (event.key !== "Enter" || event.isComposing) return;
  if (event.ctrlKey || event.metaKey) {
    event.preventDefault();
    form.requestSubmit();
  }
  // Plain Enter falls through: the textarea inserts a newline.
});