이제 진짜 개발 뿐이야! (아님)
지금까지 아토믹 디자인을 통해 디자인에서 디자인 요소들을 분리하고, 이를 Token - Atom - Molecule - Organism으로 나누었다.
이후, 개발 단계에서의 혼란스러움을 없애기 위해 잘못되어있던 컬러 체계를 깔끔하게 재정비했다.
이제 드디어 모든 환경 설정이 끝나고 개발에 들어갈 수 있는 환경이 마련된 것이다....라고 하기 전에
진짜 마지막으로 디자인 토큰들을 실제 개발 단계에 사용할 수 있도록 Tailwind CSS 설정으로 옮겨보자.
rem 단위 설정
/* global.css */
@layer base {
html {
font-size: 6.25%;
}
}
rem 단위는 루트 글꼴 크기에 비례하여 상대적으로 바뀌는 단위이다.
보통 웹 개발을 하면서 가장 많이 접하는 단위가 px일텐데, px은 고정 값으로 그 크기가 변하지 않는다.
고정값이면 디자인 측면에서 좋은게 아닌가 하지만, px이라는 고정값은 웹 접근성 측면에서 문제가 될 수 있다.
필요 시 브라우저 설정을 통해 기본 글꼴을 확대해놓을 수가 있는데 단위가 px일 경우 확대 설정이 적용 되지 않는다.
즉, 글씨를 크게 보고 싶은데 확대가 적용이 안되어 크게 볼 수 없는 경우가 생기는 것이다.
이러한 px의 고정값을 보완하기 위해 나온 단위가 rem 그리고 em이라는 단위인데,
em은 계산이 너무 힘들어서 rem을 기준으로 많이 한다.
(em은 해당 요소의 글꼴 크기를 기준으로, rem은 루트 요소의 글꼴 크기를 기준으로 계산)
보통 브라우저의 기본 루트 글꼴 크기는 16px이다.
즉, 1rem = 16px이 되는데 문제는 대부분 디자인 툴은 px만 지원하지 rem, em 단위를 지원하지 않는다는 점이다.
때문에 개발에 적용할 때는 px로 만들어진 디자인을 rem으로 바꿔 사용해야하는데 10도 1도 아닌 16px이다보니 계산이 번거롭다.
위의 6.25%는 이러한 점을 보완해주는 방법이다.
16에 0.0625를 곱하면 1이 되는데, 이를 통해 루트 요소의 글꼴 크기를 1px로 만들어주는 것이다.
이렇게 하면 1px = 1rem이 되어 px 값을 단위만 rem으로 바꾸면 rem 단위 편하게 바꿀 수 있다.
(%를 사용하는 이유는 접근성을 고려해 디자인된 장치에서는 기본 글꼴이 16px이 아닌 경우가 존재하는데
px이라는 고정값으로 적어버리면 이러한 장치의 특성이 반영되지 않기 때문이라고 한다.)
사실은 6.25%보다 보통 62.5%로 많이 사용한다.
62.5%일 경우에는 브라우저 기본 글꼴이 10px이 되어 16px은 1.6rem으로 적용할 수 있게 된다.
그러면 6.25%로 하면 더 편한데 왜 굳이 62.5%로 하는걸까?
궁금해서 찾아보니 rem 단위가 지원되지 않는 경우에 6.25%로 할 경우 글씨가 너무 작게 보이는 문제가 있을 수 있고,
예전 크롬 브라우저에서는 최소 글꼴 크기가 10px로 정해져있어서 그렇다는 답변들을 볼 수 있었다.
그러나 현재 시점에서 IE는 멀리 가셨고, rem을 지원하지 않는 브라우저도 없거니와,
크롬 브라우저에서 테스트해본 결과 문제가 없어서 개인프로젝트임을 감안하여 내가 편하게 6.25%로 설정했다.
말이 좀 길어졌는데, 어쨌든 위 코드를 통해서 px과 rem을 편하게 변환하는 설정을 할 수 있다는 말이다.
디자인 토큰 정의하기
이제 직접 만든 디자인 시스템을 Tailwind에서 사용할 수 있도록 옮겨보자.
그렇다면 디자인 시스템 상에서 어떤 것들을 Tailwind로 옮기는 게 맞을까?
Tailwind는 Atomic CSS 계열에 속하는 CSS 라이브러리이다.
디자인의 개별 요소들(Atom) 전부 개별 클래스로 만들어 이를 조합하는 방식으로 하나의 디자인을 만들어낸다.
이전에 아토믹 디자인 도입기에서 아토믹 디자인 패턴을 다루었다.
뭔가 Tailwind의 설명과 비슷해보이지 않는가?
그러나 이전 아토믹 디자인 도입기에서 다루었듯, 아토믹 디자인의 아톰은 범위가 꽤 넓다.
그래서 한 가지 하위 계층을 하나 더 만들었는데, 바로 Token이다.
그 때 Token의 정의를 UI가 될 수 없는 UI의 시각적인 요소들이라고 내렸고,
Typography, Color, Radius, Spacing 같은 요소를 포함한다고 말했다.
그리고 이제 tailwind를 보자.
module.exports = {
theme: {
spacing: {
'1': '8px',
'2': '12px',
'3': '16px',
'4': '24px',
'5': '32px',
'6': '48px',
}
}
}
그렇다. 이 아토믹 디자인에서의 Token 계층을 그대로 tailwind로 옮겨주면 되는 것이다.
그런데 우리는 앞서 6.25%를 통해 1px을 1rem으로 설정했다.
즉, 디자인 토큰을 그대로 작성해주면서 px을 rem으로만 바꿔주면 토큰화가 된다.
//tailwind.config.js
module.exports = {
theme: {
**** 여기 정의하면 기존 설정 다 날림
extend: {
**** 여기 정의하면 기존 설정에 추가
},
},
};
Tailwind의 config 형태는 위와 같다.
theme 내부에 작성해주면, 기존 Tailwind의 토큰을 싹 지우고, 내가 정한 토큰만을 표시한다.
예를 들어 theme 내부에 spacing이라는 속성을 정의하면, Tailwind의 spacing은 싹 날라가고 내가 정한 spacing만 적용된다.
theme 내부에 있는 extend에 작성하면, 반대로 기존 Tailwind의 토큰에 내가 설정한 토큰을 추가적으로 제공한다.
만약 extend 내부에 spacing이라는 속성을 정의하면, Tailwind가 제공하는 spacing에 추가로 내가 정한 spacing을 사용한다.
이를 바탕으로 토큰들을 정의하면 아래와 같다.
토큰을 정의하기 위해 필요한 각 토큰의 속성 이름은 Tailwind 공식 문서를 참고하자.
theme: {
fontSize: {
inherit: "inherit",
xs: ["12rem", "16rem"],
s: ["14rem", "16rem"],
m: ["16rem", "24rem"],
l: ["20rem", "24rem"],
xl: ["24rem", "32rem"],
"2xl": ["36rem", "40rem"],
},
fontWeight: {
regular: "400",
bold: "700",
},
spacing: {
"3xs": "2rem",
"2xs": "4rem",
xs: "8rem",
s: "12rem",
m: "16rem",
l: "20rem",
xl: "24rem",
"2xl": "32rem",
"3xl": "40rem",
},
borderRadius: {
none: "0rem",
xs: "2rem",
s: "4rem",
m: "8rem",
l: "16rem",
circle: "9999rem",
},
boxShadow: {
none: "",
xs: "0 0 24rem 0 rgba(18, 18, 18, 0.05)",
s: "0 0 24rem 0 rgba(18, 18, 18, 0.25)",
m: "0 0 24rem 0 rgba(18, 18, 18, 0.45)",
l: "0 0 24rem 0 rgba(18, 18, 18, 0.65)",
xl: "0 0 24rem 0 rgba(18, 18, 18, 0.85)",
},
borderWidth: {
DEFAULT: "1rem",
s: "1rem",
m: "3rem",
},
colors: {
current: "currentColor",
transparent: "transparent",
},
extend: {
width: {
"desktop-4": "336rem",
"desktop-8": "688rem",
"desktop-12": "1040rem",
},
},
},
tw-colors로 시멘틱 컬러 적용하기
컬러 체계 개편기에서 보았듯 치스톡은 베이직 컬러 2개를 가져와 묶은 시멘틱 컬러로 구성되어있다.
베이직 컬러를 가져와서 바로 사용한다면 상관 없었겠지만, 시멘틱 컬러라는 컬러 묶음을 사용하는지라
동일한 클래스 이름에서 라이트 모드와 다크 모드마다 다른 색상을 제공해주어야했다.
이를 위해서는 css 변수를 선언하고 tailwind.config.js로 가져와 class로 설정해주는 과정을 거쳐야했는데,
tailwind에서 css 변수를 사용하기 위해서는 RGB 형태로 변환을 해야해서
최대한 config 파일 안에서 해결할 수 있는 방법을 찾고 있었다.
그러다 찾은 것이 바로 tw-colors라는 라이브러리이다.
//tailwind.config.js
const { createThemes } = require('tw-colors');
module.exports = {
plugins: [
createThemes({
light: {
'primary': 'steelblue',
'secondary': 'darkblue',
'brand': '#F3F3F3',
},
dark: {
'primary': 'turquoise',
'secondary': 'tomato',
'brand': '#4A4A4A',
},
forest: {
'primary': '#2A9D8F',
'secondary': '#E9C46A',
'brand': '#264653',
},
})
],
};
위에 보다시피 tw-colors는 tailwind에서 테마 기능을 사용할 수 있도록 만들어주는 라이브러리다.
사용법도 매우 간단해서 위에 보이는 저 코드만 보면 테마를 정의하는 방법은 다 알았다고 해도 무방하다.
이제, tw-colors를 사용해서 시멘틱 컬러를 설정해주자.
/** @type {import('tailwindcss').Config} */
import color from "./colorPalette";
import { createThemes } from "tw-colors";
module.exports = {
...
theme: {
...
colors: {}, //기존 컬러 삭제
},
},
plugins: [
//Color - Variant/Fixed - Background/Content
createThemes({
light: {
primary: {
DEFAULT: color.blue[50],
on: color.white,
fixed: {
DEFAULT: color.blue[50],
on: color.white,
},
},
secondary: {
DEFAULT: color.blue[20],
on: color.white,
fixed: {
DEFAULT: color.blue[20],
on: color.white,
},
},
tertiary: {
DEFAULT: color.blue[70],
on: color.blue[10],
fixed: {
DEFAULT: color.blue[70],
on: color.blue[10],
},
},
surface: {
DEFAULT: color.neutral[90],
variant: {
DEFAULT: color.white,
high: color.neutral[80],
highest: color.neutral[70],
},
on: {
DEFAULT: color.neutral[0],
variant: color.neutral[20],
},
},
outline: {
DEFAULT: color.neutral[20],
variant: color.neutral[80],
},
red: {
DEFAULT: color.red[50],
on: color.white,
variant: {
DEFAULT: color.red[30],
on: color.white,
},
},
yellow: {
DEFAULT: color.yellow[50],
on: color.neutral[0],
},
green: {
DEFAULT: color.green[50],
on: color.neutral[0],
},
magenta: {
DEFAULT: color.magenta[50],
on: color.neutral[0],
},
},
dark: {
primary: {
DEFAULT: color.blue[60],
on: color.blue[90],
fixed: {
DEFAULT: color.blue[50],
on: color.white,
},
},
secondary: {
DEFAULT: color.blue[80],
on: color.neutral[10],
fixed: {
DEFAULT: color.blue[20],
on: color.white,
},
},
tertiary: {
DEFAULT: color.blue[10],
on: color.blue[90],
fixed: {
DEFAULT: color.blue[70],
on: color.blue[10],
},
},
surface: {
DEFAULT: color.blue[5],
variant: {
DEFAULT: color.neutral[10],
high: color.neutral[20],
highest: color.neutral[30],
},
on: {
DEFAULT: color.white,
variant: color.neutral[60],
},
},
outline: {
DEFAULT: color.neutral[60],
variant: color.neutral[20],
},
red: {
DEFAULT: color.red[60],
on: color.neutral[0],
variant: {
DEFAULT: color.red[80],
on: color.neutral[0],
},
},
yellow: {
DEFAULT: color.yellow[60],
on: color.neutral[0],
},
green: {
DEFAULT: color.green[40],
on: color.neutral[0],
},
magenta: {
DEFAULT: color.magenta[60],
on: color.neutral[0],
},
},
}),
],
};
//colorPalette.js
const color = {
white: "#F9F9FB",
black: "#121212",
transparent: "rgba(0,0,0,0)",
blue: {
0: "#000205",
5: "#010A1E",
10: "#011337",
20: "#022569",
30: "#03369B",
40: "#0447CD",
50: "#0A5AFA",
60: "#3C7CFB",
70: "#6E9EFC",
80: "#A0BFFD",
90: "#D2E1FE",
},
red: {
0: "#050100",
5: "#1E0601",
10: "#370A01",
20: "#691302",
30: "#9B1C03",
40: "#CD2604",
50: "#FA320A",
60: "#FB5C3C",
70: "#FC866E",
80: "#FDB0A0",
90: "#FED9D2",
},
yellow: {
0: "#050300",
5: "#1E1401",
10: "#372501",
20: "#694702",
30: "#9B6803",
40: "#CD8A04",
50: "#FAAA0B",
60: "#FBBB3C",
70: "#FCCD6E",
80: "#FDDEA0",
90: "#FEEFD2",
},
magenta: {
0: "#050002",
5: "#1E010A",
10: "#370113",
20: "#690224",
30: "#9B0336",
40: "#CD0447",
50: "#FA0A5A",
60: "#FB3C7C",
70: "#FC6E9D",
80: "#FDA0BF",
90: "#FED2E1",
},
green: {
0: "#000503",
5: "#011E14",
10: "#013725",
20: "#026947",
30: "#039B68",
40: "#04CD8A",
50: "#0AFAAA",
60: "#3CFBBB",
70: "#6EFCCD",
80: "#A0FDDE",
90: "#D2FEEF",
},
neutral: {
0: "#0A0E15",
5: "#0A0E15",
10: "#1A2738",
20: "#2A3F5B",
30: "#3A577E",
40: "#4A6FA1",
50: "#6589B8",
60: "#88A4C8",
70: "#ABBFD8",
80: "#CED9E8",
90: "#F2F5F9",
},
};
export default color;
이제 app/layout.tsx의 html 태그에 아래와 같은 클래스를 추가해주면 된다.
<html className="theme-light">
...
</html>
tw-colors는 정한 테마 이름의 앞에 theme- 접두어를 붙여서 className에 제공한다.
이제 light를 dark로 바꿔보면서, 정상적으로 동작하는지 알아보자.
잘 동작함을 볼 수 있다.
tw-colors와 Storybook 연동
이렇게 모든 설정이 끝난 줄 알았으나,
한 가지 문제가 있었으니 바로 tw-colors와 Storybook의 테마 변경이 연동되지 않는다.
이는 Storybook 설정에서 테마의 class 이름이 theme-light가 아닌 light로만 설정되어있기 때문에 발생하는 문제로
아래와 같이 수정하여 해결할 수 있다.
decorators: [
// Adds theme switching support.
// NOTE: requires setting "darkMode" to "class" in your tailwind config
withThemeByClassName({
themes: {
light: "theme-light", // 이 부분을 theme-light로 변경
dark: "theme-dark", // 이 부분을 theme-dark로 변경
},
defaultTheme: "light",
}),
],
};
이제 Storybook에서도 tw-colors가 잘 연동됨을 확인할 수 있다.
'Frontend > CSS' 카테고리의 다른 글
tailwind-merge 설정하기 (0) | 2023.08.15 |
---|