@conscience-technology/ctds 0.3.4 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/components/Button/Button.module.css +2 -2
- package/assets/components/EventListItem/EventListItem.module.css +220 -0
- package/assets/components/EventListItem/EventListItem.tsx +205 -0
- package/assets/components/EventListItem/index.ts +8 -0
- package/assets/components/Icon/Icon.tsx +80 -41
- package/assets/components/ListItem/ListItem.module.css +91 -211
- package/assets/components/ListItem/ListItem.tsx +130 -154
- package/assets/components/ListItem/index.ts +2 -2
- package/assets/components/MetricCard/MetricCard.module.css +5 -5
- package/assets/components/Sidebar/Sidebar.module.css +81 -4
- package/assets/components/Sidebar/Sidebar.tsx +11 -2
- package/assets/components/SplitButton/SplitButton.module.css +3 -3
- package/assets/components/Stepper/Stepper.module.css +196 -52
- package/assets/components/Stepper/Stepper.tsx +68 -13
- package/assets/components/Switch/Switch.module.css +1 -9
- package/assets/components/Topbar/Topbar.module.css +33 -5
- package/assets/components/Topbar/Topbar.tsx +62 -7
- package/assets/components/_data.ts +11 -4
- package/assets/components/index.ts +2 -0
- package/assets/components/tokens.css +23 -10
- package/assets/prompts/ASSEMBLY.md +219 -0
- package/assets/prompts/PRINCIPLES.md +70 -0
- package/assets/prompts/axes/icon.md +7 -1
- package/assets/prompts/axes/palette.md +11 -0
- package/assets/prompts/components/_variants.md +5 -1
- package/assets/prompts/components/callout.md +3 -3
- package/assets/prompts/components/event-list-item.md +103 -0
- package/assets/prompts/components/generated-image.md +80 -0
- package/assets/prompts/components/list-item.md +182 -204
- package/assets/prompts/components/metric-card.md +2 -2
- package/assets/prompts/components/toast.md +1 -0
- package/assets/prompts/components/topbar.md +77 -59
- package/assets/prompts/themes/focus.md +72 -0
- package/assets/skills/ctds/SKILL.md +1 -0
- package/assets/skills/ctds-page/SKILL.md +12 -2
- package/assets/skills/ctds-wireframe/SKILL.md +35 -1
- package/package.json +2 -4
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# EventListItem
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
리스트에 쌓이는 **하나의 일정** 아이템. 좌측 날짜 블록(큰 숫자 + 요일) · 세로 hairline · 우측 제목 + status Badge + meta 라인의 고정 anatomy. 캘린더 셀 안 색 바는 [[components/event-chip]]의 몫이고, 이 컴포넌트는 "다가오는 일정 리스트" "결재/행사 리스트"처럼 시간을 축으로 수직 나열되는 자리의 1 레코드.
|
|
5
|
+
|
|
6
|
+
날짜 숫자는 2자리 기준으로 트랙이 고정되어 리스트가 세로로 쌓여도 제목 시작점이 흔들리지 않는다. status는 enum 4종(upcoming/pending/completed/cancelled)으로 받아 [[components/badge]] soft + 상태색으로 자동 매핑 — 호출부가 Badge를 직접 조립하지 않는다.
|
|
7
|
+
|
|
8
|
+
### Do
|
|
9
|
+
- 한 아이템엔 **한 일정**. `date` 하나 + `title` 하나 + 선택적 `status` + `meta[]`.
|
|
10
|
+
- status는 enum 4종으로 넘긴다 — 자유 색/텍스트 Badge 조립 금지. 라벨만 override 필요하면 `statusLabel`.
|
|
11
|
+
- 메타 라인 아이템 사이는 **짧은 세로 hairline**으로 분리 (bullet·중점·|·하이픈 금지). `showMetaDivider={false}`로 끌 수 있지만 기본은 유지.
|
|
12
|
+
- 행 전체를 라우팅으로 쓰려면 `href` — hover bg + focus ring 자동.
|
|
13
|
+
- 리스트로 쌓을 때 세로 gap은 `--space-2` ~ `--space-3`. 아이템 자체가 chrome(border+radius+padding)을 들고 있어 부모는 수직 flex만. 단, `variant="line"`은 하단 hairline으로 분리되므로 부모 gap 없이 바로 쌓는다.
|
|
14
|
+
- chrome 축은 `variant`로 바꾼다 — `card`(기본, 떠있는 카드) · `line`(radius 없이 하단 hairline만) · `fill`(기본은 중립 soft 회색, status 지정 시 색상 톤).
|
|
15
|
+
|
|
16
|
+
### Don't
|
|
17
|
+
- [HARD] 안에 다시 Card chrome(추가 border/shadow) 쌓지 않는다 — "리스트 안 카드 안 카드"가 되어 depth가 깨진다.
|
|
18
|
+
- [HARD] status를 자유 색으로 덮어쓰지 않는다. "좋음=green" 같은 가치판단 반전 금지. 색은 **상태 enum**이 결정.
|
|
19
|
+
- [HARD] 날짜 요일 라벨을 UPPERCASE로 강제하지 않는다 — 소문자 유지("fri", "sun"). [[feedback_no_uppercase]].
|
|
20
|
+
- [HARD] 메타 사이 bullet/중점/`|`/em-dash 삽입 금지. 짧은 세로 hairline만 (또는 `showMetaDivider={false}`로 완전 제거).
|
|
21
|
+
- 날짜 숫자 외의 자리에 큰 숫자(가격·카운트) 추가 금지 — 좌측 트랙은 "날짜" 자리 하나.
|
|
22
|
+
- 2줄 이상 서술/본문을 title에 넣지 않는다 — 1줄 ellipsis. 본문이 길면 상세 뷰로.
|
|
23
|
+
- 날짜 블록을 Avatar/Icon으로 대체하지 않는다 — 그러면 이건 [[components/list-item]].
|
|
24
|
+
|
|
25
|
+
### Fallback
|
|
26
|
+
`status` 애매하면 생략(Badge 안 뜸, fill variant면 중립 회색). `statusLabel`은 enum 기본 라벨(Upcoming/Pending/Completed/Cancelled) 사용. 라우팅 자리 아니면 `href` 생략.
|
|
27
|
+
|
|
28
|
+
### 관련
|
|
29
|
+
- [[components/event-chip]] — 혼동 금지. EventChip은 캘린더 셀 안 색 바, EventListItem은 리스트 아이템.
|
|
30
|
+
- [[components/list-item]] — 날짜 자리에 Avatar/Icon이 오는 "누가 뭘 했다" 피드는 ListItem.
|
|
31
|
+
- [[components/metric-card]] — 같은 Card chrome 규칙(그림자 금지, line 기본).
|
|
32
|
+
- [[components/badge]] — status 매핑은 Badge soft + status.
|
|
33
|
+
- [[components/icon]] — meta 아이콘.
|
|
34
|
+
- [[tokens/color]] — `--bg` · `--bg-subtle` · `--border` · `--fg-strong` · `--fg-muted` · `--hover` · `--info-subtle` · `--warning-subtle` · `--success-subtle` · `--destructive-subtle`.
|
|
35
|
+
- [[axes/density]] — compact에선 세로 padding 줄어 리스트가 조밀해짐.
|
|
36
|
+
- [[feedback_no_bullet_separator]] — meta 사이 분리 규칙.
|
|
37
|
+
- [[feedback_no_uppercase]] — 요일 소문자 유지.
|
|
38
|
+
- [[feedback_font_weight_cap]] — title은 sm이라 medium, day는 2xl이라 semibold.
|
|
39
|
+
|
|
40
|
+
### Props 시그니처
|
|
41
|
+
```tsx
|
|
42
|
+
<EventListItem
|
|
43
|
+
date={Date} // required — 날짜 블록으로 자동 포맷
|
|
44
|
+
title={ReactNode} // required — 1줄 ellipsis
|
|
45
|
+
variant="card | line | fill" // 기본 "card". chrome 스타일 축
|
|
46
|
+
size="md | sm" // 기본 "md". padding/날짜/제목/meta가 한 단계씩
|
|
47
|
+
status="upcoming | pending | completed | cancelled" // Badge soft + 상태색 자동 (fill에선 전면 색)
|
|
48
|
+
statusLabel={ReactNode} // 기본 라벨 override (ex: 한국어 "예정")
|
|
49
|
+
meta={{ icon?: IconName; label: ReactNode }[]} // 메타 라인. 사이는 세로 hairline으로 자동 분리
|
|
50
|
+
showDateDivider={boolean} // 기본 true. 날짜 블록 오른쪽 세로 hairline
|
|
51
|
+
showMetaDivider={boolean} // 기본 true. meta 사이 세로 hairline
|
|
52
|
+
href={string} // 있으면 <a> + hover/focus ring 활성
|
|
53
|
+
/>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Variant 매트릭스
|
|
57
|
+
|
|
58
|
+
| variant | chrome | 쓰임 |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| `card` (기본) | 1px border + radius-lg + bg | 단독 떠있는 아이템. 다른 섹션과 섞이는 리스트. |
|
|
61
|
+
| `line` | radius 없음, 하단 hairline만 | 리스트가 가로 폭 꽉 채워 흐르는 자리. 아이템이 쭉 쌓임. |
|
|
62
|
+
| `fill` | **기본 중립 soft 회색**. status 지정 시 info/warning/success/destructive 톤 | 상태가 아이템 전체의 시각 신호로 중요한 자리. |
|
|
63
|
+
|
|
64
|
+
size (variant와 독립 축, 기본 `md`)
|
|
65
|
+
|
|
66
|
+
| size | padding | day | weekday | title | meta | 쓰임 |
|
|
67
|
+
|---|---|---|---|---|---|---|
|
|
68
|
+
| `md` (기본) | space-4 / space-5 | text-2xl | text-2xs | text-sm | text-xs | 대시보드·상세 뷰 등 일반 자리 |
|
|
69
|
+
| `sm` | space-3 / space-4 | text-xl | text-3xs | text-xs | text-2xs | 사이드바·밀집 리스트 등 세로 공간 아끼는 자리 |
|
|
70
|
+
|
|
71
|
+
status enum (variant와 독립 축)
|
|
72
|
+
|
|
73
|
+
| status | Badge status | 기본 라벨 | 쓰임 |
|
|
74
|
+
|---|---|---|---|
|
|
75
|
+
| `upcoming` | `info` | Upcoming | 다가올 일정 (기본 info 톤) |
|
|
76
|
+
| `pending` | `warning` | Pending | 승인·확정 대기 |
|
|
77
|
+
| `completed` | `success` | Completed | 종료·완료 |
|
|
78
|
+
| `cancelled` | `destructive` | Cancelled | 취소·무산 |
|
|
79
|
+
|
|
80
|
+
조합 결과
|
|
81
|
+
|
|
82
|
+
- `card` / `line` + `status` → Badge soft 표기, chrome은 variant 그대로
|
|
83
|
+
- `fill` + `status` 없음 → 중립 `--bg-subtle` 회색 bg (디폴트)
|
|
84
|
+
- `fill` + `status` 지정 → 아이템 전체가 status soft 톤 bg. 중복 방지로 **Badge는 자동 숨김**(색이 이미 상태를 표현)
|
|
85
|
+
|
|
86
|
+
### 조합 예시
|
|
87
|
+
```tsx
|
|
88
|
+
<div>
|
|
89
|
+
{events.map((e) => (
|
|
90
|
+
<EventListItem
|
|
91
|
+
key={e.id}
|
|
92
|
+
href={`/events/${e.id}`}
|
|
93
|
+
date={e.date}
|
|
94
|
+
title={e.title}
|
|
95
|
+
status={e.status}
|
|
96
|
+
meta={[
|
|
97
|
+
{ icon: "calendar-dots", label: e.when },
|
|
98
|
+
{ icon: "compass", label: e.where },
|
|
99
|
+
]}
|
|
100
|
+
/>
|
|
101
|
+
))}
|
|
102
|
+
</div>
|
|
103
|
+
```
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# GeneratedImage
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
AI가 생성한 **이미지 결과물의 큰 표시 자리**. 세 상태(loading / placeholder / image)가 같은 프레임 안에서 바뀐다. `aspect` 축으로 비율만 바꾸고 폭은 상위 그리드가 담당 — 자체적으로 크기를 들고 다니지 않는다. loading 상태는 image-gen 전용 lottie(sparkle breathe)로 "생성 중"을 전달하고, 플레이스홀더 배경이 이미 들어가 있어 깜빡임 없이 전환된다.
|
|
5
|
+
|
|
6
|
+
### Do
|
|
7
|
+
- 폭은 상위 그리드/부모가 결정. GeneratedImage는 `aspect`로 비율만 선언.
|
|
8
|
+
- 생성 중이면 `loading` prop, 결과가 없을 뿐이면 `loading` 생략(= placeholder만 노출).
|
|
9
|
+
- 다운로드 버튼이 필요하면 `onDownload`를 넘긴다. hover·focus-visible일 때만 노출되고, loading·placeholder 상태에서는 자동으로 숨겨진다.
|
|
10
|
+
- 결과물 레퍼런스는 세로 비율이 많아 기본값 `portrait`(3/4) 그대로 쓰기를 권장.
|
|
11
|
+
|
|
12
|
+
### Don't
|
|
13
|
+
- 폭을 CSS로 override 금지. 비율은 `aspect`가, 폭은 부모 레이아웃이 책임.
|
|
14
|
+
- `loading=true`일 때 `src`에 결과물을 미리 넣어두는 식의 선행 로딩 금지 — 상태가 꼬인다.
|
|
15
|
+
- 자체 border·bg 덮어쓰기 금지. `--bg-subtle`/`--bg-muted` 토큰이 상태를 신호한다.
|
|
16
|
+
- 다운로드 버튼 자리에 다른 액션(공유·삭제 등) 추가 금지. 이 자리는 "저장" 전용. 액션이 더 필요하면 외부 Toolbar/MessageActions로 뺀다.
|
|
17
|
+
- 결과물 위에 라벨·캡션 overlay 올리지 않는다. 캡션은 바깥 블록으로 분리.
|
|
18
|
+
|
|
19
|
+
### Fallback
|
|
20
|
+
비율 애매하면 `portrait`, 상태 애매하면 `loading` 생략(= placeholder). onDownload는 "저장이 UX의 핵심일 때만" 공급.
|
|
21
|
+
|
|
22
|
+
### 관련
|
|
23
|
+
- [[components/spinner]] — loading 상태에서 `variant="lottie-image-gen"` size `lg`로 내장.
|
|
24
|
+
- [[components/tooltip]] — 다운로드 버튼의 "다운로드" 레이블.
|
|
25
|
+
- [[components/ai-message]] — 어시스턴트 메시지 안 결과물 자리에서 자주 쌍으로 조립.
|
|
26
|
+
- [[components/attachment-thumb]] — 사용자가 올린 참조 이미지 썸네일(작은 자리). 생성 결과 큰 자리는 GeneratedImage.
|
|
27
|
+
- [[components/gallery-item]] — 여러 결과를 그리드로 늘어놓을 때 셀 안쪽 미디어 자리.
|
|
28
|
+
|
|
29
|
+
### Props 시그니처
|
|
30
|
+
```tsx
|
|
31
|
+
<GeneratedImage
|
|
32
|
+
src={string} // 결과 이미지 URL (없거나 loading이면 placeholder)
|
|
33
|
+
alt={string} // 접근성 대체 텍스트
|
|
34
|
+
aspect="square | portrait | landscape" // default: portrait (3/4)
|
|
35
|
+
loading={boolean} // 생성 중 — placeholder + sparkle breathe
|
|
36
|
+
onDownload={(e) => void} // 지정 시만 우상단 다운로드 버튼 노출
|
|
37
|
+
/>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Aspect 가이드
|
|
41
|
+
| aspect | 비율 | 쓰임 |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| `portrait` | 3/4 | **기본**. 레퍼런스/캐릭터/인물 중심 결과물. |
|
|
44
|
+
| `square` | 1/1 | 아이콘·썸네일·SNS 포맷 결과물. |
|
|
45
|
+
| `landscape` | 4/3 | 배경·풍경·와이드 포스터 결과물. |
|
|
46
|
+
|
|
47
|
+
### 상태 전환
|
|
48
|
+
1. 초기: `src` 없음, `loading` 없음 → **placeholder** (배경 이미지 + 조용히 대기)
|
|
49
|
+
2. 생성 시작: `loading=true` → **loading** (같은 배경 + image-gen lottie 중앙 중첩)
|
|
50
|
+
3. 결과 수신: `loading=false`, `src=<url>` → **image** (cover로 가득 채움)
|
|
51
|
+
4. 다운로드: `onDownload` 공급 시, image 상태에서만 우상단 버튼 hover 노출
|
|
52
|
+
|
|
53
|
+
전환 사이에 레이아웃 shift가 없다 — 세 상태 모두 동일한 `aspect-ratio` 프레임 안에서 바뀌므로 상위 그리드가 흔들리지 않는다.
|
|
54
|
+
|
|
55
|
+
### 조합 예시
|
|
56
|
+
```tsx
|
|
57
|
+
// 기본 — AI 메시지 안 결과물
|
|
58
|
+
<GeneratedImage
|
|
59
|
+
src={result.url}
|
|
60
|
+
alt={result.prompt}
|
|
61
|
+
onDownload={() => downloadImage(result.url)}
|
|
62
|
+
/>
|
|
63
|
+
|
|
64
|
+
// 생성 중
|
|
65
|
+
<GeneratedImage loading alt="생성 중" />
|
|
66
|
+
|
|
67
|
+
// 와이드 포맷 결과
|
|
68
|
+
<GeneratedImage
|
|
69
|
+
src={banner.url}
|
|
70
|
+
alt={banner.prompt}
|
|
71
|
+
aspect="landscape"
|
|
72
|
+
onDownload={() => downloadImage(banner.url)}
|
|
73
|
+
/>
|
|
74
|
+
|
|
75
|
+
// AIMessage 안쪽에서 쌍으로
|
|
76
|
+
<AIMessage>
|
|
77
|
+
<AIMessage.Body>원하신 레퍼런스예요.</AIMessage.Body>
|
|
78
|
+
<GeneratedImage src={url} alt={prompt} onDownload={handleSave} />
|
|
79
|
+
</AIMessage>
|
|
80
|
+
```
|
|
@@ -1,235 +1,213 @@
|
|
|
1
1
|
# ListItem
|
|
2
2
|
|
|
3
3
|
### 원칙
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
같은 anatomy 안에서 **variant로 자리의 성격만 바꾼다** — `activity`(과거 서술), `notification`(처리 필요), `mention`(인용), `system`(사람 아님). variant를 늘리기 전에 slot(`leading`/`trailing`/`hoverActions`)으로 조립이 되는지 먼저 본다.
|
|
7
|
-
|
|
8
|
-
컴포넌트 자신은 **단일 row**다. border/divider/카드 chrome을 자체적으로 지지 않는다 — 리스트로 묶는 건 부모 레이아웃이 결정한다. 운영 콘솔은 border로, pure 좌표는 hairline으로, 카드 안 리스트는 Callout이 담당. ([[components/metric-card]]의 `bare` variant와 같은 원리.)
|
|
9
|
-
|
|
10
|
-
**단, "chrome 없음"은 완전한 공간 없음을 뜻하지 않는다** — ListItem은 `padding-block: var(--space-3) + margin-block: -var(--space-3)`의 **self-contained 상쇄 공식**을 가진다. 부모가 세로 padding을 안 걸어도 row hover hit area가 보장되게 하려는 안전장치다. 하지만 flex item 레이아웃에서는 음의 margin이 레이아웃 박스를 축소하지 못해 **ListItem이 "상쇄된 자리"를 실제로 차지한다** — 부모 li가 padding을 또 걸면 두 번 공간이 쌓여 과하게 보인다. 자세한 공식·완화법은 아래 "조립 — 부모 wrapper 공식" 섹션.
|
|
11
|
-
|
|
12
|
-
### Do
|
|
13
|
-
- **anatomy는 네 자리로 고정**: `[leading] [who] [body / verb+target] [meta/trailing]`. 어떤 variant든 이 뼈대를 깨지 않는다.
|
|
14
|
-
- **layout은 두 형태**: `inline`(한 줄, 기본) · `stacked`(두 줄 — from이 1줄, body가 2줄). 서술 모드 둘 다에서 작동 — action 모드 stacked는 "from · verb · target" + body 2줄째, 서술 모드 stacked는 from + body 2줄째. 긴 서술/인용/멘션은 stacked.
|
|
15
|
-
- **서술은 두 모드 중 하나로**: 자유 서술은 `from` + `body`("회계팀 — 외주비 결재 요청"), 액션 서술은 `from` + `verb` + `target`("박지훈 · 댓글 남김 · ERP 2.0 사이드바"). 둘 다 넘기지 않는다.
|
|
16
|
-
- **unread는 텍스트 톤으로만 신호** (dot/badge 없음): `unread={true}`면 body/from이 가장 진하고 meta는 `--primary` 컬러로 "지금"을 강조. `unread={false}`면 전체 톤이 한두 단계 뒤로 물러나 "이미 처리됨"을 시사. `unread` 생략(undefined)이면 중립 — **리드/언리드 개념 자체가 없는 자리**(활동 로그·히스토리·타임라인)에서 자유롭게 사용.
|
|
17
|
-
- **surface는 작은 inline 토큰**: `surface="GitHub"` 같은 서비스명은 target 뒤 작게 inline. [[components/badge]] variant="soft"로 렌더되지만 text-only로 유지.
|
|
18
|
-
- **meta는 상대시간 한 토큰**: "방금", "12분", "어제". ISO 날짜는 Tooltip으로 보조.
|
|
19
|
-
- **hover-only 액션은 명시적으로**: `hoverActions={[<IconButton name="archive"/>, ...]}` — Linear Inbox의 archive/snooze/mark-read 패턴. 평상시 숨김, row hover/focus/focus-within에서 등장. [[components/menu-item]]의 hoverOnly와 같은 메커니즘.
|
|
20
|
-
- **row 전체 클릭은 `href`로**: 라우팅 아이템이면 asChild로 `<Link>`에 위임. target만 링크로 쓰고 row는 비활성인 자리도 OK.
|
|
21
|
-
- **leading 정책은 리스트 단위로 통일**: 일부엔 Avatar, 일부엔 Icon처럼 섞으면 "누구"의 자리가 어긋난다. system 메시지가 섞이는 리스트면 전부 Avatar fallback(이니셜) + system은 서비스 로고 Avatar.
|
|
22
|
-
- **leading 타입은 세 종류**: Avatar(사람·팀), Icon(단색 글리프), `ListItemLeadingIcon`(배경색 + 아이콘 마크). 세 번째는 "서비스/시스템이 상태를 색으로 신호"하는 자리 — tone 프리셋(`primary · success · warning · danger · info · neutral`)으로 bg/fg 쌍을 한 번에, 혹은 `--leading-icon-bg` · `--leading-icon-fg` 변수로 세밀 조절. Avatar와 같은 사이즈 트랙(xs/sm/md/lg)이라 섞여도 정렬 유지. 추가로 `size="auto"`는 `layout="stacked"`와 짝 — content 두 줄 전체 높이에 맞춰 정사각으로 stretch, 상태 마크를 크게 보여야 하는 알림/시스템 메시지 자리.
|
|
23
|
-
- **density는 컨테이너에 맡긴다**: ListItem 자체는 size prop 없이, 부모 리스트의 density(또는 Density 축) 토큰을 상속한다.
|
|
24
|
-
|
|
25
|
-
### Don't
|
|
26
|
-
- `verb` + `body` 동시 사용 금지 — 서술 중복. 액션 모드와 서술 모드는 배타.
|
|
27
|
-
- 채팅뷰처럼 긴 메시지 본문을 그대로 노출하지 않는다 — ListItem은 "누가 어디서 뭘 했다" 요약 라인이지, 메시지 내용의 복사본이 아니다. 멘션/댓글이어도 `verb` + `target`("어디/뭐에")로 축약하고 상세는 target 클릭 뒤 전체 뷰에서. 인용부호를 둘러친 긴 body도 금지.
|
|
28
|
-
- `leading`을 생략하고 unread dot만 두지 않는다 — dot 혼자 떠서 "누구"의 자리가 사라진다. system 메시지는 서비스 로고/아이콘 Avatar를 세운다.
|
|
29
|
-
- body를 두 줄 이상으로 늘리지 않는다 — 길면 `layout="stacked"`로 자동 승격해 target 라인과 서술 라인을 분리하거나, [[components/callout]] 영역으로 넘긴다.
|
|
30
|
-
- `trailing` + `hoverActions`를 둘 다 넘기지 않는다 — 메타 자리 하나뿐. 상시 메타(카운트·시간)와 hover 액션이 공존해야 하면 `trailing`에 배열로.
|
|
31
|
-
- unread/urgent를 색으로 중복 신호하지 않는다 — dot만. body 색을 fg-strong으로 올리거나 배경을 깔면 과잉.
|
|
32
|
-
- 자체적으로 padding·border·bg는 두지 않지만 **hover bg는 예외** — row 레벨 상호작용 피드백은 "가두는 chrome"이 아니라 "지금 이 줄" 신호이므로 default로 켜져 있다. pure 좌표에서 끄려면 색상 훅 `--list-item-hover-bg: transparent`.
|
|
33
|
-
- "댓글 남김" 같은 동사를 긴 서술로 늘이지 않는다. verb는 **동사 토큰** 하나. 긴 설명은 target 아래 2줄 body로 승격.
|
|
34
|
-
|
|
35
|
-
### Fallback
|
|
36
|
-
variant 애매하면 `activity`. layout 애매하면 `inline`. leading 애매하면 `from` 기반 Avatar(이니셜).
|
|
37
|
-
|
|
38
|
-
### 색상 훅 (컨테이너 단위 톤 조절)
|
|
39
|
-
MenuItem과 같은 패턴. 리스트 wrapper에 변수를 얹으면 안쪽 ListItem이 일괄 반응.
|
|
4
|
+
리스트에 반복되는 **한 줄 단위**의 범용 anatomy. 피드·알림·활동 로그·작업 리스트·자원 목록이 전부 이 형태다. GitHub Timeline · Linear Inbox · Jira Issue history · Notion Updates · Figma Comments 모두 같은 뼈대 위에 서식과 톤만 바뀐다.
|
|
40
5
|
|
|
41
|
-
|
|
42
|
-
|---|---|---|
|
|
43
|
-
| `--list-item-fg` | 본문 서술 색 | `--fg` |
|
|
44
|
-
| `--list-item-from-fg` | from(누구) 색 | `--fg-strong` |
|
|
45
|
-
| `--list-item-meta-fg` | meta/상대시간 색 | `--fg-muted` |
|
|
46
|
-
| `--list-item-hover-bg` | row hover bg | `--hover` |
|
|
6
|
+
**3구역 뼈대**: `[leading] [content] [trailing]`. 가로축은 이 셋이 전부고 세 구역 전부 `ReactNode`라 **자유 조립**. content 안쪽은 자주 쓰는 조립을 위한 편의 서브컴포넌트(`Title · Description · Meta · MetaDivider`)가 세로 리듬을 자동 공급하지만 강제는 아님 — 아바타 그룹·프로그레스·커스텀 차트 등 아무거나 넣을 수 있다.
|
|
47
7
|
|
|
48
|
-
|
|
49
|
-
/* 예: 조용한 활동 로그 — 메타·본문 전부 한 단계 뒤로 */
|
|
50
|
-
.quiet-activity {
|
|
51
|
-
--list-item-fg: var(--fg-muted);
|
|
52
|
-
--list-item-meta-fg: var(--fg-subtle);
|
|
53
|
-
}
|
|
8
|
+
**일정·이벤트처럼 날짜가 leading 아이덴티티인 자리는 [[components/event-list-item]] 사용** — ListItem은 범용 한 줄, EventListItem은 시간축 중심 전용.
|
|
54
9
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
10
|
+
**chrome 없음** — bg·border·radius를 자체 공급하지 않는다. ListItem은 "리스트에 녹는 한 줄"이지, 블록이 아니다. 배경·테두리로 상태를 신호해야 하는 블록이 필요하면 [[components/callout]]로 승격한다.
|
|
11
|
+
|
|
12
|
+
변주 축은 두 개: `size`(md/sm)가 세로 여백감을, `align`(center/top)이 leading 세로 정렬을 담당. 기본은 `md`(작업·자원 리스트 밀도), `sm`은 활동 로그·추출 완료처럼 분리선 없이 조밀하게 쌓이는 스트림 자리. row hover bg는 전 리스트 기본, href/onClick이 있으면 cursor pointer + focus ring 추가.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 결정 가이드 — 뭘 만들려는 거지?
|
|
17
|
+
|
|
18
|
+
### 1. 자원·문서 목록 (내가 열어볼 것들)
|
|
19
|
+
문서·프로젝트·설정 항목처럼 **"열람 가능한 것들"의 목록**. 액션보다 네비게이션이 주.
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
<ListItem
|
|
23
|
+
href="/docs/abc"
|
|
24
|
+
leading={<ListItemLeadingIcon size="md"><Icon name="file-text" /></ListItemLeadingIcon>}
|
|
25
|
+
trailing={<Icon name="caret-right" />}
|
|
26
|
+
>
|
|
27
|
+
<ListItem.Title>채용 파이프라인 QBR</ListItem.Title>
|
|
28
|
+
<ListItem.Meta><time>2029년 7월 8일 작성</time></ListItem.Meta>
|
|
29
|
+
</ListItem>
|
|
59
30
|
```
|
|
60
31
|
|
|
61
|
-
|
|
32
|
+
- **leading**: `ListItemLeadingIcon`(배경색 아이콘) → 자원의 "타입 아이덴티티"
|
|
33
|
+
- **content**: `Title` + `Meta`(생성/수정 날짜). Description은 생략 — 자원의 "본문 요약"이 필요한 자리면 그건 ListItem이 아니라 Card
|
|
34
|
+
- **trailing**: `caret-right` → "클릭하면 열림" 신호
|
|
35
|
+
- **size**: `md`, **align**: `center`
|
|
36
|
+
|
|
37
|
+
### 2. 작업·할 일 리스트 (내가 처리해야 할 것들)
|
|
38
|
+
요청·승인 대기·검토 필요처럼 **상태가 있는 작업 단위**.
|
|
39
|
+
|
|
62
40
|
```tsx
|
|
63
41
|
<ListItem
|
|
64
|
-
|
|
65
|
-
leading={
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
meta={ReactNode} // "방금" · "12분". 오른쪽 끝 default
|
|
77
|
-
trailing={ReactNode} // meta 자리 커스텀 (ReactNode 또는 배열)
|
|
78
|
-
hoverActions={ReactNode[]} // row hover 시 등장. archive/snooze/⋯
|
|
79
|
-
|
|
80
|
-
// 레이아웃·상호작용
|
|
81
|
-
variant="activity | notification | mention | system" // default: activity
|
|
82
|
-
layout="inline | stacked" // default: inline. stacked는 body 2줄.
|
|
83
|
-
href={string} // row 전체 링크. asChild로 Link 위임.
|
|
84
|
-
onClick={(e) => void} // row 선택 핸들러
|
|
85
|
-
asChild={boolean} // <Link> 등에 위임
|
|
86
|
-
/>
|
|
42
|
+
align="top"
|
|
43
|
+
leading={<Icon name="list-dashes" />}
|
|
44
|
+
trailing={
|
|
45
|
+
<>
|
|
46
|
+
<Badge variant="soft" status="warning">검토 필요</Badge>
|
|
47
|
+
<IconButton size="sm" variant="ghost" icon={<Icon name="caret-right" />} aria-label="열기" />
|
|
48
|
+
</>
|
|
49
|
+
}
|
|
50
|
+
>
|
|
51
|
+
<ListItem.Title>개발 대상과 문서 범위 확인</ListItem.Title>
|
|
52
|
+
<ListItem.Meta><time>22분 전 요청</time></ListItem.Meta>
|
|
53
|
+
</ListItem>
|
|
87
54
|
```
|
|
88
55
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
56
|
+
- **leading**: raw `<Icon>` → 체크리스트·태스크의 "종류"만 가볍게
|
|
57
|
+
- **content**: `Title` + `Meta`(요청 시각·요청자). Description은 선택 — 제목이 짧으면 추가
|
|
58
|
+
- **trailing**: 상태 `Badge` + chevron. Badge가 "지금 상태"를 shout, chevron이 "열어서 처리하라"
|
|
59
|
+
- **size**: `md`, **align**: `top`
|
|
92
60
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
| `activity` | Avatar(from) | 없음 | verb+target | 최근 활동·히스토리 로그. GitHub/Linear timeline. 보통 unread 생략 |
|
|
96
|
-
| `notification` | Avatar(from) | archive/mark-read | from+body | 알림 센터·bell 드롭다운. unread 명시 권장 |
|
|
97
|
-
| `mention` | Avatar(from) | reply/resolve | from+body(인용) | 멘션·댓글 피드. body에 인용부호 자연스러움 |
|
|
98
|
-
| `system` | Icon(서비스 로고) | 없음 | from+body | ERP/CI/빌드 등 시스템 이벤트. 보통 unread 생략 |
|
|
61
|
+
### 3. 피드·활동 로그 (지나간 이벤트 스트림)
|
|
62
|
+
알림·활동 기록·이력처럼 **읽을 뿐 처리는 거의 없는 흐름**.
|
|
99
63
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
- 한 리스트 안에 variant 혼용 — 리스트 톤은 하나로. 섞어야 하면 섹션으로 분리
|
|
64
|
+
```tsx
|
|
65
|
+
<ListItem align="top" size="sm" leading={<Avatar size="sm" name="박지훈" />}>
|
|
66
|
+
<ListItem.Title>박지훈 — ERP 2.0 사이드바 티켓에 코멘트</ListItem.Title>
|
|
67
|
+
<ListItem.Meta><time>12분 전</time></ListItem.Meta>
|
|
68
|
+
</ListItem>
|
|
69
|
+
```
|
|
107
70
|
|
|
108
|
-
|
|
71
|
+
- **leading**: Avatar(사람 이벤트) 또는 raw `<Icon>`(시스템 이벤트)
|
|
72
|
+
- **content**: `Title`에 "누가/뭘 했다"를 한 줄로. 긴 설명은 target 클릭 뒤 상세 뷰로
|
|
73
|
+
- **trailing**: 보통 없음 (hover-only 액션은 archive/mark-read 패턴)
|
|
74
|
+
- **size**: `sm` (스트림은 조밀하게), **align**: `top`
|
|
75
|
+
|
|
76
|
+
### 4. 멘션·댓글 (내가 응답해야 할 메시지)
|
|
77
|
+
멘션·@·댓글 리스트. 피드와 비슷하지만 **"응답 가능"이 핵심**.
|
|
109
78
|
|
|
110
79
|
```tsx
|
|
111
|
-
// 1) 최근 활동 피드 — verb+target 액션 서술
|
|
112
|
-
<ListItem
|
|
113
|
-
leading={<Avatar name="박지훈" />}
|
|
114
|
-
from="박지훈"
|
|
115
|
-
verb="댓글 남김"
|
|
116
|
-
target="ERP 2.0 사이드바 구조 확정"
|
|
117
|
-
surface="Figma"
|
|
118
|
-
meta="방금"
|
|
119
|
-
/>
|
|
120
|
-
|
|
121
|
-
// 2) 알림 센터 — unread + hoverActions
|
|
122
|
-
<ListItem
|
|
123
|
-
variant="notification"
|
|
124
|
-
leading={<Avatar name="회계팀" />}
|
|
125
|
-
from="회계팀"
|
|
126
|
-
body="외주비 세금계산서 결재 요청"
|
|
127
|
-
meta="방금"
|
|
128
|
-
unread
|
|
129
|
-
hoverActions={[
|
|
130
|
-
<IconButton name="archive" aria-label="보관" />,
|
|
131
|
-
<IconButton name="check" aria-label="읽음 처리" />,
|
|
132
|
-
]}
|
|
133
|
-
/>
|
|
134
|
-
|
|
135
|
-
// 3) 멘션 — body에 인용 (stacked로 2줄)
|
|
136
|
-
<ListItem
|
|
137
|
-
variant="mention"
|
|
138
|
-
layout="stacked"
|
|
139
|
-
leading={<Avatar name="장대표" />}
|
|
140
|
-
from="장대표"
|
|
141
|
-
body={<>“@김희영 1:1 전에 자료 공유 부탁”</>}
|
|
142
|
-
meta="오전 9:12"
|
|
143
|
-
unread
|
|
144
|
-
/>
|
|
145
|
-
|
|
146
|
-
// 4) 시스템 이벤트 — Icon leading, 사람 아님
|
|
147
|
-
<ListItem
|
|
148
|
-
variant="system"
|
|
149
|
-
leading={<Icon name="chart-line" />}
|
|
150
|
-
from="ERP"
|
|
151
|
-
body="주간 리포트 생성 완료"
|
|
152
|
-
meta="3시간"
|
|
153
|
-
/>
|
|
154
|
-
|
|
155
|
-
// 4-b) 상태 색을 띤 아이콘 마크 leading — 성공/경고/실패 신호
|
|
156
80
|
<ListItem
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
meta="방금"
|
|
166
|
-
/>
|
|
167
|
-
|
|
168
|
-
// 5) pure 좌표 — 부모 리스트가 hairline으로 분리 (조립 공식 "패턴 C" 참고)
|
|
169
|
-
// li에 세로 padding + ListItem self-space 중화 둘 다 필요하다.
|
|
170
|
-
<ul className="pureFeed">
|
|
171
|
-
{feed.map((f) => (
|
|
172
|
-
<li key={f.id}>
|
|
173
|
-
<ListItem
|
|
174
|
-
leading={<Avatar name={f.from} />}
|
|
175
|
-
from={f.from}
|
|
176
|
-
body={f.body}
|
|
177
|
-
meta={f.ago}
|
|
178
|
-
unread={f.unread}
|
|
179
|
-
/>
|
|
180
|
-
</li>
|
|
181
|
-
))}
|
|
182
|
-
</ul>
|
|
81
|
+
href="/thread/123"
|
|
82
|
+
align="top"
|
|
83
|
+
leading={<Avatar size="sm" name="장대표" />}
|
|
84
|
+
>
|
|
85
|
+
<ListItem.Title>장대표</ListItem.Title>
|
|
86
|
+
<ListItem.Description>@희영 1:1 전에 자료 공유 부탁</ListItem.Description>
|
|
87
|
+
<ListItem.Meta><time>오전 9:12</time></ListItem.Meta>
|
|
88
|
+
</ListItem>
|
|
183
89
|
```
|
|
184
90
|
|
|
185
|
-
|
|
186
|
-
|
|
91
|
+
- **content**: `Title`에 이름, `Description`에 **인용 한 줄**(전체 메시지 복사 금지), `Meta`에 시각
|
|
92
|
+
- **href**: row 전체가 thread 열기
|
|
93
|
+
- **align**: `top`
|
|
94
|
+
|
|
95
|
+
### 5. 데이터 레코드 (내가 보고 비교할 숫자)
|
|
96
|
+
가격·수량·기간이 핵심인 리스트. 숫자가 Title보다 중요한 자리.
|
|
97
|
+
|
|
98
|
+
→ **ListItem이 아니라 [[components/table]]의 자리**. ListItem은 "자원·텍스트 중심 한 줄", 숫자 비교·정렬·집계는 Table.
|
|
99
|
+
|
|
100
|
+
### 6. 상태 공지·보류 블록 (지금 당장 봐야 하는 알림)
|
|
101
|
+
"작업 보류", "다운타임 예정", "결제 실패"처럼 **배경 톤으로 shouts** 해야 하는 블록.
|
|
102
|
+
|
|
103
|
+
→ **ListItem이 아니라 [[components/callout]]의 자리**. ListItem은 chrome 없이 흐름에 녹는 한 줄이지, 상태 블록이 아니다.
|
|
104
|
+
|
|
105
|
+
### 7. 선택 가능한 목록 (여러 개 체크·drag)
|
|
106
|
+
멀티 셀렉션·drag reorder·bulk 작업 리스트.
|
|
187
107
|
|
|
188
|
-
|
|
189
|
-
그대로 둬도 ListItem이 자체 공간을 가진다. hover hit area는 보장되지만 **리스트처럼 세로로 쌓이면 아이템끼리 바로 붙는다** — 리스트 자리면 패턴 B/C로.
|
|
108
|
+
→ **ListItem leading에 `<Checkbox />`** 꽂아서 시작. drag handle은 leading 왼쪽에 추가 자리로. 복잡해지면 Table로 승격.
|
|
190
109
|
|
|
191
|
-
|
|
192
|
-
flex item 레이아웃에서 ListItem의 음의 margin이 레이아웃 박스를 축소시키지 못해 **공간이 두 번 쌓인다** (li padding + ListItem self-padding). 겉보기엔 세로가 과하게 커진다 — hairline과 콘텐츠 사이 호흡은 보이지만 리스트가 전반적으로 숨 막혀 보이면 이 상태다.
|
|
110
|
+
---
|
|
193
111
|
|
|
194
|
-
|
|
195
|
-
|
|
112
|
+
## 서브컴포넌트 역할
|
|
113
|
+
|
|
114
|
+
| 서브컴포넌트 | 뭘 담나 | 언제 쓰나 |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| `ListItem.Title` | 메인 제목·행위 요약 | 항상. Title 없는 ListItem은 거의 없음. Badge를 inline으로 Title 앞에 섞어도 OK |
|
|
117
|
+
| `ListItem.Description` | 제목으로 안 담기는 **인용·부제** | 멘션/댓글 본문, 설명이 1줄 더 필요한 자리. 생성일 같은 메타는 절대 Description 아님 |
|
|
118
|
+
| `ListItem.Meta` | **메타데이터** (시각·작성자·버전·상태 텍스트) | 작성일·수정일·요청자·"n개 참여" 같은 보조 수치 전부 여기 |
|
|
119
|
+
| `ListItem.MetaDivider` | Meta 안 토큰 사이 hairline 구분자 | `<span>작성 10일 전</span><Divider /><span>박지훈</span>` 같을 때 |
|
|
120
|
+
|
|
121
|
+
### Description vs Meta
|
|
122
|
+
가장 자주 실수하는 자리. 구분 공식:
|
|
123
|
+
- **Description** = "제목으로 안 들어간 **본문 성격의 한 줄**" (댓글 본문, 부제, 요약)
|
|
124
|
+
- **Meta** = "**누가/언제/몇 개** 같은 메타데이터" (날짜·사람·수치·상태 텍스트)
|
|
125
|
+
|
|
126
|
+
"2029년 7월 8일 작성"은 **Meta**. "@희영 1:1 전에 자료 공유 부탁"은 **Description**.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Do
|
|
131
|
+
- **anatomy는 3구역 고정**: leading / children(content) / trailing.
|
|
132
|
+
- **세 구역은 전부 ReactNode — 완전 자유**: 전용 서브컴포넌트는 리듬 토큰을 자동 공급하는 **편의 레이어**지 강제가 아님.
|
|
133
|
+
- **leading 타입은 리스트 단위로 통일**: 자원 리스트는 전부 IconMark, 피드는 전부 Avatar 식.
|
|
134
|
+
- **2줄 이상 본문이면 `align="top"`**: leading이 Title 라인에 top-align.
|
|
135
|
+
- **스트림 자리는 `size="sm"`**: 활동 로그·알림 스트림은 조밀하게.
|
|
136
|
+
- **row 전체 클릭은 `href`/`onClick`**: cursor pointer + focus ring 자동.
|
|
137
|
+
- **Meta 안 토큰 사이는 MetaDivider**: 가운뎃점·bullet·파이프 금지. [[feedback_no_bullet_separator]].
|
|
138
|
+
- **날짜는 [[feedback_date_writing]] 규칙 준수**: "10월 13일" 또는 "12분 전", 숫자 패턴(10. 13) 금지.
|
|
139
|
+
|
|
140
|
+
## Don't
|
|
141
|
+
- **Description에 메타데이터 쓰기 금지**: "2029년 7월 8일 작성" 같은 날짜·사람·수치는 전부 `Meta`로.
|
|
142
|
+
- **Title에 전체 메시지/긴 서술 넣기 금지**: ListItem은 요약 라인, 메시지 뷰어가 아님.
|
|
143
|
+
- **content에 여러 Title 금지**: 메인 제목은 하나.
|
|
144
|
+
- **Description은 2줄 안쪽**: 3줄 이상이면 외부 Callout·Card로.
|
|
145
|
+
- **bg·border·radius 자체 공급 금지**: 블록 외형이 필요하면 [[components/callout]].
|
|
146
|
+
- **leading에 inline `<svg>` 금지**: 반드시 `<Icon />`/Badge/Avatar 경유. [[feedback_no_inline_svg]].
|
|
147
|
+
- **한 리스트 안 size 혼용 금지**: sm 리스트는 전부 sm.
|
|
148
|
+
- **자리마다 세로 간격 손 조정 금지**: 균형 안 맞으면 `size` 또는 리듬 토큰(`--li-gap-title`·`--li-gap-body`)으로.
|
|
149
|
+
|
|
150
|
+
## Fallback
|
|
151
|
+
leading 애매하면 Icon. size 애매하면 md. align 애매하면 center(한 줄)·top(2줄 이상).
|
|
152
|
+
|
|
153
|
+
## 축 (CSS 변수)
|
|
154
|
+
|
|
155
|
+
| 훅 | 대상 | 폴백 |
|
|
156
|
+
|---|---|---|
|
|
157
|
+
| `--li-pad-y` | 세로 padding (hit area) | `var(--space-3)` |
|
|
158
|
+
| `--li-gap-x` | leading↔content↔trailing | `var(--space-3)` |
|
|
159
|
+
| `--li-gap-title` | Title↔Description·Meta ("제목+부제" 유닛감) | `var(--space-0_5)` |
|
|
160
|
+
| `--li-gap-body` | 큰 블록 간 간격 | `var(--space-2)` |
|
|
161
|
+
| `--li-gap-meta` | Meta 라인 아이템 간 가로 gap | `var(--space-2)` |
|
|
162
|
+
|
|
163
|
+
## Props 시그니처
|
|
164
|
+
```tsx
|
|
165
|
+
<ListItem
|
|
166
|
+
leading={ReactNode} // Icon | Avatar | Badge | ListItemLeadingIcon | 자유
|
|
167
|
+
trailing={ReactNode} // Badge | Button | IconButton | 조합 | 자유
|
|
168
|
+
size="md | sm" // default: md (sm은 스트림 자리)
|
|
169
|
+
align="center | top" // default: center. 2줄 이상이면 top.
|
|
170
|
+
href={string} // row 전체 링크 (클릭 가능)
|
|
171
|
+
onClick={(e) => void} // row 선택 핸들러 (클릭 가능)
|
|
172
|
+
>
|
|
173
|
+
<ListItem.Title>...</ListItem.Title>
|
|
174
|
+
<ListItem.Description>...</ListItem.Description>
|
|
175
|
+
<ListItem.Meta>
|
|
176
|
+
<time>...</time>
|
|
177
|
+
<ListItem.MetaDivider />
|
|
178
|
+
<span>...</span>
|
|
179
|
+
</ListItem.Meta>
|
|
180
|
+
</ListItem>
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## 조립 — 부모 wrapper
|
|
185
|
+
리스트로 묶을 때는 부모 ul에 hairline border만 공급.
|
|
196
186
|
|
|
197
187
|
```css
|
|
198
|
-
|
|
199
|
-
.activityList {
|
|
188
|
+
.feed {
|
|
200
189
|
list-style: none;
|
|
201
|
-
margin: 0;
|
|
202
190
|
padding: 0;
|
|
203
|
-
|
|
204
|
-
flex-direction: column;
|
|
205
|
-
--list-item-hover-bg: transparent; /* 가두는 chrome 회피 */
|
|
206
|
-
}
|
|
207
|
-
.activityList > li {
|
|
208
|
-
padding-block: var(--space-3); /* 세로 호흡은 li가 전담 */
|
|
209
|
-
border-top: 1px solid var(--border-subtle, var(--border));
|
|
210
|
-
}
|
|
211
|
-
.activityList > li:last-child {
|
|
212
|
-
border-bottom: 1px solid var(--border-subtle, var(--border));
|
|
191
|
+
margin: 0;
|
|
213
192
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
padding-block: 0;
|
|
217
|
-
margin-block: 0;
|
|
193
|
+
.feed > li {
|
|
194
|
+
border-bottom: var(--hairline) solid var(--border);
|
|
218
195
|
}
|
|
196
|
+
.feed > li:last-child { border-bottom: none; }
|
|
219
197
|
```
|
|
220
198
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
- [[components/
|
|
229
|
-
- [[components/
|
|
230
|
-
- [[components/
|
|
231
|
-
- [[components/
|
|
232
|
-
- [[
|
|
233
|
-
- [[
|
|
234
|
-
- [[
|
|
235
|
-
- [[
|
|
199
|
+
**interactive ListItem**(`href`/`onClick`)은 cursor pointer + focus ring 자동, hit area는 좌우 `space-2`만큼 자동 확장(negative margin으로 정렬 유지).
|
|
200
|
+
|
|
201
|
+
## 관련
|
|
202
|
+
- [[components/avatar]] — leading의 "사람" 자리.
|
|
203
|
+
- [[components/badge]] — Title 앞 inline 상태, leading 상태, trailing 상태 전부.
|
|
204
|
+
- [[components/button]] — trailing CTA.
|
|
205
|
+
- [[components/icon-button]] — trailing chevron/dots.
|
|
206
|
+
- [[components/callout]] — 배경·테두리로 상태 신호가 필요한 블록은 Callout.
|
|
207
|
+
- [[components/event-list-item]] — 날짜가 leading 아이덴티티인 일정·이벤트 리스트는 EventListItem.
|
|
208
|
+
- [[components/table]] — 숫자 비교·정렬이 핵심이면 Table.
|
|
209
|
+
- [[components/metric-card]] — chrome 없이 부모가 분리 담당하는 설계 철학 공유.
|
|
210
|
+
- [[axes/density]] — 전역 Density 축은 테마 레벨, ListItem의 size는 리스트 자리별 조절.
|
|
211
|
+
- [[feedback_no_bullet_separator]] — Meta 라인 구분자는 MetaDivider.
|
|
212
|
+
- [[feedback_no_inline_svg]] — leading 아이콘은 Icon 컴포넌트.
|
|
213
|
+
- [[feedback_date_writing]] — Meta 안 날짜는 "10월 13일" / "12분 전" 형식.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
- `value`에 두 줄 텍스트, 긴 서술, 통화·단위를 라벨로 분리해 큰 숫자만 외롭게 두지 않는다.
|
|
15
15
|
- `trend`와 `delta`를 둘 다 넘기지 않는다 — `delta`를 넘기면 자동 매핑이 무시된다.
|
|
16
16
|
- `trend="down"`인데 숫자상 "좋은" 하락(예: 미결 결재 ↓)일 때도 색을 success로 뒤집지 않는다. 추세 색은 **방향**만 신호한다. 좋음/나쁨 해석은 `description`이나 라벨에서 한다.
|
|
17
|
-
- 그림자로 depth 쌓지 않는다.
|
|
17
|
+
- 그림자로 depth 쌓지 않는다. `line`만 아주 은은한 `--shadow-sm`을 깐다(hairline 보조). `soft`·`bare`는 그림자 금지.
|
|
18
18
|
- 아이콘을 숫자 옆에 두지 않는다. 아이콘은 라벨 왼쪽 보조 신호.
|
|
19
19
|
|
|
20
20
|
### Fallback
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
|
|
50
50
|
| variant | 표면 | 경계 | padding | 용도 |
|
|
51
51
|
|---|---|---|---|---|
|
|
52
|
-
| `line` | `--bg` | `1px solid --border` | 있음 | 운영 콘솔·조밀 그리드. hairline
|
|
52
|
+
| `line` | `--bg` | `1px solid --border` + `--shadow-sm` | 있음 | 운영 콘솔·조밀 그리드. hairline + 아주 은은한 lift. 기본값 |
|
|
53
53
|
| `soft` | `--bg-subtle` | 투명 | 있음 | 서술형 대시보드·분위기 있는 레이아웃. 경계 대신 표면 톤으로 depth |
|
|
54
54
|
| `bare` | 투명 | 없음 | 없음 | pure 좌표 전용. 부모가 hairline/톤으로 분리를 담당하는 strip 안에서만. 혼자 쓰지 않는다 |
|
|
55
55
|
|