Skip to content
Back to skills

Tailwind

ASecurity

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

  • 13 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 28, 2026
ai-agentstypescriptreactnode

Works with

  • cursor
  • cli

Security analysis

A100/100

Scanned May 28, 2026

npx -y skills add ggombee/code-forge --skill tailwind --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Tailwind?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Tailwind
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/ggombee-tailwind/badge)](https://www.skillsdirectory.com/skills/ggombee-tailwind)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: tailwind
description: Tailwind CSS 스타일링 규칙. className 패턴, cn() 유틸 사용, tailwind.config, dark mode.
---

# Tailwind CSS 컨벤션

---

## 기본 규칙

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

---

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

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

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}
```

```typescript
// ✅ 좋은 예: 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>
  );
}
```

```typescript
// ❌ 나쁜 예: 템플릿 리터럴로 클래스 조합 (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 설정

```typescript
// 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

```typescript
// tailwind.config.ts - class 전략 (수동 제어)
const config: Config = {
  darkMode: 'class', // 또는 'media' (시스템 설정 기반)
  ...
};
```

```typescript
// 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>
  );
}
```

```typescript
// 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>;
}
```

---

## 반응형 디자인

```typescript
// 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>
  );
}
```

---

## 자주 사용하는 패턴

```typescript
// 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` 배열에 모든 파일 패턴 포함?

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.