Popover

Popover

버튼으로 트리거되어 포털(portal)에 풍부한 콘텐츠를 표시하는 컴포넌트예요.

출처: 문서

본문

버튼을 클릭하면 연결된 앵커/트리거 옆에 콘텐츠가 떠오르는 팝오버 컴포넌트예요. 모달/비모달 모드를 지원하고, 포커스가 완전히 관리되며, side/alignment/offset/collision 처리를 자유롭게 커스터마이즈할 수 있어요.

Features

  • 제어(controlled)/비제어(uncontrolled) 방식 모두 지원.
  • side, alignment, offset, collision 처리 커스터마이즈.
  • 선택적으로 가리키는 화살표(arrow) 렌더링.
  • 포커스 완전 관리 및 커스터마이즈.
  • 모달/비모달 모드 지원.
  • 닫기/Dismiss 및 레이어링 동작의 높은 커스터마이즈 가능.

Anatomy

모든 파트를 임포트해 조립해요.

import { Popover } from "radix-ui";

export default () => (
  <Popover.Root>
    <Popover.Trigger />

    <Popover.Anchor />

    <Popover.Portal>
      <Popover.Content>
        <Popover.Close />

        <Popover.Arrow />
      </Popover.Content>
    </Popover.Portal>
  </Popover.Root>
);

API Reference

Root

팝오버의 모든 파트를 담아요.

Prop Type Default
defaultOpen boolean No default value
open boolean No default value
onOpenChange function No default value
modal boolean false

Trigger

팝오버를 토글하는 버튼이에요. 기본적으로 Popover.Content는 트리거에 맞춰 위치하게 돼요.

Prop Type Default
asChild boolean false
Data attribute Values
[data-state] "open" | "closed"

Anchor

Popover.Content를 위치시킬 선택적 요소예요. 이 파트를 쓰지 않으면 콘텐츠는 Popover.Trigger 옆에 위치해요.

Prop Type Default
asChild boolean false

Portal

사용하면 content 파트를 body로 포털해요.

Prop Type Default
forceMount boolean No default value
container HTMLElement document.body

Content

팝오버가 열렸을 때 튀어나오는 컴포넌트예요.

Prop Type Default
asChild boolean false
onOpenAutoFocus function No default value
onCloseAutoFocus function No default value
onEscapeKeyDown function No default value
onPointerDownOutside function No default value
onFocusOutside function No default value
onInteractOutside function No default value
forceMount boolean No default value
side enum "bottom"
sideOffset number 0
align enum "center"
alignOffset number 0
avoidCollisions boolean true
collisionBoundary Boundary []
collisionPadding number | Padding 0
arrowPadding number 0
sticky enum "partial"
hideWhenDetached boolean false
Data attribute Values
[data-state] "open" | "closed"
[data-side] "left" | "right" | "bottom" | "top"
[data-align] "start" | "end" | "center"
CSS Variable Description
--radix-popover-content-transform-origin The transform-origin computed from the content and arrow positions/offsets
--radix-popover-content-available-width The remaining width between the trigger and the boundary edge
--radix-popover-content-available-height The remaining height between the trigger and the boundary edge
--radix-popover-trigger-width The width of the trigger
--radix-popover-trigger-height The height of the trigger

Arrow

팝오버 옆에 렌더링할 수 있는 선택적 화살표 요소예요. 앵커와 Popover.Content를 시각적으로 연결해 주는 데 도움을 줘요. Popover.Content 안에 렌더링해야 해요.

Prop Type Default
asChild boolean false
width number 10
height number 5

Close

열린 팝오버를 닫는 버튼이에요.

Prop Type Default
asChild boolean false

Examples

콘텐츠 크기 제한

콘텐츠의 너비를 트리거 너비에 맞추고 싶을 수 있어요. 높이를 뷰포트에 넘지 않게 제한할 수도 있어요.

--radix-popover-trigger-width, --radix-popover-content-available-height 같은 여러 CSS 커스텀 프로퍼티가 이를 지원해요. 콘텐츠 크기를 제한하는 데 활용하세요.

// index.jsx

import { Popover } from "radix-ui";

import "./styles.css";

export default () => (
  <Popover.Root>
    <Popover.Trigger>…</Popover.Trigger>

    <Popover.Portal>
      <Popover.Content className="PopoverContent" sideOffset={5}>
        …
      </Popover.Content>
    </Popover.Portal>
  </Popover.Root>
);
/* styles.css */

