$css Prop
Rainbow Sprinkles 기반의 $css prop으로 토큰 기반 스타일링하기
Featuring Design System의 핵심 스타일링 API. Rainbow Sprinkles 기반으로, 디자인 토큰 값과 임의 CSS 값을 하나의 prop에서 씁니다.
기본 사용법
모든 레이아웃 컴포넌트(Box, Flex, HStack, VStack, Center, Grid, Typo)와 Button에서 사용합니다.
<Box $css={{ padding: '$spacing-400', borderRadius: '$radius-200', backgroundColor: '$background-1', color: '$text-1', }}> Hello World </Box>
토큰 값 vs 임의 값
토큰 참조와 임의 CSS 값을 모두 지원합니다. 토큰 이름을 문자열로 전달하면 CSS 변수로 변환됩니다.
<Box $css={{ // 토큰 값 padding: '$spacing-400', borderRadius: '$radius-200', // 임의 CSS 값 width: '100%', maxWidth: '480px', border: '1px solid', borderColor: '$border-default', }}> 토큰 + 임의 값 혼합 </Box>
토큰은 단일 값 자리에서만 치환됩니다. padding: '0 $spacing-600'처럼 shorthand 문자열 안에 토큰을 끼워 넣으면 변환이 일어나지 않고 $spacing-600이 리터럴 문자열로 출력됩니다. 비대칭 값이 필요하면 개별 프로퍼티 또는 paddingX / paddingY shorthand로 분리하세요.
// ❌ 동작하지 않음 — 토큰이 치환되지 않음
<Box $css={{ padding: '0 $spacing-600' }} />
// ✅ 개별 프로퍼티로 분리
<Box $css={{ paddingY: 0, paddingX: '$spacing-600' }} />margin, inset, border, borderRadius 등 모든 multi-value shorthand에 동일하게 적용됩니다.
레이아웃 속성
레이아웃 관련 속성은 반응형 조건(mobile/tablet/desktop/wide)을 지원합니다.
Display & Flex
<Flex $css={{ gap: '$spacing-300', padding: '$spacing-400', borderRadius: '$radius-200', border: '1px solid', borderColor: '$border-default', }}> <Box $css={{ flex: '1', padding: '$spacing-300', bgColor: '$primary-10', borderRadius: '$radius-100', textAlign: 'center', }}>flex: 1</Box> <Box $css={{ flex: '2', padding: '$spacing-300', bgColor: '$primary-20', borderRadius: '$radius-100', textAlign: 'center', }}>flex: 2</Box> <Box $css={{ flex: '1', padding: '$spacing-300', bgColor: '$primary-10', borderRadius: '$radius-100', textAlign: 'center', }}>flex: 1</Box> </Flex>
Spacing
<HStack $css={{ gap: '$spacing-300' }}> <Box $css={{ padding: '$spacing-200', bgColor: '$primary-10', borderRadius: '$radius-100', }}>spacing-200</Box> <Box $css={{ padding: '$spacing-400', bgColor: '$primary-20', borderRadius: '$radius-100', }}>spacing-400</Box> <Box $css={{ padding: '$spacing-600', bgColor: '$primary-30', borderRadius: '$radius-100', }}>spacing-600</Box> </HStack>
Sizing
<Box $css={{
width: '100%',
height: '48px',
minWidth: '200px',
maxWidth: '640px',
minHeight: '100vh',
size: '40px', // width + height 동시 설정
}} />Border & Radius
<HStack $css={{ gap: '$spacing-300' }}> <Box $css={{ size: '64px', bgColor: '$primary-20', borderRadius: '$radius-100', display: 'flex', alignItems: 'center', justifyContent: 'center', }}>100</Box> <Box $css={{ size: '64px', bgColor: '$primary-30', borderRadius: '$radius-200', display: 'flex', alignItems: 'center', justifyContent: 'center', }}>200</Box> <Box $css={{ size: '64px', bgColor: '$primary-40', borderRadius: '$radius-full', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'white', }}>full</Box> </HStack>
Grid
<Grid $css={{ gridTemplateColumns: 'repeat(3, 1fr)', gap: '$spacing-300', }}> {[1,2,3,4,5,6].map(i => ( <Box key={i} $css={{ padding: '$spacing-400', bgColor: '$primary-10', borderRadius: '$radius-100', textAlign: 'center', border: '1px solid', borderColor: '$primary-30', }}> {i} </Box> ))} </Grid>
Typography
<Box $css={{
fontSize: '14px',
fontWeight: '600',
fontFamily: 'var(--global-typography-font-sans)',
lineHeight: '1.5',
textAlign: 'center',
textDecoration: 'underline',
textTransform: 'uppercase',
letterSpacing: '-0.02em',
wordBreak: 'break-word',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
}} />Position
<Box $css={{
position: 'absolute',
top: '0',
right: '0',
bottom: '0',
left: '0',
inset: '0', // top + right + bottom + left
insetX: '16px', // left + right
insetY: '0', // top + bottom
zIndex: 10,
}} />Visual
<Box $css={{
opacity: 0.8,
overflow: 'hidden',
overflowX: 'auto',
overflowY: 'scroll',
cursor: 'pointer',
pointerEvents: 'none',
userSelect: 'none',
visibility: 'hidden',
objectFit: 'cover',
objectPosition: 'center',
aspectRatio: '16/9',
}} />Transform & Transition
<Box $css={{
transform: 'translateX(-50%)',
transition: 'all 0.2s ease',
animation: 'fadeIn 0.3s ease',
scale: '1.05',
rotate: '45deg',
translate: '10px 20px',
}} />색상 속성
색상 관련 속성은 인터랙티브 조건(hover/active/focus/disabled 등)을 지원합니다.
기본 색상 속성
<HStack $css={{ gap: '$spacing-300' }}> <Box $css={{ padding: '$spacing-400', bgColor: '$background-1', color: '$text-1', borderRadius: '$radius-200', border: '1px solid', borderColor: '$border-default', }}>background-1</Box> <Box $css={{ padding: '$spacing-400', bgColor: '$background-2', color: '$text-2', borderRadius: '$radius-200', }}>background-2</Box> <Box $css={{ padding: '$spacing-400', bgColor: '$primary-60', color: 'white', borderRadius: '$radius-200', }}>primary-60</Box> </HStack>
인터랙티브 조건
색상 속성에 객체를 전달하면 상태별 스타일이 적용됩니다. 아래 박스에 마우스를 올려보세요.
<Box $css={{ padding: '$spacing-400', borderRadius: '$radius-200', cursor: 'pointer', transition: 'all 0.2s ease', bgColor: { default: '$background-1', hover: '$primary-10', active: '$primary-20', }, color: { default: '$text-1', hover: '$primary-60', }, borderColor: { default: '$border-default', hover: '$primary-60', }, border: '1px solid', elevation: { default: '$elevation-2', hover: '$elevation-8', }, }}> Hover & Click me </Box>
사용 가능한 조건
| 조건 | CSS 선택자 | 설명 |
|---|---|---|
default | — | 기본 상태 |
hover | &:hover | 마우스 호버 |
active | &:active | 클릭 중 |
focus | &:focus-visible | 키보드 포커스 |
disabled | &:disabled | 비활성 상태 |
readOnly | &:read-only | 읽기 전용 |
focusWithin | &:focus-within | 자식 요소 포커스 |
groupHover | [data-group]:hover & | 부모 그룹 호버 |
사용 가능한 토큰 색상
토큰 색상은 이름으로 직접 참조합니다:
글로벌 색상: primary-10primary-100, gray-5gray-90, red-10red-100, blue-10blue-100, green-10green-100, orange-10orange-100, yellow-10yellow-100, purple-10purple-100 등
시맨틱 색상: background-1background-4, border-default, border-1border-4, text-1text-6, support-error-1support-error-4, support-success-1~support-success-4 등
특수 값: transparent, currentColor, inherit
className / style 콜백
$css 외에 상태 기반 className과 style도 지원합니다.
<Button.Root
$css={{ padding: '$spacing-400' }}
className={(state) => state.disabled ? 'opacity-50' : ''}
style={(state) => ({
transform: state.loading ? 'scale(0.98)' : undefined,
})}
>
<Button.Text>Submit</Button.Text>
</Button.Root>다른 스타일링과 함께 사용
$css prop은 기존 className, style과 함께 사용할 수 있습니다. 병합 우선순위는 다음과 같습니다:
$css의 토큰 스타일 (가장 낮음)$css의 className- 내부 컴포넌트 props
- 소비자의 className / style (가장 높음)
<Box
$css={{ padding: '$spacing-400', bgColor: '$background-1' }}
className="my-custom-class"
style={{ border: '2px solid red' }}
>
Content
</Box>