I HATE FLYING BUGS logoFE Chapter

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에서 처리해야 합니다.