Emotion + Tailwind CSS

박정호·2023년 1월 27일
0
post-thumbnail

🚀 Start

지금까지 여러방법으로 스타일링을 진행하였다. 가장 일반적인 방법으로는 CSS, Module CSS, SaSS 그리고 요즘들어 가장 간편하게 사용하고 있는 TailwindCSS, 마지막으로 간단하게 JS파일에 작성하는 Styled Components가 있었다.

코드를 작성하다보면 항상 느꼈던 점이 있다. Tailwind CSS는 Utlity Fisrt컨셉을 가진 프레임워크라 내가 원하는 디자인에 대해 찾아서 그대로 작성만 하면 돼서 정말 편하게 사용했다. 하지만, 태그안의 className에 모두 작성을 하다보니 은근 코드가 길어지고 가독성이 떨어졌었다.

반면, Styled Components (or Emotion)를 사용할 때는 하나의 파일 안에서 별도의 코드로 스타일링을 작성할 수 있다는 장점이 있지만, 일반적인 CSS 문법을 이용해야해서 내가 원하는 디자인을 구현하려면 Tailwind에 비해서 접근성이 떨어졌었다.

그래서, TailwindCSS 와 Styled Components를 합친 기능은 없을까 라는 생각을 하게 되었고, twin marco가 나의 고민을 해결해 줄 것 같다.


👍 twin marco

공식 문서에 따르면 다음과 같이 소개한다.

: The magic of Tailwind with the flexibility of css-in-js.
(css-in-js의 유연성과 Tailwind의 마법같은 기능들?)

👉 공식문서

간단하게 말하면, Styled ComponentsEmotion 기능에 TailwindCSS도 사용이 가능하다고 알면 될거 같다. 자세한 사용법은 직접 코드를 작성하며 알아보자~



⚙️ Setting

twin marco는 emotion, styled-component 라이브러리에 대해 의존성을 가지기 때문에 별도의 설치가 필요하고, 설정해야할 것들이 꽤 있다.


Install

 "dependencies": {
    "@emotion/react": "^11.10.5",
    "@emotion/styled": "^11.10.5",
    "styled-components": "^5.3.6"
  },
 "devDependencies": {
    "@babel/core": "^7.20.5",
    "@babel/preset-react": "^7.18.6",
    "@babel/preset-typescript": "^7.18.6",
    "@emotion/babel-preset-css-prop": "^11.10.0",
    "@tailwindcss/typography": "^0.5.8",
    "babel-loader": "^9.1.0",
    "babel-plugin-macros": "^3.1.0",
    "babel-plugin-twin": "^1.1.0",
    "tailwindcss": "^3.2.4",
    "twin.macro": "^3.1.0",
    "typescript": "^4.9.4",
    "webpack": "^5.75.0",
    "webpack-cli": "^5.0.1",
    "webpack-dev-server": "^4.11.1"
  },

Preset

// babelrc
{
  "presets": [
    "@babel/preset-env",
    ["@babel/preset-react", { "runtime": "automatic" }],
    "@emotion/babel-preset-css-prop",
    "@babel/preset-typescript"
  ],
  "plugins": ["babel-plugin-twin", "babel-plugin-macros"]
}

// package.json
  "babelMacros": {
    "twin": {
      "preset": "emotion"
    }
  }

.d.ts 파일

// types/twin.d.ts
import 'twin.macro'
import { css as cssImport } from '@emotion/react'
import styledImport from '@emotion/styled'
import { CSSInterpolation } from '@emotion/serialize'

declare module 'twin.macro' {
  // The styled and css imports
  const styled: typeof styledImport
  const css: typeof cssImport
}

declare module 'react' {
  // The tw and css prop
  interface DOMAttributes<T> {
    tw?: string
    css?: CSSInterpolation
  }
}

💡 d.ts 파일
타입스크립트 선언 파일 d.ts는 타입스크립트 코드의 타입 추론을 돕는 파일.

예를 들어, 전역 변수로 선언한 변수를 특정 파일에서 import 구문 없이 사용하는 경우 해당 변수를 인식하지 못합니다. 그럴 때 아래와 같이 해당 변수를 선언해서 에러가 나지 않게 할 수 있습니다.

tsconfig.json

// tsconfig.json
{
	"compileOptions:{
    	...
        "jsxImportSource": "@emotion/react"

    },
    "include":["types"]
}

