List
동질한 항목을 세로로 나열합니다. 열이 2~3개 이하이고 항목마다 제목·부제·액션이 있으면 표 대신 List.
1. Default
제목(.primary) + 부제(.meta) + 우측 영역(.end).
- 외부 USB 차단사용
- 스크립트 실행 제한사용
- 원격 접속 허용 목록중지
HTML
<ul class="list" style="max-width:560px">
<li><div><span class="primary">외부 USB 차단</span><span class="meta">전체 · 1,284대 · 2026-09-06</span></div><span class="end"><span class="tag ok">사용</span></span></li>
<li><div><span class="primary">스크립트 실행 제한</span><span class="meta">개발팀 제외 · 1,102대</span></div><span class="end"><span class="tag ok">사용</span></span></li>
<li><div><span class="primary">원격 접속 허용 목록</span><span class="meta">인프라팀 · 48대</span></div><span class="end"><span class="tag">중지</span></span></li>
</ul>CSS · .list, .avatar, .tag, .indicator
/* JS Design System v0.6 · .list, .avatar, .tag, .indicator · 토큰(var(--*))은 assets/style.css :root 참고 */
.tag{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;padding:3px 10px;border-radius:var(--radius-full);line-height:1.6;background:var(--bg-panel);color:var(--text-secondary);white-space:nowrap}
.tag::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}
.tag.critical{background:var(--sev-critical-bg);color:var(--sev-critical-text)}
.tag.high{background:var(--sev-high-bg);color:var(--sev-high-text)}
.tag.medium{background:var(--sev-medium-bg);color:var(--sev-medium-text)}
.tag.low{background:var(--sev-low-bg);color:var(--sev-low-text)}
.tag.info{background:var(--sev-info-bg);color:var(--sev-info-text)}
.tag.ok{background:var(--success-subtle);color:var(--success-text)}
.tag.accent{background:var(--accent-subtle);color:var(--accent-text)}
.tag.sm{font-size:12px;padding:1px 8px}
.tag.lg{font-size:12px;padding:5px 12px}
.tag.plain::before{display:none}
.tag.outline{background:transparent;box-shadow:inset 0 0 0 1px currentColor}
.tag.solid.critical{background:var(--sev-critical);color:var(--sev-critical-on)}
.tag.solid.high{background:var(--sev-high);color:var(--sev-high-on)}
.tag.solid.medium{background:var(--sev-medium);color:var(--sev-medium-on)}
.tag.solid.low{background:var(--sev-low);color:var(--sev-low-on)}
.tag.solid.info{background:var(--sev-info);color:var(--sev-info-on)}
.tag.solid.ok{background:var(--success);color:var(--success-on)}
.tag.solid.accent{background:var(--accent);color:var(--accent-on)}
.avatar{width:32px;height:32px;border-radius:50%;background:var(--accent-subtle);color:var(--accent-text);display:inline-grid;place-items:center;font-size:12px;font-weight:700;flex:none}
.avatar.sm{width:24px;height:24px;font-size:10px}
.indicator{display:inline-flex;align-items:center;gap:8px;font-size:14px}
.indicator::before{content:"";width:8px;height:8px;border-radius:50%;background:var(--interaction-inactive);flex:none}
.indicator.ok::before{background:var(--success)}
.indicator.warn::before{background:var(--warning)}
.indicator.danger::before{background:var(--danger)}
.indicator.info::before{background:var(--info)}
.indicator.accent::before{background:var(--accent-graphic)}
.indicator.pulse::before{box-shadow:0 0 0 0 var(--danger);animation:pulse 1.6s infinite}
.list{list-style:none;margin:0;padding:0;width:100%;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-lg);overflow:hidden;max-width:none}
.list li{display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid var(--border-default);font-size:14px;margin:0}
.list li:last-child{border-bottom:0}
.list.hover li:hover{background:var(--bg-subtle);cursor:pointer}
.list li.on{background:var(--accent-subtle)}
.list .primary{font-weight:500;display:block}
.list .meta{display:block;font-size:12px;color:var(--text-tertiary)}
.list .end{margin-left:auto;display:flex;gap:8px;align-items:center;flex:none}
.list.plain{border:0;background:transparent;border-radius:0}
.list.plain li{padding:10px 0}
.list.dense li{padding:8px 16px;font-size:14px}
.tile .tag{position:absolute;top:12px;right:12px}
.status-item .indicator{font-weight:600}
@keyframes pulse{70%{box-shadow:0 0 0 8px transparent}}React · @jiran/ds-react
import { List, ListItem, Tag } from "@jiran/ds-react";
export default function Example() {
return (
<List style={{ maxWidth: 560 }}>
<ListItem primary="외부 USB 차단" meta="전체 · 1,284대 · 2026-09-06" end={<Tag tone="ok">사용</Tag>} />
<ListItem primary="스크립트 실행 제한" meta="개발팀 제외 · 1,102대" end={<Tag tone="ok">사용</Tag>} />
<ListItem primary="원격 접속 허용 목록" meta="인프라팀 · 48대" end={<Tag>중지</Tag>} />
</List>
);
}2. With Avatar / Actions
선행 아이콘·아바타와 후행 액션 버튼.
- 김김민준
- 이이서연
HTML
<ul class="list" style="max-width:560px">
<li><span class="avatar">김</span><div><span class="primary">김민준</span><span class="meta">보안팀 · 관리자</span></div><span class="end"><button type="button" class="btn sm tertiary icon" aria-label="편집"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 20h4l10.5 -10.5a2.828 2.828 0 1 0 -4 -4l-10.5 10.5v4"/><path d="M13.5 6.5l4 4"/></svg></button><button type="button" class="btn sm tertiary icon" aria-label="더 보기"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/><path d="M11 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/><path d="M18 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/></svg></button></span></li>
<li><span class="avatar">이</span><div><span class="primary">이서연</span><span class="meta">인프라팀 · 운영자</span></div><span class="end"><button type="button" class="btn sm tertiary icon" aria-label="편집"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 20h4l10.5 -10.5a2.828 2.828 0 1 0 -4 -4l-10.5 10.5v4"/><path d="M13.5 6.5l4 4"/></svg></button><button type="button" class="btn sm tertiary icon" aria-label="더 보기"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/><path d="M11 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/><path d="M18 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/></svg></button></span></li>
</ul>CSS · .list, .avatar, .tag, .indicator
/* JS Design System v0.6 · .list, .avatar, .tag, .indicator · 토큰(var(--*))은 assets/style.css :root 참고 */
.tag{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;padding:3px 10px;border-radius:var(--radius-full);line-height:1.6;background:var(--bg-panel);color:var(--text-secondary);white-space:nowrap}
.tag::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}
.tag.critical{background:var(--sev-critical-bg);color:var(--sev-critical-text)}
.tag.high{background:var(--sev-high-bg);color:var(--sev-high-text)}
.tag.medium{background:var(--sev-medium-bg);color:var(--sev-medium-text)}
.tag.low{background:var(--sev-low-bg);color:var(--sev-low-text)}
.tag.info{background:var(--sev-info-bg);color:var(--sev-info-text)}
.tag.ok{background:var(--success-subtle);color:var(--success-text)}
.tag.accent{background:var(--accent-subtle);color:var(--accent-text)}
.tag.sm{font-size:12px;padding:1px 8px}
.tag.lg{font-size:12px;padding:5px 12px}
.tag.plain::before{display:none}
.tag.outline{background:transparent;box-shadow:inset 0 0 0 1px currentColor}
.tag.solid.critical{background:var(--sev-critical);color:var(--sev-critical-on)}
.tag.solid.high{background:var(--sev-high);color:var(--sev-high-on)}
.tag.solid.medium{background:var(--sev-medium);color:var(--sev-medium-on)}
.tag.solid.low{background:var(--sev-low);color:var(--sev-low-on)}
.tag.solid.info{background:var(--sev-info);color:var(--sev-info-on)}
.tag.solid.ok{background:var(--success);color:var(--success-on)}
.tag.solid.accent{background:var(--accent);color:var(--accent-on)}
.avatar{width:32px;height:32px;border-radius:50%;background:var(--accent-subtle);color:var(--accent-text);display:inline-grid;place-items:center;font-size:12px;font-weight:700;flex:none}
.avatar.sm{width:24px;height:24px;font-size:10px}
.indicator{display:inline-flex;align-items:center;gap:8px;font-size:14px}
.indicator::before{content:"";width:8px;height:8px;border-radius:50%;background:var(--interaction-inactive);flex:none}
.indicator.ok::before{background:var(--success)}
.indicator.warn::before{background:var(--warning)}
.indicator.danger::before{background:var(--danger)}
.indicator.info::before{background:var(--info)}
.indicator.accent::before{background:var(--accent-graphic)}
.indicator.pulse::before{box-shadow:0 0 0 0 var(--danger);animation:pulse 1.6s infinite}
.list{list-style:none;margin:0;padding:0;width:100%;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-lg);overflow:hidden;max-width:none}
.list li{display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid var(--border-default);font-size:14px;margin:0}
.list li:last-child{border-bottom:0}
.list.hover li:hover{background:var(--bg-subtle);cursor:pointer}
.list li.on{background:var(--accent-subtle)}
.list .primary{font-weight:500;display:block}
.list .meta{display:block;font-size:12px;color:var(--text-tertiary)}
.list .end{margin-left:auto;display:flex;gap:8px;align-items:center;flex:none}
.list.plain{border:0;background:transparent;border-radius:0}
.list.plain li{padding:10px 0}
.list.dense li{padding:8px 16px;font-size:14px}
.tile .tag{position:absolute;top:12px;right:12px}
.status-item .indicator{font-weight:600}
@keyframes pulse{70%{box-shadow:0 0 0 8px transparent}}React · @jiran/ds-react
import { Avatar, Button, Icon, List, ListItem } from "@jiran/ds-react";
const actions = (
<>
<Button size="sm" variant="tertiary" icon aria-label="편집"><Icon name="pencil" /></Button>
<Button size="sm" variant="tertiary" icon aria-label="더 보기"><Icon name="dots" /></Button>
</>
);
export default function Example() {
return (
<List style={{ maxWidth: 560 }}>
<ListItem leading={<Avatar initials="김" />} primary="김민준" meta="보안팀 · 관리자" end={actions} />
<ListItem leading={<Avatar initials="이" />} primary="이서연" meta="인프라팀 · 운영자" end={actions} />
</List>
);
}3. Selectable / Dense
행 클릭 선택(.hover + .on). 밀도 높은 목록은 .dense.
- PC-2041
- PC-1187
- PC-0932
HTML
<ul class="list hover dense" style="max-width:480px" role="listbox">
<li role="option"><span class="indicator ok">PC-2041</span><span class="end meta">10.0.4.21</span></li>
<li role="option" class="on" aria-selected="true"><span class="indicator danger">PC-1187</span><span class="end meta">10.0.4.33</span></li>
<li role="option"><span class="indicator">PC-0932</span><span class="end meta">오프라인</span></li>
</ul>CSS · .list, .avatar, .tag, .indicator
/* JS Design System v0.6 · .list, .avatar, .tag, .indicator · 토큰(var(--*))은 assets/style.css :root 참고 */
.tag{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;padding:3px 10px;border-radius:var(--radius-full);line-height:1.6;background:var(--bg-panel);color:var(--text-secondary);white-space:nowrap}
.tag::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}
.tag.critical{background:var(--sev-critical-bg);color:var(--sev-critical-text)}
.tag.high{background:var(--sev-high-bg);color:var(--sev-high-text)}
.tag.medium{background:var(--sev-medium-bg);color:var(--sev-medium-text)}
.tag.low{background:var(--sev-low-bg);color:var(--sev-low-text)}
.tag.info{background:var(--sev-info-bg);color:var(--sev-info-text)}
.tag.ok{background:var(--success-subtle);color:var(--success-text)}
.tag.accent{background:var(--accent-subtle);color:var(--accent-text)}
.tag.sm{font-size:12px;padding:1px 8px}
.tag.lg{font-size:12px;padding:5px 12px}
.tag.plain::before{display:none}
.tag.outline{background:transparent;box-shadow:inset 0 0 0 1px currentColor}
.tag.solid.critical{background:var(--sev-critical);color:var(--sev-critical-on)}
.tag.solid.high{background:var(--sev-high);color:var(--sev-high-on)}
.tag.solid.medium{background:var(--sev-medium);color:var(--sev-medium-on)}
.tag.solid.low{background:var(--sev-low);color:var(--sev-low-on)}
.tag.solid.info{background:var(--sev-info);color:var(--sev-info-on)}
.tag.solid.ok{background:var(--success);color:var(--success-on)}
.tag.solid.accent{background:var(--accent);color:var(--accent-on)}
.avatar{width:32px;height:32px;border-radius:50%;background:var(--accent-subtle);color:var(--accent-text);display:inline-grid;place-items:center;font-size:12px;font-weight:700;flex:none}
.avatar.sm{width:24px;height:24px;font-size:10px}
.indicator{display:inline-flex;align-items:center;gap:8px;font-size:14px}
.indicator::before{content:"";width:8px;height:8px;border-radius:50%;background:var(--interaction-inactive);flex:none}
.indicator.ok::before{background:var(--success)}
.indicator.warn::before{background:var(--warning)}
.indicator.danger::before{background:var(--danger)}
.indicator.info::before{background:var(--info)}
.indicator.accent::before{background:var(--accent-graphic)}
.indicator.pulse::before{box-shadow:0 0 0 0 var(--danger);animation:pulse 1.6s infinite}
.list{list-style:none;margin:0;padding:0;width:100%;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-lg);overflow:hidden;max-width:none}
.list li{display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid var(--border-default);font-size:14px;margin:0}
.list li:last-child{border-bottom:0}
.list.hover li:hover{background:var(--bg-subtle);cursor:pointer}
.list li.on{background:var(--accent-subtle)}
.list .primary{font-weight:500;display:block}
.list .meta{display:block;font-size:12px;color:var(--text-tertiary)}
.list .end{margin-left:auto;display:flex;gap:8px;align-items:center;flex:none}
.list.plain{border:0;background:transparent;border-radius:0}
.list.plain li{padding:10px 0}
.list.dense li{padding:8px 16px;font-size:14px}
.tile .tag{position:absolute;top:12px;right:12px}
.status-item .indicator{font-weight:600}
@keyframes pulse{70%{box-shadow:0 0 0 8px transparent}}React · @jiran/ds-react
import { useState } from "react";
import { Indicator, List, ListItem } from "@jiran/ds-react";
const HOSTS = [
{ id: "PC-2041", ip: "10.0.4.21", tone: "ok" as const },
{ id: "PC-1187", ip: "10.0.4.33", tone: "danger" as const },
{ id: "PC-0932", ip: "오프라인", tone: undefined },
];
export default function Example() {
const [sel, setSel] = useState("PC-1187");
return (
<List hover dense role="listbox" style={{ maxWidth: 480 }}>
{HOSTS.map((h) => (
<ListItem key={h.id} role="option" selected={sel === h.id} onClick={() => setSel(h.id)} end={h.ip} endMeta>
<Indicator tone={h.tone}>{h.id}</Indicator>
</ListItem>
))}
</List>
);
}Props
HTML 에서는 클래스와 속성으로, React(@jiran/ds-react)에서는 같은 이름의 prop(variant · size …)으로 변형·상태를 지정합니다.
| Prop / Class | Type · Values | Default | Description |
|---|---|---|---|
| .list | ul | — | 보더 + radius/lg. li 는 flex row |
| .primary / .meta / .end | elements | — | 제목 / 부제 / 우측 영역 |
| .hover / li.on | boolean | false | 행 hover 배경 / 선택 |
| .dense / .plain | boolean | false | 행 40 / 보더 없음 |
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 { List, ListItem } 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 .list, .avatar, .tag, .indicator
/* JS Design System v0.6 · .list, .avatar, .tag, .indicator · 토큰(var(--*))은 assets/style.css :root 참고 */
.tag{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;padding:3px 10px;border-radius:var(--radius-full);line-height:1.6;background:var(--bg-panel);color:var(--text-secondary);white-space:nowrap}
.tag::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}
.tag.critical{background:var(--sev-critical-bg);color:var(--sev-critical-text)}
.tag.high{background:var(--sev-high-bg);color:var(--sev-high-text)}
.tag.medium{background:var(--sev-medium-bg);color:var(--sev-medium-text)}
.tag.low{background:var(--sev-low-bg);color:var(--sev-low-text)}
.tag.info{background:var(--sev-info-bg);color:var(--sev-info-text)}
.tag.ok{background:var(--success-subtle);color:var(--success-text)}
.tag.accent{background:var(--accent-subtle);color:var(--accent-text)}
.tag.sm{font-size:12px;padding:1px 8px}
.tag.lg{font-size:12px;padding:5px 12px}
.tag.plain::before{display:none}
.tag.outline{background:transparent;box-shadow:inset 0 0 0 1px currentColor}
.tag.solid.critical{background:var(--sev-critical);color:var(--sev-critical-on)}
.tag.solid.high{background:var(--sev-high);color:var(--sev-high-on)}
.tag.solid.medium{background:var(--sev-medium);color:var(--sev-medium-on)}
.tag.solid.low{background:var(--sev-low);color:var(--sev-low-on)}
.tag.solid.info{background:var(--sev-info);color:var(--sev-info-on)}
.tag.solid.ok{background:var(--success);color:var(--success-on)}
.tag.solid.accent{background:var(--accent);color:var(--accent-on)}
.avatar{width:32px;height:32px;border-radius:50%;background:var(--accent-subtle);color:var(--accent-text);display:inline-grid;place-items:center;font-size:12px;font-weight:700;flex:none}
.avatar.sm{width:24px;height:24px;font-size:10px}
.indicator{display:inline-flex;align-items:center;gap:8px;font-size:14px}
.indicator::before{content:"";width:8px;height:8px;border-radius:50%;background:var(--interaction-inactive);flex:none}
.indicator.ok::before{background:var(--success)}
.indicator.warn::before{background:var(--warning)}
.indicator.danger::before{background:var(--danger)}
.indicator.info::before{background:var(--info)}
.indicator.accent::before{background:var(--accent-graphic)}
.indicator.pulse::before{box-shadow:0 0 0 0 var(--danger);animation:pulse 1.6s infinite}
.list{list-style:none;margin:0;padding:0;width:100%;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-lg);overflow:hidden;max-width:none}
.list li{display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid var(--border-default);font-size:14px;margin:0}
.list li:last-child{border-bottom:0}
.list.hover li:hover{background:var(--bg-subtle);cursor:pointer}
.list li.on{background:var(--accent-subtle)}
.list .primary{font-weight:500;display:block}
.list .meta{display:block;font-size:12px;color:var(--text-tertiary)}
.list .end{margin-left:auto;display:flex;gap:8px;align-items:center;flex:none}
.list.plain{border:0;background:transparent;border-radius:0}
.list.plain li{padding:10px 0}
.list.dense li{padding:8px 16px;font-size:14px}
.tile .tag{position:absolute;top:12px;right:12px}
.status-item .indicator{font-weight:600}
@keyframes pulse{70%{box-shadow:0 0 0 8px transparent}}