
다양한 스타일 라이브러리나 큰 프로젝트를 경험했다면 색상, 크기, 길이, 너비 등 다양한 값들을 px 혹은 % 로 각각 박아서 쓰지 않고,
획일화된 크기나 값으로 테마화 해서 사용하는 것을 알 것이다.
예를 들자면 fontSize : sm : 12px / md: 16px / lg : 18px 요런식으로,
이렇게 프로젝트에서 테마로 사용될 다양한 변수들을 미리 정의하고, 또 다크모드 와 같이 특정 테마에서 의 스타일 변수등을 미리 지정하는데 사용하는 옵션이다.
전체 애플리케션에서 사용될 전역적인 테마 변수를 정의할 수 있는 함수다.
첫번째는 루트 선택자 이고 두번째는 오브젝트다.
const commonVars = createGlobalTheme(':root', {
color: {
primary: '#3361FF',
white: '#FFFFFF',
red: '#FF3737',
green: '#00D98B',
yellow: '#FFB800',
...
위처럼 선언해 놓고 나중에 commonVars.color.primary 이런식으로 가져다가 쓴다.
테마의 구조 를 정의하는 함수이다. 우리가 다크 모드와 같이 전반적인 테마를 사용해야 할때 유용하게 쓰이는데,
미리 테마의 구조를 잡고, 기본일때, 다크모드일때 의 css 속성을 유동적으로 교체 할 수 있게 해준다.
const themeColor = createThemeContract({
border: null,
overlay: null,
background: {
primary: null,
layout: null,
},
text: {
default: null,
primary: null,
secondary: null,
},
...
위처럼 선언된 테마 구조를 아래와 같이 사용한다.
// 일반 모드일때
createGlobalTheme(':root', themeColor, {
border: commonVars.color.gray['200'],
overlay: 'rgba(24, 28, 64, 0.50)',
background: {
primary: commonVars.color.white,
layout: 'linear-gradient(to bottom, #3361ff 274px, #F4F5FA 274px)',
},
text: {
default: commonVars.color.blue['200'],
primary: commonVars.color.blue['200'],
secondary: commonVars.color.gray['400'],
},
}
// 다크 모드일때
createGlobalTheme('.dark', themeColor, {
border: commonVars.color.black['200'],
overlay: 'rgba(0, 0, 0, 0.70)',
background: {
primary: commonVars.color.black['100'],
layout: commonVars.color.black['100'],
},
text: {
default: commonVars.color.white,
primary: commonVars.color.gray['400'],
secondary: commonVars.color.gray['400'],
},
}
이렇게 하면 동일한 구조를 사용해서 각 테마에 맞게 css 속성을 쉽게 교체할 수 있다.
위 createThemeContract 가 다크모드 만들때 주로 쓰인다면 Sprinkles 는 주로 반응형 작업할때 쓴다고 생각하면 좋다.
Sprinkles 를 사용하려면 먼저 defineProperties 를 만들어야 한다.
예를들면 mobile : 미디어 쿼리 몇몇 tablet 은 미디어쿼리 몇몇 이런식으로?
빠르게 예시 코드를 확인해보자
const responsiveProperties = defineProperties({
// 조건 정의
conditions: {
mobile: {}, // 기본 (mobile)
tablet: { '@media': 'screen and (min-width: 768px)' }, // 태블릿 이상
desktop: { '@media': 'screen and (min-width: 1024px)' }, // 데스크톱 이상
},
defaultCondition: 'mobile', // 조건이 없으면 mobile이 기본값
properties: {
color: ['red', 'blue', 'green'], // 사용할 색상 값
padding: ['sm', 'md', 'lg'], // 사용할 패딩 값
margin: ['sm', 'md', 'lg'], // 사용할 마진 값
},
});
이렇게 defineProperties 를 통해 조건들을 작성하고,
const buttonStyle = sprinkles({
color: {
mobile: 'red', // 기본 상태: 빨간색
tablet: 'blue', // 태블릿 이상: 파란색
},
padding: {
mobile: 'sm', // 기본 상태: 작은 패딩
tablet: 'md', // 태블릿 이상: 중간 패딩
},
});
sprinkles 를 통해 이렇게 사용할 수 있다.
이게 실제로는
.sprinkles1 {
color: red;
padding: var(--padding-sm);
}
@media screen and (min-width: 768px) {
.sprinkles1 {
color: blue;
padding: var(--padding-md);
}
}
이런식으로 만들어진다.
아마 제일 많이 쓰게될 친구이지 않을까 싶다.
이름 그대로 컴포넌트에서 다양한 props 를 가지고 조건을 걸어서 스타일의 레시피? 를 만들어 쓰는 느낌이다
바로 예제를 보자
import { buttonStyle } from './button.css';
const Button = ({ colorSchema = 'primary', size = 'small' }) => {
return <button className={buttonStyle({ color, size })}>Click Me</button>;
};
// 스타일
export const buttonStyle = recipe({
base: {
fontWeight: 'bold',
borderRadius: '8px',
padding: '12px 24px',
},
variants: {
colorSchema: {
primary: { backgroundColor: 'blue', color: 'white' },
secondary: { backgroundColor: 'gray', color: 'black' },
},
size: {
small: { fontSize: '12px', padding: '8px 16px' },
large: { fontSize: '18px', padding: '16px 32px' },
},
},
defaultVariants: {
colorSchema: 'primary',
size: 'small',
},
});
대충 감이 올 것이다. 주로 공통컴포넌트 만들때 자주 보게 될 친구인데,
Button 에 colorSchema 와 size 를 props 로 던지면,
각 props 에 맞는 스타일들을 종합적으로 만들 수 있게 된다.
위 소스에서 볼 수 있듯, recipe 의 기본 성질은 base 에 , 레시피북은 variants,
그리고 디폴트값을 정할 수 있는 defaultVariants 가 있다.
함수 닉값 처럼 들어오는 props 의 값을 조합해서 특정한 값을 만들 수 있다.
(사용하기 조금 복잡하다...)
얼른 예시를 보자
import { buttonStyle } from './button.css';
export const Button = ({ variant = 'primary', size = 'small' }) => {
return (
<button className={buttonStyle({ variant, size })}>
Click Me
</button>
);
};
// 스타일 영역
import { recipe } from '@vanilla-extract/recipes';
export const buttonStyle = recipe({
base: {
fontWeight: 'bold',
borderRadius: '8px',
padding: '12px 24px',
},
variants: {
colorSchema: {
primary: { backgroundColor: 'blue', color: 'white' },
secondary: { backgroundColor: 'gray', color: 'black' },
},
size: {
small: { fontSize: '12px' },
large: { fontSize: '18px' },
},
},
compoundVariants: [
{
variants: { colorSchema: 'primary', size: 'small' },
style: { backgroundColor: 'darkblue' }, // primary + small 조합일 때
},
{
variants: { colorSchema: 'secondary', size: 'large' },
style: { backgroundColor: 'darkgray' }, // secondary + large 조합일 때
},
],
defaultVariants: {
colorSchema: 'primary',
size: 'small',
},
});
위 compoundVariants 를 자세히 살펴보자
variants 값으로 즉 props 로 colorSchema : primary , size : 'small' 일때 아래에 있는 style 에 작성된 스타일이 적용된다.
즉 Variants 조합이 너무 많아지거나 특정한 조건의 경우 다른 스타일이 필요하다면 적절히 사용해 볼 수 있을것 같다.
(근데 너무 남발하면 어려울듯...)
Recipes 를 쓸때 해당 컴포넌트의 props 의 타입과 Recipes 를 같이 관리해주기가 귀찮을 수 도 있다.
이럴땐 Recipes 에 variants 의 값들도 타입으로 끌어다가 쓸 수 있는데 이러면 우리가 컴포넌트에다가 props 의 타입을 따로 관리 안해줘도
Recipes 에 variants 로 추가되면 자동으로 타입이 되니 이 얼마나 편한가?
바로 예시 소스를 보자
import { ButtonHTMLAttributes, ReactNode } from 'react';
import { RecipeVariants } from '@vanilla-extract/recipes';
import { button } from './button.css.ts';
type ButtonVariants = RecipeVariants<typeof button>;
type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> &
ButtonVariants & {
label?: string;
icon?: ReactNode;
};
const Button = ({
label = '',
onClick,
icon,
...variant
}: ButtonProps) => {
return (
<button
className={button(variant)}
onClick={onClick}
style={{ width }}
>
{icon}
{label}
</button>
);
};
export default Button;
/// 스타일
// button.css.ts
import { recipe } from '@vanilla-extract/recipes';
export const button = recipe({
base: {
padding: '10px 20px',
borderRadius: '4px',
fontWeight: 'bold',
cursor: 'pointer',
},
variants: {
variant: {
primary: { backgroundColor: 'blue', color: 'white' },
secondary: { backgroundColor: 'gray', color: 'black' },
},
size: {
small: { fontSize: '12px' },
medium: { fontSize: '16px' },
large: { fontSize: '20px' },
},
},
defaultVariants: {
variant: 'primary',
size: 'medium',
},
});
보면
type ButtonProps = ButtonHTMLAttributes &
ButtonVariants & {
label?: string;
icon?: ReactNode;
};
이것처럼 ButtonVariants 타입을 가져와 사용해서
Variants 로 선언한 스타일 구조를 바로 타입으로 사용할 수 있게 된다.
날먹 가능!