tailwind.config.js

// tailwind.config.js
module.exports = { // 사용자 설정 및 지원하지 않는 기능 가져오기
  theme: {
    extend: {
      colors: {
        electric: "#db00ff",
        ribbon: "#0047ff",
      },
    },
  },
  plugins: [],
};


🌈 Example

👉 일반적인 사용

Emotion

// 1️⃣ SSR에서 별도의 설정 없이 동작 가능

// 2️⃣ typescript로 자동 타입지정
type ThemeType = keyof typeof themes;
type SizeType = keyof typeof size;

// 3️⃣ css props를 결합하여 복잡한 스타일링을 진행
<div css={[style, themes[theme], sizes[size]]} />

// 4️⃣ css props 기능
const themes = {
  primary: css`
    color: red;
  `,  
  secondary: css`
    color: blue;
  `
}
const sizes = {
  small: css`
    fontSize: 0.75rem;
  `,
  medium: css`
    fontSize: 1rem;
  `
}

Styled Components

const Circle = styled.div`
  width: 5rem;
  height: 5rem;
  background: ${props => props.color || 'black'};
  border-radius: 50%;
  ${props =>
    props.huge &&
    css`
      width: 10rem;
      height: 10rem;
    `}
`;

function App() {
  return <Circle color="red" huge />;
}

Tailwind CSS

<button class="py-2 px-4 rounded-lg shadow-md text-white bg-blue-500">
 Click me
</button>

👉 [React] emotion과 styled-component의 차이
👉 styled-components 과 emotion, 도대체 차이가 뭔가?
👉 Emotion 공식문서
👉 Styled Components 공식문서
👉 Tailwind CSS 공식문서



👉 twin marco


taildwind CSS + 일반 CSS 조합

import tw, { css } from "twin.macro";

const styles = {
  test: [tw`bg-green-400`, css`font-size: 30px`]
};
...

<button css={styles.test}>버튼</button>

props 기능

const styles = {                       // 타입 지정
  container: ({ hasBackground }: { hasBackground: boolean }) => [
    tw`bg-red-400 items-center justify-center`,
    hasBackground && tw`bg-green-400`,
  ]
};

...

// boolean값에 따른 조건부 스타일 지정
<div css={styles.container({ hasBackground: true })}>
 ...      
</div>

컴포넌트 단위 스타일링

// component/Button.tsx
import tw, { styled, css, theme } from "twin.macro";

interface ButtonProps { // 타입 지정
  variant?: "primary" | "secondary";
  isSmall?: boolean;
}

const Button = styled.button(({ variant, isSmall }: ButtonProps) => [
  // 1️⃣ 공통 버튼 스타일 설정
  tw`px-8 py-2 rounded transform duration-75`,

  // 2️⃣ 변형 스타일 설정
  tw`hocus:(scale-105 text-yellow-400)`,

  // 3️⃣ props에 따른 조건부 스타일 설정
  variant === "primary" && tw`bg-black text-white border-black`,

  // 4️⃣ 일반 CSS + Tailwind CSS 조합 가능
  variant === "secondary" && [
    css`
      background-color: red;
    `,
    tw`border-2 border-yellow-600`,
  ],

  // 5️⃣ props에 따른 조건부 스타일 설정
  isSmall ? tw`text-sm` : tw`text-lg`,

  // 6️⃣ tailwind.config.js에서 설정해놓은 값 가져오기
  css`
    color: ${theme`colors.white`};
  `,
]);
export default Button;

// index.tsx
const Home = () => (
  <>
      <Button variant="primary">dd</Button>
      <Button variant="secondary">dd</Button>
  </>
);


🍀 회고

아직까지는 익숙하지 않아서 조금은 스타일 구현 속도가 느리다. 하지만, 직접 사용해보고나니 훨씬 깔끔하게 사용할 수 있을 것 같다. 다른 스타일의 버튼을 구현할 때마다 늘 하나하나 스타일을 입혀줬는데 공통된 스타일도 지정 가능하고, props 기능들, tailwind 와 css를 조합하여 사용한다는 장점들이 존재해서 이번 기회에 진행 중인 프로젝트에 적용시켜봐야겠다!

profile
기록하여 기억하고, 계획하여 실천하자. will be a FE developer (HOME버튼을 클릭하여 Notion으로 놀러오세요!)

0개의 댓글