Tailwind CSS는 모든 HTML 파일, 자바스크립트 구성 요소 및 클래스 이름에 대한 다른 템플릿을 스캔하여 해당 스타일을 생성한 후 정적 CSS 파일에 쓰는 방식으로 작동한다. 런타임이 없고 빠르고 유연하며 안정적이다.
- 출처: tailwind 공식문서 번역
npx create-next-app@latest
➡️ next 프로젝트를 생성할 때 tailwind를 추가하고 tailwind.config.js
과 postcss.config.js.
를 추가해준다. (명령어에서 y를 눌러 생성해준다.)
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./app/**/*.{js,ts,jsx,tsx,mdx}",
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
// Or if using `src` directory:
"./src/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {},
},
plugins: [],
}
➡️ tailwind.config.js
에 content 배열 안에 모든 템플릿 파일에 대한 경로와 확장자를 위와 같이 넣어준다.
@tailwind base;
@tailwind components;
@tailwind utilities;
➡️ globals.css
상단에 각 레이어에 대한 지시문을 추가해준다.
export default function Home() {
return (
<h1 className="text-3xl font-bold underline">
Hello world!
</h1>
)
}
Radix UI 및 Tailwind CSS를 사용하여 구축된 재사용 가능한 컴포넌트. 컴포넌트 라이브러리가 아닌 앱에 copy and paste 하여 재사용 가능한 구성요소 모음이다. npm install 을 하여 종속성 설치를 하지 않는다는 뜻이다.
...
shadcn에서 디자인된 컴포넌트를 copy and paste로 간편하게 사용 가능하다.
npx shadcn-ui@latest init
Would you like to use TypeScript (recommended)? no / yes
Which style would you like to use? › Default
Which color would you like to use as base color? › Slate
Where is your global CSS file? › › app/globals.css
Do you want to use CSS variables for colors? › no / yes
Where is your tailwind.config.js located? › tailwind.config.js
Configure the import alias for components: › @/components
Configure the import alias for utils: › @/lib/utils
Are you using React Server Components? › no / yes
npx shadcn-ui@latest add button
import { Button } from "@/components/ui/button"
export default function Home() {
return (
<div>
<Button>Click me</Button>
</div>
)
}
더 많은 컴포넌트와 variant는 아래 링크에서 확인할 수 있다.
https://ui.shadcn.com/docs/components/accordion
shadcn/ui
가 tailwind
css 시스템을 도입하고 있기 때문이고 또, 기존 npx 이후 커스텀 또한 가능한 장점이 있다.
먼저 shadcn 기존 Button 을 추가한 경우 아래와 같은 코드가 생성된다.
import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority';
import * as React from 'react';
import { cn } from '@/lib/utils';
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
},
);
export interface IButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, IButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : 'button';
return (
<Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
);
},
);
Button.displayName = 'Button';
export { Button, buttonVariants };
이제 variant 종류와 font-weight, font-size를 추가해 커스텀 해 볼 것이다.
variants: {
variant: {
// ...
// 진한 네이비 버튼
deepnavy:
'bg-primaryBlue-700 text-[14px] text-white rounded-[0px] hover:bg-primaryBlue-700/80 transition-all duration-300',
// 연한 블루 버튼
lightblue:
'bg-primaryBlue-100 text-[14px] font-bold text-primaryBlue-default rounded-full hover:bg-primaryBlue-100/50 transition-all duration-300',
},
// ... 생략
font: {
default: 'text-sm', // 기본 14px
xs: 'text-xs', // 12px
base: 'text-base', // 16px
lg: 'text-lg', // 18px
xl: 'text-xl', // 20px
},
weight: {
default: 'font-normal',
medium: 'font-medium',
bold: 'font-bold',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
font: 'default', // defaultVariants에 추가
weight: 'default', // defaultVariants에 추가
}
// 추가 속성을 props에 전달
const Button = React.forwardRef<HTMLButtonElement, IButtonProps>(
({ className, variant, size, font, weight asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : 'button';
return (
<Comp className={cn(buttonVariants({ variant, size, font, weight, className }))} ref={ref} {...props} />
);
},
);
➡️ variants 객체 안에 추가된 속성인 font, weight의 css를 작성해주고 기존 variant는 스타일을 추가해준다.
➡️ Button props에 추가된 속성인 font, weight를 추가해주면 끝!
<Button variant='lightblue' font='xs'>
중복 확인
</Button>
default 속성을 사용할 경우 속성을 전달할 필요가 없다!
// src/lib/utils.ts
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export const cn = (...inputs: ClassValue[]) => {
return twMerge(clsx(inputs));
};
➡️components.json 구성 시에 Configure the import alias for utils: › @/lib/utils 경로를 설정해주었을 때 해당 경로에 생성되는 utils.ts 파일인데 해당 파일은 tailwind를 merge할 때 발생할 수 있는 클래스 충돌 문제를 해결해주는 역할을 한다.