핵심 개념
Tailwind 프로젝트에서 커스텀 스타일을 추가하는 최적의 방법
프레임워크를 사용할 때 가장 큰 어려움은 프레임워크가 제공하지 않는 기능이 필요할 때 어떻게 해야 할지 알아내는 것입니다.
Tailwind는 처음부터 확장성과 커스터마이징을 염두에 두고 설계되었습니다. 따라서 어떤 것을 만들더라도 프레임워크와 싸우는 느낌이 들지 않습니다.
이 가이드에서는 디자인 토큰을 커스터마이징하는 방법, 필요할 때 제약을 벗어나는 방법, 커스텀 CSS를 추가하는 방법, 그리고 플러그인으로 프레임워크를 확장하는 방법 등을 다룹니다.
색상 팔레트, 간격 스케일, 타이포그래피 스케일, 브레이크포인트 등을 변경하려면 CSS에서 @theme
지시어를 사용해 커스텀 설정을 추가할 수 있습니다.
@theme { --font-display: "Satoshi", "sans-serif"; --breakpoint-3xl: 1920px; --color-avocado-100: oklch(0.99 0 0); --color-avocado-200: oklch(0.98 0.04 113.22); --color-avocado-300: oklch(0.94 0.11 115.03); --color-avocado-400: oklch(0.92 0.19 114.08); --color-avocado-500: oklch(0.84 0.18 117.33); --color-avocado-600: oklch(0.53 0.12 118.34); --ease-fluid: cubic-bezier(0.3, 0, 0, 1); --ease-snappy: cubic-bezier(0.2, 0, 0, 1); /* ... */}
테마 커스터마이징에 대해 더 알아보려면 테마 변수 문서를 참고하세요.
일반적으로 잘 설계된 디자인을 만들 때는 제한된 디자인 토큰 세트를 사용하지만, 가끔은 픽셀 단위로 완벽한 디자인을 위해 이러한 제약을 벗어나야 할 때가 있습니다.
예를 들어 배경 이미지를 정확한 위치에 배치하기 위해 top: 117px
같은 값이 필요하다면, Tailwind의 대괄호 표기법을 사용하여 임의의 값으로 클래스를 즉시 생성할 수 있습니다.
<div class="top-[117px]"> <!-- ... --></div>
이 방식은 기본적으로 인라인 스타일과 유사하지만, hover
같은 인터랙티브 수정자나 lg
같은 반응형 수정자와 함께 사용할 수 있다는 큰 장점이 있습니다.
<div class="top-[117px] lg:top-[344px]"> <!-- ... --></div>
이 기능은 배경색, 글꼴 크기, 의사 엘리먼트 콘텐츠 등 프레임워크의 모든 요소에 적용할 수 있습니다.
<div class="bg-[#bada55] text-[22px] before:content-['Festivus']"> <!-- ... --></div>
임의 값으로 CSS 변수를 참조해야 한다면, 커스텀 프로퍼티 문법을 사용할 수 있습니다.
<div class="fill-(--my-brand-color) ..."> <!-- ... --></div>
이는 var()
함수를 자동으로 추가해주는 fill-[var(--my-brand-color)]
의 축약형입니다.
Tailwind에서 기본적으로 제공하지 않는 CSS 속성을 사용해야 할 때가 있다면, 대괄호 표기법을 사용해 완전히 임의의 CSS를 작성할 수 있습니다.
<div class="[mask-type:luminance]"> <!-- ... --></div>
이 방법은 인라인 스타일과 매우 유사하지만, 수정자를 사용할 수 있다는 장점이 있습니다.
<div class="[mask-type:luminance] hover:[mask-type:alpha]"> <!-- ... --></div>
이 방법은 특히 조건에 따라 변경되어야 하는 CSS 변수를 다룰 때 유용합니다.
<div class="[--scroll-offset:56px] lg:[--scroll-offset:44px]"> <!-- ... --></div>
임의 변형은 임의 값과 비슷하지만, HTML에서 직접 대괄호 표기법을 사용해 hover:{utility}
같은 내장 의사 클래스 변형이나 md:{utility}
같은 반응형 변형처럼 선택자를 즉석에서 수정할 수 있는 기능입니다.
<ul role="list"> {#each items as item} <li class="lg:[&:nth-child(-n+3)]:hover:underline">{item}</li> {/each}</ul>
더 자세한 내용은 임의 변형 문서에서 확인할 수 있습니다.
임의의 값에 공백이 포함되어야 할 경우, 언더스코어(_
)를 대신 사용하면 Tailwind가 빌드 시점에 이를 공백으로 자동 변환합니다:
<div class="grid grid-cols-[1fr_500px_2fr]"> <!-- ... --></div>
언더스코어가 일반적이지만 공백이 유효하지 않은 상황에서는, Tailwind가 언더스코어를 공백으로 변환하지 않고 그대로 유지합니다. 예를 들어 URL에서:
<div class="bg-[url('/what_a_rush.png')]"> <!-- ... --></div>
드물게 실제로 언더스코어를 사용해야 하지만 공백도 유효한 경우라서 모호한 상황에서는, 백슬래시로 언더스코어를 이스케이프 처리하면 Tailwind가 이를 공백으로 변환하지 않습니다:
<div class="before:content-['hello\_world']"> <!-- ... --></div>
JSX와 같이 렌더링된 HTML에서 백슬래시가 제거되는 경우, String.raw()를 사용하여 백슬래시가 JavaScript 이스케이프 문자로 처리되지 않도록 합니다:
<div className={String.raw`before:content-['hello\_world']`}> <!-- ... --></div>
Tailwind의 많은 유틸리티는 공통 네임스페이스를 공유하지만 서로 다른 CSS 속성에 매핑됩니다. 예를 들어 text-lg
와 text-black
은 모두 text-
네임스페이스를 공유하지만, 하나는 font-size
를 위한 것이고 다른 하나는 color
를 위한 것입니다.
임의의 값을 사용할 때, Tailwind는 일반적으로 전달한 값을 기반으로 이 모호성을 자동으로 처리할 수 있습니다:
<!-- 폰트 크기 유틸리티를 생성합니다 --><div class="text-[22px]">...</div><!-- 색상 유틸리티를 생성합니다 --><div class="text-[#bada55]">...</div>
하지만 CSS 변수를 사용할 때와 같이 정말로 모호한 경우도 있습니다:
<div class="text-(--my-var)">...</div>
이런 상황에서는 값 앞에 CSS 데이터 타입을 추가하여 Tailwind에게 기본 타입을 "힌트"로 제공할 수 있습니다:
<!-- 폰트 크기 유틸리티를 생성합니다 --><div class="text-(length:--my-var)">...</div><!-- 색상 유틸리티를 생성합니다 --><div class="text-(color:--my-var)">...</div>
Tailwind는 대부분의 스타일링 요구를 충족하도록 설계되었지만, 필요할 때는 일반 CSS를 작성하는 것도 가능합니다:
@import "tailwindcss/";.my-custom-style { /* ... */}
페이지의 기본 설정(예: 텍스트 색상, 배경 색상, 폰트 패밀리)을 지정하고 싶다면, 가장 쉬운 방법은 html
또는 body
엘리먼트에 클래스를 추가하는 것입니다:
<!doctype html><html lang="en" class="bg-gray-100 font-serif text-gray-900"> <!-- ... --></html>
이렇게 하면 기본 스타일 결정을 마크업에 함께 두어 다른 스타일과 함께 관리할 수 있습니다. 별도의 파일에 숨겨두지 않아도 됩니다.
특정 HTML 엘리먼트에 대한 기본 스타일을 직접 추가하고 싶다면, @layer
지시어를 사용하여 Tailwind의 base
레이어에 스타일을 추가할 수 있습니다:
@layer base { h1 { font-size: var(--text-2xl); } h2 { font-size: var(--text-xl); }}
components
레이어는 프로젝트에 추가하고 싶은 복잡한 클래스를 정의할 때 사용합니다. 이 클래스들은 유틸리티 클래스로 재정의할 수 있도록 설계되었습니다.
전통적으로 이러한 클래스들은 card
, btn
, badge
와 같은 형태로 사용됩니다.
@layer components { .card { background-color: var(--color-white); border-radius: var(--rounded-lg); padding: var(--spacing-6); box-shadow: var(--shadow-xl); }}
components
레이어에 컴포넌트 클래스를 정의하면, 필요할 때 유틸리티 클래스를 사용해 이를 재정의할 수 있습니다.
<!-- 카드처럼 보이지만 모서리가 둥글지 않음 --><div class="card rounded-none"> <!-- ... --></div>
Tailwind를 사용하면 이러한 유형의 클래스가 생각보다 자주 필요하지 않을 수 있습니다. 중복 관리 가이드를 참고해 권장 사항을 확인하세요.
components
레이어는 또한 사용 중인 서드파티 컴포넌트에 대한 커스텀 스타일을 추가하기에도 적합합니다.
@layer components { .select2-dropdown { /* ... */ }}
Tailwind에 기본으로 제공되는 유틸리티 외에도, 여러분만의 커스텀 유틸리티를 추가할 수 있습니다. 이는 Tailwind에서 기본적으로 제공하지 않는 CSS 기능을 프로젝트에서 사용하고 싶을 때 유용합니다.
@utility
지시자를 사용해 프로젝트에 커스텀 유틸리티를 추가할 수 있습니다:
@utility content-auto { content-visibility: auto;}
이제 이 유틸리티를 HTML에서 사용할 수 있습니다:
<div class="content-auto"> <!-- ... --></div>
또한 hover
, focus
, lg
와 같은 변형(variants)과도 함께 작동합니다:
<div class="hover:content-auto"> <!-- ... --></div>
커스텀 유틸리티는 프레임워크의 내장 유틸리티와 함께 자동으로 utilities
레이어에 삽입됩니다.
커스텀 유틸리티가 단일 클래스 이름보다 복잡한 경우, 중첩을 사용하여 유틸리티를 정의할 수 있습니다:
@utility scrollbar-hidden { &::-webkit-scrollbar { display: none; }}
@utility
지시자를 사용해 간단한 유틸리티를 등록하는 것 외에도, 인자를 받는 함수형 유틸리티를 등록할 수 있습니다:
@utility tab-* { tab-size: --value(--tab-size-*);}
특수한 --value()
함수는 유틸리티 값을 해결하는 데 사용됩니다.
--value(--theme-key-*)
구문을 사용하여 테마 키 세트에 대해 유틸리티 값을 해결할 수 있습니다:
@theme { --tab-size-2: 2; --tab-size-4: 4; --tab-size-github: 8;}@utility tab-* { tab-size: --value(--tab-size-*);}
이렇게 하면 tab-1
, tab-4
, tab-github
와 같은 유틸리티가 매칭됩니다.
값을 bare value로 해석하려면 --value({type})
구문을 사용합니다. 여기서 {type}
은 bare value를 검증할 데이터 타입입니다:
@utility tab-* { tab-size: --value(integer);}
이 구문은 tab-1
이나 tab-76
과 같은 유틸리티를 매칭합니다.
임의 값을 지원하려면 --value([{type}])
구문을 사용합니다. 이때 대괄호를 주의 깊게 확인하세요. 이 구문은 Tailwind에게 어떤 타입이 임의 값으로 지원되는지 알려줍니다:
@utility tab-* { tab-size: --value([integer]);}
이렇게 하면 tab-[1]
이나 tab-[76]
과 같은 유틸리티가 매칭됩니다. 모든 데이터 타입을 지원하고 싶다면 --value([*])
를 사용할 수 있습니다.
--value()
함수의 세 가지 형태 모두 규칙 내에서 여러 선언으로 사용할 수 있으며, 해결되지 않은 선언은 출력에서 제외됩니다:
@theme { --tab-size-github: 8;}@utility tab-* { tab-size: --value([integer]); tab-size: --value(integer); tab-size: --value(--tab-size-*);}
이를 통해 필요한 경우 각 경우에 따라 값을 다르게 처리할 수 있습니다. 예를 들어, 기본 정수 값을 백분율로 변환할 수 있습니다:
@utility opacity-* { opacity: --value([percentage]); opacity: calc(--value(integer) * 1%); opacity: --value(--opacity-*);}
--value()
함수는 여러 인수를 받을 수도 있으며, 반환 값을 다른 경우에 다르게 처리할 필요가 없다면 왼쪽에서 오른쪽으로 해결합니다:
@theme { --tab-size-github: 8;}@utility tab-* { tab-size: --value(--tab-size-*, integer, [integer]);}@utility opacity-* { opacity: calc(--value(integer) * 1%); opacity: --value(--opacity-*, [percentage]);}
음수 값을 지원하려면 양수와 음수 유틸리티를 각각 별도의 선언으로 등록해야 합니다:
@utility inset-* { inset: calc(--var(--spacing) * --value([percentage], [length]));}@utility -inset-* { inset: calc(--var(--spacing) * --value([percentage], [length]) * -1);}
Modifiers는 --modifier()
함수를 사용해 처리합니다. 이 함수는 --value()
함수와 동일하게 작동하지만, modifier가 있을 때만 동작합니다:
@utility text-* { font-size: --value(--font-size-*, [length]); line-height: --modifier(--line-height-*, [length], [*]);}
modifier가 없으면, modifier에 의존하는 선언은 결과에 포함되지 않습니다.
분수를 처리하기 위해 CSS ratio
데이터 타입을 사용합니다. 이 타입을 --value()
와 함께 사용하면, Tailwind가 값과 수정자를 단일 값으로 처리하라는 신호가 됩니다:
@utility aspect-* { aspect-ratio: --value(--aspect-ratio-*, ratio, [ratio]);}
이 코드는 aspect-square
, aspect-3/4
, aspect-[7/9]
와 같은 유틸리티와 매칭됩니다.
Tailwind 변형을 커스텀 CSS에 적용하려면 @variant
지시자를 사용하세요:
.my-element { background: white; @variant dark { background: black; }}
여러 변형을 동시에 적용해야 한다면 중첩을 사용하세요:
@variant dark { @variant hover { background: black; }}