I HATE FLYING BUGS logoFE Chapter
Bak

Emotion CSS에서 Panda CSS로의 마이그레이션 가이드

Emotion CSS를 Panda CSS로 마이그레이션하는 방법과 코드 변환 가이드

1. 코드 변환 가이드

1.1 기본 스타일 변환

Emotion CSS:

const style = css`
  color: red;
  background: white;
`;

Panda CSS:

import { css } from '@mildang/styled-system/css';

const style = css({
  color: 'red',
  background: 'white',
});

1.2 중첩 선택자 변환

Emotion CSS:

const style = css`
  & .child {
    color: red;
  }
  &:hover {
    background: blue;
  }
`;

Panda CSS:

import { css } from '@mildang/styled-system/css';

const style = css({
  '& .child': {
    color: 'red',
  },
  '&:hover': {
    background: 'blue',
  },
});

1.3 외부 참조 중첩 선택자 변환

Emotion CSS:

import { menuClasses } from '@mildang/mui';
const menuStyle = emotionCSS`
  & .${menuClasses.list} {
    padding-top: 4px;
    padding-bottom: 4px;
  }
`;

Panda CSS:

import { css } from '@mildang/styled-system/css';

const style = css({
  '& .MuiMenu-list': {
    paddingTop: '4px',
    paddingBottom: '4px',
  },
});

1.4 조건부 스타일 변환 (CVA 사용)

Emotion CSS:

const style = ({ isActive }) => css`
  color: red;
  ${isActive &&
  css`
    background: blue;
  `}
`;

Panda CSS (CVA 사용):

import { cva } from '@mildang/styled-system/css';

const style = cva({
  base: {
    color: 'red',
  },
  variants: {
    isActive: {
      true: {
        background: 'blue',
      },
    },
  },
});

// 사용 예시
const Component = ({ isActive }) => {
  return <div className={style({ isActive })}>Content</div>;
};

1.5 테마 사용 변환

Emotion CSS:

const style = ({ theme }) => css`
  color: ${theme.palette.primary.main};
`;

Panda CSS:

import { css } from '@mildang/styled-system/css';

const style = css({
  color: 'primary.main', // Panda CSS의 토큰 시스템 사용
});

2. 실제 예시 변환 (CVA 사용)

2.1 복잡한 컴포넌트 스타일 변환

Emotion CSS:

const editorViewerStyle = ({
  theme,
  editorCanvasOpen,
}: {
  theme: Theme;
  editorCanvasOpen: boolean;
}) => emotionCSS`
  width: inherit;
  height: 100%;
  background-color: white;
  & .excalidraw__canvas {
    border-radius: 32px;
  }
  .excalidraw {
    .dropdown-menu-button {
      display: none;
    }
    .help-icon {
      display: none;
    }
    .layer-ui__wrapper__footer {
      bottom: ${GAP + BOTTOM_TOOLBAR_HEIGHT}px;
    }
  }
  ${
    editorCanvasOpen &&
    emotionCSS`
    display: none;
  `
  }
`;

Panda CSS (CVA 사용):

import { cva } from '@mildang/styled-system/css';

const editorViewerStyle = cva({
  base: {
    width: 'inherit',
    height: '100%',
    backgroundColor: 'white',
    '& .excalidraw__canvas': {
      borderRadius: '32px',
    },
    '& .excalidraw': {
      '& .dropdown-menu-button': {
        display: 'none',
      },
      '& .help-icon': {
        display: 'none',
      },
      '& .layer-ui__wrapper__footer': {
        bottom: `${GAP + BOTTOM_TOOLBAR_HEIGHT}px`,
      },
    },
  },
  variants: {
    editorCanvasOpen: {
      true: {
        display: 'none',
      },
      false: {
        display: 'block',
      },
    },
  },
});

// 사용 예시
const EditorViewer = ({ editorCanvasOpen }: { editorCanvasOpen: boolean }) => {
  return <div className={editorViewerStyle({ editorCanvasOpen })}>{/* 컴포넌트 내용 */}</div>;
};

3. CVA 사용 시 고려사항

  1. 기본 스타일과 변형 분리

    • base 속성에 공통 스타일 정의
    • variants 속성에 조건부 스타일 정의
  2. 타입 안정성

    • CVA는 자동으로 타입을 생성하여 타입 안정성 제공
    • 변형의 모든 가능한 조합에 대한 타입 체크 가능
  3. 복합 변형

    • 여러 조건을 조합하여 사용 가능
    • 각 변형의 조합에 따른 스타일 정의 가능
