공식문서 core-concepts
공식문서 Adding-Custom Styles
테일윈드에서 core-conepts로 소개하는것중 몇가지로, 반응형, 다크모드, Hover등의 state에 따른 style등을 사용하여 하나의 레이아웃을 만들어보겠습니다.
아래 예제에서 사용한 Card 및 Toggle 컴포넌트는 tailwinduikit 사이트를 참조하여 생성하였습니다.(추가적으로 일부 width 설정 및 애니메이션을 추가 하였습니다.)
tailwinduikit
시작에 앞서, 많이 사용되는 기본적인 TailwindCSS 문법에 대한 설명을 하자면.
w-full md:w-[768px]
darkMode: "class",
를 추가하여야 합니다.만들어볼 Layout의 기능은 크게 3가지 입니다.
1. header / main / footer 총 3개로 영역으로 이루어져있습니다.
2. Darkmode토글 기능을 지원합니다.
3. Grid 형식으로 카드를 보여주고, 웹의 width에 따라 반응형으로 카드의 갯수를 다르게 보여줍니다.
다크모드는 HTML의 클래스를 바꾸는 형식이 아닌, 상위 Elment를 만들고 하위 컴포넌트를 구현하는 방식으로 하였습니다.
function App() {
const [isDark, setIsDark] = useState(false);
const toggleDarkmode = () => {
setIsDark((prev) => !prev);
};
return (
//Darkmode Wrapper
<div className={`${isDark && "dark"}`}>
{/* 레이아웃 wrapper */}
<div className="flex flex-col min-h-screen h-full w-screen min-w-full dark:bg-slate-800 overflow-x-hidden">
<header className="relative text-center h-12 border-b shadow-lg dark:text-white">
<div>Header</div>
<div className="flex absolute top-0 right-4">
<Toggle toggleFn={toggleDarkmode} />
</div>
</header>
{/* 레이아웃 wrapper */}
<div className="flex-grow">
<main className="grid grid-cols-1 md:grid-cols-3 2xl:grid-cols-4 p-4 gap-2 gap-y-4 ">
<Card />
<Card />
...
</main>
</div>
<footer className="text-center h-12 border-t shadow-inner dark:text-white">
Footer
</footer>
</div>
</div>
);
}
구현한 코드는 위와 같은 형식으로 작성하였습니다.
Dark mode만을 담당하는 Wrppaer를 만들고,
하위 엘리먼트로 header,main,footer를 담을 Wrapper를 만들었습니다.
header 엘리먼트에는 relative를 사용하여 우측에 toggle 컴포넌트를 absolute를 사용하여 만들고 darkmode를 토글하는 함수를 전달하여 사용하였습니다.
main에서 grid를 사용하였고, 기본값으로 1개를 보여주고, md(768xp)에서 3개를 보여주고, 2xl(1536px)에서는 4개를 보여주도록 하였습니다.
main 영역이 나머지 전체 영역을 차지할수있도록, 상위 엘리먼트에 flex-grow를 설정하엿습니다.
Card컴포넌트는 기본값을 w-full (100%)로, md사이즈에서는 448px을 사용하는 md:max-w-md 설정을 사용하였습니다.
(다크모드 사용 및 md사이즈 이상일때)
(다크모드를 사용하지 않고, md사이즈 미만일때)
기능자체는 크게 문제 없이 작성하지만, 이런식으로 작성하게 되면 className이 너무 길어져 한눈에 파악하기도 힘들고, 유지보수가 힘들어지게 될것입니다.
이런경우 단순히 className에 사용할 tailwind옵션들을 변수를 만들어 사용하는 방법도 있을 수 있습니다.
하지만, CSS-IN-JS 및 컴포넌트화를 지원하는 emotion 및 styled-components와 같은 컴포넌트를 tailwind에서도 사용할 수 있습니다.
이런경우 twin.macro를 사용하게 되는데, 설치 및 설정이 까다로울 수 있습니다.
때문에 이번에는 간단히 사용할 수있는 tailwind-styled-components Lib를 사용해보겠습니다.
yarn add -D tailwind-styled-components
를 하여 설치합니다.
사용할 컴포넌트 상단에
import tw from "tailwind-styled-components"
를 추가 합니다.
Wrppaer를 담당하는 엘리먼트를 tailwind-styled-components를 사용하여 컴포넌트화 할 경우 아래와 같습니다.
const Wrapper = tw.header`
flex
flex-col
min-h-screen
h-full
w-screen
min-w-full
dark:bg-slate-800
overflow-x-hidden
`;
사용방법..
<Wrapper>
...
</Wrapper>
CSS-IN-JS가 사용되는 Darkmode Wrapper는 아래와 같이 컴포넌트화 할 수 있습니다.
const DarkModeWrapper = tw.div`
${(p) => (p.dark ? "dark" : "")}
`;
사용방법..
<DarkModeWrapper dark={isDark}>
...
</DarkModeWrapper>
tailwind-styled-components는 확실히 기존의 styled-componets와 같이 간단하고 가볍게 사용할 수 있습니다.
하지만 TS를 사용할경우 문제가 생겨 사용하기가 매우 번거로워집니다.
1. TS를 사용할경우, tw.div와 같이 설정할 El의 타입을 적용할때, 타입을 직접 적어주어야 합니다.
2. 가장 중요한것으로, div를 비롯해 몇몇 El의 타입이 적용이 되지 않습니다. div 타입을 명시해두어도 에러가 발생합니다.
2.2 때문에 any 타입을 명시해두어야 하는데, 이럴경우 jsx의 이벤트 자동완성이 작동하지 않습니다.(onClick과 같은.., 실제 작동은 됩니다.)
이러하게 tailwind-styled-components는 JS에서는 크게 문제없이 돌아가지만, TS호환이 제대로 되지 않는 문제점들을 가지고 있습니다. any 이슈는 현재 git Issue를 통해 문제에 대해 인지하고, 곧 고쳐질것 같지만, 매 컴포넌트마다 타입을 지정하기는 매우 번거롭습니다.
때문에 이러한 문제점들을 보완한 twin.macro를 다음 글에서 설치 및 사용해보겠습니다.