Frontend/CSS

tailwind-merge 설정하기

제주도랏맨 2023. 8. 15. 11:56

 

tailwind-merge 설정하기

tailwind-merge는 Tailwind CSS 사용 시 발생할 수 있는 클래스 충돌 문제를

마지막에 위치한 클래스만을 남겨두는 방식으로 병합하여 해결하는 라이브러리다.

 

className="sticky absolute relative" // relative
className="px-3 py-4 p-2" // p-2

 

tailwind-merge는 Tailwind CSS의 설정을 건드리지 않았을 경우 정상적으로 동작한다.

그러나, 커스텀 설정을 사용할 경우 tailwind-merge에 이를 설정해주지 않는다면 정상적으로 동작하지 않을 수 있다.

(정확하게는 기본 Tailwind CSS 설정에 없는 className을 지정할 경우)

 

className="text-customBlueColor text-small" // text-small
className="px-large py-small p-medium" // px-large py-small p-medium

 

위처럼 text에 색상과 크기를 커스텀 설정으로 지정해줄 경우

tailwind-merge에서는 이를 인지하지 못해 text-라는 prefix를 가진 클래스를 전부 병합해 글씨 크기만 남게 된다.

혹은 커스텀 spacing을 사용하는 경우, padding이나 margin에 대해서 정상적인 병합이 이루어지지 않게 된다.

 

이를 해결하기 위해서는 tailwind-merge 설정에 커스텀한 Tailwind CSS의 설정을 인지시켜줄 필요가 있다.

오늘은 이 tailwind-merge 설정을 어떻게 하면 되는지 알아보자.

 

tailwind-merge에 커스텀 설정을 적용하는 방법

tailwind-merge에서 커스텀 설정을 적용하는 가장 쉬운 방법은 extendTailwindMerge 함수를 사용하는 것이다.

extendTailwindMerge는 기존의 tailwind-merge 설정에 커스텀 설정을 추가해주는 함수이다.

 

//customTwMerge.ts

import { extendTailwindMerge } from "tailwind-merge";

const twMerge = extendTailwindMerge({
  //커스텀 설정
  ...
});

export default twMerge;

 

위처럼 extendTailwindMerge 함수로 내부 설정을 적어주고, 이를 export 해준다.

사용할 때는 export된 twMerge 함수를 사용하면 된다.

 

import { cx } from "class-variance-authority";
import { ClassNameValue } from "tailwind-merge";

import twMerge from "./customTwMerge";

const classMerge = (classes: ClassNameValue[]) => {
  return twMerge(cx(classes));
};

export default classMerge;

 

tailwind-merge 설정 구조

사용방법을 알았으니 이제 설정 방법을 알아보자.

tailwind-merge 설정 구조는 아래와 같다.

 

const tailwindMergeConfig = {
    // ↓ Set how many values should be stored in cache.
    cacheSize: 500,
    // ↓ Optional prefix from TaiLwind config
    prefix: 'tw-',
    // ↓ Optional separator from TaiLwind config
    separator: '_',
    theme: {
        // Theme scales are defined here
        // This is not the theme object from your Tailwind config
    },
    classGroups: {
        // Class groups are defined here
    },
    conflictingClassGroups: {
        // Conflicts between class groups are defined here
    },
    conflictingClassGroupModifiers: {
        // Conflicts between postfix modifier of a class group and another class group are defined here
    },
}

 

여기서 커스텀 설정에 대해서 수정해야할 부분은

theme, classGroups, conflictingClassGroups, conflictingClassGroupModifiers이다.

하나씩 알아보자.

 

- theme

theme은 커스텀 설정의 color, spacing, borderRadius와 같은 설정들을 tailwind-merge에 알려주는 설정이다.

tailwind-merge의 default-config.ts에서는 theme을 아래와 같이 설정해놓고 있다.

 

//default-config.ts

theme: {
  colors: [isAny],
  spacing: [isLength],
  blur: ['none', '', isTshirtSize, isArbitraryValue],
  brightness: getNumber(),
  borderColor: [colors],
  borderRadius: ['none', '', 'full', isTshirtSize, isArbitraryValue],
  borderSpacing: getSpacingWithArbitrary(),
  borderWidth: getLengthWithEmpty(),
  contrast: getNumber(),
  grayscale: getZeroAndEmpty(),
  hueRotate: getNumberAndArbitrary(),
  invert: getZeroAndEmpty(),
  gap: getSpacingWithArbitrary(),
  gradientColorStops: [colors],
  gradientColorStopPositions: [isPercent, isArbitraryLength],
  inset: getSpacingWithAutoAndArbitrary(),
  margin: getSpacingWithAutoAndArbitrary(),
  opacity: getNumber(),
  padding: getSpacingWithArbitrary(),
  saturate: getNumber(),
  scale: getNumber(),
  sepia: getZeroAndEmpty(),
  skew: getNumberAndArbitrary(),
  space: getSpacingWithArbitrary(),
  translate: getSpacingWithArbitrary(),
},

 

- classGroups

classGroups는 위의 theme에서 설정한 부분 외의 부분들을 설정해주는 공간이다.

 

//default-config.ts

position: ['static', 'fixed', 'absolute', 'relative', 'sticky'],

 

default-config에는 위와 같이 설정되어있는데 배열 내에 속한 속성들을 중복되는 속성으로 취급하겠다는 설정이다.

이 설정을 통해 아래와 같은 병합이 이루어진다.

 

className="sticky absolute relative" // relative

 

이 외에 어떤 것들이 있는지는 tailwind-merge의 default-config.ts의 classGroups를 참조하자.

 

- conflictingClassGroups

conflictingClassGroups은 충돌 시 제거될 클래스들을 설정하는 부분이다.

 

//default-config.ts

