@olundot/ui · Surfaces
Card
새 페이지 영역을 만들지 않고 관련 콘텐츠, 상태, 액션을 묶는 경계 있는 서피스 컨테이너입니다.
- variant
- default · elevated(shadow) · interactive(hover 애니메이션) · notice(accent 테두리)
- 슬롯
- CardHeader · CardTitle · CardDescription · CardContent · CardFooter
- interactive
- hover -translate-y-[1px] + border-accent + elevation-2. 전체 카드가 단일 목적지일 때만
- 중첩
- elevated 안에 default 허용 — 인터랙티브 중첩 금지
기본
정적 (default · elevated · notice 3 variant 비교)기본 카드
관련 콘텐츠를 묶는 기본 컨테이너입니다.
카드 본문 내용이 여기에 들어갑니다.
강조 카드
레이어드 레이아웃에서 계층감을 주는 elevated 카드입니다.
그림자로 입체감을 더합니다.
알림 카드
시스템 안내나 주의사항을 표시하는 notice 카드입니다.
브랜드 컬러 테두리와 배경으로 주목도를 높입니다.
코드 보기tsx
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@olundot/ui";
export function CardBasic() {
return (
<div style={{ display: "grid", gap: "16px" }}>
<Card>
<CardHeader>
<CardTitle>기본 카드</CardTitle>
<CardDescription>관련 콘텐츠를 묶는 기본 컨테이너입니다.</CardDescription>
</CardHeader>
<CardContent>
<p>카드 본문 내용이 여기에 들어갑니다.</p>
</CardContent>
</Card>
<Card variant="elevated">
<CardHeader>
<CardTitle>강조 카드</CardTitle>
<CardDescription>레이어드 레이아웃에서 계층감을 주는 elevated 카드입니다.</CardDescription>
</CardHeader>
<CardContent>
<p>그림자로 입체감을 더합니다.</p>
</CardContent>
</Card>
<Card variant="notice">
<CardHeader>
<CardTitle>알림 카드</CardTitle>
<CardDescription>시스템 안내나 주의사항을 표시하는 notice 카드입니다.</CardDescription>
</CardHeader>
<CardContent>
<p>브랜드 컬러 테두리와 배경으로 주목도를 높입니다.</p>
</CardContent>
</Card>
</div>
);
}푸터 액션
정적 (CardFooter 액션 버튼 포함)모의고사 2회
32문항 · 45분 · 금요일까지
내과 · 외과 · 소아과 영역 통합 문제입니다. 제한 시간 내 완료해야 합니다.
코드 보기tsx
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@olundot/ui";
import { Button } from "@olundot/ui";
export function CardWithFooter() {
return (
<Card style={{ maxWidth: "400px" }}>
<CardHeader>
<CardTitle>모의고사 2회</CardTitle>
<CardDescription>32문항 · 45분 · 금요일까지</CardDescription>
</CardHeader>
<CardContent>
<p style={{ fontSize: "0.875rem", color: "var(--text-secondary)" }}>
내과 · 외과 · 소아과 영역 통합 문제입니다. 제한 시간 내 완료해야 합니다.
</p>
</CardContent>
<CardFooter>
<Button variant="outline" size="sm">미리보기</Button>
<Button variant="primary" size="sm">응시 시작</Button>
</CardFooter>
</Card>
);
}클릭 가능
라이브 (interactive variant — hover 애니메이션 + focus-visible)내과
강의 12개 · 문제 240문항
외과
강의 12개 · 문제 240문항
소아과
강의 12개 · 문제 240문항
코드 보기tsx
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@olundot/ui";
// interactive variant는 hover 시 -translate-y-[1px] + border-accent + elevation-2.
// 카드 전체가 단일 목적지인 경우에만 사용. 버튼/링크 중첩 금지.
export function CardInteractive() {
return (
<div style={{ display: "grid", gap: "12px" }}>
{["내과", "외과", "소아과"].map((subject) => (
<Card
key={subject}
variant="interactive"
tabIndex={0}
role="button"
aria-label={`${subject} 강의 목록 보기`}
onClick={() => {}}
onKeyDown={(e) => e.key === "Enter" && e.currentTarget.click()}
>
<CardHeader>
<CardTitle>{subject}</CardTitle>
<CardDescription>강의 12개 · 문제 240문항</CardDescription>
</CardHeader>
</Card>
))}
</div>
);
}중첩 레이아웃
정적 (Card 안에 Card — elevated 내부 default)2026년 1학기 시험 일정
전체 교과목 기준
내과학06월 15일
외과학06월 22일
코드 보기tsx
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@olundot/ui";
export function CardNested() {
return (
<Card variant="elevated">
<CardHeader>
<CardTitle>2026년 1학기 시험 일정</CardTitle>
<CardDescription>전체 교과목 기준</CardDescription>
</CardHeader>
<CardContent>
<div style={{ display: "grid", gap: "8px" }}>
{[
{ name: "내과학", date: "06월 15일", status: "예정" },
{ name: "외과학", date: "06월 22일", status: "예정" },
].map((exam) => (
<Card key={exam.name}>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<span style={{ fontWeight: 600 }}>{exam.name}</span>
<span style={{ fontSize: "0.875rem", color: "var(--text-tertiary)" }}>{exam.date}</span>
</div>
</Card>
))}
</div>
</CardContent>
</Card>
);
}Description wrap (1-2줄)
정적 (CardDescription = single semantic paragraph — wrap만 허용, 다중 단락 금지)1줄 설명
간결한 한 줄 설명입니다.
2줄 설명
두 줄에 걸친 설명입니다. 강의 제목이나 시험 안내처럼 조금 더 긴 맥락을 담을 때 사용합니다.
코드 보기tsx
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@olundot/ui";
export function CardDescriptionWrap() {
return (
<div style={{ display: "grid", gap: "16px" }}>
<Card>
<CardHeader>
<CardTitle>1줄 설명</CardTitle>
<CardDescription>간결한 한 줄 설명입니다.</CardDescription>
</CardHeader>
</Card>
<Card>
<CardHeader>
<CardTitle>2줄 설명</CardTitle>
<CardDescription>
두 줄에 걸친 설명입니다. 강의 제목이나 시험 안내처럼 조금 더 긴 맥락을 담을 때 사용합니다.
</CardDescription>
</CardHeader>
</Card>
</div>
);
}Multi-paragraph body (CardProse)
정적 (다중 단락/리스트 body — CardContent > CardProse 패턴)종합 모의고사 안내
내과학 · 외과학 · 소아과학 · 산부인과학
이번 모의고사는 본 시험과 동일한 형식으로 진행됩니다.
제한 시간 내 완료하지 않으면 자동 제출되며, 부분 점수는 인정되지 않습니다.
- 총 240문항 · 제한 시간 240분
- 안정적인 네트워크 환경 권장
- 응시 후 결과 확인은 즉시 가능
코드 보기tsx
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardProse } from "@olundot/ui";
// R14 — CardDescription은 single semantic paragraph 한정.
// 다중 단락/리스트/구조화 body는 CardContent > CardProse 조합으로.
export function CardMultiParagraph() {
return (
<Card>
<CardHeader>
<CardTitle>종합 모의고사 안내</CardTitle>
<CardDescription>내과학 · 외과학 · 소아과학 · 산부인과학</CardDescription>
</CardHeader>
<CardContent>
<CardProse>
<p>이번 모의고사는 본 시험과 동일한 형식으로 진행됩니다.</p>
<p>제한 시간 내 완료하지 않으면 자동 제출되며, 부분 점수는 인정되지 않습니다.</p>
<ul>
<li>총 240문항 · 제한 시간 240분</li>
<li>안정적인 네트워크 환경 권장</li>
<li>응시 후 결과 확인은 즉시 가능</li>
</ul>
</CardProse>
</CardContent>
</Card>
);
}Content density
정적 (density 3종 — compact / comfortable / spacious 패딩 비교)Compact
dense list item — 8×12px
내과학 · 문제 120문항 · 마감 D-3
Comfortable
default padding — var(--space-2)
내과학 · 문제 120문항 · 마감 D-3
Spacious
넓은 여백 — 24px
내과학 · 문제 120문항 · 마감 D-3
코드 보기tsx
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@olundot/ui";
// 현재는 CardContent 패딩으로 density를 조정합니다.
// 권장: compact=8px 12px / comfortable=16px(기본) / spacious=24px
export function CardDensity() {
return (
<div style={{ display: "grid", gap: "16px" }}>
<Card>
<CardHeader>
<CardTitle>Compact</CardTitle>
<CardDescription>dense list item — 8×12px</CardDescription>
</CardHeader>
<CardContent style={{ padding: "8px 12px" }}>
<p style={{ fontSize: "0.8125rem", color: "var(--text-secondary)" }}>
내과학 · 문제 120문항 · 마감 D-3
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Comfortable</CardTitle>
<CardDescription>default padding — var(--space-2)</CardDescription>
</CardHeader>
<CardContent>
<p style={{ fontSize: "0.875rem", color: "var(--text-secondary)" }}>
내과학 · 문제 120문항 · 마감 D-3
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Spacious</CardTitle>
<CardDescription>넓은 여백 — 24px</CardDescription>
</CardHeader>
<CardContent style={{ padding: "24px" }}>
<p style={{ fontSize: "0.875rem", color: "var(--text-secondary)" }}>
내과학 · 문제 120문항 · 마감 D-3
</p>
</CardContent>
</Card>
</div>
);
}