컴포넌트
Alert
화면 안에 안내 메시지를 표시합니다.
설치
pnpm dlx ply-ui add alert공통 설치 설정을 먼저 적용합니다.
사용법
AlertTitle과 AlertDescription으로 제목과 설명을 구분합니다.
화면에 유지되는 안내에 사용합니다. 일시적인 작업 결과는 Toast를 사용합니다.
종류
tone으로 안내의 종류를 고릅니다. 종류는 테두리와 옅은 바탕색으로 구분하고, 글자색은 대비를 지키기 위해 바꾸지 않습니다. 색을 구분하기 어려운 사용자도 알 수 있도록 제목에 상황을 적습니다.
tone | 쓰는 곳 | 기본 role |
|---|---|---|
neutral (기본값) | 일반 안내 | status |
success | 끝난 작업의 결과 | status |
warning | 곧 문제가 될 수 있는 상태 | status |
error | 작업을 막는 오류 | alert |
role="alert"는 보조기기가 하던 말을 멈추고 바로 읽으므로 사용자가 바로 알아야 하는 오류에만 씁니다. 처음부터 화면에 있는 안내라면 role을 직접 지정해 바꿀 수 있습니다.
제목 수준
AlertTitle은 기본 h3입니다. 페이지 제목 바로 아래에 두면 as="h2", 제목 계층에 넣지 않을 때는 as="p"를 씁니다.
구성
| 구성 요소 | 역할 |
|---|---|
Alert | 알림 내용을 묶습니다. 기본 role은 tone="error"일 때 alert, 나머지는 status입니다. |
AlertTitle | 알림 제목입니다. as로 제목 수준을 고릅니다. |
AlertDescription | 알림의 상세 내용입니다. |
구현 코드
components/ui/alert.tsx
import type { ComponentProps } from "react";
import { cx } from "../../lib/cx";
import "./alert.css";
export type AlertTone = "neutral" | "success" | "warning" | "error";
export type AlertProps = ComponentProps<"div"> & {
/** 안내의 종류입니다. 색만으로 뜻을 전하지 않도록 제목에도 상황을 적습니다. @defaultValue "neutral" */
tone?: AlertTone;
};
/** 오류는 바로 읽히도록 `role="alert"`, 나머지는 `role="status"`가 기본입니다. `role`로 바꿀 수 있습니다. */
export function Alert({ tone = "neutral", className, ...props }: AlertProps) {
return (
<div
role={tone === "error" ? "alert" : "status"}
{...props}
data-tone={tone}
className={cx("rbx-alert", className)}
/>
);
}
export type AlertTitleProps = ComponentProps<"h3"> & {
/** 페이지의 제목 계층에 맞게 선택합니다. 제목이 필요 없으면 "p"를 씁니다. @defaultValue "h3" */
as?: "h2" | "h3" | "h4" | "h5" | "h6" | "p";
};
export function AlertTitle({
as: Tag = "h3",
className,
...props
}: AlertTitleProps) {
return <Tag {...props} className={cx("rbx-alert-title", className)} />;
}
export function AlertDescription({ className, ...props }: ComponentProps<"p">) {
return <p {...props} className={cx("rbx-alert-description", className)} />;
}
components/ui/alert.css
@layer theme, base, components, utilities;
@layer components.ply-base, components.ply;
@layer components.ply {
/* alert — semantic HTML composition. */
.rbx-alert {
--rbx-alert-accent: var(--rbx-color-stroke-emphasis);
display: grid;
gap: 4px;
border: 1px solid var(--rbx-alert-accent);
border-radius: 8px;
padding: 16px;
background: var(--rbx-color-surface-100);
min-width: 0;
max-width: 100%;
overflow-wrap: anywhere;
}
/* 종류는 테두리와 옅은 바탕으로 구분합니다. 글자색은 대비를 위해 바꾸지 않습니다. */
.rbx-alert:not([data-tone="neutral"]) {
background: color-mix(
in srgb,
var(--rbx-alert-accent) 8%,
var(--rbx-color-surface-100)
);
}
.rbx-alert[data-tone="success"] {
--rbx-alert-accent: var(--rbx-color-system-success);
}
.rbx-alert[data-tone="warning"] {
--rbx-alert-accent: var(--rbx-color-system-warning);
}
.rbx-alert[data-tone="error"] {
--rbx-alert-accent: var(--rbx-color-system-alert);
}
.rbx-alert-title {
margin: 0;
color: var(--rbx-color-content-emphasis);
font: var(--rbx-typography-title-large-font);
}
.rbx-alert-description {
margin: 0;
color: var(--rbx-color-content-default);
font: var(--rbx-typography-body-medium-font);
}
}