개발

props 타입 상속으로 시맨틱 태그에 테마 적용 하기

배우겠습니다 2024. 4. 4. 17:38

frame work

  • vanilla extract, nextjs
  • react와 css(+module)를 사용해도 상관 없다.

문제 상황

  • 만약에 props로 스타일링만 제어하고 나머지 버튼의 기능은 사용하고 싶다고 하자.
  • props를 설정할 때 모든 버튼의 attributes를 적어주는건 매우 고된 일이다. 게다가 버튼말고도 다른 태그에도 이런 요구사항이 있다면?
  • 하나하나 적다가 빼먹거나 잘못적는 attribute가 나올수도 있고, 기존 내가 추가로 넣을 props를 관리하기도 어려워진다.
  • 각 attribute마다 정확한 타입을 적용하는 것도 태스크가 많이 들어갈 것이다.

해결

  • React는 이미 내부적으로 각 태그마다 props의 타입을 정의해 놓았고 export 시켜놓았다.

    DetailedHTMLProps<XXXHTMLAttributes<HTMLXXXElement>,HTMLXXXElement>
    // 예를들어 button은
    DetailedHTMLProps<
      ButtonHTMLAttributes<HTMLButtonElement>,
      HTMLButtonElement
    > 
  • 우린 위 타입을 사용하지 않을 이유가 없다.

  • 타입 상속으로 위 타입을 받아오고 우리는 우리가 추가로 설정할 props만 적용하면 된다.

    interface PropType
    extends DetailedHTMLProps<
      ButtonHTMLAttributes<HTMLButtonElement>,
      HTMLButtonElement
    > {
    size?: "small" | "medium" | "large";
    color?: "primary" | "negative" | "positive" | "white";
    fullWidth?: boolean;
    flat?: boolean;
    children?: ReactNode;
    // 추가적인 props
    }
  • 그럼 PropType에는 react buttom element의 attribute로 가능한 것들을 이미 사용할 수 있다.

  • 하지만 실제로 이걸 이용해 태그에 attribute를 적용하는 방법은 아직은 미지수이다.

    export default function Button({
    size = "medium",
    color = "primary",
    children,
    fullWidth = false,
    flat = false,
    ...props
    }: PropType) {
    return (
      <button
        {...props}
      >
        {children}
      </button>
    );
    }
  • 스프레드를 이용해, 우리가 추가적으로 선언한 것 외의 props = attributes of button tag를 받아올 수 있다.

  • 따라서 이것을 버튼에 적용시켜주면 된다.

스타일링 적용: Vanilla extract

  • 순수한 css를 사용하겠다고 한다면 pass

  • 이제 props의 값에 따라 className을 변형시켜 버튼의 스타일을 조정할 수 있다.

  • recipe는 variant값에 따른 className을 리턴시켜준다.

    const buttonStyle = recipe({
    base: {
      // 버튼에 적용할 기본적인 스타일
    },
    variants: {
      변수1:{
          값1:{
              //style
          },
             값2:{
              //style
          },
      },
      변수2:{
          값1:{
              //style
          },
          값2:{
              //style
          },
      }
    },
    });
    export default buttonStyle;
  • 이제 이걸 button tag의 className에 넣어준다.
className={classNames(
props?.className, // 추가적으로 선언하고 싶은 스타일
buttonStyle({
color: color, // props로 color size fullWidth flat을 받아온다.
size: size,
fullWidth: fullWidth,
flat: flat,
})
)