2025年5月8日

Tailwind CSSのVariantsガイドライン

Tailwind CSS Variants 使用ルール

このプロジェクトでは、すべてのスタイリングにおいて tailwindcss-variantstv 関数を使用してください。

  • JSX の className 属性に 直接 Tailwind CSS のユーティリティクラスの文字列を記述することを禁止 します。

  • ❌ 禁止例:

    • 例1: className に直接文字列を書いている

      export const Example1 = () => {
        return (
          <div className="rounded-lg bg-white p-4 text-red-500">Hello World</div>
        )
      }
      
      • 問題点
        • className に直接 Tailwind CSS のユーティリティクラスを記述。
        • tv 関数を使っていない。
    • 例2: コンポーネントに className を直接渡している tsx コピーする 編集する

      import React from 'react'
      
      interface ButtonProps
        extends React.ButtonHTMLAttributes<HTMLButtonElement> {}
      
      const Button = (props: ButtonProps) => (
        <button {...props}>{props.children}</button>
      )
      
      export const ClickButton = () => {
        return (
          <Button className="bg-gray-100 text-blue-500 hover:bg-gray-200">
            Click me
          </Button>
        )
      }
      
      • 問題点
        • Button コンポーネントに className を直接文字列で渡している。
        • ClickButtonはtv 関数を利用していない。
  • ✅ 推奨例:

    • 例1: 基本的な tv 関数の利用

      import { tv } from 'tailwind-variants'
      
      const box = tv({
        base: 'text-red-500 bg-white p-4 rounded-lg',
      })
      
      export const Box = () => {
        return <div className={box()}>Hello World</div>
      }
      
      • 良い点
        • tv 関数でスタイルを定義。
        • className には tv() の戻り値を指定。
    • 例2: ボタンコンポーネントに tv を使用

      import React from 'react'
      import { tv } from 'tailwind-variants'
      
      const button = tv({
        base: 'text-white bg-blue-500 rounded',
        variants: {
          size: {
            small: 'px-2 py-1 text-sm',
            large: 'px-4 py-2 text-lg',
          },
        },
      })
      
      interface ButtonProps
        extends React.ButtonHTMLAttributes<HTMLButtonElement> {
        size?: 'small' | 'large'
      }
      
      const Button = ({ size = 'large', ...props }: ButtonProps) => (
        <button className={button({ size })} {...props}>
          {props.children}
        </button>
      )
      
      export const Example2 = () => {
        return <Button>Click me</Button>
      }
      
      • 良い点
        • tv 関数で Button コンポーネントのスタイルを定義。
        • 外部から className を渡さず、内部で tv を利用してスタイル適用。
    • 例3: 複数バリアント・条件分岐のあるtv関数の利用

      import { tv } from 'tailwind-variants'
      
      type Status = 'success' | 'error' | 'warning'
      
      interface BadgeProps {
        status: Status
        isPremium: boolean
      }
      
      const badge = tv({
        base: 'inline-block px-2 py-1 rounded-full font-semibold',
        variants: {
          color: {
            success: 'bg-green-100 text-green-800',
            error: 'bg-red-100 text-red-800',
            warning: 'bg-yellow-100 text-yellow-800',
          },
          size: {
            sm: 'text-xs',
            md: 'text-sm',
            lg: 'text-lg',
          },
        },
      })
      
      export const StatusBadge = ({ status, isPremium }: BadgeProps) => {
        const badgeColor = status
        const badgeSize = isPremium ? 'lg' : 'md'
      
        return (
          <span className={badge({ color: badgeColor, size: badgeSize })}>
            {status.toUpperCase()}
          </span>
        )
      }
      
      • 良い点
        • 外部で条件分岐し、tv 内にはロジックを持たせない設計。

理由

  • tv 関数を使用することで、Tailwind CSS のユーティリティクラスの競合を自動的に解決します。
  • JSX 内の className が長文化する問題を解消し、可読性を維持します。
  • スタイリングの一貫性を保ち、メンテナンス性の高いコードを実現します。

その他

  • 他のコンポーネントでも同様に tv 関数を使い、className に直接文字列は書かないでください。
  • 既存コードの修正時もこのルールを適用してください。