HTML/CSS(Styled-Component) 프로젝트에 디자인 시스템 도입하기

YOONG·2025년 10월 16일

[DIVE SOPT 37th] Web

목록 보기
1/6
post-thumbnail

SOPT 1주차 과제를 진행하면서 CSS 변수를 사용해 스타일을 관리했습니다! 처음에는 색상을 변수로 빼면 편하겠다는 생각으로 글로벌 CSS를 정의하기 시작했지만, 이전에 진행했던 TSX + Vite 프로젝트를 다시 보니 훨씬 체계적인 디자인 시스템이 구축되어 있었어서, 이 구조를 이번 과제에도 적용할 수 있지 않을까?! 싶어서 적용해본 과정을 공유하려고 합니다.

디자인 시스템이란?

디자인 시스템은 일관된 사용자 경험을 제공하기 위한 설계 원칙, 컴포넌트, 가이드라인들 입니다!
디자인 토큰과 컴포넌트 라이브러리로 나뉩니다.

디자인 토큰

  • 색상, 타이포그래피, 간격, 그림자 등 디자인의 작은 단위
  • ex. --color-primary, --spacing-md, --font-size-large

컴포넌트 라이브러리

  • 버튼, 카드, 모달 등 재사용 가능한 UI 컴포넌트

작은 프로젝트에서도 디자인 시스템이 필요한 이유

1. 협업 시 일관성 확보 가능

