Accordion
긴 설명이나 고급 설정을 접어 두고 필요할 때 펼칩니다. 네이티브 details/summary 를 써 JS 없이 동작하며, 펼침 상태 전달과 키보드 조작(Tab · Enter · Space)도 브라우저 기본 동작이라 기본·그룹에는 별도 ARIA 가 필요 없습니다. 예외는 비활성뿐입니다 — pointer-events 와 tabindex="-1" 은 마우스·키보드만 막고 스크린리더에는 아무것도 알리지 않으므로 aria-disabled="true" 를 함께 둡니다.
1. Default
details.accordion > summary + .body. open 속성으로 초기 펼침.
고급 탐지 옵션 3개 설정
<details class="accordion" open style="max-width:560px">
<summary>고급 탐지 옵션 <small>3개 설정</small></summary>
<div class="body">휴리스틱 분석, 메모리 스캔, 스크립트 차단을 개별로 켜고 끕니다. 기본값은 모두 켬입니다.</div>
</details>/* JS Design System v0.6 · .accordion, .accordion-group · 토큰(var(--*))은 assets/style.css :root 참고 */
.accordion{border:1px solid var(--border-default);border-radius:var(--radius-lg);background:var(--bg-surface);width:100%;interpolate-size:allow-keywords}
.accordion summary{list-style:none;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 20px;font-weight:600;font-size:16px;line-height:24px;cursor:pointer}
.accordion summary::-webkit-details-marker{display:none}
.accordion summary::after{content:"";width:16px;height:16px;flex:none;background:var(--text-secondary);-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22 %3E%3Cpath d=%22M6 9l6 6l6 -6%22 /%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22 %3E%3Cpath d=%22M6 9l6 6l6 -6%22 /%3E%3C/svg%3E") center/contain no-repeat;transition:transform var(--motion-fast)}
.accordion[open] summary::after{transform:rotate(180deg)}
.accordion summary small{font-weight:400;color:var(--text-tertiary);margin-left:auto;font-size:12px}
.accordion summary>svg{flex:none}
.accordion .body{padding:0 20px 16px;font-size:14px;color:var(--text-secondary);line-height:1.6}
.accordion.disabled{opacity:.5;pointer-events:none}
.accordion.flat{border:0;border-radius:0;border-bottom:1px solid var(--border-default)}
.accordion.flat summary{padding:14px 4px}
.accordion.flat .body{padding:0 4px 16px}
.accordion.flat:last-child{border-bottom:0}
.accordion-group{display:flex;flex-direction:column;gap:8px;width:100%}
.accordion-group.flat{gap:0;background:var(--bg-surface);padding:0 20px;border-radius:var(--radius-lg)}
.accordion::details-content{height:0;overflow:hidden;transition:height var(--motion-base),content-visibility var(--motion-base);transition-behavior:allow-discrete}
.accordion[open]::details-content{height:auto}import { Accordion } from "@jiran/ds-react";
export default function Example() {
return (
<Accordion title="고급 탐지 옵션" extra="3개 설정" defaultOpen style={{ maxWidth: 560 }}>
휴리스틱 분석, 메모리 스캔, 스크립트 차단을 개별로 켜고 끕니다. 기본값은 모두 켬입니다.
</Accordion>
);
}2. Group
여러 항목. 하나만 펼치려면 같은 name 을 줍니다(브라우저 지원 시).
정책은 언제 적용되나요?
차단된 파일은 어디에 보관되나요?
예외 처리는 어떻게 하나요?
<div class="accordion-group" style="max-width:560px">
<details class="accordion" name="faq" open><summary>정책은 언제 적용되나요?</summary><div class="body">배포 버튼을 누른 뒤 에이전트가 다음 체크인(최대 5분)에서 받아 적용합니다.</div></details>
<details class="accordion" name="faq"><summary>차단된 파일은 어디에 보관되나요?</summary><div class="body">격리 저장소에 30일 보관 후 자동 삭제됩니다.</div></details>
<details class="accordion" name="faq"><summary>예외 처리는 어떻게 하나요?</summary><div class="body">정책 › 예외 목록에서 해시 또는 경로를 등록합니다.</div></details>
</div>/* JS Design System v0.6 · .accordion, .accordion-group · 토큰(var(--*))은 assets/style.css :root 참고 */
.accordion{border:1px solid var(--border-default);border-radius:var(--radius-lg);background:var(--bg-surface);width:100%;interpolate-size:allow-keywords}
.accordion summary{list-style:none;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 20px;font-weight:600;font-size:16px;line-height:24px;cursor:pointer}
.accordion summary::-webkit-details-marker{display:none}
.accordion summary::after{content:"";width:16px;height:16px;flex:none;background:var(--text-secondary);-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22 %3E%3Cpath d=%22M6 9l6 6l6 -6%22 /%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22 %3E%3Cpath d=%22M6 9l6 6l6 -6%22 /%3E%3C/svg%3E") center/contain no-repeat;transition:transform var(--motion-fast)}
.accordion[open] summary::after{transform:rotate(180deg)}
.accordion summary small{font-weight:400;color:var(--text-tertiary);margin-left:auto;font-size:12px}
.accordion summary>svg{flex:none}
.accordion .body{padding:0 20px 16px;font-size:14px;color:var(--text-secondary);line-height:1.6}
.accordion.disabled{opacity:.5;pointer-events:none}
.accordion.flat{border:0;border-radius:0;border-bottom:1px solid var(--border-default)}
.accordion.flat summary{padding:14px 4px}
.accordion.flat .body{padding:0 4px 16px}
.accordion.flat:last-child{border-bottom:0}
.accordion-group{display:flex;flex-direction:column;gap:8px;width:100%}
.accordion-group.flat{gap:0;background:var(--bg-surface);padding:0 20px;border-radius:var(--radius-lg)}
.accordion::details-content{height:0;overflow:hidden;transition:height var(--motion-base),content-visibility var(--motion-base);transition-behavior:allow-discrete}
.accordion[open]::details-content{height:auto}import { Accordion, AccordionGroup } from "@jiran/ds-react";
export default function Example() {
return (
<AccordionGroup style={{ maxWidth: 560 }}>
<Accordion name="faq" title="정책은 언제 적용되나요?" defaultOpen>배포 버튼을 누른 뒤 에이전트가 다음 체크인(최대 5분)에서 받아 적용합니다.</Accordion>
<Accordion name="faq" title="차단된 파일은 어디에 보관되나요?">격리 저장소에 30일 보관 후 자동 삭제됩니다.</Accordion>
<Accordion name="faq" title="예외 처리는 어떻게 하나요?">정책 › 예외 목록에서 해시 또는 경로를 등록합니다.</Accordion>
</AccordionGroup>
);
}3. Flat / Disabled
카드 안에서는 보더 없는 .flat — 배경·여백·radius 는 .accordion-group.flat 이 그립니다. 권한이 없으면 .disabled.
일반
스케줄
감사 로그 권한 필요
<div class="accordion-group flat" style="max-width:560px">
<details class="accordion flat" open><summary>일반</summary><div class="body">정책 이름, 설명, 적용 그룹</div></details>
<details class="accordion flat"><summary>스케줄</summary><div class="body">적용 요일과 시간대</div></details>
<details class="accordion flat disabled"><summary tabindex="-1" aria-disabled="true">감사 로그 <small>권한 필요</small></summary><div class="body">관리자 권한이 있어야 열람할 수 있습니다.</div></details>
</div>/* JS Design System v0.6 · .accordion, .accordion-group · 토큰(var(--*))은 assets/style.css :root 참고 */
.accordion{border:1px solid var(--border-default);border-radius:var(--radius-lg);background:var(--bg-surface);width:100%;interpolate-size:allow-keywords}
.accordion summary{list-style:none;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 20px;font-weight:600;font-size:16px;line-height:24px;cursor:pointer}
.accordion summary::-webkit-details-marker{display:none}
.accordion summary::after{content:"";width:16px;height:16px;flex:none;background:var(--text-secondary);-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22 %3E%3Cpath d=%22M6 9l6 6l6 -6%22 /%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22 %3E%3Cpath d=%22M6 9l6 6l6 -6%22 /%3E%3C/svg%3E") center/contain no-repeat;transition:transform var(--motion-fast)}
.accordion[open] summary::after{transform:rotate(180deg)}
.accordion summary small{font-weight:400;color:var(--text-tertiary);margin-left:auto;font-size:12px}
.accordion summary>svg{flex:none}
.accordion .body{padding:0 20px 16px;font-size:14px;color:var(--text-secondary);line-height:1.6}
.accordion.disabled{opacity:.5;pointer-events:none}
.accordion.flat{border:0;border-radius:0;border-bottom:1px solid var(--border-default)}
.accordion.flat summary{padding:14px 4px}
.accordion.flat .body{padding:0 4px 16px}
.accordion.flat:last-child{border-bottom:0}
.accordion-group{display:flex;flex-direction:column;gap:8px;width:100%}
.accordion-group.flat{gap:0;background:var(--bg-surface);padding:0 20px;border-radius:var(--radius-lg)}
.accordion::details-content{height:0;overflow:hidden;transition:height var(--motion-base),content-visibility var(--motion-base);transition-behavior:allow-discrete}
.accordion[open]::details-content{height:auto}import { Accordion, AccordionGroup } from "@jiran/ds-react";
export default function Example() {
return (
<AccordionGroup flat style={{ maxWidth: 560 }}>
<Accordion flat title="일반" defaultOpen>정책 이름, 설명, 적용 그룹</Accordion>
<Accordion flat title="스케줄">적용 요일과 시간대</Accordion>
<Accordion flat disabled title="감사 로그" extra="권한 필요">관리자 권한이 있어야 열람할 수 있습니다.</Accordion>
</AccordionGroup>
);
}Props
HTML 에서는 클래스와 속성으로, React(@jiran/ds-react)에서는 같은 이름의 prop(variant · size …)으로 변형·상태를 지정합니다.
| Prop / Class | Type · Values | Default | Description |
|---|---|---|---|
| details.accordion | container | — | summary + .body. 제목은 React title |
| open | attribute | — | 펼침 상태. React 는 open(제어) 또는 defaultOpen + onOpenChange |
| name | attribute | — | 같은 name 은 하나만 펼침(브라우저 지원 시) |
| .flat | boolean | false | 보더 없는 구분선형. 마지막 항목은 아래 선을 지웁니다 |
| .disabled | boolean | false | 펼칠 수 없음 — summary 에 tabindex="-1" 과 aria-disabled="true" 를 함께 둡니다(React disabled 가 둘 다 넣습니다) |
| summary small | element | — | 우측 보조 정보(React extra). 제목 앞 아이콘은 summary > svg 로 두면 16~18 로 고정됩니다 |
| .body | element | — | 본문. 높이를 제한하려면 max-height + overflow(React bodyStyle) |
| .accordion-group | container | — | 여러 항목 묶음(8px 간격). React AccordionGroup |
| .accordion-group.flat | boolean | false | 카드형 묶음 — 간격 0 에 Surface 배경 · 좌우 20 · radius/lg. 항목에는 .flat 을 함께 |
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 { Accordion, AccordionGroup } 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 .accordion, .accordion-group
/* JS Design System v0.6 · .accordion, .accordion-group · 토큰(var(--*))은 assets/style.css :root 참고 */
.accordion{border:1px solid var(--border-default);border-radius:var(--radius-lg);background:var(--bg-surface);width:100%;interpolate-size:allow-keywords}
.accordion summary{list-style:none;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 20px;font-weight:600;font-size:16px;line-height:24px;cursor:pointer}
.accordion summary::-webkit-details-marker{display:none}
.accordion summary::after{content:"";width:16px;height:16px;flex:none;background:var(--text-secondary);-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22 %3E%3Cpath d=%22M6 9l6 6l6 -6%22 /%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22 %3E%3Cpath d=%22M6 9l6 6l6 -6%22 /%3E%3C/svg%3E") center/contain no-repeat;transition:transform var(--motion-fast)}
.accordion[open] summary::after{transform:rotate(180deg)}
.accordion summary small{font-weight:400;color:var(--text-tertiary);margin-left:auto;font-size:12px}
.accordion summary>svg{flex:none}
.accordion .body{padding:0 20px 16px;font-size:14px;color:var(--text-secondary);line-height:1.6}
.accordion.disabled{opacity:.5;pointer-events:none}
.accordion.flat{border:0;border-radius:0;border-bottom:1px solid var(--border-default)}
.accordion.flat summary{padding:14px 4px}
.accordion.flat .body{padding:0 4px 16px}
.accordion.flat:last-child{border-bottom:0}
.accordion-group{display:flex;flex-direction:column;gap:8px;width:100%}
.accordion-group.flat{gap:0;background:var(--bg-surface);padding:0 20px;border-radius:var(--radius-lg)}
.accordion::details-content{height:0;overflow:hidden;transition:height var(--motion-base),content-visibility var(--motion-base);transition-behavior:allow-discrete}
.accordion[open]::details-content{height:auto}