StatusBadge
status × variant 조합으로 시맨틱 상태를 시각적으로 표현하는 Compound 배지 컴포넌트.
개요
StatusBadge는 Compound Pattern 기반의 상태 배지 컴포넌트입니다. StatusBadge.Root가 context를 통해 status, variant, size를 하위 서브컴포넌트에 전달합니다.
- 6가지
status—default,informative,primary,success,warning,error로 의미 표현 - 4가지
variant—filled,tint,outline,subtle로 시각 강도 조절 - 4가지
size—sm,md,lg,xl. 반응형 객체{ mobile, tablet, desktop, wide }지원 - Compound 서브컴포넌트 —
StatusBadge.Dot,StatusBadge.Icon,StatusBadge.Text,StatusBadge.Loader를 자유롭게 조합 iconOnly모드 — padding 제거,width = height로 원형 배지로 변환renderprop — 기본<div>를<button>,<a>등 다른 요소로 교체 가능
언제 사용하나요
- 항목의 현재 상태(진행 중, 완료, 오류, 경고 등)를 레이블로 표시할 때
- 테이블 셀, 목록 항목, 카드에서 상태를 한눈에 구분해야 할 때
언제 사용하면 안 되나요
- 단순한 숫자 카운트 표시는 별도 뱃지(숫자 배지) 컴포넌트를 사용하세요.
- 필터 칩이나 선택 가능한 태그는
Tag컴포넌트를 사용하세요. - 텍스트 레이블만 필요하고 상태 의미가 없다면
Typo컴포넌트로 충분합니다.
접근성
StatusBadge는 기본적으로 <div> 렌더링으로 시맨틱 역할이 없습니다. 소비자가 의미에 맞게 적절한 속성을 추가해야 합니다.
- 상태를 스크린리더에 전달하려면 상위 또는 인접 요소에
aria-label이나aria-describedby를 추가하세요. render={<button />}으로 변환 시 키보드 포커스와Enter/Space동작이 자동으로 지원됩니다.StatusBadge.Loader의 로딩 인디케이터는 시각적 요소이므로aria-label="로딩 중"등을 별도로 추가하는 것을 권장합니다.StatusBadge.Icon내 아이콘 SVG에는aria-hidden="true"가 자동 적용됩니다.
| 역할 | 권장 속성 |
|---|---|
| 단순 상태 표시 | 부모 요소에 aria-label |
| 클릭 가능한 트리거 | render={<button />} + aria-label |
| 로딩 상태 | aria-live="polite" 영역과 함께 사용 |
Usage
기본 사용법
status와 variant를 조합해 의미와 시각 강도를 결정합니다.
<VStack $css={{ gap: '$spacing-200', alignItems: 'flex-start' }}> <HStack $css={{ gap: '$spacing-150', flexWrap: 'wrap' }}> <StatusBadge.Root status="default" variant="filled"><StatusBadge.Text>Default</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="informative" variant="filled"><StatusBadge.Text>Informative</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="primary" variant="filled"><StatusBadge.Text>Primary</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="success" variant="filled"><StatusBadge.Text>Success</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="warning" variant="filled"><StatusBadge.Text>Warning</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="error" variant="filled"><StatusBadge.Text>Error</StatusBadge.Text></StatusBadge.Root> </HStack> <HStack $css={{ gap: '$spacing-150', flexWrap: 'wrap' }}> <StatusBadge.Root status="default" variant="tint"><StatusBadge.Text>Default</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="informative" variant="tint"><StatusBadge.Text>Informative</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="primary" variant="tint"><StatusBadge.Text>Primary</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="success" variant="tint"><StatusBadge.Text>Success</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="warning" variant="tint"><StatusBadge.Text>Warning</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="error" variant="tint"><StatusBadge.Text>Error</StatusBadge.Text></StatusBadge.Root> </HStack> <HStack $css={{ gap: '$spacing-150', flexWrap: 'wrap' }}> <StatusBadge.Root status="default" variant="outline"><StatusBadge.Text>Default</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="informative" variant="outline"><StatusBadge.Text>Informative</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="primary" variant="outline"><StatusBadge.Text>Primary</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="success" variant="outline"><StatusBadge.Text>Success</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="warning" variant="outline"><StatusBadge.Text>Warning</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="error" variant="outline"><StatusBadge.Text>Error</StatusBadge.Text></StatusBadge.Root> </HStack> <HStack $css={{ gap: '$spacing-150', flexWrap: 'wrap' }}> <StatusBadge.Root status="default" variant="subtle"><StatusBadge.Text>Default</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="informative" variant="subtle"><StatusBadge.Text>Informative</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="primary" variant="subtle"><StatusBadge.Text>Primary</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="success" variant="subtle"><StatusBadge.Text>Success</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="warning" variant="subtle"><StatusBadge.Text>Warning</StatusBadge.Text></StatusBadge.Root> <StatusBadge.Root status="error" variant="subtle"><StatusBadge.Text>Error</StatusBadge.Text></StatusBadge.Root> </HStack> </VStack>
Dot + Text
StatusBadge.Dot은 context의 status와 variant를 읽어 색상을 자동 결정합니다. variant="filled"이면 항상 white dot을 사용하고, 그 외에는 status별 색상을 사용합니다.
<HStack $css={{ gap: '$spacing-150', flexWrap: 'wrap' }}> <StatusBadge.Root status="default" variant="tint"> <StatusBadge.Dot /> <StatusBadge.Text>Default</StatusBadge.Text> </StatusBadge.Root> <StatusBadge.Root status="success" variant="tint"> <StatusBadge.Dot /> <StatusBadge.Text>Success</StatusBadge.Text> </StatusBadge.Root> <StatusBadge.Root status="warning" variant="tint"> <StatusBadge.Dot /> <StatusBadge.Text>Warning</StatusBadge.Text> </StatusBadge.Root> <StatusBadge.Root status="error" variant="tint"> <StatusBadge.Dot /> <StatusBadge.Text>Error</StatusBadge.Text> </StatusBadge.Root> <StatusBadge.Root status="primary" variant="tint"> <StatusBadge.Dot /> <StatusBadge.Text>Primary</StatusBadge.Text> </StatusBadge.Root> <StatusBadge.Root status="informative" variant="tint"> <StatusBadge.Dot /> <StatusBadge.Text>Informative</StatusBadge.Text> </StatusBadge.Root> </HStack>
Icon + Text
StatusBadge.Icon은 context의 size에 따라 내부 아이콘 크기를 자동으로 조절합니다. 아이콘을 children으로 전달하면 됩니다.
<HStack $css={{ gap: '$spacing-150', flexWrap: 'wrap' }}> <StatusBadge.Root status="success" variant="tint"> <StatusBadge.Icon><IconCheckOutline /></StatusBadge.Icon> <StatusBadge.Text>완료</StatusBadge.Text> </StatusBadge.Root> <StatusBadge.Root status="warning" variant="tint"> <StatusBadge.Icon><IconWarningOutline /></StatusBadge.Icon> <StatusBadge.Text>경고</StatusBadge.Text> </StatusBadge.Root> <StatusBadge.Root status="error" variant="tint"> <StatusBadge.Icon><IconInformationOutline /></StatusBadge.Icon> <StatusBadge.Text>오류</StatusBadge.Text> </StatusBadge.Root> <StatusBadge.Root status="primary" variant="filled"> <StatusBadge.Icon><IconRefreshOutline /></StatusBadge.Icon> <StatusBadge.Text>처리 중</StatusBadge.Text> </StatusBadge.Root> </HStack>
Loader
비동기 처리 중 상태를 나타낼 때 StatusBadge.Loader를 사용합니다. 텍스트 앞(leading) 또는 뒤(trailing)에 배치할 수 있습니다.
<VStack $css={{ gap: '$spacing-200', alignItems: 'flex-start' }}> <HStack $css={{ gap: '$spacing-150', flexWrap: 'wrap' }}> <StatusBadge.Root status="primary" variant="filled" size="md"> <StatusBadge.Loader /> <StatusBadge.Text>로딩 중</StatusBadge.Text> </StatusBadge.Root> <StatusBadge.Root status="default" variant="tint" size="md"> <StatusBadge.Loader /> <StatusBadge.Text>처리 중</StatusBadge.Text> </StatusBadge.Root> </HStack> <HStack $css={{ gap: '$spacing-150', flexWrap: 'wrap' }}> <StatusBadge.Root status="default" variant="filled" size="md"> <StatusBadge.Text>처리 중</StatusBadge.Text> <StatusBadge.Loader /> </StatusBadge.Root> <StatusBadge.Root status="default" variant="tint" size="md"> <StatusBadge.Text>처리 중</StatusBadge.Text> <StatusBadge.Loader /> </StatusBadge.Root> <StatusBadge.Root status="default" variant="outline" size="md"> <StatusBadge.Text>처리 중</StatusBadge.Text> <StatusBadge.Loader /> </StatusBadge.Root> <StatusBadge.Root status="default" variant="subtle" size="md"> <StatusBadge.Text>처리 중</StatusBadge.Text> <StatusBadge.Loader /> </StatusBadge.Root> </HStack> </VStack>
크기
sm부터 xl까지 네 단계를 제공합니다. 높이, padding, gap, 아이콘 크기, typography가 함께 조정됩니다.
<HStack $css={{ gap: '$spacing-200', alignItems: 'center', flexWrap: 'wrap' }}> <StatusBadge.Root status="primary" variant="filled" size="sm"> <StatusBadge.Dot /><StatusBadge.Text>sm</StatusBadge.Text> </StatusBadge.Root> <StatusBadge.Root status="primary" variant="filled" size="md"> <StatusBadge.Dot /><StatusBadge.Text>md</StatusBadge.Text> </StatusBadge.Root> <StatusBadge.Root status="primary" variant="filled" size="lg"> <StatusBadge.Dot /><StatusBadge.Text>lg</StatusBadge.Text> </StatusBadge.Root> <StatusBadge.Root status="primary" variant="filled" size="xl"> <StatusBadge.Dot /><StatusBadge.Text>xl</StatusBadge.Text> </StatusBadge.Root> </HStack>
크기 Override (own > ctx)
StatusBadge.Root.size는 Icon / Loader 등에 컨텍스트로 전파됩니다. 개별 sub-component에 자체 size를 명시하면 컨텍스트보다 우선합니다.
<HStack $css={{ gap: '$spacing-300', alignItems: 'center' }}> <StatusBadge.Root size="md" status="primary" variant="filled"> <StatusBadge.Dot /> <StatusBadge.Text>md (ctx 폴백)</StatusBadge.Text> <StatusBadge.Icon><IconChevronRightOutline /></StatusBadge.Icon> </StatusBadge.Root> <StatusBadge.Root size="md" status="primary" variant="filled"> <StatusBadge.Dot /> <StatusBadge.Text>icon=lg (own)</StatusBadge.Text> <StatusBadge.Icon size="lg"><IconChevronRightOutline /></StatusBadge.Icon> </StatusBadge.Root> </HStack>
Trailing Loader
StatusBadge.Loader를 Text 뒤에 배치하면 "처리 중" 같은 trailing loader 패턴이 됩니다.
<HStack $css={{ gap: '$spacing-200', alignItems: 'center' }}> {['filled', 'tint', 'soft', 'outline'].map((v) => ( <StatusBadge.Root key={v} status="default" variant={v} size="md"> <StatusBadge.Text>처리 중</StatusBadge.Text> <StatusBadge.Loader /> </StatusBadge.Root> ))} </HStack>
반응형 크기
size에 breakpoint 객체를 전달하면 화면 너비에 따라 크기가 변합니다.
<StatusBadge.Root status="success" variant="tint" size={{ mobile: 'sm', desktop: 'lg' }}> <StatusBadge.Dot /> <StatusBadge.Text>반응형 — 리사이즈해보세요</StatusBadge.Text> </StatusBadge.Root>
Icon Only 모드
iconOnly prop을 사용하면 padding이 제거되고 width = height로 원형 배지가 됩니다. status × variant 색상은 그대로 유지됩니다.
<HStack $css={{ gap: '$spacing-200', alignItems: 'center', flexWrap: 'wrap' }}> <StatusBadge.Root status="success" variant="filled" size="md" iconOnly> <StatusBadge.Dot /> </StatusBadge.Root> <StatusBadge.Root status="error" variant="tint" size="md" iconOnly> <StatusBadge.Icon><IconInformationOutline /></StatusBadge.Icon> </StatusBadge.Root> <StatusBadge.Root status="primary" variant="outline" size="lg" iconOnly> <StatusBadge.Dot /> </StatusBadge.Root> <StatusBadge.Root status="warning" variant="filled" size="xl" iconOnly> <StatusBadge.Loader /> </StatusBadge.Root> </HStack>
실제 사용 맥락
테이블이나 목록에서 항목 상태를 표시하는 전형적인 사용 예입니다.
<VStack $css={{ gap: '$spacing-0', width: '400px' }}> {[ { name: '주문 #1024', status: 'success', label: '배송 완료' }, { name: '주문 #1025', status: 'warning', label: '배송 지연' }, { name: '주문 #1026', status: 'primary', label: '처리 중' }, { name: '주문 #1027', status: 'error', label: '취소됨' }, ].map(({ name, status, label }) => ( <HStack key={name} $css={{ justifyContent: 'space-between', alignItems: 'center', padding: '$spacing-200', borderBottom: '1px solid $border-default' }}> <Typo variant="$body-2">{name}</Typo> <StatusBadge.Root status={status} variant="tint" size="sm"> <StatusBadge.Dot /> <StatusBadge.Text>{label}</StatusBadge.Text> </StatusBadge.Root> </HStack> ))} </VStack>
$css로 커스터마이징
$css prop으로 디자인 토큰 기반 스타일을 추가할 수 있습니다.
<HStack $css={{ gap: '$spacing-200', flexWrap: 'wrap' }}> <StatusBadge.Root status="primary" variant="tint" $css={{ minWidth: '80px', justifyContent: 'center' }}> <StatusBadge.Text>중앙 정렬</StatusBadge.Text> </StatusBadge.Root> <StatusBadge.Root status="success" variant="filled" $css={{ borderRadius: '$radius-100' }}> <StatusBadge.Text>각진 배지</StatusBadge.Text> </StatusBadge.Root> </HStack>
Ellipsis Overflow (소비자 opt-in)
StatusBadge.Root는 기본적으로 white-space: nowrap + width: fit-content + flex-shrink: 0이라 자기 크기를 유지합니다. 좁은 컨테이너에서 매우 긴 라벨이 들어오면 컨테이너를 가로로 overflow하므로, 한 줄 ellipsis가 필요하면 StatusBadge.Root에 maxWidth/minWidth를 잡고 StatusBadge.Text에 $css로 opt-in합니다.
<HStack $css={{ gap: '$spacing-200', width: '200px', padding: '$spacing-200', border: '1px dashed $border-2' }}> <StatusBadge.Root status="warning" variant="tint" $css={{ maxWidth: '100%', minWidth: 0 }}> <StatusBadge.Dot /> <StatusBadge.Text $css={{ overflow: 'hidden', textOverflow: 'ellipsis' }}> 배송 지연 — 자세한 사유 확인 필요 </StatusBadge.Text> </StatusBadge.Root> </HStack>
CoreStatusBadge에서 마이그레이션
CoreStatusBadge는 단일 컴포넌트에 text, leadingElement, trailingElement를 props로 전달하는 방식이었습니다. StatusBadge는 Compound Pattern으로 재설계되어 서브컴포넌트를 직접 조합합니다.
Before (CoreStatusBadge)
import { CoreStatusBadge } from '@featuring-corp/components';
import { IconErrorFilled } from '@featuring-corp/icons';
<CoreStatusBadge
text="오류 발생"
status="error"
type="outline"
size="md"
leadingElement={{ dot: true }}
/>
<CoreStatusBadge
text="처리 중"
status="primary"
type="filled"
size="md"
leadingElement={{ loader: { color: 'primary' } }}
/>
<CoreStatusBadge
text="Badge"
status="success"
type="tint"
size="md"
leadingElement={<IconCheckCircleFilled size="$icon-xs" />}
trailingElement={<IconCaretDownFilled size="$icon-xs" />}
/>After (StatusBadge)
import { StatusBadge } from '@featuring-corp/components';
import { IconCheckCircleFilled, IconCaretDownFilled } from '@featuring-corp/icons';
<StatusBadge.Root status="error" variant="outline" size="md">
<StatusBadge.Dot />
<StatusBadge.Text>오류 발생</StatusBadge.Text>
</StatusBadge.Root>
<StatusBadge.Root status="primary" variant="filled" size="md">
<StatusBadge.Loader />
<StatusBadge.Text>처리 중</StatusBadge.Text>
</StatusBadge.Root>
<StatusBadge.Root status="success" variant="tint" size="md" render={<button />}>
<StatusBadge.Icon><IconCheckCircleFilled /></StatusBadge.Icon>
<StatusBadge.Text>Badge</StatusBadge.Text>
<StatusBadge.Icon><IconCaretDownFilled /></StatusBadge.Icon>
</StatusBadge.Root>| CoreStatusBadge | StatusBadge | 비고 |
|---|---|---|
type | variant | prop 이름 변경 |
text="..." | <StatusBadge.Text>...</StatusBadge.Text> | 서브컴포넌트로 변경 |
leadingElement={{ dot: true }} | <StatusBadge.Dot /> | 서브컴포넌트로 변경 |
leadingElement={{ loader: {...} }} | <StatusBadge.Loader /> | 서브컴포넌트로 변경 |
leadingElement={<Icon />} | <StatusBadge.Icon><Icon /></StatusBadge.Icon> | 서브컴포넌트로 변경 |
trailingElement={<Icon />} | trailing 위치에 <StatusBadge.Icon> 배치 | 위치는 JSX 순서로 결정 |
반응형 size 없음 | size={{ mobile: 'sm', desktop: 'lg' }} | 신규 기능 |
iconOnly 없음 | iconOnly prop | 신규 기능 |
render prop 없음 | render={<button />} 등 | 신규 기능 |
Props
공통 Props —
$css,render,className,style는 모든 서브컴포넌트에서 지원됩니다. useRenderComponent 가이드 →
StatusBadge.Root
기본 태그는 <div>입니다.
Prop
Type
StatusBadge.Dot
StatusBadge.Root context에서 status와 size를 읽어 색상과 크기를 자동 결정합니다. props를 직접 지정하면 context 값을 오버라이드합니다.
Prop
Type
StatusBadge.Icon
context의 size에 따라 내부 아이콘 크기를 자동 조절하는 wrapper입니다. 기본 태그는 <span>입니다.
Prop
Type
StatusBadge.Text
내부적으로 Typo를 사용합니다. context의 size에 맞는 typography variant와 status × variant에 맞는 텍스트 색상을 자동 적용합니다.
Prop
Type
StatusBadge.Loader
내부적으로 Loader 컴포넌트를 사용합니다. size를 명시하지 않으면 부모 StatusBadge.Root의 size에서 자동 매핑됩니다 — sm/md는 xxs(8px), lg/xl은 xs(16px).
Prop
Type
스타일
기본 스타일 속성
display:inline-flexwidth:fit-contentjustify-content:centeralign-items:centerborder-radius:radius-fullborder:1px solidflex-shrink:0white-space:nowrap—StatusBadge.Text는 줄바꿈되지 않습니다. ellipsis가 필요하면 위 Ellipsis Overflow 참고transition:0.2s
Size Variants
| Size | Height | Padding X | Gap | Icon Size | Loader Size | Typography |
|---|---|---|---|---|---|---|
sm | 16px | spacing-100 (4px) | spacing-50 (2px) | 12px | xxs (8px) | $body-1 |
md | 20px | spacing-150 (6px) | spacing-100 (4px) | 12px | xxs (8px) | $body-1 |
lg | 24px | spacing-200 (8px) | spacing-150 (6px) | 14px | xs (16px) | $body-2 |
xl | 30px | spacing-250 (10px) | spacing-200 (8px) | 16px | xs (16px) | $body-3 |
Status × Variant 색상 토큰
| Status | Variant | Background | Border | Text |
|---|---|---|---|---|
default | filled | gray-40 | gray-40 | white |
default | tint | gray-10 | border-1 | text-3 |
default | outline | white | border-1 | text-3 |
default | subtle | — | — | text-3 |
error | filled | support-error-3 | support-error-3 | white |
error | tint | support-error-1 | support-error-2 | support-error-4 |
error | outline | white | support-error-2 | support-error-4 |
warning | filled | support-warning-3 | support-warning-3 | white |
warning | tint | support-warning-1 | support-warning-2 | support-warning-4 |
success | filled | support-success-3 | support-success-3 | white |
success | tint | support-success-1 | support-success-2 | support-success-4 |
primary | filled | support-info-3 | support-info-3 | white |
primary | tint | support-info-1 | support-info-2 | support-info-4 |
informative | filled | gray-90 | gray-90 | white |
informative | tint | background-2 | border-2 | text-2 |
Dot 자동 매핑
StatusBadge.Dot은 variant="filled"이면 항상 white dot을, 그 외에는 status별 색상을 사용합니다.
| Status | Dot Color (non-filled) |
|---|---|
default | gray |
informative | black |
primary | purple |
success | green |
warning | orange |
error | red |
Badge size와 Dot size 자동 매핑:
| Badge Size | Dot Size |
|---|---|
sm | xs (6px) |
md | xs (6px) |
lg | sm (8px) |
xl | md (10px) |
StatusBadgeSize
Prop
Type
StatusBadgeStatus
Prop
Type
StatusBadgeVariant
Prop
Type