Chat Message
A conversation surface with physical turn-taking — user messages are placed onto the surface, assistant messages are milled into it.
@emboss/chat-message
Align the tape heads.
Checking alignment…
Installation
pnpm dlx shadcn@latest add @emboss/chat-messageUsage
import { ChatLog, ChatMessage } from "@/components/ui/chat-message";
export function Example() {
return (
<ChatLog aria-label="Conversation">
<ChatMessage from="user">Align the heads.</ChatMessage>
<ChatMessage from="assistant">
Aligning. Azimuth is within tolerance.
</ChatMessage>
</ChatLog>
);
}API reference
ChatLog
The conversation container: role=log makes appended messages announce politely. Label it with aria-label.
ChatMessage
| Prop | Type | Default | Description |
|---|---|---|---|
| from | "user" | "assistant" | "system" | "assistant" | Who is speaking; sets alignment and depth (placed plate, milled output, or flush annotation). |
| Data attribute | Description |
|---|---|
| data-from | Reflects the speaker for styling. |
Source
View source — chat-message.tsxHide source — chat-message.tsx
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
/**
* A conversation surface with physical turn-taking: user messages are
* plates placed onto the surface, assistant messages are milled into it.
* The log container is a polite live region, so appended messages announce
* themselves without per-token noise.
*
* @example
* <ChatLog aria-label="Conversation">
* <ChatMessage role="user">Align the heads.</ChatMessage>
* <ChatMessage role="assistant">Aligning. Azimuth is within tolerance.</ChatMessage>
* </ChatLog>
*/
function ChatLog({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="chat-log"
role="log"
className={cn("flex w-full flex-col gap-3", className)}
{...props}
/>
);
}
const chatMessageVariants = cva(
"flex max-w-[85%] flex-col gap-1 rounded-lg px-4 py-2.5 text-sm leading-relaxed",
{
variants: {
from: {
/** A plate you placed onto the surface. */
user: "self-end bg-surface-2 text-ink shadow-emboss-1",
/** The machine's engraved output, milled into the surface. */
assistant: "self-start bg-well text-ink shadow-deboss-1",
/** Quiet system annotations, flush with the page. */
system:
"tracking-label max-w-full justify-center self-center bg-surface-1 font-mono text-label text-ink-faint uppercase shadow-flush",
},
},
defaultVariants: {
from: "assistant",
},
},
);
export type ChatMessageProps = React.ComponentProps<"div"> &
VariantProps<typeof chatMessageVariants>;
function ChatMessage({ className, from, ...props }: ChatMessageProps) {
return (
<div
data-slot="chat-message"
data-from={from ?? "assistant"}
className={cn(chatMessageVariants({ from }), className)}
{...props}
/>
);
}
export { ChatLog, ChatMessage };