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로 트리거가 아닌 다른 요소에 콘텐츠를 위치시킬 수 있어요.