Props 타입 추출
emotion-variants는 설정에서 variant 타입을 알아서 추론해요. 이 레시피에서는 ev() 결과에서 React 컴포넌트 props를 끌어내, 컴포넌트의 prop 타입과 스타일이 항상 같이 움직이도록 만드는 방법을 다뤄요.
재사용할 수 있는 VariantProps 헬퍼
emotion-variants는 VariantProps 헬퍼를 기본 제공하지 않아요. 하지만 프로젝트 안에 한 줄로 정의해 두면 돼요.
ts
// types.ts (앱 또는 디자인 시스템 내부)
export type VariantProps<T extends (...args: any) => any> =
NonNullable<Parameters<T>[0]>;이제 어떤 ev() 결과에서도 variant 타입을 끌어낼 수 있어요.
ts
import { ev } from 'emotion-variants';
import type { VariantProps } from './types';
const button = ev({
variants: {
size: {
small: {},
medium: {},
large: {},
},
color: {
primary: {},
danger: {},
},
},
});
type ButtonVariants = VariantProps<typeof button>;
// { size?: 'small' | 'medium' | 'large'; color?: 'primary' | 'danger' }variants.size에 새 값을 추가하면 ButtonVariants['size']도 자동으로 따라와요.
HTML 요소 props와 결합
button처럼 HTML 요소를 감싸는 컴포넌트라면 ComponentPropsWithoutRef와 교집합 타입으로 묶어 줘요.
tsx
import type { ComponentPropsWithoutRef } from 'react';
export type ButtonProps = ComponentPropsWithoutRef<'button'> & ButtonVariants;
export function Button({ size, color, ...rest }: ButtonProps) {
return <button css={button({ size, color })} {...rest} />;
}기본값과 함께 쓰기
기본값은 defaultVariants 한 곳에만 선언하고, props는 그대로 넘기세요. 생략된 prop은 undefined로 도착해도 기본값으로 해석되기 때문에, prop 경계에 기본값을 한 번 더 두면 두 값이 어긋날 자리만 생겨요.
tsx
const button = ev({
variants: {
/* ... */
},
defaultVariants: {
size: 'medium',
color: 'primary',
},
});
export function Button({ size, color, ...rest }: ButtonProps) {
return <button css={button({ size, color })} {...rest} />;
}해석 규칙은 기본 Variants에서 확인하세요.
ref 전달
tsx
import { forwardRef, type ComponentPropsWithoutRef } from 'react';
export type ButtonProps = ComponentPropsWithoutRef<'button'> & ButtonVariants;
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ size, color, ...rest }, ref) => (
<button ref={ref} css={button({ size, color })} {...rest} />
),
);일부 축만 노출하기
특정 축(예: density)을 공개 API에서 숨기고 싶다면 Pick을 써 주세요.
ts
type PublicButtonProps =
ComponentPropsWithoutRef<'button'> &
Pick<ButtonVariants, 'size' | 'color'>;디자인 시스템에서 re-export
emotion-variants를 내부에서 쓰는 디자인 시스템 패키지를 만든다면, prop 타입을 함께 export해서 사용자가 내부 스타일 파일을 직접 import하지 않아도 되게 해 주세요.
ts
// design-system/src/button/index.ts
export { Button } from './button';
export type { ButtonProps } from './button';사용자는 패키지에서 ButtonProps만 import하면 되고, 내부 ev() 설정은 만질 일이 없어요.