본문으로 건너뛰기
컴포넌트

위험 영역 카드

되돌릴 수 없는 동작 하나를 담는 빨간 테두리 카드예요.

  • #arc
  • #component
  • #danger
  • #delete
  • #settings
  • #account
  • #destructive

설치

이 명령 한 줄이면 필요한 파일과 패키지가 함께 설치돼요.

터미널bash
npx shadcn@latest add https://dsui.vercel.app/r/danger-zone.json

danger-zone.module.csscss
/* The fill stays `surface`: a red card reads as "something has gone wrong", and nothing has — yet. */
.zone { display: flex; flex-direction: column; gap: var(--space-2); align-items: flex-start; border: 1px solid var(--danger); border-radius: var(--radius-panel); padding: var(--space-5) var(--space-6); background: var(--surface); }
.title { margin: 0; color: var(--danger); font-size: var(--text-base); font-weight: 600; line-height: 1.4; }
.description { margin: 0; color: var(--text-secondary); font-size: var(--text-sm); line-height: 1.6; word-break: keep-all; }
.action { margin-block-start: var(--space-2); }
danger-zone.tsxtsx
import type { HTMLAttributes, ReactNode } from "react";
import styles from "./danger-zone.module.css";

export interface DangerZoneProps extends HTMLAttributes<HTMLElement> {
  title: string;
  /** What will happen, and that it cannot be undone. */
  description: ReactNode;
  /** The destructive control. Usually a danger Button, or ConfirmMorph for a second look. */
  action: ReactNode;
  /** The heading level. Default 2, for a section of a settings page. */
  headingLevel?: 2 | 3 | 4;
}

/**
 * The card at the end of a settings page holding the one action a person cannot take back. The red edge is a warning
 * about the whole area, not about an error that has happened — so the fill stays `surface` and only the border and
 * the heading carry `danger`. A filled red card reads as "something is wrong" and would cry wolf on every visit.
 *
 * Put exactly one of these on a page, last. The description says what goes and that it is final; the button says
 * what it does, not "확인".
 */
export function DangerZone({ className, title, description, action, headingLevel = 2, ...props }: DangerZoneProps) {
  const Heading = `h${headingLevel}` as "h2" | "h3" | "h4";
  return (
    <section {...props} className={[styles.zone, className].filter(Boolean).join(" ")}>
      <Heading className={styles.title}>{title}</Heading>
      <p className={styles.description}>{description}</p>
      <div className={styles.action}>{action}</div>
    </section>
  );
}

DangerZone.displayName = "DangerZone";

export default DangerZone;