import { cva } from '@mildang/styled-system/css';

const style = cva({
  base: {
    // 기본 스타일
  },
  variants: {
    size: {
      sm: {
        /* ... */
      },
      md: {
        /* ... */
      },
      lg: {
        /* ... */
      },
    },
    variant: {
      primary: {
        /* ... */
      },
      secondary: {
        /* ... */
      },
    },
  },
  compoundVariants: [
    {
      size: 'sm',
      variant: 'primary',
      css: {
        // 특정 조합에 대한 스타일
      },
    },
  ],
});
  1. 기본값 설정
    • defaultVariants를 사용하여 기본 변형값 설정 가능
import { cva } from '@mildang/styled-system/css';

const style = cva({
  // ... base와 variants 정의
  defaultVariants: {
    size: 'md',
    variant: 'primary',
  },
});

4. 마이그레이션 체크리스트

  • 기존 Emotion CSS 코드 분석
  • 토큰 시스템 정의
  • 조건부 스타일을 CVA로 변환
  • 컴포넌트 단위 마이그레이션
  • 테스트 및 검증
  • Emotion CSS 의존성 제거

5. 컬러 토큰 변환 가이드

emotion css의 컬러는 ${theme.palette.primary.main}과 같이 적용되어있는데, 이는 panda css에서는 common.7로 변환 되어야합니다. 왼쪽 컬러와 매칭되는 오른쪽 컬러 토큰으로 변경해주세요

stroke: common.4
divider: common.4
snackbar: common.12
activeRating: yellow-light-600

common.black: common.7
common.white: common.1

grey.50: common.2
grey.100: common.3
grey.200: common.4
grey.300: common.5
grey.400: common.6
grey.500: common.7
grey.600: common.10
grey.700: common.11
grey.800: common.11
grey.900: common.7
grey.A100: common.3
grey.A200: common.4
grey.A400: common.6
grey.A700: common.11

primary.dark: common.7
primary.darkActive: common.9
primary.darkHover: common.8
primary.main: common.7
primary.stroke: common.5
primary.strokeHover: common.6
primary.lightHover: common.2
primary.lightActive: common.4
primary.light: common.1
primary.contrastText: common.1

secondary.dark: brand.10
secondary.darkActive: brand.9
secondary.darkHover: brand.8
secondary.main: brand.7
secondary.stroke: brand.5
secondary.strokeHover: brand.6
secondary.lightActive: brand.3
secondary.lightHover: brand.2
secondary.light: brand.1
secondary.contrastText: common.1

error.dark: error.10
error.darkActive: error.9
error.darkHover: error.8
error.main: error.7
error.stroke: error.5
error.strokeHover: error.6
error.lightHover: error.2
error.lightActive: error.3
error.light: error.1
error.contrastText: common.1

success.dark: success.10
success.darkActive: success.9
success.darkHover: success.8
success.main: success.7
success.stroke: success.5
success.strokeHover: success.6
success.lightHover: success.2
success.lightActive: success.3
success.light: success.1
success.contrastText: common.1

warning.dark: warning.10
warning.darkActive: warning.9
warning.darkHover: warning.8
warning.main: warning.7
warning.stroke: warning.5
warning.strokeHover: warning.6
warning.lightActive: warning.3
warning.lightHover: warning.2
warning.light: warning.1
warning.contrastText: common.1

info.dark: info.10
info.darkActive: info.9
info.darkHover: info.8
info.main: info.7
info.stroke: info.5
info.strokeHover: info.6
info.lightActive: info.3
info.lightHover: info.2
info.light: info.1
info.contrastText: common.1

text.primary: common.12
text.secondary: common.11
text.tertiary: common.10

background.primary: common.1
background.secondary: common.2
background.tertiary: common.3
background.paper: common.1
background.default: common.1

