|
| 1 | +--- |
| 2 | +title: TailwindCSS 같은 디자인시스템 구축하기 |
| 3 | +date: 2023-04-21 |
| 4 | +description: 디자인토큰을 만들고 class기반 디자인시스템을 구축해봅니다. |
| 5 | +category: other |
| 6 | +--- |
| 7 | + |
| 8 | +--- |
| 9 | + |
| 10 | +<div className="mokcha"> |
| 11 | + <div className="mokcha-container"> |
| 12 | + <h2>INDEX</h2> |
| 13 | + <a href="#1" className="mokcha-container__list"> |
| 14 | + 1. 디자인 시스템이란 뭐죠? |
| 15 | + </a> |
| 16 | + <a href="#2" className="mokcha-container__list"> |
| 17 | + 2. 디자인 토큰의 종류 |
| 18 | + </a> |
| 19 | + <a href="#3" className="mokcha-container__list"> |
| 20 | + 3. TailwindCSS같은 디자인시스템 구축하기 |
| 21 | + </a> |
| 22 | + </div> |
| 23 | +</div> |
| 24 | + |
| 25 | +--- |
| 26 | + |
| 27 | +<h2 id="1"></h2> |
| 28 | + |
| 29 | +<br></br> |
| 30 | + |
| 31 | +<h2 id="1" className={`dark:text-white text-center`}> |
| 32 | + <div>1. 디자인 시스템이란 뭐죠?</div> |
| 33 | +</h2> |
| 34 | + |
| 35 | +<br></br> |
| 36 | + |
| 37 | +디자인 시스템은 간단하게 말해서 `MUI`, `TailwindCSS` 같은 통일된 디자인 색상, 타이포그래피, 여백, 그림자 등을 한곳에서 정리 및 관리를 통해서 디자이너, 개발자 간의 협업할 때 통합, 생산성을 높이기 위해서 사용되는 것이라고 생각하시면 됩니다. 이떄 색상, 타이포그래피... 등등을 정의한 CSS 변수를 <b>디자인 토큰</b>이라고 합니다. |
| 38 | + |
| 39 | +<br></br> |
| 40 | + |
| 41 | +<a |
| 42 | + className={`dark:text-white`} |
| 43 | + target="_blank" |
| 44 | + id="link" |
| 45 | + href="https://www.lightningdesignsystem.com/design-tokens/" |
| 46 | +> |
| 47 | + lightningdesignsystem design-tokens |
| 48 | +</a> |
| 49 | + |
| 50 | +<a |
| 51 | + className={`dark:text-white`} |
| 52 | + target="_blank" |
| 53 | + id="link" |
| 54 | + href="https://m3.material.io/styles/color/overview" |
| 55 | +> |
| 56 | + https://m3.material.io/styles/color/overview |
| 57 | +</a> |
| 58 | + |
| 59 | +<br></br> |
| 60 | + |
| 61 | +위 사이트는 여러 회사에서 정의한 디자인 토큰을 설명하는 페이지입니다. 이렇게 회사마다 회사의 통일된 디자인을 유지보수하기 위해서 디자인 토큰을 정의하고 그것을 바탕으로 완성된 디자인 시스템을 사용합니다. |
| 62 | + |
| 63 | +--- |
| 64 | + |
| 65 | +<h2 id="2"></h2> |
| 66 | + |
| 67 | +<br></br> |
| 68 | + |
| 69 | +<h2 id="2" className={`dark:text-white text-center`}> |
| 70 | + <div>2. 디자인 토큰의 종류</div> |
| 71 | +</h2> |
| 72 | + |
| 73 | +<br></br> |
| 74 | + |
| 75 | +회사마다 디자인 토큰의 정의하는 개념과 기준이 사람마다 다르지만 대체로 디자인 토큰을 3가지로 나누어 집니다. |
| 76 | + |
| 77 | +<h3 className={`dark:text-white`}>1. Global tokens</h3> |
| 78 | +<br></br> |
| 79 | + |
| 80 | +<div className="ml-div"> |
| 81 | + |
| 82 | + `Global tokens`는 가장 작은 단위의 디자인입니다. 예를 들어서 색상, 폰트 크기, 폰트 간격... 등등이 존재합니다. Global tokens를 어떤 곳에서는 `Foundation`이라고도 명칭 하기도 합니다. |
| 83 | + |
| 84 | + ```css |
| 85 | + --blue-300: blue // Global tokens;; |
| 86 | + ``` |
| 87 | + |
| 88 | +</div> |
| 89 | + |
| 90 | +<br></br> |
| 91 | +<h3 className={`dark:text-white`}>2. Alias tokens</h3> |
| 92 | +<br></br> |
| 93 | + |
| 94 | +<div className="ml-div"> |
| 95 | + |
| 96 | + `Alias tokens`은 원자 단위의 Global tokens의 별칭을 지어주는 것입니다. 예를들어서 |
| 97 | + |
| 98 | + ```css |
| 99 | + --blue-300 : blue // Global tokens |
| 100 | + |
| 101 | + --background-color : var(--blue-300) // Alias tokens |
| 102 | + ``` |
| 103 | + |
| 104 | + 가장 기본이 되는 blue 색상은 Global tokens이 되며 Global tokens을 사용해 --background-color라는 Alias tokens을 생성할 수 있습니다. |
| 105 | + |
| 106 | +</div> |
| 107 | + |
| 108 | +<br></br> |
| 109 | +<h3 className={`dark:text-white`}>3. Components tokens</h3> |
| 110 | +<br></br> |
| 111 | + |
| 112 | +<div className="ml-div"> |
| 113 | + |
| 114 | + `Components tokens`은 좀 더 확장된 개념인데 --background-color라는 Alias token을 사용해 특정 컴포넌트를 명시하는 이름으로 할당합니다. |
| 115 | + |
| 116 | + ```CSS |
| 117 | + --background-color : var(--blue-300) // Alias tokens |
| 118 | + |
| 119 | + --btn-background-color : var(--background-color) // Components tokens |
| 120 | + ``` |
| 121 | + |
| 122 | + --background-color 토큰을 사용해 btn 컴포넌트에서 쓰이는 Components tokens을 생성합니다. |
| 123 | + |
| 124 | +</div> |
| 125 | + |
| 126 | +--- |
| 127 | + |
| 128 | +<h2 id="3"></h2> |
| 129 | + |
| 130 | +<br></br> |
| 131 | + |
| 132 | +<h2 id="3" className={`dark:text-white text-center`}> |
| 133 | + <div>3. TailwindCSS같은 디자인시스템 구축하기</div> |
| 134 | +</h2> |
| 135 | + |
| 136 | +<br></br> |
| 137 | + |
| 138 | +간단하게 제가 구축한 디자인 시스템의 <b>순서</b>를 설명해겠습니다. |
| 139 | + |
| 140 | +<br></br> |
| 141 | + |
| 142 | +<div className={`step-by-step`}> |
| 143 | + |
| 144 | +<h3>디자인 시스템 구축 순서</h3> |
| 145 | + |
| 146 | +1. CSS 변수들을 사용해 디자인 토큰을 만듭니다. |
| 147 | + |
| 148 | +2. 완성된 디자인 토큰 변수들을 style 태그 `:root` 안에 한 줄씩 작성해 head 태그 안에 삽입합니다. |
| 149 | + |
| 150 | +3. 해당 디자인 토큰들을 가지고 css class를 생성합니다. |
| 151 | + |
| 152 | +4. 완성된 class를 태그의 class에 삽입해 디자인을 적용합니다. |
| 153 | + |
| 154 | +</div> |
| 155 | + |
| 156 | +<br></br> |
| 157 | +<br></br> |
| 158 | + |
| 159 | +<b>이제는 실제로 구축해 보겠습니다.</b> |
| 160 | + |
| 161 | +<br></br> |
| 162 | +<h3 className={`dark:text-white`}>1. css 변수들을 사용해 디자인 토큰을 생성</h3> |
| 163 | +<br></br> |
| 164 | + |
| 165 | +```ts:cssVariables.ts showLineNumbers |
| 166 | +export const cssVariables: CSSVariables = { |
| 167 | + |
| 168 | +// Global token |
| 169 | +static_colors: { |
| 170 | + black: "#000000", |
| 171 | + white: "#ffffff", |
| 172 | + gray300: "#f6f6f6", |
| 173 | + blue600: "#0056b3", |
| 174 | + blue800: "#1f3764", |
| 175 | +}, |
| 176 | +scale_font_sizes: { |
| 177 | + size10: "0.625rem", |
| 178 | + ... |
| 179 | + } |
| 180 | + .. |
| 181 | + . |
| 182 | + } |
| 183 | + |
| 184 | +// Alias token & Components token |
| 185 | +cssVariables.semantic_colors = { |
| 186 | + background: cssVariables.static_colors.gray300, |
| 187 | + text: cssVariables.static_colors.black, |
| 188 | + btn_background: cssVariables.static_colors.blue800, |
| 189 | + btn_background_hover: cssVariables.static_colors.blue600, |
| 190 | + btn_text: cssVariables.static_colors.white, |
| 191 | +}; |
| 192 | +. |
| 193 | +. |
| 194 | + |
| 195 | +``` |
| 196 | + |
| 197 | +- cssVariables 객체안에 Global token을 할당합니다. |
| 198 | + |
| 199 | +- Global token을 이용해 Alias token, Components token 들을 생성합니다. |
| 200 | + |
| 201 | +<br></br> |
| 202 | +<h3 className={`dark:text-white`}>2. css 변수를 style태그안 :root 안 삽입</h3> |
| 203 | +<br></br> |
| 204 | + |
| 205 | +```ts:injectCSSVariables.utils.ts showLineNumbers |
| 206 | +function createStyleTag(variables: CSSVariables): HTMLStyleElement { |
| 207 | + const styleTag = document.createElement("style"); |
| 208 | + let root = ":root {\n"; |
| 209 | + |
| 210 | + for (const category in variables) { |
| 211 | + for (const variableName in variables[category]) { |
| 212 | + const replacedCategory = category.replace(/_/g, "-"); |
| 213 | + const replacedVariableName = variableName.replace(/_/g, "-"); |
| 214 | + const cssVariableName = `--${replacedCategory}-${replacedVariableName}`; |
| 215 | + const cssVariableValue = variables[category][variableName]; |
| 216 | + root += ` ${cssVariableName}: ${cssVariableValue};\n`; |
| 217 | + } |
| 218 | + } |
| 219 | + |
| 220 | + styleTag.textContent = root + "}"; |
| 221 | + return styleTag; |
| 222 | +} |
| 223 | + |
| 224 | +export default function injectCSSVariables(variables: CSSVariables): void { |
| 225 | + const styleTag = createStyleTag(variables); |
| 226 | + document.head.appendChild(styleTag); |
| 227 | +} |
| 228 | + |
| 229 | +injectCSSVariables(cssVariables) |
| 230 | +``` |
| 231 | + |
| 232 | +- injectCSSVariables 함수에 css 토큰 객체를 매개변수로 실행합니다. |
| 233 | + |
| 234 | +- createStyleTag함수로 style 태그를 생성합니다. |
| 235 | + |
| 236 | + - cssVariables.ts 에서는 <b>"-"(하이픈)</b>을 사용할수없어서 <b>"\_"(언더바)</b>를 사용했기때문에 css규칙을 지키기 위해 정규표현식으로 하이픈을 언더바로 변환합니다. |
| 237 | + |
| 238 | + - 최종적으로 `:root { --semantic-colors-background ... }` 형태로 저장됩니다. |
| 239 | + <br></br> |
| 240 | + |
| 241 | +- styleTag를 <b>document.head</b>에 삽입합니다. |
| 242 | + |
| 243 | + 삽입된 토큰들 👇 |
| 244 | + |
| 245 | + <img |
| 246 | + width="50%" |
| 247 | + src="https://user-images.githubusercontent.com/75124028/232970681-7a6b5541-bd23-4349-9256-c043a2e9a146.png" |
| 248 | + /> |
| 249 | + |
| 250 | +<br></br> |
| 251 | +<h3 className={`dark:text-white`}>3. css 클래스 생성</h3> |
| 252 | +<br></br> |
| 253 | + |
| 254 | +```css:typography.css |
| 255 | +.semantic-typography-h1 { |
| 256 | + font-size: var(--semantic-typography-h1-font-size); |
| 257 | + font-weight: var(--semantic-typography-h1-font-weight); |
| 258 | + line-height: var(--semantic-typography-h1-line-height); |
| 259 | + letter-spacing: var(--semantic-typography-h1-letter-spacing); |
| 260 | +} |
| 261 | + |
| 262 | +.semantic-typography-h2 { |
| 263 | + font-size: var(--semantic-typography-h2-font-size); |
| 264 | + font-weight: var(--semantic-typography-h2-font-weight); |
| 265 | + line-height: var(--semantic-typography-h2-line-height); |
| 266 | + letter-spacing: var(--semantic-typography-h2-letter-spacing); |
| 267 | +} |
| 268 | +... |
| 269 | +.. |
| 270 | +. |
| 271 | +``` |
| 272 | + |
| 273 | +- css변수를 사용해 class를 생성합니다 |
| 274 | + |
| 275 | +<br></br> |
| 276 | +<h3 className={`dark:text-white`}>4. 실제 사용</h3> |
| 277 | +<br></br> |
| 278 | + |
| 279 | +```ts:components/Layout/Header showLineNumbers |
| 280 | + export default function Header({ title }: HeaderProps) { |
| 281 | + return ( |
| 282 | + <Styled.HeaderContainer> |
| 283 | + <Styled.HeaderTitle className="semantic-typography-title3-regular"> |
| 284 | + {title} |
| 285 | + </Styled.HeaderTitle> |
| 286 | + </Styled.HeaderContainer> |
| 287 | + ); |
| 288 | + } |
| 289 | +``` |
| 290 | + |
| 291 | +위 코드는 실제로 제가 기업의 사전과제중 구현해 사용한 실제 코드입니다. |
| 292 | + |
| 293 | +이렇게 디자인 class를 적용했을때 크롬 개발자 도구로 검사하게되면 적용이 된 모습을 볼수 있습니다. 👇 |
| 294 | + |
| 295 | +<div className={`flex flex-col justify-center`}> |
| 296 | + <img |
| 297 | + src="https://user-images.githubusercontent.com/75124028/232972878-3c9a16b0-260b-47b3-bdf6-40f51f6ed5c5.png" |
| 298 | + width="100%" |
| 299 | + /> |
| 300 | + <img |
| 301 | + src="https://user-images.githubusercontent.com/75124028/232972885-cc696043-a782-49d6-bbf6-06fcb22a9968.png" |
| 302 | + width="100%" |
| 303 | + /> |
| 304 | +</div> |
| 305 | + |
| 306 | +📌 참고로 `letter-spacing` 같은경우 아직 토큰을 생성 하지 않았습니다. |
| 307 | + |
| 308 | +이렇게 tailwind와 같은 유틸리티 디자인 시스템을 구축 및 사용해보았습니다. 이렇게 사용하게 되면 컴포넌트 페이지에서 해당 엘리먼트가 어떤 식으로 생겼는지 실제 렌더링하지 않고도 이름에 명확히 명시가 되어있기 때문에 가독성이 좋아집니다. 중복된 코드들도 많이 줄어들게 되고요. 하지만 이런 class를 많이 사용하게 될 경우 또한 코드가 길어지기 때문에 좀 더 개선해야 한다고 생각합니다. |
| 309 | + |
| 310 | +<br></br> |
| 311 | + |
| 312 | +참고로 제가 디자인 토큰을 생성할때 사용한 css 변수 값들은 당근마켓 seed-design을 참고했습니다. |
| 313 | + |
| 314 | +<a |
| 315 | + className={`dark:text-white`} |
| 316 | + target="_blank" |
| 317 | + id="link" |
| 318 | + href="https://github.com/daangn/seed-design/blob/main/packages/stylesheet/global.css" |
| 319 | +> |
| 320 | + https://github.com/daangn/seed-design/blob/main/packages/stylesheet/global.css |
| 321 | +</a> |
| 322 | + |
| 323 | +--- |
| 324 | + |
| 325 | +<br></br> |
| 326 | + |
| 327 | +<b>참고 문서</b> |
| 328 | + |
| 329 | +<br></br> |
| 330 | + |
| 331 | +<a |
| 332 | + className={`dark:text-white`} |
| 333 | + target="_blank" |
| 334 | + id="link" |
| 335 | + href="https://spectrum.adobe.com/page/design-tokens/" |
| 336 | +> |
| 337 | + https://spectrum.adobe.com/page/design-tokens/ |
| 338 | +</a> |
| 339 | + |
| 340 | +<a |
| 341 | + className={`dark:text-white`} |
| 342 | + target="_blank" |
| 343 | + id="link" |
| 344 | + href="https://gsretail.tistory.com/20" |
| 345 | +> |
| 346 | + https://gsretail.tistory.com/20 |
| 347 | +</a> |
| 348 | + |
| 349 | +<a |
| 350 | + className={`dark:text-white`} |
| 351 | + target="_blank" |
| 352 | + id="link" |
| 353 | + href="https://yozm.wishket.com/magazine/detail/1619/" |
| 354 | +> |
| 355 | + https://yozm.wishket.com/magazine/detail/1619/ |
| 356 | +</a> |
0 commit comments