Featuring Design System

Theming

브랜드 테마 전환과 Vanilla Extract를 활용한 고급 테마 구성

Featuring Design System은 두 가지 브랜드 테마를 지원합니다. CSS 파일 하나로 전체 테마를 전환할 수 있습니다.

브랜드 테마

테마Primary 색상CSS 파일
Featuring#5e51ff (보라색 계열)featuring.css
DataEffect#0065ff (파란색 계열)dataEffect.css

테마 전환

앱 진입점에서 import하는 Preset만 변경하면 전체 테마가 전환됩니다. Preset은 Reset, Normalize, 브랜드 토큰, CSS Layer 순서를 한 파일로 제공합니다.

// Featuring 테마 (Reset + Normalize + 토큰 + Layer 순서 포함)
import '@featuring-corp/components/preset/featuring';

// DataEffect 테마
import '@featuring-corp/components/preset/dataEffect';

Preset은 @layer ft.reset, ft.normalize, ft.components, ft.utilities 순서를 선언합니다. 소비자 CSS는 레이어 밖에 있으므로 항상 디자인 시스템 스타일보다 우선합니다.

Primary 색상만 달라지며, 나머지 글로벌 토큰(gray, red, blue 등)과 시맨틱 토큰은 동일합니다.

테마별 Primary 팔레트

ShadeFeaturingDataEffect
primary-10#ecefff#edf5ff
primary-20#dce2ff#d0e2ff
primary-30#c0c8ff#a6c8ff
primary-40#9aa3ff#4c9aff
primary-50#7273ff#2684ff
primary-60#5e51ff#0065ff
primary-70#5032f9#0052cc
primary-80#3821b2#0747a6
primary-90#31238c#001d6c
primary-100#1f1551#001141

CSS 변수로 테마 사용

CSS 파일을 import하면 모든 토큰이 :root에 CSS 변수로 등록됩니다.

.my-button {
  background-color: var(--global-colors-primary-60);
  color: var(--global-colors-white);
  border-radius: var(--global-radius-100);
  padding: var(--global-spacing-200) var(--global-spacing-400);
}

.my-button:hover {
  background-color: var(--global-colors-primary-70);
}

시맨틱 토큰도 CSS 변수로 사용할 수 있습니다:

.my-card {
  background-color: var(--semantic-color-background-1);
  border: 1px solid var(--semantic-color-border-default);
  color: var(--semantic-color-text-1);
}

JavaScript에서 토큰 접근

import { global, semantic, colorSet } from '@featuring-corp/design-tokens';

// Global tokens
global.colors.red[50]          // '#e97259'
global.spacing[400]            // '1rem'
global.radius[200]             // '8px'
global.elevation[4]            // CSS box-shadow 값
global.typography.fontSize[300] // '0.875rem'

// Semantic tokens (CSS 변수 참조)
semantic.color.background[1]   // 'var(--global-colors-white)'
semantic.color.text[1]         // 'var(--global-colors-gray-90)'

// Color sets (실제 hex 값)
colorSet.featuringPrimary[60]  // '#5e51ff'
colorSet.dataEffectPrimary[60] // '#0065ff'

Vanilla Extract 고급 설정

프로젝트에서 Vanilla Extract를 직접 사용하여 타입 안전한 테마를 구성할 수 있습니다.

설치

npm install -D @vanilla-extract/css @vanilla-extract/next-plugin

Next.js 설정

// next.config.ts
import { createVanillaExtractPlugin } from '@vanilla-extract/next-plugin';
import type { NextConfig } from 'next';

const withVanillaExtract = createVanillaExtractPlugin();

const nextConfig: NextConfig = {
  transpilePackages: ['@featuring-corp/components'],
};

export default withVanillaExtract(nextConfig);

Theme Contract 생성

// theme.css.ts
import { getGlobalColors, getVarName, global, semantic } from '@featuring-corp/design-tokens';
import { createGlobalTheme, createGlobalThemeContract } from '@vanilla-extract/css';

export const vars = createGlobalThemeContract(
  {
    global: {
      colors: getGlobalColors('featuring'),
      elevation: global.elevation,
      spacing: global.spacing,
      radius: global.radius,
      typography: global.typography,
    },
    semantic,
  },
  getVarName,
);

createGlobalTheme(':root', vars, {
  global: {
    colors: getGlobalColors('featuring'),
    elevation: global.elevation,
    spacing: global.spacing,
    radius: global.radius,
    typography: global.typography,
  },
  semantic,
});

사용 예시

// card.css.ts
import { style } from '@vanilla-extract/css';
import { vars } from './theme.css';

export const card = style({
  backgroundColor: vars.semantic.color.background[1],
  border: `1px solid ${vars.semantic.color.border.default}`,
  borderRadius: vars.global.radius[200],
  padding: vars.global.spacing[400],
  boxShadow: vars.global.elevation[4],
});

export const cardTitle = style({
  fontSize: vars.semantic.typography.heading[3].fontSize,
  fontWeight: vars.semantic.typography.heading[3].fontWeight,
  lineHeight: vars.semantic.typography.heading[3].lineHeight,
  color: vars.semantic.color.text[1],
});

Typography Variants

// typography.css.ts
import { styleVariants, ComplexStyleRule, StyleRule } from '@vanilla-extract/css';
import { vars } from './theme.css';

const base = { fontFamily: vars.global.typography.font.sans } as const;
const getTypoObj = (typo: unknown) => ({ ...base, ...(typo as StyleRule) }) as ComplexStyleRule;

export const heading = styleVariants(vars.semantic.typography.heading, getTypoObj);
export const body = styleVariants(vars.semantic.typography.body, getTypoObj);
export const caption = styleVariants(vars.semantic.typography.caption, getTypoObj);

커스텀 Primary 색상

getGlobalColors에 커스텀 색상 객체를 전달하여 새로운 브랜드 테마를 만들 수 있습니다.

import { getGlobalColors } from '@featuring-corp/design-tokens';

const customColors = getGlobalColors({
  10: '#fef3e2',
  20: '#fde4b8',
  30: '#fbd38d',
  40: '#f9b851',
  50: '#f6a623',
  60: '#e8930a',
  70: '#c47a08',
  80: '#9f6206',
  90: '#7b4c05',
  100: '#573603',
});