6. 주의사항

  1. 토큰 시스템 활용

    • Panda CSS의 토큰 시스템을 적극 활용하여 일관된 디자인 시스템 구축
    • 색상, 간격, 타이포그래피 등의 값을 토큰으로 정의
    • 기존 theme 참조: apps/design-system/src/styles/
    • 신규 theme 참조: apps/design-system/src/preset/
  2. 성능 최적화

    • Panda CSS는 빌드 타임에 CSS를 생성하므로 런타임 성능이 향상됨
    • 동적 스타일링이 필요한 경우 css.raw() 사용 고려
  3. 점진적 마이그레이션

    • 큰 프로젝트의 경우 컴포넌트 단위로 점진적 마이그레이션 진행
    • Emotion과 Panda CSS를 동시에 사용할 수 있음
  4. 일반적인 문제 해결

    • 중첩 선택자 문제: Panda CSS에서는 & 선택자를 명시적으로 사용
    • 동적 스타일링: 조건부 스타일은 CVA를 사용하여 구현
    • 테마 값 접근: Emotion의 theme 객체 대신 Panda CSS의 토큰 시스템 사용
  5. ⚠️ 중요: 마이그레이션 범위 제한

    CSS 시스템 변환에만 집중하고 다른 변경사항 제외:

    다음 사항들은 CSS 마이그레이션과 무관하므로 변경하지 마세요:

    // ❌ 잘못된 예시 - import 문 포맷팅 변경 (CSS 변환과 무관)
    - import {
    -   Menu,
    -   MenuItem,
    -   Stack,
    - } from '@mildang/mui';
    + import { Menu, MenuItem, Stack } from '@mildang/mui';
    
    // ❌ 잘못된 예시 - JSX 포맷팅 변경 (CSS 변환과 무관)
    - <Tooltip
    -   title="example"
    -   placement="top"
    -   arrow
    - >
    + <Tooltip title="example" placement="top" arrow>
    
    // ❌ 잘못된 예시 - 변수명 변경, 코드 구조 최적화 등 (CSS 변환과 무관)

    올바른 마이그레이션 범위 - CSS 시스템만 변경:

    // ✅ 올바른 예시 - CSS 시스템만 변경
    - css={(theme: Theme) => emotionStyle(theme)}
    + className={pandaStyle({ variant: 'default' })}
    
    // ✅ 올바른 예시 - 스타일 정의 변환만
    - const style = emotionCSS`color: red;`;
    + const style = css({ color: 'red' });
  6. ⚠️ 중요: CSS 시스템 혼용 금지

    절대 금지사항: Emotion CSS와 Panda CSS 혼용

    다음과 같이 emotion CSS 스타일을 panda CSS 객체에 스프레드하는 것은 런타임 에러를 발생시킵니다:

    // ❌ 잘못된 예시 - 절대 하지 마세요!
    import { timeInputStyle } from '@mildang/design-system-legacy/esm/styles/commonStyle';
    import { css } from '@mildang/styled-system/css';
    
    const style = css({
      width: '100%',
      ...timeInputStyle, // ❌ emotion CSS를 panda CSS에 스프레드
    });

    올바른 해결책:

    // ✅ 올바른 예시 - emotion CSS 내용을 panda CSS로 직접 변환
    import { css } from '@mildang/styled-system/css';
    
    const style = css({
      width: '100%',
      '& input[type="time"]::-webkit-calendar-picker-indicator': {
        width: '22px',
        height: '22px',
      },
    });

    이유:

    • Emotion CSS는 css tagged template 또는 css() 함수로 emotion 전용 객체/클래스명을 반환
    • Panda CSS는 완전히 다른 빌드타임 CSS-in-JS 시스템으로 일반 CSS 객체만 허용
    • 두 시스템을 혼용하면 타입 에러와 런타임 스타일 깨짐 현상 발생

    마이그레이션 시 확인 포인트:

    • import에서 emotion CSS 스타일을 가져와서 panda CSS에 스프레드하는 부분 검색
    • ...someEmotionStyle 패턴을 찾아 해당 스타일을 panda CSS로 직접 변환
    • emotion CSS 관련 import 제거
  7. 🎯 마이그레이션 원칙: 최소 변경

    DO (해야 할 것):

    • CSS 스타일 정의만 변환 (emotionCSScss, cva)
    • CSS 적용 방식만 변경 (css={style}className={style})
    • CSS 관련 import만 업데이트
    • 사용하지 않는 CSS 관련 import만 제거

    DON'T (하지 말아야 할 것):

    • 코드 포맷팅 변경 (import 문 정렬, JSX 속성 정렬 등)
    • 변수명 변경 또는 리팩토링
    • 컴포넌트 구조 변경
    • 비즈니스 로직 수정
    • CSS와 무관한 import 정리
    • 린터 규칙 적용 (CSS 변환과 무관한 경우)

    이유: CSS 마이그레이션은 스타일 시스템 전환에만 집중해야 하며, 다른 변경사항은 별도 PR에서 처리해야 합니다.