Receber por email
← Voltar para o catálogo

tailwind

Tailwind CSS 스타일링 규칙. className 패턴, cn() 유틸 사용, tailwind.config, dark mode.

12estrelas
Atualizado há 4 meses

Ver no GitHub ↗ Licença: MIT

Como adicionar

/plugin marketplace add ggombee/code-forge

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/tailwind-ggombee/svg)](https://www.skillteca.com.br/skills/tailwind-ggombee)

Alerta por categoria

Receba novas skills de Design e Frontend toda segunda

1 email curto, só com as skills novas de Design e Frontend. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Tailwind CSS 컨벤션

기본 규칙

  • 스타일은 Tailwind className으로 작성한다
  • 조건부/동적 className은 cn() 유틸을 사용한다
  • 커스텀 값이 필요할 때는 tailwind.config에 추가한다
  • 인라인 style prop 사용은 최소화한다

cn() 유틸 (clsx + tailwind-merge)

// src/utils/cn.ts
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}
// ✅ 좋은 예: cn()으로 조건부 클래스 처리
import { cn } from '@/utils/cn';

interface ButtonProps {
  variant?: 'primary' | 'secondary' | 'danger';
  size?: 'sm' | 'md' | 'lg';
  disabled?: boolean;
  className?: string;
  children: React.ReactNode;
}

function Button({ variant = 'primary', size = 'md', disabled, className, children }: ButtonProps) {
  return (
    <button
      disabled={disabled}
      className={cn(
        // 기본 스타일
        'inline-flex items-center justify-center rounded-md font-medium transition-colors',
        'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
        // variant
        {
          'bg-blue-600 text-white hover:bg-blue-700 focus-visible:ring-blue-500': variant === 'primary',
          'bg-gray-100 text-gray-900 hover:bg-gray-200 focus-visible:ring-gray-500': variant === 'secondary',
          'bg-red-600 text-white hover:bg-red-700 focus-visible:ring-red-500': variant === 'danger',
        },
        // size
        {
          'h-8 px-3 text-sm': size === 'sm',
          'h-10 px-4 text-sm': size === 'md',
          'h-12 px-6 text-base': size === 'lg',
        },
        // disabled
        disabled && 'cursor-not-allowed opacity-50',
        // 외부 className (마지막에 적용)
        className,
      )}
    >
      {children}
    </button>
  );
}
// ❌ 나쁜 예: 템플릿 리터럴로 클래스 조합 (Tailwind merge 없음)
const className = `btn ${variant === 'primary' ? 'bg-blue-600' : 'bg-gray-100'} ${disabled ? 'opacity-50' : ''}`;

// ❌ 나쁜 예: 동적 클래스명 생성 (Tailwind PurgeCSS가 감지 못함)
const color = 'blue';
const className = `bg-${color}-600`; // ❌ 제거됨

tailwind.config 설정

// tailwind.config.ts
import type { Config } from 'tailwindcss';

const config: Config = {
  content: [
    './src/**/*.{js,ts,jsx,tsx,mdx}',
    './app/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  theme: {
    extend: {
      colors: {
        primary: {
          50: '#eff6ff',
          500: '#3b82f6',
          600: '#2563eb',
          700: '#1d4ed8',
        },
        brand: '#1677ff',
      },
      fontFamily: {
        sans: ['Pretendard', 'system-ui', 'sans-serif'],
      },
      spacing: {
        18: '4.5rem',
        88: '22rem',
      },
      borderRadius: {
        '4xl': '2rem',
      },
      screens: {
        xs: '475px', // 커스텀 브레이크포인트
      },
    },
  },
  plugins: [
    require('@tailwindcss/forms'),       // 폼 스타일 초기화
    require('@tailwindcss/typography'),  // prose 스타일
  ],
};

export default config;

Dark Mode

// tailwind.config.ts - class 전략 (수동 제어)
const config: Config = {
  darkMode: 'class', // 또는 'media' (시스템 설정 기반)
  ...
};
// Dark mode 적용 예시
function Card({ children }: { children: React.ReactNode }) {
  return (
    <div
      className={cn(
        'rounded-lg border p-6',
        'bg-white text-gray-900',             // light
        'dark:bg-gray-800 dark:text-gray-100', // dark
        'border-gray-200 dark:border-gray-700',
      )}
    >
      {children}
    </div>
  );
}
// Dark mode 토글 (class 전략)
function ThemeToggle() {
  const [isDark, setIsDark] = useState(false);

  const toggleTheme = () => {
    setIsDark(!isDark);
    document.documentElement.classList.toggle('dark');
  };

  return <button onClick={toggleTheme}>{isDark ? '라이트 모드' : '다크 모드'}</button>;
}

반응형 디자인

// Mobile-first 반응형 클래스
function ResponsiveGrid() {
  return (
    <div
      className={cn(
        'grid gap-4',
        'grid-cols-1',      // 기본 (mobile)
        'sm:grid-cols-2',   // sm: 640px+
        'md:grid-cols-3',   // md: 768px+
        'lg:grid-cols-4',   // lg: 1024px+
      )}
    >
      {items.map((item) => (
        <div key={item.id} className="rounded-lg border p-4">
          {item.name}
        </div>
      ))}
    </div>
  );
}

자주 사용하는 패턴

// Flexbox 중앙 정렬
'flex items-center justify-center'

// 텍스트 말줄임
'truncate'               // 1줄
'line-clamp-2'           // 2줄 (CSS)

// 카드 기본 스타일
'rounded-lg border border-gray-200 bg-white p-6 shadow-sm'

// 반응형 숨김
'hidden md:block'        // mobile 숨김, md+ 표시
'block md:hidden'        // md+ 숨김, mobile 표시

// 포커스 링
'focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500'

// 트랜지션
'transition-colors duration-200'
'transition-all duration-300 ease-in-out'

체크리스트

  • [ ] 동적/조건부 className은 cn() 사용?
  • [ ] 동적 클래스명을 템플릿 리터럴로 생성하지 않음?
  • [ ] 커스텀 색상/간격은 tailwind.config에 추가?
  • [ ] Mobile-first 반응형 작성 (sm:, md:, lg: 순서)?
  • [ ] Dark mode 클래스는 dark: 접두사로 작성?
  • [ ] content 배열에 모든 파일 패턴 포함?
Compartilhar X LinkedIn