.PopoverContent {
  width: var(--radix-popover-trigger-width);

  max-height: var(--radix-popover-content-available-height);
}

Origin 인지 애니메이션

--radix-popover-content-transform-origin CSS 커스텀 프로퍼티를 노출해요. side, sideOffset, align, alignOffset과 충돌을 기반으로 계산된 origin에서 콘텐츠를 애니메이션하는 데 사용하세요.

// index.jsx

import { Popover } from "radix-ui";

import "./styles.css";

export default () => (
  <Popover.Root>
    <Popover.Trigger>…</Popover.Trigger>

    <Popover.Portal>
      <Popover.Content className="PopoverContent">…</Popover.Content>
    </Popover.Portal>
  </Popover.Root>
);
/* styles.css */

.PopoverContent {
  transform-origin: var(--radix-popover-content-transform-origin);

  animation: scaleIn 0.5s ease-out;
}

@keyframes scaleIn {
  from {
    opacity: 0;
    transform: scale(0);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

Collision 인지 애니메이션

data-side와 data-align 속성을 노출해요. 이 값들은 충돌을 반영해 런타임에 바뀌어요. 충돌과 방향에 인지된 애니메이션을 만드는 데 사용하세요.

// index.jsx

import { Popover } from "radix-ui";

import "./styles.css";

export default () => (
  <Popover.Root>
    <Popover.Trigger>…</Popover.Trigger>

    <Popover.Portal>
      <Popover.Content className="PopoverContent">…</Popover.Content>
    </Popover.Portal>
  </Popover.Root>
);
/* styles.css */

.PopoverContent {
  animation-duration: 0.6s;
  animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}

.PopoverContent[data-side="top"] {
  animation-name: slideUp;
}

.PopoverContent[data-side="bottom"] {
  animation-name: slideDown;
}

@keyframes slideDown {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes slideUp {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

커스텀 앵커와 함께

트리거를 앵커로 쓰고 싶지 않다면 콘텐츠를 다른 요소에 앵커할 수 있어요.

// index.jsx

import { Popover } from "radix-ui";

import "./styles.css";

export default () => (
  <Popover.Root>
    <Popover.Anchor asChild>
      <div className="Row">
        Row as anchor <Popover.Trigger>Trigger</Popover.Trigger>
      </div>
    </Popover.Anchor>

    <Popover.Portal>
      <Popover.Content>…</Popover.Content>
    </Popover.Portal>
  </Popover.Root>
);
/* styles.css */

.Row {
  background-color: gainsboro;
  padding: 20px;
}

Accessibility

Dialog WAI-ARIA 디자인 패턴을 준수해요.

키보드 상호작용

Key Description
Space Opens/closes the popover.
Enter Opens/closes the popover.
Tab Moves focus to the next focusable element
Shift + Tab Moves focus to the previous focusable element
Esc Closes the popover and moves focus to Popover.Trigger.

Custom APIs

primitive 파트를 자신의 컴포넌트로 추상화해 나만의 API를 만들 수 있어요.

화살표 추상화와 기본 설정

이 예제는 Popover.Arrow 파트를 추상화하고 기본 sideOffset 설정을 둬요.

사용법

import { Popover, PopoverTrigger, PopoverContent } from "./your-popover";

export default () => (
  <Popover>
    <PopoverTrigger>Popover trigger</PopoverTrigger>

    <PopoverContent>Popover content</PopoverContent>
  </Popover>
);

구현

// your-popover.jsx

import * as React from "react";

import { Popover as PopoverPrimitive } from "radix-ui";

export const Popover = PopoverPrimitive.Root;

export const PopoverTrigger = PopoverPrimitive.Trigger;

export const PopoverContent = React.forwardRef(({ children, ...props }, forwardedRef) => (
  <PopoverPrimitive.Portal>
    <PopoverPrimitive.Content sideOffset={5} {...props} ref={forwardedRef}>
      {children}

      <PopoverPrimitive.Arrow />
    </PopoverPrimitive.Content>
  </PopoverPrimitive.Portal>
));

더 알아보기 (Learn more)

  • 기본이 비모달(modal={false})이라 팝오버 밖을 클릭하면 닫혀요.
  • Popover.Anchor로 트리거가 아닌 다른 요소에 콘텐츠를 위치시킬 수 있어요.