TailwindCss
TailwindCss는 아주 크고, 대빵 큰 CSS파일이라고 보면 됩니다. 많은 클래스 네임을 가지고 있는 겁니다. 이를 가지고 우리는 아름다운 많은 것을 만드는 겁니다.
먼저 vscode에서 tailwind intelisence를 설치 해야지 개발에 수월하다.
TailwindCss Utils
일단 기초적인 TailwindCss의 문법에 대해서 알아보겠다.
그리고 내가 알아낸 것인데, Tailwindcss도 styled-component처럼 작성할 수 있다. 바로 tailwind-styled-components를 깔면 된다.
npm i -D tailwind-styled-components
그리고 아래와 같이 import해서 사용 가능하다.
import tw from "tailwind-styled-components"
Test Drive part One
pages/index.tsx
import type { NextPage } from "next";
import tw from "tailwind-styled-components";
const Container = tw.div`
flex
flex-col
space-y-5
bg-slate-400
py-20
px-10flex
`;
const Home: NextPage = () => {
return (
<Container>
<div className="rounded-2xl bg-white p-6 shadow-2xl">
<span className="text-3xl font-semibold">Select Item</span>
<div className="my-2 flex justify-between">
<span className="text-gray-500">Grey Chair</span>
<span className="font-semibold">$19</span>
</div>
<div className="flex justify-between">
<span className="text-gray-500">Grey Chair</span>
<span className="font-semibold">$19</span>
</div>
<div className="mt-2 flex justify-between border-t-2 border-dashed pt-2">
<span>Total</span>
<span className="font-semibold">$10</span>
</div>
<div className="mx-auto mt-5 w-1/2 rounded-xl bg-blue-500 p-5 text-center text-white">
Checkout
</div>
</div>
<div className="rounded-2xl bg-white p-10 shadow-2xl"></div>
<div className="rounded-2xl bg-white p-10 shadow-2xl"></div>
</Container>
);
};
export default Home;
이는 3개의 카드박스 중 한개의 div만 스타일링 한 것이다. 일단 결과를 먼저 보자
일단 tailwind-styled-components로 container를 만들고 그 안에는 순수 tailwindCss로 스타일링을 진행했다. 그냥 자동완성 기능 활용하면서 rounded, p( padding ), shadow, m( margin ), font, ... 등을 활용했다. 그리고 이 외에도 space-y-5와 같이 spacing ( 간격 )을 예쁘게 주는 helper함수도 존재했다. 그리고 다양한 숫자들이 tailwindCss에 존재했는데, 어떤 경우는 rem, px, 등등 다양했다. 즉 자동완성기능 활용해 가면서 어떻게 적용되는지 확인하면서 천천히 적응해 갈 필요가 있는 것 같다.
Test Drive part Two
pages/index.tsx
이제 두번쨰 카드박스를 채워보도록 하겠다.
import type { NextPage } from "next";
import tw from "tailwind-styled-components";
const Container = tw.div`
flex
flex-col
space-y-5
bg-slate-400
py-20
px-20
flex
`;
const Home: NextPage = () => {
return (
<Container>
<div>
....
</div>
<div className="overflow-hidden rounded-3xl bg-white shadow-xl">
<div className="bg-blue-500 p-6 pb-14">
<span className="text-2xl text-white">Profile</span>
</div>
<div className="relative -top-5 rounded-3xl bg-white p-6">
<div className="relative -top-16 flex items-end justify-between">
<div className="flex flex-col items-center">
<span className="text-sm text-gray-500">Orders</span>
<span className="font-medium">340</span>
</div>
<div className="h-24 w-24 rounded-full bg-red-400"></div>
<div className="flex flex-col items-center">
<span className="text-sm text-gray-500">Spent</span>
<span className="font-medium">$2,310</span>
</div>
</div>
<div className="-mt-10 -mb-5 flex flex-col items-center">
<span className="text-lg">Tony Molloy</span>
<span className="text-sm text-gray-500">미국</span>
</div>
</div>
</div>
<div className="rounded-2xl bg-white p-10 shadow-2xl"></div>
</Container>
);
};
export default Home;
이렇게 디자인된 결과물을 볼 수 있다. 여기서 스타일링의 핵심적인 내용은 flex, relative인 것 같다. TailwindCss는 css의 flexbox와 relative속성까지 지원한다. 여기서 위의 코드를 보면 위로 움직이고 싶은 div의 className을 relative로 하고 -top-[number]를 해서 위로 올리는 것이 많다.
Test Drive part Three
이제 마지막인 3번째 카드를 채워보도록 하겠다. 이는 2번째에 걸쳐서 작성할 예정이다.
pages/index.tsx
import type { NextPage } from "next";
import tw from "tailwind-styled-components";
const Container = tw.div`
flex
flex-col
space-y-5
bg-slate-400
py-20
px-20
flex
min-h-screen
`;
const Home: NextPage = () => {
return (
<Container>
<div>
...
</div>
<div>
...
</div>
<div className="rounded-2xl bg-white p-10 shadow-2xl">
<div className="mb-5 flex items-center justify-between">
<span>⬅</span>
<div className="space-x-3">
<span>⭐️4.9</span>
<span className="rounded-md p-2 shadow-xl">💖</span>
</div>
</div>
<div className="mb-5 h-72 bg-zinc-400" />
<div className="flex flex-col">
<span className="mb-1.5 text-xl font-medium">Swoon Lounge</span>
<span className="text-xs text-gray-500">Chair</span>
<div className="mt-3 mb-5 flex items-center justify-between">
<div>
<input type="radio" />
<input type="radio" />
<input type="radio" />
</div>
<div className="flex items-center space-x-5">
<button className="flex aspect-square w-8 items-center justify-center rounded-xl bg-blue-200 text-xl text-gray-500">
-
</button>
<span>1</span>
<button className="flex aspect-square w-8 items-center justify-center rounded-xl bg-blue-200 text-xl text-gray-500">
+
</button>
</div>
</div>
<div className="flex items-center justify-between">
<span className="text-2xl font-medium">$450</span>
<button className="rounded-lg bg-blue-500 py-2 px-8 text-center text-xs text-white">
Add to cart
</button>
</div>
</div>
</div>
</Container>
);
};
export default Home;
이의 결과는 다음과 같다. 여기서의 핵심은 TailwindCss의 helper함수( space-x-[number], space-y-[number] )와 aspect-square등이 있을 것 같다. space-x, -y속성은 매우 놀라웠는데 그냥 css에서 다 계산해서 넣어줘야 하는 고충을 다 계산해서 함수로 만들
어 놓았기 때문이다. 그리고 aspect-square는 자동으로 w-[number]값만 주면 알아서 정사각형을 number대로 만들어 준다.
Modifiers
우선 위에서 못만든 동그라미 버튼과 Tailwind의 modifiers에 대해 알아보자
pages/index.tsx
import type { NextPage } from "next";
import tw from "tailwind-styled-components";
const Container = tw.div`
flex
flex-col
space-y-5
bg-slate-400
py-20
px-20
flex
min-h-screen
`;
const Home: NextPage = () => {
return (
<Container>
<div className="rounded-2xl bg-white p-6 shadow-2xl">
<span className="text-3xl font-semibold">Select Item</span>
<div className="my-2 flex justify-between">
<span className="text-gray-500">Grey Chair</span>
<span className="font-semibold">$19</span>
</div>
<div className="flex justify-between">
<span className="text-gray-500">Grey Chair</span>
<span className="font-semibold">$19</span>
</div>
<div className="mt-2 flex justify-between border-t-2 border-dashed pt-2">
<span>Total</span>
<span className="font-semibold">$10</span>
</div>
<button
className="mx-auto mt-5 block w-1/2 rounded-xl bg-blue-500 p-5 text-center
text-white hover:bg-teal-500 hover:text-black focus:bg-red-500 active:bg-yellow-500
"
>
Checkout
</button>
</div>
<div>
...
</div>
<div className="rounded-2xl bg-white p-10 shadow-2xl">
<div className="mb-5 flex items-center justify-between">
<span>⬅</span>
<div className="space-x-3">
<span>⭐️4.9</span>
<span className="rounded-md p-2 shadow-xl">💖</span>
</div>
</div>
<div className="mb-5 h-72 bg-zinc-400" />
<div className="flex flex-col">
<span className="mb-1.5 text-xl font-medium">Swoon Lounge</span>
<span className="text-xs text-gray-500">Chair</span>
<div className="mt-3 mb-5 flex items-center justify-between">
<div className="space-x-2">
<button className="h-5 w-5 rounded-full bg-yellow-500" />
<button className="h-5 w-5 rounded-full bg-indigo-500" />
<button className="h-5 w-5 rounded-full bg-teal-500" />
</div>
<div className="flex items-center space-x-5">
<button className="flex aspect-square w-8 items-center justify-center rounded-xl bg-blue-200 text-xl text-gray-500">
-
</button>
<span>1</span>
<button className="flex aspect-square w-8 items-center justify-center rounded-xl bg-blue-200 text-xl text-gray-500">
+
</button>
</div>
</div>
<div className="flex items-center justify-between">
<span className="text-2xl font-medium">$450</span>
<button className="rounded-lg bg-blue-500 py-2 px-8 text-center text-xs text-white">
Add to cart
</button>
</div>
</div>
</div>
</Container>
);
};
export default Home;
위의 내용과 반복되는 부분이 있지만 일단 첫번쨰, 세번쨰의 카드만 집중적으로 보자. 이에 대한 결과는 아래와 같다.
여기서 핵심적인 내용은 card-1에 적용된 Tailwind의 modifier다. 원래 기존의 css라면 일일이 :hover {} 와 같이 가상 클래스 선택자를 이용해서 디자인을 해야 했다. 하지만 Tailwind는 엄청 방대한 css파일 이라고 한 만큼 이 모든 것을 예상이라도 한 듯 다 만들어 놓아서 배포 한 것이다. Tailwind에서는 hover:bg-teal-500이라는 클래스 자체가 존재 한다는 것이다. 이 외에도 focus, active등이 있다.
이 외에도 더 많은 TailwindCSS Modifier리스트가 존재한다.
```
Modifier(왼쪽), CSS(오른쪽)
hover (&:hover)
focus (&:focus)
active (&:active)
first (&:first-child)
disabled (&:disabled)
sm (@media (min-width: 640px))
md ( @media (min-width: 768px))
lg (@media (min-width: 1024px))
dark (@media (prefers-color-scheme: dark))
등등
```
https://tailwindcss.com/docs/hover-focus-and-other-states#quick-reference
Transitions
여기서는 card-3의 button design을 할겁니다. design에 transition과 선택되었을 때의 효과를 입혀보겠습니다.
pages/index.tsx
import type { NextPage } from "next";
import tw from "tailwind-styled-components";
const Container = tw.div`
flex
flex-col
space-y-5
bg-slate-400
py-20
px-20
flex
min-h-screen
`;
const Home: NextPage = () => {
return (
<Container>
<div>
...
</div>
<div>
...
</div>
<div className="rounded-2xl bg-white p-10 shadow-2xl">
<div className="mb-5 flex items-center justify-between">
<span>⬅</span>
<div className="space-x-3">
<span>⭐️4.9</span>
<span className="rounded-md p-2 shadow-xl">💖</span>
</div>
</div>
<div className="mb-5 h-72 bg-zinc-400" />
<div className="flex flex-col">
<span className="mb-1.5 text-xl font-medium">Swoon Lounge</span>
<span className="text-xs text-gray-500">Chair</span>
<div className="mt-3 mb-5 flex items-center justify-between">
<div className="space-x-2">
<button className="h-5 w-5 rounded-full bg-yellow-500 bg-opacity-80 ring-yellow-500 ring-offset-1 transition focus:ring-2" />
<button className="h-5 w-5 rounded-full bg-indigo-500 bg-opacity-80 ring-indigo-500 ring-offset-1 transition focus:ring-2" />
<button className="h-5 w-5 rounded-full bg-teal-500 bg-opacity-80 ring-teal-500 ring-offset-1 transition focus:ring-2" />
</div>
<div className="flex items-center space-x-5">
<button className="flex aspect-square w-8 items-center justify-center rounded-xl bg-blue-200 text-xl text-gray-500">
-
</button>
<span>1</span>
<button className="flex aspect-square w-8 items-center justify-center rounded-xl bg-blue-200 text-xl text-gray-500">
+
</button>
</div>
</div>
<div className="flex items-center justify-between">
<span className="text-2xl font-medium">$450</span>
<button className="rounded-lg bg-blue-500 py-2 px-8 text-center text-xs text-white">
Add to cart
</button>
</div>
</div>
</div>
</Container>
);
};
export default Home;
이와 같이 디자인은 별로 차이가 없다고 느껴질 수도 있다. 하지만 여기서는 Tailwind의 ring, transition속성을 통해 혁신적인 디자인을 한 것이다. 우선 focus:ring-[number]를 통해 버튼이 focus되었을 때만 ring속성을 적용해 줍니다. 그리고 ring-yellow-[number]와 ring-offset-[number]를 적용해 주었는데. 이들은 css에서의 변수와도 같은 것이여서, focus를 줄 필요가 없다. 예를 들어 ring-offset-1은 --tw-ring-offset-width: 1px; 라는 변수를 선언한 것인데, ring-2에서 이를 사용하기 때문이다. 그리고 마지막으로 transition을 적용해 주었는데, 이는 focus되었을 때, 스무스하게 변화하게 하고 싶기 때문이다. transition-color, transition-all과 같이 transition뒤에 변화하고 싶은 값을 넣을 수도 있는데, 여기서는 box-shadow가 변화(ring-[number]가 box-shadow를 바꿔서 디자인 하는 것이기 때문)하는 것인데 defualt transition에 box-shadow가 포함되어 있다.
이처럼 Tailwind는 변수를 통해 확장이 가능하다는 장점이 있다. 이러한 이유에서 Tailwind가 강력한 디자인 Framework이라고도 하고 변화하는 변수들을 보고 이를 활용하는 속성을 찾음으로서 디자인이 수월해 진다.
Modifiers for Lists
여기서는 card1의 디자인을 바꿔보면서 리스트에서의 modifier가 어떻게 동작하는지 간단한 예시를 들어 보겠다.
pages/index.tsx
import type { NextPage } from "next";
import tw from "tailwind-styled-components";
const Container = tw.div`
flex
flex-col
space-y-5
bg-slate-400
py-20
px-20
flex
min-h-screen
`;
const Home: NextPage = () => {
return (
<Container>
<div className="rounded-2xl bg-white p-6 shadow-2xl">
<span className="text-3xl font-semibold">Select Item</span>
<ul>
{[1, 2, 3, 4, 5].map((i) => (
<div
key={i}
className="my-2 flex justify-between odd:bg-yellow-200 even:bg-blue-100"
> <span className="text-gray-500">Grey Chair</span>
<span className="font-semibold">$19</span>
</div>
))}
<ul>
{["a", "b", "c", ""].map((c, i) => (
<li key={i} className="bg-red-500 py-2 empty:hidden">
{c}
</li>
))}
</ul>
</ul>
<div className="mt-2 flex justify-between border-t-2 border-dashed pt-2">
<span>Total</span>
<span className="font-semibold">$10</span>
</div>
<button
className="mx-auto mt-5 block w-1/2 rounded-xl bg-blue-500 p-5 text-center
text-white hover:bg-teal-500 hover:text-black focus:bg-red-500 active:bg-yellow-500
"
>
Checkout
</button>
</div>
<div>
...
</div>
<div>
...
</div>
</Container>
);
};
export default Home;
다음과 같이 Grey Chair를 [1, 2, 3, 4, 5]를 통해서 5개 만들어 주었는데, odd:bg-yellow-200, even:bg-blue-100을 통해서 바탕의 색을 홀수, 짝수번째 있을 때 각각 다르게 해주었다. 이는 css의 :nth-child(odd, even)을 활용한 것이라고 볼 수 있다. 그리고 이 외에도 last:bg-blue-50, first:bg-blue-50과 같이 첫번쨰, 마지막의 요소에도 스타일을 Tailwind의 modifier를 통해 지정해 줄 수 있다.
두 번째로 ["a", "b," ,"c", ""]를 통해 배열을 만들었는데, ""이 렌더링 되지 않았는데 이는 empty:hidden을 활용했기 때문이다. 이는 empty인 요소를 display: none한 것과 동일한 효과를 낸다. 주로 empty한 요소를 처리해 줘야할 필요가 없기 때문에, 이를 자주 활용한다고 한다.
Modifiers for Forms
먼저 group에 대한 Tailwind의 방식을 알아보자. 만약에
만약 이런 카드가 있다고 하는데 다음 card를 hover하면 저 Profile사진이 빨갛게 변하게 하려면 어떻게 해야할까?. 기존의 css라면 :hover .profile {} 이렇게 하면 된다. 하지만 Tailwind에서는 이게 불가능 하기에 다음과 같이 해주어야 한다.
pages/index.tsx
import type { NextPage } from "next";
import tw from "tailwind-styled-components";
const Container = tw.div`
flex
flex-col
space-y-5
bg-slate-400
py-20
px-20
flex
min-h-screen
`;
const Home: NextPage = () => {
return (
<Container>
<div>
...
</div>
<div className="group overflow-hidden rounded-3xl bg-white shadow-xl">
<div className="bg-blue-500 p-6 pb-14">
<span className="text-2xl text-white">Profile</span>
</div>
<div className="relative -top-5 rounded-3xl bg-white p-6">
<div className="relative -top-16 flex items-end justify-between">
<div className="flex flex-col items-center">
<span className="text-sm text-gray-500">Orders</span>
<span className="font-medium">340</span>
</div>
<div className="h-24 w-24 rounded-full bg-zinc-300 transition-colors group-hover:bg-red-300" />
<div className="flex flex-col items-center">
<span className="text-sm text-gray-500">Spent</span>
<span className="font-medium">$2,310</span>
</div>
</div>
<div className="-mt-10 -mb-5 flex flex-col items-center">
<span className="text-lg">Tony Molloy</span>
<span className="text-sm text-gray-500">미국</span>
</div>
</div>
</div>
<div>
...
</div>
</Container>
);
};
export default Home;
card-2의 부모 div의 className으로 group을 준다. 그 다음에 profile을 담는 div의 className에 group-hover:bg-red-300을 주었다. group-hover라는 것은 group라는 클래스가 hovere되었을 때 트리거 되도록 하는 것이다. 이 외에도 Tailwind에서는 group-과 관련된 다양한 것들을 지원한다.
다음과 같이 카드 위에 hover되었을 떄 Profile이 빨간색으로 변하는 것을 볼 수 있다.
이러한 Modifier는 Form에서 더욱 유용하게 쓰인다.
pages/index.tsx
import type { NextPage } from "next";
import tw from "tailwind-styled-components";
const Container = tw.div`
flex
flex-col
space-y-5
bg-slate-400
py-20
px-20
flex
min-h-screen
`;
const Home: NextPage = () => {
return (
<Container>
<div>
...
</div>
<div>
...
</div>
<div>
...
</div>
<form className="flex flex-col space-y-2 bg-blue-300 p-5 focus-within:bg-blue-500">
<input
type="text"
required
placeholder="Username"
// disabled
// className="invalid:bg-red-500"
// className="placeholder:text-red-500"
// className="disabled:opacity-0"
className="required:bg-yellow-500 valid:bg-teal-500 invalid:bg-red-500"
></input>
<input type="password" required placeholder="Password"></input>
<input type="submit" value="Login" className="bg-white"></input>
</form>
</Container>
);
};
export default Home;
다음과 같이 form에 input3개를 넣었습니다. 우선 form의 className에 focus-within:bg-blue-500이 있는데 form이 focus당할 때 색이 변하는 것이고 첫번째 input에 주석과 함께 다양한 className들이 있다. disabled를 하면 input이 비활성화 되는데, disabled:opacity-0을 하면 비활성화된 input이 없어지게 된다. 또한 invalid:bg-red-500은 required가 되어 있는 input태그에 아무것도 작성이 안되면 invalid한 것이라고 하는데, 그 때 bg-red-500이 되는 것이다. 또한 placehoder:text-red-500이 있는데, placeholder의 색을 바꾸는 것이고, required:bg-yellow-500는 invalid하고 상충되긴 하지만 required된 input에 bg-yellow-500을 설정하는 것이다.
또한 form을 하나 더 만들겠습니다. 위의 group과 같은 것을 활용해 보겠습니다.
pages/index.tsx
import type { NextPage } from "next";
import tw from "tailwind-styled-components";
const Container = tw.div`
flex
flex-col
space-y-5
bg-slate-400
py-20
px-20
flex
min-h-screen
`;
const Home: NextPage = () => {
return (
<Container>
<div>
...
</div>
<div>
...
</div>
<div>
...
</div>
<form>
...
</form>
<form className="flex flex-col space-y-2 p-5">
<input
type="text"
required
placeholder="Username"
className="peer rounded-lg border border-gray-400 p-1"
/>
<span className="hidden peer-invalid:block peer-invalid:text-red-500">
This input is invalid
</span>
<span className="hidden peer-valid:block peer-valid:text-teal-500">
Awesome username
</span>
<span className="hidden peer-hover:block peer-hover:text-amber-500">
Hello
</span>
<input type="submit" value="Login" className="bg-white"></input>
</form>
</Container>
);
};
export default Home;
우선 Username을 입력받는 input의 className으로 peer를 주었습니다. 이는 group과 비슷하지만, 일반형 형제 선택자를 사용합니다. input바로 아래에 span3개가 있는데, 기존의 css에서 input ~ span하면 아래 3개의 span3개가 모두 선택되는 것을 활용한 것입니다. span의 역할을 각각, 바로 위의 Input이 invalid, valid, hover될 떄 나타나도록 한 것입니다. 이를 수행하기 위해서 peer-invalid:block을 통해 peer가 invalid일 때 block되게 하였고, text-red-500을 주어 글자색을 주었습니다. 아래 2개도 이와 동일한 원리입니다.
group
상위(부모) 상태를 기반으로 한 스타일 지정
일부 부모 요소의 상태를 기반으로 요소의 스타일을 지정해야 하는 경우 부모를 group 클래스로 표시하고 group-hover와 같은 group-* 수정자를 사용하여 대상 요소의 스타일을 지정합니다.
이 패턴은 group-focus, group-active 또는 group-odd와 같은 모든 유사 클래스 수정자와 함께 작동합니다.
```
< a href="#" class="group">
< h3 class="group-hover:text-white">New project< /h3>
< /a>
```
https://tailwindcss.com/docs/hover-focus-and-other-states#styling-based-on-parent-state
peer
형제 상태를 기반으로 한 스타일 지정
형제 요소의 상태를 기반으로 요소의 스타일을 지정해야 하는 경우 형제를 peer 클래스로 표시하고 peer-invalid와 같은 peer-* 수정자를 사용하여 대상 요소의 스타일을 지정합니다. 이 패턴은 모든 유사 클래스 수정자(예: peer-focus, peer-required 및 peer-disabled)와 함께 작동합니다.
< input class="peer"/ >
< p class="peer-invalid:visible"> Pizza< /p>
```
https://tailwindcss.com/docs/hover-focus-and-other-states#styling-based-on-sibling-state
이라고 정리해 볼 수 있겠다.
'Web > CloneCoding' 카테고리의 다른 글
[Carrot Market] #5 TAILWIND CLONING UI - 3 (0) | 2022.05.01 |
---|---|
[Carrot Market] #5 TAILWIND CLONING UI - 2 (0) | 2022.05.01 |
[Carrot Market] #5 TAILWIND CLONING UI - 1 (0) | 2022.05.01 |
[Carrot-Market] #4 TAILWIND - 2 (0) | 2022.04.30 |
[Carrot-Market] #3 SETUP (0) | 2022.04.29 |