p: ['px', 'py', 'ps', 'pe', 'pt', 'pr', 'pb', 'pl'],

 

default-config에는 위와 같이 설정되어있는데 배열 내의 태그들이 p라는 태그와 중복되는 태그라고 설정하는 것이다.

이 설정을 통해 만약 px가 p보다 앞에 있으면 px가 제거된다.

(px가 p보다 뒤에 있으면 제거되지 않는다.)

 

className="px-3 py-4 p-2" // p-2

 

이 역시 이외에 어떤 것들이 있는지는 tailwind-merge의 default-config.ts의 conflictingClassGroups를 참조하자.

 

- conflictingClassGroupModifiers

conflictingClassGroupModifiersconflictingClassGroups에 postfix가 사용되는 경우,

이 postfix와 겹치는 클래스들을 제거하기 위한 설정이다.

 

className="text-small/7"

 

예를 들어 위와 같이 Tailwind CSS에서는 글씨 크기 small 뒤에 /7이라는 postfix가 설정될 수 있는데

이는 line-height를 설정하는 것이다.

만약 이 클래스 앞에 line-height에 관한 클래스가 존재한다면 이를 병합 시 제거해야하는데,

conflictingClassGroupModifiers는 이 경우 제거할 클래스들을 설정하는 곳이라 이해하면 된다.

 

//default-config.ts

'font-size': ['leading'],

 

default-config에는 위와 같이 설정되어있는데 이 설정을 통해 아래와 같은 병합이 이루어진다.

(leading은 Tailwind CSS에서 line-height를 설정하는 클래스 이름)

 

className="leading-3 text-small/7" // text-small/7

 

설정하기

import resolveConfig from "tailwindcss/resolveConfig";
import { extendTailwindMerge } from "tailwind-merge";

import tailwindConfig from "@root/tailwind.config";

const customTailwindTheme = resolveConfig(tailwindConfig).theme;

const getToken = (tokenName: Exclude<keyof typeof customTailwindTheme, "extend">) =>
  Object.keys(customTailwindTheme[tokenName]).filter((key) => key !== "DEFAULT");

const twMerge = extendTailwindMerge({
  theme: {
    colors: getToken("colors"),
    spacing: getToken("spacing"),
    borderRadius: getToken("borderRadius"),
    borderWidth: getToken("borderWidth"),
  },
  classGroups: {
    "font-size": [{ text: getToken("fontSize") }],
  },
});

export default twMerge;
``;

 

커스텀한 Tailwind config로부터 token을 가져오는 getToken 함수를 설정하여 twMerge를 설정해주었다.

뭔가 tailwind-merge에 fromTheme이라는 함수가 있어서 써보려고 했는데 이런 목적으로 쓰는 함수가 아닌지

계속 에러가 나는 바람에 이런 방식으로 대체했다.

 

더보기

만약 getToken 함수 없이 설정하고 싶다면 아래처럼 직접 적어주면 된다.

import { extendTailwindMerge } from "tailwind-merge";

const twMerge = extendTailwindMerge({
  theme: {
    colors: [
      "current",
      "transparent",
      "primary",
      "primary-on",
      "primary-fixed",
      "primary-fixed-on",
      "secondary",
      "secondary-on",
      "secondary-fixed",
      "secondary-fixed-on",
      "tertiary",
      "tertiary-on",
      "tertiary-fixed",
      "tertiary-fixed-on",
      "surface",
      "surface-variant",
      "surface-variant-high",
      "surface-variant-highest",
      "surface-on",
      "surface-on-variant",
      "outline",
      "outline-variant",
      "red",
      "red-on",
      "red-variant",
      "red-variant-on",
      "yellow",
      "yellow-on",
      "green",
      "green-on",
      "magenta",
      "magenta-on",
    ],
    spacing: ["3xs", "2xs", "xs", "s", "m", "l", "xl", "2xl", "3xl"],
    borderRadius: ["none", "xs", "s", "m", "l", "circle"],
    borderWidth: ["s", "m"],
  },
  classGroups: {
    "font-size": [{ text: ["inherit", "xs", "s", "m", "l", "xl", "2xl"] }],
  },
});

export default twMerge;

 

위와 같이 tailwind 설정을 import하여 사용하는 방법은 프로젝트 설정에 따라 다르지만,

나의 경우 tailwind.config 파일이 es6 module(import/export) 방식이어야 사용 할 수 있었다.

혹시 tailwind.config를 import/export로 해야하는데 commonJS로 설정되어있다면,

아래 명령어를 통해 tailwind.config를 다시 만들고 재설정해주면 된다.

기존 설정 파일이 날라갈 수 있으니 필히 백업해두자.

 

//TypeScript

npx tailwindcss init --ts

//JavaScript import/export

npx tailwindcss init --esm

 

이제 커스텀한 tailwond 테마를 사용해도 정상적으로 merge될 것이다.

 

 

관련 글

 

Tailwind에서 재사용 가능한 컴포넌트로

Tailwind에서 재사용 가능한 컴포넌트로 지금까지 아토믹 디자인을 적용해 UI의 시각적인 요소들을 토큰이라는 계층으로 분류하고, 이 토큰들을 Tailwind에서 사용할 수 있도록 class로 옮겼다. 드디

bh2980.tistory.com

 

Reference

 

GitHub - dcastil/tailwind-merge: Merge Tailwind CSS classes without style conflicts

Merge Tailwind CSS classes without style conflicts - GitHub - dcastil/tailwind-merge: Merge Tailwind CSS classes without style conflicts

github.com

 

Tailwind CSS v3.3: Extended color palette, ESM/TS support, logical properties, and more - Tailwind CSS

Tailwind CSS v3.3 is here — bringing a bunch of new features people have been asking for forever, and a bunch of new stuff you didn't even know you wanted.

tailwindcss.com