Conversation

Conversation.Root는 채팅의 메시지를 담고, 가장 새 메시지가 자라는 동안 화면에 보이게 합니다. 끝에서 열리고, 메시지가 스트리밍되는 동안 끝을 따라갑니다. 읽는 사람이 위로 스크롤하면 따라가기를 멈추고, Conversation.ScrollButton이 다시 끝으로 데려옵니다.

이 페이지의 내용
<Conversation.Root style={{ blockSize: '24rem' }}>
  <Conversation.Viewport>
    <Conversation.Content>
      {messages.map((message) => (
        <Message.Root key={message.id} from={message.role} streaming={message.streaming} aria-label={message.author}>
          <Message.Content>…</Message.Content>
        </Message.Root>
      ))}
    </Conversation.Content>
  </Conversation.Viewport>
  <Conversation.ScrollButton />
</Conversation.Root>
What does Conversation do?

It holds the messages of a chat and scrolls with them.

What happens when a reply is long?

Ask for one below and watch.

루트에 높이를 주거나, flex 열처럼 높이를 주는 레이아웃 안에 두세요. 뷰포트는 그 안에서 스크롤합니다.

파트

  • Conversation.Viewport는 스크롤하는 <section>입니다. aria-label이나 aria-labelledby로 이름을 주지 않으면 conversation 문자열로 "Conversation"이라는 이름이 붙은 region 랜드마크입니다. ScrollArea처럼 콘텐츠 상자에 포커스 링이 들어갈 여백이 있어서, 가장자리 버튼의 링이 잘리지 않습니다.
  • Conversation.Content는 메시지를 담고 conversation.gap만큼 띄웁니다.
  • Conversation.ScrollButton은 뷰포트 끝 위에 놓이는 Button으로, 가장 새 메시지로 스크롤합니다. 읽는 사람이 끝에 있을 때는 숨겨집니다. children이 없으면 chevron 글리프를 보여 주고 scrollToLatest 문자열로 이름이 붙습니다.

끝 따라가기

읽는 사람이 끝에 있는 동안, 뷰포트는 콘텐츠나 자기 크기가 바뀔 때마다 끝으로 돌아가므로 자라는 메시지가 계속 보입니다. 위로 스크롤하면 이를 멈추고, 다시 끝까지 스크롤하거나 Conversation.ScrollButton을 누르면 다시 시작합니다. 버튼은 부드럽게 스크롤하고, 동작 줄이기 설정에서는 바로 이동합니다.

알림

스트리밍되는 텍스트는 도착하는 대로 읽히지 않습니다. 뷰포트는 live region이 아닙니다. 대신 Conversation은 메시지가 도착하기 시작할 때와 다 도착했을 때 useAnnouncer로 스크린 리더에 알립니다. Conversation.Content 안의 article에서 Message의 streaming이 설정하는 aria-busy="true"를 지켜봅니다. 시작될 때 messageStarted 문자열을, 그것이 풀릴 때 messageFinished를 읽습니다. 그때 메시지 안에 오류 Alert처럼 role="alert"인 요소가 있으면 아무것도 말하지 않습니다. 알림이 읽히기 때문입니다. 한 메시지가 끝나면서 다른 메시지가 시작되면 messageStarted만 읽습니다. 마운트될 때 이미 스트리밍 중인 메시지는 시작을 알리지 않습니다. 직접 안내하려면 루트에 announceMessages={false}를 주세요.

접근성을 지키는 조합

Todae가 맡는 것: 기본 이름이 있는 대화의 region 랜드마크. 뷰포트가 넘치고 안에 Tab 정지점이 없을 때 키보드로 스크롤할 수 있도록 뷰포트에 주는 Tab 정지점. 메시지가 시작되고 끝날 때의 안내. Conversation.ScrollButton이 포커스를 가진 채 숨겨지면 포커스는 페이지가 아니라 뷰포트로 옮겨집니다.

직접 제공할 것: 누가 썼는지 말하는 각 메시지의 이름. 페이지에 대화가 둘 이상일 때 누구의 대화인지 말하는 뷰포트의 이름. 실패한 메시지의 오류를 role="alert"인 요소에 담기.

주의할 점: 대화를 live region이나 그 하나인 role="log"에 넣는 것. 스트리밍되는 단어가 모두 읽힙니다. JavaScript 가상화처럼 화면 밖으로 스크롤된 메시지를 없애는 것. 페이지 내 찾기와 스크린 리더가 더는 닿지 못합니다.

언제 쓰나요

쓸 때: 어시스턴트나 사람과의 채팅처럼 새 메시지가 끝에 도착하고 도착하는 동안 자랄 수 있을 때.

쓰지 않을 때: 읽는 사람이 요청할 때만 늘어나는 목록 → ScrollArea. 나타났다 사라지는 알림 → Toast.

Props

Conversation.Root props
이름타입설명
announceMessagesboolean

메시지가 도착하기 시작할 때와 다 도착했을 때 스크린 리더에 알릴지 여부. Conversation.Content 안 article의 aria-busy로 판단합니다. 텍스트는 messageStarted와 messageFinished 문자열입니다.

기본값: true

childrenReact.ReactNode

Conversation.Viewport와 Conversation.ScrollButton.

Conversation.Viewport props
이름타입설명
childrenReact.ReactNode

Conversation.Content.

Conversation.Content props
이름타입설명
childrenReact.ReactNode

Message.Root 요소.

Conversation.ScrollButton props
이름타입설명
childrenReact.ReactNode

버튼의 내용. 없으면 chevron 글리프를 보여 주고 scrollToLatest 문자열로 이름이 붙습니다.