/* 디자인 시스템 없을 경우 */
.button-1 { color: #FF8A00; }
.button-2 { color: #ff8900; }	/* 미묘하게 다른 색상 !! */
.button-3 { color: orange; }

/* 디자인 시스템이 있는 경우 */
.button { color: var(--color-main); }

2. 빠른 UI 구성 가능

작은 프로젝트에 디자인이 없는 경우, 색상/폰트/간격을 매번 고민해야 하는데, 이때 디자인 시스템이 있으면 디자인 토큰만 조합해서 새로운 UI를 빠르게 구성할 수 있음!

3. 쉬운 리팩토링

/* 메인 컬러를 변경하고 싶을 때 */
:root {
  /* --main-color: #FF8A00;  기존 */
  --main-color: #2c2c2c;  /* 변경 */
}
/* 이 한 줄만 수정하면 전체 사이트의 색상이 전부 변경됨! */

이전 프로젝트의 디자인 시스템 분석

Tailwind CSS의 @theme 활용

이전 프로젝트는 Tailwind CSS v4의 @theme 블록을 사용하여 디자인 토큰을 정의했습니다.

@theme {
  /* Green */
  --color-green-light: oklch(0.98 0.01 134);
  --color-green-light-hover: oklch(0.96 0.02 134);
  --color-green: oklch(0.75 0.21 134);
  --color-green-hover: oklch(0.69 0.2 134);
  --color-green-dark: oklch(0.56 0.16 134);
  
  /* Orange */
  --color-orange-light: oklch(0.97 0.01 45);
  --color-orange: oklch(0.75 0.27 45);
  --color-orange-dark: oklch(0.61 0.22 45);
}

OKLCH 색상 시스템이란?

OKLCH는 밝기/채도/색상 세 가지 값으로 색을 표현하는 시스템입니다.

그렇다면 왜 RGB/HEX가 아닌 OKLCH를 사용할까요?

1. 균일함
RGB/HEX는 같은 밝기 값을 주어도 색상에 따라 실제 밝기가 다르게 보입니다.

/* RGB는 노란색이 파란색보다 훨씬 밝게 보임 */
color: rgb(255, 255, 0);	/* 노란색 */
color: rgb(0, 0, 255);	/* 파란색 */

/* 그러나 OKLCH는 같은 L값이면 실제로도 비슷한 밝기 */
color: oklch(0.75 0.21 60);	/* 노란색 */
color: oklch(0.75 0.21 240);	/* 파란색 */

2. 색상 변형이 쉬움

/* 기본 컬러에서 밝기만 조절 */
--color-green: oklch(0.75 0.21 134);
--color-green-light: oklch(0.85 0.21 134);	/* L만 증가 */
--color-green-dark: oklch(0.65 0.21 134);	/* L만 감소 */

3. 더 넓은 색 범위
OKLCH는 RGB보다 훨씬 많은 색을 표현할 수 있으며, 특히 비비드한 컬러 표현에 강합니다.


컬러 네이밍 규칙

프로젝트 협업시에는 체계적인 네이밍 규칙으로 색상의 용도를 명확히 해야합니다!

--color-{색상}-{밝기}-{상태}

/* 예시 */
--color-green-light        /* 밝은 초록 */
--color-green-light-hover  /* 밝은 초록 (ex. hover 상태) */
--color-green              /* 기본 초록 */
--color-green-hover        /* 기본 초록 (ex. hover 상태) */
--color-green-dark         /* 어두운 초록 */

이런 규칙을 통해, 색상의 용도를 변수명만 봐도 파악이 가능합니다. 또한 인터랙션 상태(hover, active)를 위한 색상을 미리 정의해서 빠른 UI 구성에 도움이 되고, 역시 디자인에 일관성이 보장이 되어 협업에 유리합니다.

타이포그래피 유틸리티 클래스

@layer utilities {
  /* Title */
  .text-title-semibold {
    font-size: clamp(1.375rem, 1.1vw + 1rem, 1.5rem);
    font-weight: 600;
    line-height: 2rem;
  }
  
  /* Body */
  .text-body-regular {
    font-size: clamp(0.9375rem, 0.5vw + 0.5rem, 0.9375rem);
    font-weight: 400;
    line-height: 1.25rem;
  }
}

Pretendard 폰트 스택 설정

.font-pretendard {
  font-family:
    BlinkMacSystemFont,
    -apple-system,
    Roboto,
    Pretendard,
    'Noto Sans KR',
    'Segoe UI',
    sans-serif;
}

폰트 스택의 의미는

BlinkMacSystemFont, -apple-system: macOS의 시스템 폰트
Roboto: Android 기본 폰트
Pretendard: 프로젝트의 주 폰트
Noto Sans KR: 한글 대체 폰트
sans-serif: 최종 대체 폰트

→ 사용자 환경에 따라 최적의 폰트를 자동으로 선택하는 것입니다!

clamp() 함수로 반응형 폰트 크기 구현

clamp() 함수란?

font-size: clamp(최소값, 선호값, 최대값);

이 때의 선호값이란, 뷰 포트 크기에 따라 유동적으로 변하는 값입니다.

실제 예시:

.text-title-semibold {
  /* 모바일(320px): 22px
     태블릿(768px): 약 23.4px
     데스크톱(1440px): 24px (최대값) */
  font-size: clamp(1.375rem, 1.1vw + 1rem, 1.5rem);
}

왜 좋은가?

  • 미디어 쿼리 없이도 반응형 타이포그래피를 구현할 수 있음
  • 모든 화면 크기에서 자연스럽게 조정됨
  • 코드가 간결해짐

일관된 타이포그래피 시스템

아래처럼 계층적 구조로 텍스트 크기를 정의하는 시스템입니다.

Title    (24px) - 페이지 제목
Subtitle (20px) - 섹션 제목
Body     (15px) - 본문
Small    (12px) - 보조 정보
Tiny     (10px) - 캡션

이때 각 크기마다 여러 weight를 제공합니다

ex.

semibold (600)
medium (500)
regular (400)

왜 좋은가?

  • 디자인 토큰 구현이 가능함
  • 일관성 있음
  • 유지보수가 쉬움

CSS로 구현한 디자인 시스템

저는 이번 프로젝트에서 tailwind CSS가 아닌 Styled-Component를 사용했습니다.
그래서 Tailwind의 @theme 블록을 CSS 변수로 변환해서 사용했고, HEX 컬러와 조합해서 사용했습니다.
간단한 과제였기 때문에 프로젝트 규모에 맞게 네이밍도 단순화해서 사용했습니다.

/*CSS Variables + HEX*/

:root {
  --main-color: #2c2c2c;
  --point-color: #00c8ff;
  --text-color: #000000;
  --bg-color: #ffffff;
  --border-color: #e0e0e0;
}

또한 Tailwind의 타이포그래피 클래스인 @layer utilities를 일반 CSS 클래스로 변환했습니다.

/* 타이포그래피 유틸리티 */
.text-title-semibold {
  font-size: clamp(1.375rem, 1.1vw + 1rem, 1.5rem);
  font-weight: 600;
  line-height: 2rem;
}

.text-body-regular {
  font-size: clamp(0.9375rem, 0.5vw + 0.5rem, 0.9375rem);
  font-weight: 400;
  line-height: 1.25rem;
}

아래는 제가 1주차 과제에 적용한 최종 styles.css의 일부입니다.

/* CSS Reset */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: 'Noto Sans KR', sans-serif;
}

/* 글로벌 스타일 - 디자인 토큰 */
:root {
  /* 색상 */
  --main-color: #2c2c2c;
  --point-color: #00c8ff;
  --text-color: #000000;
  --bg-color: #ffffff;
  --border-color: #e0e0e0;

  /* 폰트 크기 */
  --text-small: 12px;
  --text-medium: 16px;
  --text-large: 20px;
  --text-xlarge: 24px;

  /* 기타 */
  --border-radius: 4px;
  --header-height: 80px;
  --max-width: 375px;
}

body {
  min-height: 100vh;
  background-color: var(--bg-color);
  color: var(--text-color);
}

활용 예시:

/* 헤더 */
.header {
  position: fixed;
  height: var(--header-height);
  background-color: var(--bg-color);
  border-bottom: 1px solid var(--border-color);
}

/* 버튼 */
.btn-primary {
  background-color: var(--main-color);
  color: white;
  border-radius: var(--border-radius);
}

/* 타이틀 */
.introduce-title {
  font-size: var(--text-xlarge);
  font-weight: bold;
  color: var(--text-color);
}

아쉬운 부분은 CSS에서 OKLCH를 직접 사용하지 못한 부분입니다
요즘 대부분의 프로젝트에서 Tailwind를 사용하는 걸로 알지만, CSS로 디자인 시스템을 구현해보면서 도구보단 그 안의 원칙에 집중해서 디자인 시스템 설계를 고민해볼 수 있었습니다!

Tailwind 없이 CSS로만 디자인 시스템을 구현하면서

  • 왜 디자인 토큰이 필요한지
  • 왜 일관된 네이밍 규칙이 중요한지
  • 왜 변수로 관리하면 유지보수가 쉬운지를 생각해보며,

중요한 건 Tailwind를 사용하든, CSS 변수를 사용하든,

  • 일관성 있는 디자인 시스템을 구축하는 것
  • 재사용 가능한 토큰을 정의하는 것
  • 유지보수하기 쉬운 구조를 만드는 것

라는 걸 느꼈습니다. 좋은 설계에 더 가까워진 기분이었습니다!

profile
처음 공부했을 때의 궁금했던 걸 기록하고 글을 읽는 사람이 이해하기 쉬운 글을 작성하려고 노력합니다

0개의 댓글