Chip
Data Input
짧은 텍스트나 상태·분류를 작게 담는 칩.
Usage
짧은 텍스트/상태/분류를 작게 표시하고 선택·제거·실행. 필터·태그·키워드
import
import
import { Chip } from '@mildang/design-system/Chip';예제를 복사해 쓸 때 필요한 준비
• @mildang/icons 를 따로 설치한다. DS 패키지에 아이콘 컴포넌트가 포함되지 않는다.
• @mildang/styled-system 은 이 저장소에서 Panda 가 생성하는 산출물이다. 저장소 안에서는 turbo run ship 이후 쓸 수 있고, 패키지 소비자는 자기 Panda 산출물이나 다른 레이아웃 수단으로 바꿔야 한다.
API Reference
Chip Props
Prop
Type
Default
"div" | "span"
div
boolean
false
string
지정 안 함
"brand" | "neutral" | "green" | "orange" | "blue" | "red"
neutral
ReactNode
지정 안 함
boolean
false
ReactNode
지정 안 함
string
지정 안 함
ReactNode
지정 안 함
boolean
false
((event: MouseEvent<HTMLElement, MouseEvent>) => void) & MouseEventHandler<HTMLDivElement>
지정 안 함
(event: KeyboardEvent<HTMLElement> | MouseEvent<HTMLButtonElement, MouseEvent>) => void
지정 안 함
"xs" | "sm" | "md" | "lg"
md
ReactNode
지정 안 함
SystemStyleObject | (SystemStyleObject & SystemStyleObject[])
지정 안 함
number
지정 안 함
"fill" | "outlined" | "text" | "transparent"
fill
크기
xs부터 lg까지 4단계다. 글자 크기·좌우 여백·아이콘 크기가 함께 바뀌고, 칩 높이는 그 조합의 결과다.
| size | 높이(px, mildang 기준) | 주 용도 |
|---|---|---|
lg | 32 | 가시성이 필요한 태그/선택(강조용), 멀티라인 최대 2줄 허용. 모바일 주요 필터·선택 토큰 등 터치 우선 화면 |
md(기본값) | 24 | 기본 사이즈(대부분의 화면). 필터/선택 토큰/카테고리 등 일반 용도 |
sm | 20 | 조밀 레이아웃(리스트·카드·테이블 보조 정보). 보조 액션/짧은 라벨 표기 |
xs | 18 | PC 전용 초밀도(테이블 셀 상태 태그). 정적 태그 위주, 모바일 핵심 입력은 비권장 |
높이는 mildang/school/goes 테마 기준이다. educore는 sm 20→24px, md 24→28px로
다르다(xs·lg는 동일).
Sizes
xs부터 lg까지 4단계 크기 스케일을 비교합니다.
import { Chip, type ChipProps } from '@mildang/design-system/Chip';
import { useState } from 'react';
const ChipArrayDemo = ({
color,
type,
size,
disabled,
}: Pick<ChipProps, 'color' | 'type' | 'size' | 'disabled'>) => {
const [items, setItems] = useState<string[]>(['Angular', 'Polymer', 'React', 'Vue.js']);
return (
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
{items.map((label) => (
<Chip
key={label}
label={label}
color={color}
type={type}
size={size}
disabled={disabled}
onDelete={() => setItems((prev) => prev.filter((l) => l !== label))}
/>
))}
</div>
);
};
const STORY_DEFAULT_ARGS = { ...({}), ...({ color: 'neutral', type: 'fill', size: 'md' }) } as ChipProps;
const ChipArrayDeletableExampleRender = (args: ChipProps) => <ChipArrayDemo {...args} />;
export default function ChipArrayDeletableExample(props: Partial<ChipProps>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ChipProps;
return ChipArrayDeletableExampleRender(mergedProps);
}
타입 · 색
type 4가지(fill/outlined/text/transparent)는 배경·테두리·글자색 슬롯을 채우는
방식이 다르고, color 6가지(brand/neutral/green/orange/blue/red)와
조합된다. 슬롯 매핑은 색마다 개별로 정의돼 있어 조합별로 값이 다르다.
예외가 하나 있다: fill+orange 조합만 다른 색들과 달리 전용 글자색 토큰을 쓴다 —
나머지 색은 공통 흰색 글자를 쓰지만, 오렌지 배경 위 흰 글자는 대비가 부족해 별도로
보정한 값이다.
Types
6가지 color와 4가지 type의 24개 조합을 한 화면에서 비교합니다.
코드
import { Chip, type ChipProps } from '@mildang/design-system/Chip';
import { ChipVariant } from '@mildang/styled-system/recipes';
const chipVariantValues = {
size: ['xs', 'sm', 'md', 'lg'] as ChipVariant['size'][],
color: ['brand', 'neutral', 'green', 'orange', 'blue', 'red'] as ChipVariant['color'][],
type: ['fill', 'outlined', 'text', 'transparent'] as ChipVariant['type'][],
multiline: [true, false] as ChipVariant['multiline'][],
};
const chipColorLabels: Record<NonNullable<ChipVariant['color']>, string> = {
brand: 'Brand',
neutral: 'Neutral',
green: 'Green',
orange: 'Orange',
blue: 'Blue',
red: 'Red',
};
const chipTypeLabels: Record<NonNullable<ChipVariant['type']>, string> = {
fill: 'Fill',
outlined: 'Outlined',
text: 'Text',
transparent: 'Transparent',
};
const STORY_DEFAULT_ARGS = { ...({}), ...({ size: 'md' }) } as ChipProps;
const ChipTypeColorMatrixExampleRender = (args: ChipProps) => (
<div style={{ display: 'grid', gap: 20 }}>
{chipVariantValues.type.map((type) => (
<div key={type} style={{ display: 'grid', gap: 8 }}>
<strong>{chipTypeLabels[type]}</strong>
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
{chipVariantValues.color.map((color) => (
<Chip
key={`${type}-${color}`}
{...args}
label={chipColorLabels[color]}
color={color}
type={type}
/>
))}
</div>
</div>
))}
</div>
);
export default function ChipTypeColorMatrixExample(props: Partial<ChipProps>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ChipProps;
return ChipTypeColorMatrixExampleRender(mergedProps);
}
예제
Action vs Input
onClick(Action)과 onDelete(Input) — Chip이 실제로 구분하는 두 상호작용입니다.
코드
import { Chip, type ChipProps } from '@mildang/design-system/Chip';
import Hashtag from '@mildang/icons/react/custom/hashtag';
import { ChipVariant } from '@mildang/styled-system/recipes';
const chipVariantValues = {
size: ['xs', 'sm', 'md', 'lg'] as ChipVariant['size'][],
color: ['brand', 'neutral', 'green', 'orange', 'blue', 'red'] as ChipVariant['color'][],
type: ['fill', 'outlined', 'text', 'transparent'] as ChipVariant['type'][],
multiline: [true, false] as ChipVariant['multiline'][],
};
const chipColorLabels: Record<NonNullable<ChipVariant['color']>, string> = {
brand: 'Brand',
neutral: 'Neutral',
green: 'Green',
orange: 'Orange',
blue: 'Blue',
red: 'Red',
};
const chipTypeLabels: Record<NonNullable<ChipVariant['type']>, string> = {
fill: 'Fill',
outlined: 'Outlined',
text: 'Text',
transparent: 'Transparent',
};
const STORY_DEFAULT_ARGS = { ...({}), ...({ size: 'md' }) } as ChipProps;
const ChipStatusStatesExampleRender = (args: ChipProps) => (
<div style={{ display: 'grid', gap: 32 }}>
{chipVariantValues.type.map((type) => (
<div key={type} style={{ display: 'grid', gap: 8 }}>
<strong>{type ? chipTypeLabels[type] : ''}</strong>
<div
style={{
display: 'grid',
// `auto` 최대 트랙은 남는 폭을 흡수하고, grid item 은 기본적으로 stretch 되므로
// Chip(Figma 정본 HUG = `inline-flex`)이 셀 폭까지 늘어난다.
// 늘어난 칩은 `justifyContent: center` 때문에 좌우 패딩이 커진 것처럼 보인다.
// → 트랙 최대치를 `max-content` 로, item 정렬을 `start` 로 고정해 고유 폭을 유지한다.
gridTemplateColumns: '96px repeat(6, minmax(72px, max-content))',
gap: 12,
alignItems: 'center',
justifyItems: 'start',
}}
>
<span />
{chipVariantValues.color.map((color) => (
<span key={color} style={{ fontSize: 12, color: '#8b8b8b' }}>
{color ? chipColorLabels[color] : ''}
</span>
))}
<span style={{ fontSize: 12, color: '#8b8b8b' }}>default</span>
{chipVariantValues.color.map((color) => (
<Chip
key={color}
{...args}
type={type}
color={color}
label="Chips"
startIcon={<Hashtag />}
onDelete={() => {}}
/>
))}
{/* hover 행: pseudo 애드온이 :hover 를 강제. onDelete(hasDelete) 로 clickable=true → 오버레이 CSS 생성 */}
<span style={{ fontSize: 12, color: '#8b8b8b' }}>hover</span>
{chipVariantValues.color.map((color) => (
<Chip
key={color}
{...args}
type={type}
color={color}
label="Chips"
startIcon={<Hashtag />}
onDelete={() => {}}
id={`chip-${type}-${color}-hover`}
/>
))}
</div>
</div>
))}
</div>
);
export default function ChipStatusStatesExample(props: Partial<ChipProps>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ChipProps;
return ChipStatusStatesExampleRender(mergedProps);
}
TypeGuide
import { Chip, type ChipProps } from '@mildang/design-system/Chip';
const STORY_DEFAULT_ARGS = { ...({}), ...({ color: 'green', size: 'md' }) } as ChipProps;
const ChipTypeGuideExampleRender = (args: ChipProps) => (
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, auto)', gap: 24 }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, alignItems: 'center' }}>
<strong>Fill</strong>
<Chip {...args} label="Chips" type="fill" />
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, alignItems: 'center' }}>
<strong>Text</strong>
<Chip {...args} label="Chips" type="text" />
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, alignItems: 'center' }}>
<strong>Transparency</strong>
<Chip {...args} label="Chips" type="transparent" />
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, alignItems: 'center' }}>
<strong>Outline</strong>
<Chip {...args} label="Chips" type="outlined" />
</div>
</div>
);
export default function ChipTypeGuideExample(props: Partial<ChipProps>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ChipProps;
return ChipTypeGuideExampleRender(mergedProps);
}
IconGuide
코드
import { Chip, type ChipProps } from '@mildang/design-system/Chip';
import { Fragment } from 'react';
import { ChipVariant } from '@mildang/styled-system/recipes';
import Hashtag from '@mildang/icons/react/custom/hashtag';
const chipVariantValues = {
size: ['xs', 'sm', 'md', 'lg'] as ChipVariant['size'][],
color: ['brand', 'neutral', 'green', 'orange', 'blue', 'red'] as ChipVariant['color'][],
type: ['fill', 'outlined', 'text', 'transparent'] as ChipVariant['type'][],
multiline: [true, false] as ChipVariant['multiline'][],
};
const chipColorLabels: Record<NonNullable<ChipVariant['color']>, string> = {
brand: 'Brand',
neutral: 'Neutral',
green: 'Green',
orange: 'Orange',
blue: 'Blue',
red: 'Red',
};
const chipTypeLabels: Record<NonNullable<ChipVariant['type']>, string> = {
fill: 'Fill',
outlined: 'Outlined',
text: 'Text',
transparent: 'Transparent',
};
const iconConfigs = [
{ label: 'None', props: {} },
{ label: 'Left icon', props: { startIcon: <Hashtag /> } },
{ label: 'Right icon', props: { onDelete: () => {} } },
{ label: 'Left + Right', props: { startIcon: <Hashtag />, onDelete: () => {} } },
];
const STORY_DEFAULT_ARGS = { ...({}), ...({ size: 'md' }) } as ChipProps;
const ChipIconGuideExampleRender = (args: ChipProps) => (
<div style={{ display: 'flex', flexDirection: 'column', gap: 32 }}>
{chipVariantValues.type.map((type) => (
<div key={type}>
<p style={{ fontWeight: 700, marginBottom: 12 }}>{chipTypeLabels[type]}</p>
<div
style={{
display: 'grid',
gridTemplateColumns: `120px repeat(${iconConfigs.length}, auto)`,
gap: 12,
alignItems: 'center',
}}
>
<div />
{iconConfigs.map(({ label }) => (
<strong key={label} style={{ textAlign: 'center', fontSize: 12 }}>
{label}
</strong>
))}
{chipVariantValues.color.map((color) => (
<Fragment key={color}>
<strong style={{ fontSize: 12 }}>{chipColorLabels[color]}</strong>
{iconConfigs.map(({ label, props }) => (
<div key={label} style={{ display: 'flex', justifyContent: 'center' }}>
<Chip {...args} label="Chips" color={color} type={type} {...props} />
</div>
))}
</Fragment>
))}
</div>
</div>
))}
</div>
);
export default function ChipIconGuideExample(props: Partial<ChipProps>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ChipProps;
return ChipIconGuideExampleRender(mergedProps);
}
Multiline
import { Chip, type ChipProps } from '@mildang/design-system/Chip';
const STORY_DEFAULT_ARGS = { ...({}), ...({
label: 'This is a chip that has multiple lines. ',
color: 'green',
type: 'fill',
multiline: true,
}) } as ChipProps;
const ChipMultilineExampleRender = (args: ChipProps) => (
<div style={{ width: '100px' }}>
<Chip {...args} />
</div>
);
export default function ChipMultilineExample(props: Partial<ChipProps>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ChipProps;
return ChipMultilineExampleRender(mergedProps);
}
Ellipsis
import { Chip, type ChipProps } from '@mildang/design-system/Chip';
const STORY_DEFAULT_ARGS = { ...({}), ...({
label: 'This is a chip that has multiple lines. ',
color: 'green',
type: 'fill',
}) } as ChipProps;
const ChipEllipsisExampleRender = (args: ChipProps) => (
<div style={{ width: '100px' }}>
<Chip {...args} />
</div>
);
export default function ChipEllipsisExample(props: Partial<ChipProps>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ChipProps;
return ChipEllipsisExampleRender(mergedProps);
}
Demo
import type { ComponentProps } from 'react';
import { Chip, type ChipProps } from '@mildang/design-system/Chip';
const STORY_DEFAULT_ARGS = { ...({}), ...({
label: 'Chip',
color: 'neutral',
type: 'fill',
size: 'md',
// 문자열 키로 둔다 — argTypes 의 `mapping` 이 실제 ReactNode 로 변환한다
startIcon: 'none',
endIcon: 'none',
onDelete: false,
} as unknown as ChipProps) } as ComponentProps<typeof Chip>;
export default function ChipDemoExample(props: Partial<ComponentProps<typeof Chip>> = {}) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof Chip>;
return <Chip {...mergedProps} />;
}