16 KiB
Card
현재 HM 구현의 기준 문서. 공통 원칙은 docs/guidelines/프로젝트-지침.md(§6, §7), Token 구조 원칙은 테마-토큰-가이드.md를 따른다. 이 문서는 Card 고유의 API·Token·상태·결정만 기록한다.
아래 외부 ui_viz 문서는 ZIP 미포함 초기 출처이며 실행·구현의 필수 의존성이 아니다. 현재 기준은 이 문서와 HM 구현·대표 Story다. ui_viz에는
docs/components/card.md가 없다. Card 규격은docs/patterns/content-container.md(Type B Content Card)와docs/patterns/content-header.md(contentprofile)가 소유한다. 두 문서는 "범용 Card System이 아니다"라고 명시하며, 상태·Footer·Media·elevation을 만들지 않았다(Out of Scope). HM Card는 이 Type B를 shadcn Card에 적용한 것이다. Type A(Section Panel)는 구현하지 않았다.
1. 상태
| 항목 | 내용 |
|---|---|
| 구현 상태 | HM 스타일 적용 완료 (<Card />만으로 적용, hm-card class 없음) |
| 기준일 | 2026-10-02 (문서·주석 정합성 정정 2026-10-08, 디자인 값 변경 없음) |
| 기반 | shadcn/ui Card (div 기반, Radix 없음) |
| 대표 Story | Components/Data & Display/Card → All (이름 "Card") |
| 보류 | Eyebrow, Divider, Footer·Media, 반응형, 폭 (§11) |
2. 적용 근거
- ui_viz Confirmed: 컨테이너 radius 4px(
radius.container.default), 표면 White(color.bg.surface)/Beige(color.bg.content), 제목 전경color.fg.primary. - ui_viz 채택(SOURCE_ADOPTED, Canonical Pattern Q6
YES, 2026-09-28): border 1px, body-padding 24px 사방, header↔body 간격 24px(D-093 D-042/D-059), title↔action 간격 20px, 교차축 중앙 정렬, 상태 없음. 이 값들은 문서상Confirmed표기가 아니라 owner가 채택한 Canonical 값이다. HM Card는 이 채택 값을 적용한다. - ui_viz LOCAL_EXCEPTION(컴포넌트 범위 값): border 색
#DEDBD2(전역 token 아님, Tabs·Search Field와 값만 같고 alias 금지), Beige border#ECD4A2(Primitivehm-brown-light-4와 같은 값). 현재 HM 구현은 이 두 값을 컴포넌트 전용 값으로 두지 않고 공유 Semantic Token에 연결한다. 차이는 §6.1에 기록한다. - ui_viz CANONICAL_OVERRIDE: Title Source 19px/700 →
heading-md20/28/600. - HM 사용자 결정: 없음.
- shadcn 유지: 아래 §7.
- ui_viz 미정의: Description, Footer,
size="sm", Media, Hover/Selected/Clickable, shadow.
3. 기본 규격
| 항목 | 값 | 근거 |
|---|---|---|
| Background | White (--hm-sem-bg-surface) / Beige (--hm-sem-bg-content) |
Confirmed |
| Border | 1px solid --hm-sem-stroke-input-default(Default #dedbd2) / Beige는 --hm-sem-palette-brown-light-4(Default #ecd4a2) |
두께는 채택. 색 값은 LOCAL_EXCEPTION 값과 같고 Token 연결은 §6.1 |
| Radius | 4px | Confirmed |
| Shadow | 없음 | ui_viz: "Source의 컨테이너는 그림자를 쓰지 않는다" |
| Padding | 24px 사방 (Card 위/아래, Header·Content 좌우) | 채택 |
| Header ↔ Content 간격 | 24px | 채택 (D-093) |
| 자식 clipping | 하지 않음 (overflow: visible) |
채택 (Type B) |
| Title | 20px / 28px / 600, --hm-sem-fg-primary |
CANONICAL_OVERRIDE |
| Header 안 Title ↔ Action 간격 | 20px (column-gap) |
채택 |
| Action 정렬 | Title 반대쪽 끝, 교차축 중앙 | 채택 |
| Description | shadcn 기본 (text-sm text-muted-foreground), HM Card CSS 미적용. 사용 범위는 §5.1 |
ui_viz 미정의 |
| 폭/높이 | 내용과 레이아웃이 결정. 고정 token 없음 | ui_viz: "Card width token을 만들지 않는다" |
4. 상태 규격
- 존재하는 상태는 Default 하나다. Hover / Focus / Pressed / Selected / Disabled는 ui_viz가
NOT_APPLICABLE로 명시했고 추가하지 않았다. Clickable·Interactive 변형도 없다. - Card 안의 Button·Input 등은 각자의 컴포넌트 문서에 따른 상태를 가진다.
5. API
Card:
size?: "default" | "sm" (shadcn 그대로)
→
size?: "default" | "sm" (유지)
surface?: "surface" | "content" (신규) 기본 "surface" — ui_viz surface 축 (White / Beige)
CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter: shadcn API 그대로
- 출력 속성:
data-slot="card",data-size,data-surface. - 새 prop은
surface하나다. ui_viz가 Content Card의 표면 축(surface/content)을 정의했기 때문이다.variant·interactive·selected등은 근거가 없어 만들지 않았다. - Eyebrow는 shadcn Card에 대응 요소가 없다. 필요하면 소비자가 조합한다(미구현, §11).
5.1 CardDescription 사용 범위
- 공개 API로 export되며(
packages/ui/src/index.ts) shadcn 구현 그대로다.packages/ui/src/theme/card.css는card-description을 선택하지 않으므로 HM 전용 Typography·색이 없다(text-sm text-muted-foreground). - ui_viz Content Card Anatomy에 Description이 없다. 대표 Story
All에는 넣지 않으며, HM Card 규격 요소가 아니라 shadcn 호환 요소로만 둔다. - 현재 저장소의 실제 사용처:
| 위치 | 용도 | 비고 |
|---|---|---|
src/components/card.stories.tsx Compatibility |
shadcn 호환 확인 | verification 전용 |
src/patterns/dashboard-layout.stories.tsx |
업무 목록 건수(role="status"), KPI 라벨(Title 없이 Description만), 가로 스크롤 안내 |
Pattern Story 안의 소비 예시. 별도 스타일 없음 |
src/App.tsx (Demo) |
Panel 부제 | .hm-panel-header가 11px·--hm-muted로 덮어씀. Demo 전용(§8.1) |
- Header에 Description이 있으면 shadcn의
has-data-[slot=card-description]:grid-rows-[auto_auto]로 2행 grid가 되고,CardAction은row-span-2로 두 행에 걸친다. 이 동작은 shadcn 그대로다. - Description의 HM Token·크기·색은 미정의다. 필요해지면 이 문서에서 결정한 뒤 Token으로 추가한다(§11).
6. Token
Component Mapping 위치: packages/ui/src/theme/card.css (--hm-card-*), packages/ui/src/theme.css에서 import.
- 재사용한 공통 값:
--hm-radius-container(radius),--hm-sem-bg-surface,--hm-sem-bg-content,--hm-sem-fg-primary,--hm-sem-stroke-input-default(White border),--hm-sem-palette-brown-light-4(Beige border). Border 연결 상세는 §6.1. - Card 전용 값:
--hm-card-radius,--hm-card-border-width(1px),--hm-card-padding(24px),--hm-card-border-color,--hm-card-border-color-on-content,--hm-card-surface,--hm-card-surface-content,--hm-card-text,--hm-card-header-gap(20px),--hm-card-title-font-size|line-height|font-weight|color. - Primitive 신규 추가 1건:
--hm-brown-light-4: #ecd4a2(packages/ui/src/theme/primitives.css). ui_viz가 Confirmed Primitivehm-brown-light-4로 명시한 값이다. Card CSS는 이 Primitive를 직접 참조하지 않고 Semantic alias--hm-sem-palette-brown-light-4를 참조한다. - shadcn의
--card-spacing변수를 24px(--hm-card-padding)로 채운다.size="sm"은 TSX class의 12px을 그대로 쓴다. - Input·Select·Button과 값(4px,
#dedbd2)이 겹쳐도 그쪽 Component Token을 alias하지 않는다.--hm-card-radius는--hm-radius-container를 참조하고--hm-radius-input과는 연결하지 않는다(ui_viz가 alias 금지를 명시). --card-spacing과 Title 규칙은:where()로 감싸 specificity를 낮췄으므로 소비자className과 Demo 스타일이 덮어쓸 수 있다. border·radius·surface 규칙([data-slot="card"],[data-surface="content"])은:where()없는 attribute selector다.
6.1 Border Token 실제 연결 (LOCAL_EXCEPTION과의 차이)
| Card 변수 | 실제 참조 (card.css) |
Runtime 계약 key (contract.ts) |
HM Default 해석 경로 (default.css → primitives.css) |
Default 값 |
|---|---|---|---|---|
--hm-card-border-color |
var(--hm-sem-stroke-input-default) |
color.stroke.input.default |
--hm-color-border-control → --hm-sem-palette-warm-neutral-pale → --hm-warm-neutral-pale |
#dedbd2 |
--hm-card-border-color-on-content |
var(--hm-sem-palette-brown-light-4) |
color.palette.brown-light-4 |
--hm-brown-light-4 |
#ecd4a2 |
- ui_viz 기술: 두 border 색은 LOCAL_EXCEPTION 컴포넌트 값이며, 전역 token이 아니고 다른 컴포넌트 값과 alias하지 않는다.
- 현재 구현: Default 렌더 값은 ui_viz 값과 같다. 다만 White border는 Input·Select·Dialog·Separator 등이 함께 쓰는 공유 Semantic
--hm-sem-stroke-input-default를, Beige border는 공유 Palette Semantic--hm-sem-palette-brown-light-4를 참조한다. 다른 Component Token(--hm-input-*등)은 alias하지 않는다. - 결과: 외부 Theme JSON이
color.stroke.input.default또는color.palette.brown-light-4를 override하면 Card border도 함께 바뀐다. Card만 따로 조정하려면 Component 변수--hm-card-border-color/--hm-card-border-color-on-content를 덮어써야 하며, 이 변수는 Runtime 계약 key가 아니다. - 이 절은 문서 정정이다. Token 연결과 렌더 값은 바꾸지 않았다. Card 전용 border token으로 분리할지는 미결정이다(§11).
7. shadcn / Radix에서 유지한 항목
semantic HTML(div), React composition(Header/Title/Description/Action/Content/Footer), data-slot, cn(..., className), header의 grid 구조와 has-data-[slot=card-action] 2열 전환, Footer(border-t, bg-muted/50, 전체 폭), 첫/마지막 img 처리, has-data-[slot=card-footer]:pb-0, size="sm", Description 스타일. Radix 사용 없음.
8. HM에서 변경한 항목
rounded-xl,bg-card,ring-1 ring-foreground/10,overflow-hidden, 기본--card-spacing: 16px을 TSX에서 제거하고 theme CSS(border, radius 4px, surface, 24px)가 담당.- ring 대신 실제 1px border 사용, shadow 없음.
- Title을 20/28/600으로 변경(
size="sm"제외). - Header 열 간격 20px, Action 교차축 중앙 정렬.
- Footer 아래 모서리를 컨테이너 radius(border 안쪽 3px)에 맞춤. Footer 표면은 shadcn 그대로다.
surfaceprop 추가.
8.1 Demo .hm-panel과 운영 Card 구분
- 운영 Card:
@hm-design/ui의<Card />와packages/ui/src/theme/card.css. 이 문서 §3의 값(padding 24px, Title 20/28/600 등)과 대표 StoryAll이 기준이다. 소비 프로젝트는hm-panel*class 없이 이 API만 쓴다. - Demo Panel:
src/App.tsx의 4개 Card에 붙은hm-panel,hm-panel-header,hm-panel-icon,hm-panel-contentclass와src/hm-theme.css규칙. 패키지(packages/ui)에 포함되지 않으며 Card 규격 Reference가 아니다. .hm-panel은 Card 외형(border, radius, background, shadow)을 지정하지 않는다. 다만 Demo class 묶음은 운영 Card 값을 다음처럼 덮어쓴다.
| Demo class | 덮어쓰는 값 | 운영 Card 값 |
|---|---|---|
.hm-panel |
gap: 0, --card-spacing: 16px |
gap·padding 24px |
.hm-panel-header |
아이콘 34px 열 grid, column-gap: 11px, border-bottom: 1px solid #ede9df, padding: 22px 23px 17px |
Header 좌우 24px, column-gap 20px, divider 없음 |
.hm-panel-header [data-slot="card-title"] |
15px / 1.35 / 700, letter-spacing -.02em | 20 / 28 / 600 |
.hm-panel-header [data-slot="card-description"] |
11px, --hm-muted, margin-top 2px |
shadcn 기본 text-sm text-muted-foreground |
.hm-panel-icon |
34px 아이콘 박스 | 해당 요소 없음 |
.hm-panel-content |
padding: 23px |
좌우 24px |
- Demo 값은 HM Token이 아닌 hex 값을 포함한다. Card Token·Story의 근거로 쓰지 않으며, Demo 화면을 Card 디자인 Reference로 캡처·인용하지 않는다.
9. Storybook
- 대표 Story
All("Card")이 기본 sidebar의 유일한 Card 항목이다 → 1뎁스. 구역:surface(White),content(Beige),action(Title + Action),form(본문에 Input). - verification(
tags: ["verification"]):Surfaces,Action,Compatibility(HM 전용: Description, Footer,size="sm"). - 운영 규칙은
프로젝트-지침.md§6.3, §7 참조. - 대표 Story에 Hover/Selected/Footer/Media는 넣지 않았다(ui_viz 미정의).
10. 사용자 확정 결정
추가 사용자 결정 없음.
11. 미확정 / 보류
- Eyebrow: ui_viz
contentheader는 Eyebrow(11→12px/700, letter-spacing .12em,#3E3523)를 필수 요소로 둔다. shadcn Card에 대응 요소가 없고 이번 범위에 추가하지 않았다. 현재 Title만 있는 Header는 ui_viz Anatomy의 부분 구현이다. - Divider(선택): ui_viz는 Header 뒤 24px + 1px + 18px의 선택 divider를 정의한다(
CONTENT_CARD_DIVIDER_OPTIONAL). 기본은 없음이라 구현하지 않았다. - Tabs Meta: Meta가 Tabs일 때 Header↔Body 간격이 0이 되는 예외는 구현하지 않았다.
- ui_viz 미정의: Description, Footer(
footer variant는 Out of Scope), Media,size="sm", 반응형, Card 폭. - 자식 clipping: ui_viz Type B는 하지 않음이다.
overflow-hidden을 제거했다. 대신 Footer 모서리를 CSS로 맞췄고 이미지 모서리는 shadcnrounded-*가 남아 있다. - LOCAL_EXCEPTION: ui_viz는 border 색 두 값을 전역 token이 아닌 컴포넌트 값으로 두고 tokenization을 follow-up으로 둔다. 현재 HM 구현은 공유 Semantic(
color.stroke.input.default,color.palette.brown-light-4)에 연결되어 있다(§6.1). Card 전용 Semantic/Runtime key로 분리할지는 미결정이며, 이번 정정에서 Token 연결을 바꾸지 않았다. - CardDescription HM 규격: Typography·색·간격 미정의. shadcn 호환 요소로만 둔다(§5.1).
- Type A Section Panel: 구현하지 않았고 미구현 상태를 유지한다. Demo
.hm-panel은 Type A 구현이 아니며 대체 Reference로 쓰지 않는다(§8.1). - Confirmed 표기 해석: border 1px·padding 24px 등은 ui_viz에서
Confirmed가 아니라 owner 채택 Canonical 값이다. Confirmed만 적용한다는 기준으로 보면 재확인이 필요한 항목이다.
12. 관련 파일
packages/ui/src/components/ui/card.tsx
packages/ui/src/theme/card.css
packages/ui/src/theme/primitives.css (--hm-brown-light-4, --hm-warm-neutral-pale)
packages/ui/src/theme/default.css (--hm-sem-stroke-input-default, --hm-sem-palette-brown-light-4 연결)
packages/ui/src/theme/contract.ts (Runtime key color.stroke.input.default, color.palette.brown-light-4)
themes/default.json (HM Default 컬러 원본)
packages/ui/src/theme.css (card.css import)
src/components/card.stories.tsx
src/components/story-board.tsx
src/patterns/dashboard-layout.stories.tsx (CardDescription 소비 예시)
src/App.tsx (Demo, hm-panel* class 사용)
src/hm-theme.css (.hm-panel* Demo 전용 스타일, §8.1)
13. 검증 상태
- TypeScript,
npm run build: 통과(2026-10-02). - Storybook computed style(대표 Story 4개 구역): background White/Beige(
rgb(255,255,255)/rgb(245,240,229)), border 1pxrgb(222,219,210)/rgb(236,212,162), radius 4px, shadow none, 위 padding 24px, Header·Content 좌우 24px, Header→Content 24px, overflow visible, Title 20px/28px/600rgb(10,34,29), column-gap 20px, Action 교차축 center, Action–Title 간격 20px — 문서 값과 일치. - Compatibility: Footer 아래 모서리 3px,
size="sm"위 padding 12px·Title 14px 확인. - Demo(App): 4개 Card가 border 1px/radius 4px/shadow none, 위 padding 16px, row-gap 0으로 렌더 확인. 이 값은 Demo
.hm-panel결과이며 운영 Card 값이 아니다(§8.1). - 2026-10-08 문서 정정: Border Token 연결(
card.css↔default.css↔contract.ts↔themes/default.json), CardDescription 사용처, Demo class 규칙을 코드와 대조했다.card.tsx·card.css는 주석만 고쳤고 CSS 값·Component 동작·공개 API·Story는 바꾸지 않았다. - Story:
All+ verification 3개 ID 확인, 기본 sidebar는All하나만 보이는 1뎁스 구조(실제 사이드바 화면은 확인하지 못함). - 미확인: 사이드바 실제 화면, 브라우저별 렌더, Demo 화면 시각 비교(그림자 제거로 달라짐), 이미지 포함 Card.