저번 Tailwind포스팅에서는 JS없이 구현할 수 있는 엄청난 기능들에 대해서 알아보았습니다. 이어서 엄청난 UI 패턴들에 대해서 알아보갰숩나더. ( 지금부터 습니다... 체 쓰겠습니다.. )
More Modifiers
src/index.js
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>
<details className="select-none open:bg-indigo-400 open:text-white">
<summary className="cursor-pointer">What is my fav. food.</summary>
<span>김치</span>
</details>
<ul className="list-decimal marker:text-teal-500">
<li>hi</li>
<li>hi</li>
<li>hi</li>
</ul>
<input
type="file"
className="file:cursor-pointer file:rounded-xl file:border-0 file:bg-purple-500
file:px-5 file:text-white file:transition-colors file:duration-1000
file:hover:border-purple-400 file:hover:bg-white file:hover:text-purple-400"
/>
<p className="transition first-letter:text-7xl first-letter:hover:text-purple-400">
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Magni sed illo iure
tenetur magnam optio impedit in ratione inventore! Neque?
</p>
</Container>
);
};
export default Home;
이에 대한 결과는 위와 같습니다. 우리가 많이 보지 못했던 html의 details태그와 summary태그에 Tailwind의 open modifier를 활용하여 열리면 bg-indigo-400와 같이 배경색을 변경할 수 있습니다. 또한 li태그의 marker modifier는 li의 decimal을 조정할 수 있습니다. 또한 file input을 조절할 수 있습니다. file input에는 두가지 element가 있는데, 첫번쨰 파일선택이 써있는 부분은 기존의 css에서 file-selector-button으로 접근할 수 있습니다. 그리고 Tailwind에서는 file modifier로 접근할 수 있는데, 여기서 복합적으로 modifier를 써줄 수도 있습니다. file:hover:bg-white와 같이 하면, file-selector-button을 hover하게 되면 바탕이 하얗게 되는 원리입니다. 마지막으로 first-letter와 같이 자식요소의 첫번쨰 textContent의 글자를 가리키게 할 수도 있습니다.
HTML details 요소는 "열림" 상태일 때만 내부 정보를 보여주는 정보 공개 위젯을 생성합니다. 요약이나 레이블은 summary 요소를 통해 제공할 수 있습니다. 정보 공개 위젯은 보통 레이블 옆의 작은 삼각형이 돌아가면서 열림/닫힘 상태를 나타냅니다. details 요소의 첫 번째 자식이 summary 요소라면, summary의 콘텐츠를 위젯의 레이블로 사용합니다.
```
< details>
< summary>Details
Something small enough to escape casual notice.
< /details>
```
https://developer.mozilla.org/ko/docs/Web/HTML/Element/details
File input buttons
파일 수정자를 사용하여 파일 입력의 버튼 스타일 지정
ex) file:mr-4 file:py-2 file:px-4
https://tailwindcss.com/docs/hover-focus-and-other-states?email=george%40krugerindustrial&password=Bosco#file-input-buttons
::file-selector-button
::file-selector-button CSS 의사 요소는 type="file"의 input 버튼을 나타냅니다.
ex) input[type=file]::file-selector-button
https://developer.mozilla.org/en-US/docs/Web/CSS/::file-selector-button
와 같이 정리해 볼 수 있겠다.
Responsive Modifiers
이번에는 Tailwind의 조금 신박한 반응형 Modifiers에 대해 알아보겠다.
src/index.js
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 sm:bg-red-400 sm:hover:bg-pink-700 md:bg-teal-400 lg:bg-indigo-400 xl:bg-yellow-400 2xl:bg-pink-500">
<span className="text-3xl font-semibold">Select Item</span>
<ul>
{[1, 2].map((i) => (
<div key={i} className="my-2 flex justify-between">
<span className="text-gray-500">Grey Chair</span>
<span className="font-semibold">$19</span>
</div>
))}
</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;
이렇게 하면 화면의 크기에 따라 card-1의 배경색이 바뀌게 됩니다.
여기서의 핵심적인 개념은 Mobild First입니다.
기본적으로 Tailwind는 Bootstrap과 같은 다른 프레임워크에서 사용하는 것과 유사한 모바일 우선 breakpoint 시스템을 사용합니다. 이것이 의미하는 바는 접두사가 붙지 않은 유틸리티(예: uppercase)는 모든 화면 크기에 적용되는 반면 접두사가 붙은 유틸리티(예: md:uppercase)는 지정된 breakpoint 이상에서만 적용됩니다.
이 접근 방식이 사람들을 가장 자주 놀라게 하는 부분은 모바일용으로 스타일을 지정하려면 sm: 접두사가 붙은 버전이 아니라 접두사가 없는 버전의 유틸리티를 사용해야 한다는 것입니다. sm을 "작은 화면에서"를 의미하는 것으로 생각하지 마십시오. "작은 breakpoint"로 생각하십시오.
div class="sm:text-center" => 작은 사이즈 (not 모바일)
이러한 이유로 디자인을 위한 모바일 레이아웃을 먼저 구현한 다음 sm 화면에 적합한 변경 사항을 레이어링한 다음 md 화면 등을 적용하는 것이 좋습니다.
```
sm 640px @media (min-width: 640px) { ... }
md 768px @media (min-width: 768px) { ... }
lg 1024px @media (min-width: 1024px) { ... }
xl 1280px @media (min-width: 1280px) { ... }
2xl 1536px @media (min-width: 1536px) { ... }
```
https://tailwindcss.com/docs/responsive-design#mobile-first
Customizing breakpoints
https://tailwindcss.com/docs/responsive-design#customizing-breakpoints
따라서 위의 예시의 결과를 보겠습니다.
Mobile First는 Tailwind에서 중요한 concept입니다.
Responsive Modifiers - 2
이제 Responsive Modifiers를 활용하여 더욱 고급스럽게 elemente들을 디자인 해보겠습니다.
src/index.js
import type { NextPage } from "next";
import tw from "tailwind-styled-components";
const Container = tw.div`
grid
gap-10
space-y-5
bg-slate-400
py-20
px-20
min-h-screen
xl:grid-cols-3
`;
const Home: NextPage = () => {
return (
<div className="grid min-h-screen gap-10 space-y-5 bg-slate-400 py-20 px-20 lg:grid-cols-2 xl:grid-cols-3 xl:place-content-center">
<div className="flex flex-col justify-between rounded-2xl bg-white p-6 shadow-2xl">
<span className="text-3xl font-semibold">Select Item</span>
<ul>
{[1, 2].map((i) => (
<div key={i} className="my-2 flex justify-between">
<span className="text-gray-500">Grey Chair</span>
<span className="font-semibold">$19</span>
</div>
))}
</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 className="group overflow-hidden rounded-3xl bg-white shadow-xl">
<div className="bg-blue-500 p-6 pb-14 xl:pb-40 portrait:bg-indigo-500 landscape:bg-teal-400">
<span className="text-2xl text-white">Profile</span>
</div>
<div className="group 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-gray-400 group-hover:bg-red-300"></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 lg:col-span-2 xl:col-span-1">
<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>
</div>
);
};
export default Home;
다음과 같이 responsive하게 웹이 반응하는 것을 볼 수 있습니다. 이 코드에 적용된 핵심적인 내용에 대해서 알아보겠습니다.
우선 Tailwind의 특성상 mobile first이므로 작은 순서대로 디자인했습니다. 일단 card-2의 변화가 가장 중요합니다. lg:col-span-2, xl-col-span-1을 적용하여 위와 같이 card-2가 2칸을 먹기도, 1칸을 먹게 설정합니다. 또한, 부수적인 layout도 flex, flex-cols, justify-between을 활용하여 lg, xl일때 설정해 줍니다. 여기서 중요한 점은 lg에서 설정한 것은 그 이상 (min-height)이므로 xl에서 상쇄하고 싶으면 꼭 xl도 설정해줘야 합니다.
그 외에도 모바일에서의 반응형도 설정해 주었습니다. landscape:bg-teal-400, portrait:bg-indigo-500을 설정해 주어, 모바일에서의 가로모드, 세로므드에서의 card-2 Profile의 색을 달리 해주었습니다.
Viewport orientation 은 다음과 같습니다.
portrait 및 landscape 수정자를 사용하여 뷰포트가 특정 방향에 있을 때 조건부로 스타일을 추가합니다.
```
div class="portrait:hidden"
div class="landscape:hidden"
```
https://tailwindcss.com/docs/hover-focus-and-other-states#viewport-orientation
Dark Mode
이제 DarkMode를 구현하는 방법에 대해서 알아봅시다.
src/index.js
import type { NextPage } from "next";
import tw from "tailwind-styled-components";
const Container = tw.div`
grid
gap-10
space-y-5
bg-slate-400
py-20
px-20
min-h-screen
xl:grid-cols-3
`;
const Home: NextPage = () => {
return (
<div className="dark grid min-h-screen gap-10 space-y-5 bg-slate-400 py-20 px-20 lg:grid-cols-2 xl:grid-cols-3 xl:place-content-center">
<div className="flex flex-col justify-between rounded-2xl bg-white p-6 shadow-2xl dark:bg-black">
<span className="text-3xl font-semibold dark:text-white">Select Item</span>
<ul>
{[1, 2].map((i) => (
<div key={i} className="my-2 flex justify-between">
<span className="text-gray-500 dark:text-gray-100">Grey Chair</span>
<span className="font-semibold dark:text-white">$19</span>
</div>
))}
</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
dark:border dark:border-white dark:bg-black dark:hover:bg-white dark:hover:text-black
"
>
Checkout
</button>
</div>
<div>
...
</div>
<div>
...
</div>
</div>
);
};
export default Home;
일단 dark modifier를 사용하여 다크모드 시, 디자인을 다르게 해줄 수 있습니다. card-1의 디자인을 dark:bg-black을 시작으로 다양한 글자 색이나 버튼이 hover되었을 떄의 색을 적절히 바꾸어 주면 위의 화면처럼 결과가 나오게 됩니다. 이렇게 DarkMode를 적용하는 것은 브라우저의 설정을 따라 갑니다. 저는 맥을 사용하는데 맥에서 DarkMode를 설정해 주게 되면, 자동으로 브라우저에 DarkMode가 적용되어 디자인이 바뀌게 됩니다.
하지만 이 방법 외에도 다크 모드를 디자인 하는 방법이 하나 더 있습니다. 우리가 수동으로 브라우저의 버튼을 누르면 다크모드로 설정되어 디자인이 바뀌게 하고 싶다면 어떻게 할까요? 그럼 tailwind.config.js을 바꿔야 합니다. 아래를 봅시다.
tailwind.config.js
module.exports = {
content: ["./pages/**/*.{js,jsx,ts,tsx}", "./components/**/*.{js,jsx,ts,tsx}"],
theme: {
extend: {},
},
darkMode: "media", // class
plugins: [],
};
기본적으로 Tailwind의 darkMode의 속성값은 media입니다. 이는 브라우저 그대로 따라간다는 것이고, 만약에 위에서 말한 것처럼 수동으로 바꾸어 주고 싶다면 darkMode: class로 바꾸어 주어야 합니다. 그리고 부모 요소에 dark라는 속성을 주어주어야 합니다. 이를 쉽게 하려면 app.js에 들어가서 <div className="dark"></div>로 감싸주면 됩니다.
Dark Mode 를 정리해보면 아래와 같습니다.
Tailwind에는 dark 모드가 활성화되어 있을 때 사이트 스타일을 다르게 지정할 수 있습니다. 현재 사용 중인 컴퓨터에서 설정한 라이트 모드 또는 다크 모드에 따라 dark가 자동으로 적용됩니다.
ex) dark:bg-slate-900
https://tailwindcss.com/docs/dark-mode
수동으로 다크 모드 전환
운영 체제 기본 설정에 의존하는 대신 수동으로 다크 모드 전환을 지원하려면 media 대신 class을 사용하십시오.
```
// tailwind.config.js
module.exports = {
// 클래스를 기준으로 다크모드 적용 (최상위 부모에 dark클래스 지정)
darkMode: 'class',
// @media(prefers-color-scheme)를 기준으로 다크모드 적용 (기본 값)
darkMode: "media",
}
```
https://tailwindcss.com/docs/dark-mode#toggling-dark-mode-manually
prefers-color-scheme
prefers-color-scheme CSS 미디어 특성은 사용자의 시스템이 라이트 테마나 다크 테마를 사용하는지 탐지하는 데에 사용됩니다.
```
@media (prefers-color-scheme: light) {
.themed {
background: white;
color: black;
}
}
```
https://developer.mozilla.org/ko/docs/Web/CSS/@media/prefers-color-scheme
Just In Time Compiler
이는 Tailwindcss에서 가장 중요한 핵심적 기술중 하나입니다. 예를 들어서 dark:sm:hover:bg-red-50이라는클래스가 있다고 해 봅시다. 근데 이렇게 중첩해서 여러개를 쓰게되면 너무 많은 경우의 수가 나오게 됩니다. 이론상 이건 제한된 css의 용량따문에 불가능해 보입니다. 하지만 실제로 Tailwind에는 실제로 저런 클래스가 존재하게 됩니다. 이는 이전 Tailwind에서는 불가능했습니다. 하지만 Just-In-Time Compiler가 등장하고 이가 가능해졌습니다.
이전에는 우리가 웹사이트 배포를 위해 프로젝트를 빌드할 때, 모든 파일들의 코드를 스캔해서, CSS파일에 포함된 클래스명을 제외하고 나머지 사용하지 않는 클래스들을 전부 삭제했습니다. 이를 purging이라고 합니다. 이는 스타일링을 할 때 엄청난 제약을 걸게 됩니다.
반면 이후에 등장한 JIT컴파일러 라는 건, 코드를 실시간으로 감시하면서 필요한 클래스를 생성하는 기능을 합니다.
간단한 예시를 보면서 JIT컴파일러의 어마무시함을 체감해 보도록 합시다.
src/index.js
import type { NextPage } from "next";
const Home: NextPage = () => {
return (
<div className="dark:md:hover:bg-teal-400">
<h2 className="bg-[url('/vercel.svg')] bg-no-repeat text-[200px] text-[#000]">
Hello
</h2>
</div>
);
};
export default Home;
다음과 같은 코드가 있다고 해 봅시다. 여기서 npm run dev를 치고 <head></head>부분의 <style></style>부분을 보면 우리가 필요한 부분만 JIT컴파일러가 생성한 것을 볼 수 있습니다.
다음과 같이 복합적인 className을 통해 그대로 css문을 작성한 것을 볼 수 있고, 만약 기존의 text-9xl의 한계를 뛰어넘고 더 큰 글자크기를 원해서 작성한 text-[200px]와 같이 그대로 class와 이에 해당하는 css문을 JIT컴파일러가 작성한 것을 볼 수 있습니다. bg-도 이와 같이 할 수 있습니다.
앞서서 우리가 작성해 준 global.css덕분이기도 합니다.
styles/global.css
@tailwind base;
@tailwind components;
@tailwind utilities;
이와같이 Tailwind를 사용하면 방대한 css파일을 purging할 필요도 없이 더 빠르게 코드를 배포하고 돌려볼 수 있게 됩니다. 즉 어떠한 css Framework보다 생산성이 뛰어남을 의미합니다.
Migrating to the JIT engine 에 대해서 정리해 보겠습니다.
2021년 3월에 발표한 새로운 Just-in-Time 엔진이 Tailwind CSS v3.0의 클래식 엔진을 대체했습니다. 새로운 엔진은 프로젝트에 필요한 스타일을 주문형으로 생성합니다.
Tailwind CSS v3.0 이전: 거대한 CSS파일을 생성하고, 그 파일에 이미 정의해놓은 클래스들을 가져와 사용하는 방식.
대략 20만줄 정도 되는 클래스로 가득찬 파일을 가져와 개발 단계에서 사용하기 때문에 매우 무겁고, 배포 전에는 purge를 해줘야 해서 번거로움
Tailwind CSS v3.0이후: 사용자가 사용하는 스타일들만 그때 그때 생성해서 사용하는 방식. 여러 클래스들을 조합해서 사용할 수 있고, 매우 가볍고, 배포 전 purge를 해주지 않아도 되서 편함
https://tailwindcss.com/docs/upgrade-guide#migrating-to-the-jit-engine
'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 - 1 (0) | 2022.04.30 |
[Carrot-Market] #3 SETUP (0) | 2022.04.29 |