Featuring Design System
ComponentsBadge

StatusBadge

status × variant 조합으로 시맨틱 상태를 시각적으로 표현하는 Compound 배지 컴포넌트.

개요

StatusBadgeCompound Pattern 기반의 상태 배지 컴포넌트입니다. StatusBadge.Root가 context를 통해 status, variant, size를 하위 서브컴포넌트에 전달합니다.

  • 6가지 statusdefault, informative, primary, success, warning, error로 의미 표현
  • 4가지 variantfilled, tint, outline, subtle로 시각 강도 조절
  • 4가지 sizesm, md, lg, xl. 반응형 객체 { mobile, tablet, desktop, wide } 지원
  • Compound 서브컴포넌트StatusBadge.Dot, StatusBadge.Icon, StatusBadge.Text, StatusBadge.Loader를 자유롭게 조합
  • iconOnly 모드 — padding 제거, width = height로 원형 배지로 변환
  • render prop — 기본 <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

기본 사용법

statusvariant를 조합해 의미와 시각 강도를 결정합니다.

<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의 statusvariant를 읽어 색상을 자동 결정합니다. 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.sizeIcon / 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.LoaderText 뒤에 배치하면 "처리 중" 같은 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.RootmaxWidth/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>
CoreStatusBadgeStatusBadge비고
typevariantprop 이름 변경
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에서 statussize를 읽어 색상과 크기를 자동 결정합니다. 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.Rootsize에서 자동 매핑됩니다 — sm/mdxxs(8px), lg/xlxs(16px).

Prop

Type

스타일

기본 스타일 속성

  • display: inline-flex
  • width: fit-content
  • justify-content: center
  • align-items: center
  • border-radius: radius-full
  • border: 1px solid
  • flex-shrink: 0
  • white-space: nowrapStatusBadge.Text는 줄바꿈되지 않습니다. ellipsis가 필요하면 위 Ellipsis Overflow 참고
  • transition: 0.2s

Size Variants

SizeHeightPadding XGapIcon SizeLoader SizeTypography
sm16pxspacing-100 (4px)spacing-50 (2px)12pxxxs (8px)$body-1
md20pxspacing-150 (6px)spacing-100 (4px)12pxxxs (8px)$body-1
lg24pxspacing-200 (8px)spacing-150 (6px)14pxxs (16px)$body-2
xl30pxspacing-250 (10px)spacing-200 (8px)16pxxs (16px)$body-3

Status × Variant 색상 토큰

StatusVariantBackgroundBorderText
defaultfilledgray-40gray-40white
defaulttintgray-10border-1text-3
defaultoutlinewhiteborder-1text-3
defaultsubtletext-3
errorfilledsupport-error-3support-error-3white
errortintsupport-error-1support-error-2support-error-4
erroroutlinewhitesupport-error-2support-error-4
warningfilledsupport-warning-3support-warning-3white
warningtintsupport-warning-1support-warning-2support-warning-4
successfilledsupport-success-3support-success-3white
successtintsupport-success-1support-success-2support-success-4
primaryfilledsupport-info-3support-info-3white
primarytintsupport-info-1support-info-2support-info-4
informativefilledgray-90gray-90white
informativetintbackground-2border-2text-2

Dot 자동 매핑

StatusBadge.Dotvariant="filled"이면 항상 white dot을, 그 외에는 status별 색상을 사용합니다.

StatusDot Color (non-filled)
defaultgray
informativeblack
primarypurple
successgreen
warningorange
errorred

Badge size와 Dot size 자동 매핑:

Badge SizeDot Size
smxs (6px)
mdxs (6px)
lgsm (8px)
xlmd (10px)

StatusBadgeSize

Prop

Type

StatusBadgeStatus

Prop

Type

StatusBadgeVariant

Prop

Type