Popup
사용자의 확인이나 짧은 입력이 필요할 때 화면 위에 띄우는 모달입니다. 되돌리기 어려운 동작 앞에 반드시 둡니다.
1. Default
제목 · 본문 · 액션으로 이루어진 기본 구성입니다.
정책을 배포할까요?
변경 사항이 1,284대에 적용됩니다.
HTML
<div class="popup" role="dialog" aria-modal="true" aria-labelledby="d-title">
<h3 class="popup-title" id="d-title">정책을 배포할까요?</h3>
<p class="popup-body">변경 사항이 1,284대에 적용됩니다.</p>
<div class="popup-actions"><button type="button" class="btn md secondary" data-popup-close>취소</button><button type="button" class="btn md primary">배포</button></div>
</div>CSS · .popup, .popup-backdrop
/* JS Design System v0.6 · .popup, .popup-backdrop · 토큰(var(--*))은 assets/style.css :root 참고 */
.popup-backdrop{position:fixed;inset:0;background:var(--dimmer);display:grid;place-items:center;z-index:100}
.popup{position:relative;width:440px;max-width:100%;background:var(--bg-surface);border-radius:var(--radius-xl);box-shadow:var(--shadow-3);padding:24px;display:flex;flex-direction:column;gap:12px}
.popup .popup-title{font-size:18px;font-weight:700;margin:0;display:flex;align-items:center;gap:10px}
.popup .popup-body{font-size:14px;color:var(--text-secondary);margin:0;line-height:1.6}
.popup .popup-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:12px}
.popup .popup-close{position:absolute;top:16px;right:16px}
.popup.lg{width:640px}
.popup.danger .popup-title{color:var(--danger-text)}
.popup .popup-form{display:flex;flex-direction:column;gap:16px;margin-top:4px}React · @jiran/ds-react
import { useState } from "react";
import { Button, Popup, PopupActions, PopupBody, PopupTitle } from "@jiran/ds-react";
export default function Example() {
const [open, setOpen] = useState(false);
const close = () => setOpen(false);
return (
<>
<Button onClick={() => setOpen(true)}>배포</Button>
<Popup open={open} onClose={close}>
<PopupTitle>정책을 배포할까요?</PopupTitle>
<PopupBody>변경 사항이 1,284대에 적용됩니다.</PopupBody>
<PopupActions>
<Button variant="secondary" onClick={close}>취소</Button>
<Button onClick={close}>배포</Button>
</PopupActions>
</Popup>
</>
);
}2. Confirm
실제 화면에서는 .popup-backdrop 으로 감쌉니다(코드 참조). 예제는 팝업만 표시.
정책을 배포할까요?
변경 사항이 12개 그룹, 1,284대에 적용됩니다. 에이전트는 다음 체크인(최대 5분)에서 정책을 받습니다.
HTML
<div class="popup" role="dialog" aria-modal="true" aria-labelledby="c-title">
<h3 class="popup-title" id="c-title">정책을 배포할까요?</h3>
<p class="popup-body">변경 사항이 12개 그룹, 1,284대에 적용됩니다. 에이전트는 다음 체크인(최대 5분)에서 정책을 받습니다.</p>
<div class="popup-actions"><button type="button" class="btn md secondary" data-popup-close>취소</button><button type="button" class="btn md primary">배포</button></div>
</div>CSS · .popup, .popup-backdrop
/* JS Design System v0.6 · .popup, .popup-backdrop · 토큰(var(--*))은 assets/style.css :root 참고 */
.popup-backdrop{position:fixed;inset:0;background:var(--dimmer);display:grid;place-items:center;z-index:100}
.popup{position:relative;width:440px;max-width:100%;background:var(--bg-surface);border-radius:var(--radius-xl);box-shadow:var(--shadow-3);padding:24px;display:flex;flex-direction:column;gap:12px}
.popup .popup-title{font-size:18px;font-weight:700;margin:0;display:flex;align-items:center;gap:10px}
.popup .popup-body{font-size:14px;color:var(--text-secondary);margin:0;line-height:1.6}
.popup .popup-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:12px}
.popup .popup-close{position:absolute;top:16px;right:16px}
.popup.lg{width:640px}
.popup.danger .popup-title{color:var(--danger-text)}
.popup .popup-form{display:flex;flex-direction:column;gap:16px;margin-top:4px}React · @jiran/ds-react
import { useState } from "react";
import { Button, Popup, PopupActions, PopupBody, PopupTitle } from "@jiran/ds-react";
export default function Example() {
const [open, setOpen] = useState(false);
const close = () => setOpen(false);
return (
<>
<Button onClick={() => setOpen(true)}>배포</Button>
<Popup open={open} onClose={close}>
<PopupTitle>정책을 배포할까요?</PopupTitle>
<PopupBody>변경 사항이 12개 그룹, 1,284대에 적용됩니다. 에이전트는 다음 체크인(최대 5분)에서 정책을 받습니다.</PopupBody>
<PopupActions>
<Button variant="secondary" onClick={close}>취소</Button>
<Button onClick={close}>배포</Button>
</PopupActions>
</Popup>
</>
);
}3. Danger Confirm
삭제·초기화. 제목은 danger 색, 주 액션은 Danger 버튼, 필요하면 확인 입력.
정책 12개를 삭제할까요?
삭제한 정책은 복구할 수 없습니다. 해당 정책이 적용된 에이전트는 기본 정책으로 돌아갑니다.
HTML
<div class="popup danger" role="dialog" aria-modal="true" aria-labelledby="d-title">
<h3 class="popup-title" id="d-title">정책 12개를 삭제할까요?</h3>
<p class="popup-body">삭제한 정책은 복구할 수 없습니다. 해당 정책이 적용된 에이전트는 기본 정책으로 돌아갑니다.</p>
<div class="field"><label for="confirm">확인을 위해 <b>삭제</b> 를 입력하세요</label><input id="confirm" placeholder="삭제"></div>
<div class="popup-actions"><button type="button" class="btn md secondary" data-popup-close>취소</button><button type="button" class="btn md danger" disabled>삭제</button></div>
</div>CSS · .popup, .popup-backdrop
/* JS Design System v0.6 · .popup, .popup-backdrop · 토큰(var(--*))은 assets/style.css :root 참고 */
.popup-backdrop{position:fixed;inset:0;background:var(--dimmer);display:grid;place-items:center;z-index:100}
.popup{position:relative;width:440px;max-width:100%;background:var(--bg-surface);border-radius:var(--radius-xl);box-shadow:var(--shadow-3);padding:24px;display:flex;flex-direction:column;gap:12px}
.popup .popup-title{font-size:18px;font-weight:700;margin:0;display:flex;align-items:center;gap:10px}
.popup .popup-body{font-size:14px;color:var(--text-secondary);margin:0;line-height:1.6}
.popup .popup-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:12px}
.popup .popup-close{position:absolute;top:16px;right:16px}
.popup.lg{width:640px}
.popup.danger .popup-title{color:var(--danger-text)}
.popup .popup-form{display:flex;flex-direction:column;gap:16px;margin-top:4px}React · @jiran/ds-react
import { useState } from "react";
import { Button, PopupActions, PopupBody, PopupSurface, PopupTitle, TextField } from "@jiran/ds-react";
export default function Example() {
const [confirm, setConfirm] = useState("");
return (
<PopupSurface danger>
<PopupTitle>정책 12개를 삭제할까요?</PopupTitle>
<PopupBody>삭제한 정책은 복구할 수 없습니다. 해당 정책이 적용된 에이전트는 기본 정책으로 돌아갑니다.</PopupBody>
<TextField id="confirm" label={<>확인을 위해 <b>삭제</b> 를 입력하세요</>} placeholder="삭제" value={confirm} onChange={(e) => setConfirm(e.target.value)} />
<PopupActions>
<Button variant="secondary">취소</Button>
<Button variant="danger" disabled={confirm !== "삭제"}>삭제</Button>
</PopupActions>
</PopupSurface>
);
}4. Form
짧은 입력(필드 1~3개). 그 이상은 별도 페이지나 드로어.
예외 추가
HTML
<div class="popup" role="dialog" aria-modal="true" aria-labelledby="f-title">
<h3 class="popup-title" id="f-title">예외 추가</h3>
<div class="popup-form">
<div class="field"><label for="hash">파일 해시(SHA-256)</label><input id="hash" placeholder="64자리 16진수"></div>
<div class="field"><label for="reason">사유</label><input id="reason" placeholder="예: 사내 배포 도구"></div>
<label class="checkbox"><input type="checkbox"><span>모든 그룹에 적용</span></label>
</div>
<div class="popup-actions"><button type="button" class="btn md secondary" data-popup-close>취소</button><button type="button" class="btn md primary">추가</button></div>
</div>CSS · .popup, .popup-backdrop
/* JS Design System v0.6 · .popup, .popup-backdrop · 토큰(var(--*))은 assets/style.css :root 참고 */
.popup-backdrop{position:fixed;inset:0;background:var(--dimmer);display:grid;place-items:center;z-index:100}
.popup{position:relative;width:440px;max-width:100%;background:var(--bg-surface);border-radius:var(--radius-xl);box-shadow:var(--shadow-3);padding:24px;display:flex;flex-direction:column;gap:12px}
.popup .popup-title{font-size:18px;font-weight:700;margin:0;display:flex;align-items:center;gap:10px}
.popup .popup-body{font-size:14px;color:var(--text-secondary);margin:0;line-height:1.6}
.popup .popup-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:12px}
.popup .popup-close{position:absolute;top:16px;right:16px}
.popup.lg{width:640px}
.popup.danger .popup-title{color:var(--danger-text)}
.popup .popup-form{display:flex;flex-direction:column;gap:16px;margin-top:4px}React · @jiran/ds-react
import { Button, Checkbox, PopupActions, PopupForm, PopupSurface, PopupTitle, TextField } from "@jiran/ds-react";
export default function Example() {
return (
<PopupSurface>
<PopupTitle>예외 추가</PopupTitle>
<PopupForm>
<TextField id="hash" label="파일 해시(SHA-256)" placeholder="64자리 16진수" />
<TextField id="reason" label="사유" placeholder="예: 사내 배포 도구" />
<Checkbox>모든 그룹에 적용</Checkbox>
</PopupForm>
<PopupActions>
<Button variant="secondary">취소</Button>
<Button>추가</Button>
</PopupActions>
</PopupSurface>
);
}Props
HTML 에서는 클래스와 속성으로, React(@jiran/ds-react)에서는 같은 이름의 prop(variant · size …)으로 변형·상태를 지정합니다.
| Prop / Class | Type · Values | Default | Description |
|---|---|---|---|
| .popup-backdrop | overlay | — | fixed, rgba(15,23,42,.65), 중앙 정렬 |
| .popup | dialog | — | role=dialog aria-modal aria-labelledby. 폭 440 (lg 640) |
| .popup-title / .popup-body / .popup-actions | elements | — | 제목 18 / 본문 14 / 액션 우측 |
| .danger | boolean | false | 제목 danger 색 |
| .popup-form | container | — | 필드 세로 16px 간격 |
Usage
React: @jiran/ds-react 패키지를 설치하고 style.css 를 한 번 import 하면 위 React 탭 코드를 그대로 쓸 수 있습니다(React Package). HTML: assets/style.css 를 연결하고(일부만 필요하면 아래 컴포넌트 CSS 복사), 동작이 필요하면 assets/ds.js 를 함께 넣습니다 — 이 페이지의 프리뷰가 그 스크립트로 동작합니다. 색은 전부 토큰(var(--accent) 등)을 참조하므로 --accent-* 만 제품 컬러로 바꾸면 됩니다.
React @jiran/ds-react
npm i @jiran/ds-react
// main.tsx — 앱 진입점에서 한 번
import "@jiran/ds-react/style.css";
// 컴포넌트
import { Popup, PopupSurface, PopupTitle, PopupBody, PopupActions, PopupForm } from "@jiran/ds-react";HTML
<!-- 1) 토큰 + 전체 컴포넌트 스타일 한 번에 연결 -->
<link rel="stylesheet" href="assets/style.css">
<!-- 동작(드롭다운·탭·달력·표 정렬/선택·토스트 등) — 순수 JS 한 파일. React 제품은 불필요 -->
<script src="assets/ds.js" defer></script>
<!-- 2) 또는 아래 "Component CSS" 만 프로젝트 CSS 에 붙이고,
:root 토큰은 Foundations › Design Token 에서 복사 -->Component CSS .popup, .popup-backdrop
/* JS Design System v0.6 · .popup, .popup-backdrop · 토큰(var(--*))은 assets/style.css :root 참고 */
.popup-backdrop{position:fixed;inset:0;background:var(--dimmer);display:grid;place-items:center;z-index:100}
.popup{position:relative;width:440px;max-width:100%;background:var(--bg-surface);border-radius:var(--radius-xl);box-shadow:var(--shadow-3);padding:24px;display:flex;flex-direction:column;gap:12px}
.popup .popup-title{font-size:18px;font-weight:700;margin:0;display:flex;align-items:center;gap:10px}
.popup .popup-body{font-size:14px;color:var(--text-secondary);margin:0;line-height:1.6}
.popup .popup-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:12px}
.popup .popup-close{position:absolute;top:16px;right:16px}
.popup.lg{width:640px}
.popup.danger .popup-title{color:var(--danger-text)}
.popup .popup-form{display:flex;flex-direction:column;gap:16px;margin-top:4px}