コンポーネントの抽出

重複に対処し、ユーティリティファーストプロジェクトを維持可能に保つ。


Tailwindは、utility-firstワークフローを推奨します。このワークフローでは、早期の抽象化を回避するために、ユーティリティクラスのみを使用して設計が最初に実装されます。

Woman paying for a purchase
Marketing
Finding customers for your new business

Getting a new business off the ground is a lot of hard work. Here are five ideas you can use to find your first customers.

<!-- A marketing page card built entirely with utility classes -->
<div class="md:flex">
  <div class="md:flex-shrink-0">
    <img class="rounded-lg md:w-56" src="https://images.unsplash.com/photo-1556740738-b6a63e27c4df?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=448&q=80" width="448" height="299" alt="Woman paying for a purchase">
  </div>
  <div class="mt-4 md:mt-0 md:ml-6">
    <div class="uppercase tracking-wide text-sm text-indigo-600 font-bold">Marketing</div>
    <a href="#" class="block mt-1 text-lg leading-tight font-semibold text-gray-900 hover:underline">Finding customers for your new business</a>
    <p class="mt-2 text-gray-600">Getting a new business off the ground is a lot of hard work. Here are five ideas you can use to find your first customers.</p>
  </div>
</div>

しかし、プロジェクトが成長するにつれて、必然的に、多くの異なる場所で同じコンポーネントを再作成するために、一般的なユーティリティの組み合わせを繰り返すことに気付くでしょう。 これは、ボタン、フォーム要素、バッジなどの小さなコンポーネントで最も顕著です。

<!-- Repeating these classes for every button can be painful -->
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
  Button
</button>

ユーティリティクラスの長いリストを多くのコンポーネントインスタンス間で同期させると、すぐに実際のメンテナンスの負担になる可能性があるため、 このような面倒な重複が発生した場合は、コンポーネントを抽出することをお勧めします。


Extracting HTML components

UIコンポーネントを定義するために必要なすべての情報が完全にCSSに存在できることは非常にまれです。 ほとんどの場合、使用する必要があり重要な対応をするHTML構造もいくつかあります。

複雑なコンポーネントを抽出するためにCSSクラスに依存しないでください

Beach
Private Villa
$299 USD per night
<style>
  .vacation-card { /* ... */ }
  .vacation-card-info { /* ... */ }
  .vacation-card-eyebrow { /* ... */ }
  .vacation-card-title { /* ... */ }
  .vacation-card-price { /* ... */ }
</style>

<!-- Even with custom CSS, you still need to duplicate this HTML structure -->
<div class="vacation-card">
  <img class="vacation-card-image" src="..." alt="Beach in Cancun">
  <div class="vacation-card-info">
    <div>
      <div class="vacation-card-eyebrow">Private Villa</div>
      <div class="vacation-card-title">
        <a href="/vacations/cancun">Relaxing All-Inclusive Resort in Cancun</a>
      </div>
      <div class="vacation-card-price">$299 USD per night</div>
    </div>
  </div>
</div>

このため、カスタムCSSクラスを作成するよりも、UIの再利用可能な部分をtemplate partialsまたはJavaScript componentsに抽出する方がよい場合がよくあります。

テンプレートの信頼できる唯一の情報源を作成することにより、同じクラスを複数の場所に複製することによって生じるメンテナンスの負担なしに、ユーティリティクラスを使い続けることができます。

テンプレートの部分コンポーネントまたはJavaScriptコンポーネントを作成する

<!-- In use -->
<VacationCard
  img="/img/cancun.jpg"
  imgAlt="Beach in Cancun"
  eyebrow="Private Villa"
  title="Relaxing All-Inclusive Resort in Cancun"
  pricing="$299 USD per night"
  url="/vacations/cancun"
/>

<!-- ./components/VacationCard.vue -->
<template>
  <div>
    <img class="rounded" :src="img" :alt="imgAlt">
    <div class="mt-2">
      <div>
        <div class="text-xs text-gray-600 uppercase font-bold">{{ eyebrow }}</div>
        <div class="font-bold text-gray-700 leading-snug">
          <a :href="url" class="hover:underline">{{ title }}</a>
        </div>
        <div class="mt-2 text-sm text-gray-600">{{ pricing }}</div>
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    props: ['img', 'imgAlt', 'eyebrow', 'title', 'pricing', 'url']
  }
</script>

上記例ではVueを使用しています。 しかし同じ方法はReact components, ERB partials, Blade components, Twig includesなどでも使われます。


Extracting CSS components with @apply

ボタンやフォーム要素などの小さなコンポーネントの場合、テンプレートの一部分またはJavaScriptコンポーネントを作成すると、単純なCSSクラスに比べて重く感じることがよくあります。

このような状況では、Tailwindの@applyディレクティブを使用して、一般的なユーティリティパターンをCSSコンポーネントクラスに簡単に抽出できます。

.btn-blueクラスが@applyを使用して既存のユーティリティから構成すると、次のようになります。

<button class="btn-blue">
  Button
</button>

<style>
.btn-blue {
  @apply bg-blue-500 text-white font-bold py-2 px-4 rounded;
}
.btn-blue:hover {
  @apply bg-blue-700;
}
</style>

hover:,focus:,{screen}:などのバリアントは直接適用できないため、 代わりにプレーンバージョンのユーティリティを適切な疑似セレクターまたはメディアクエリに適用することに注意してください。

意図しない特異性の問題を回避するために、カスタムコンポーネントスタイルを@layer components {...}ディレクティブでラップして、それらのスタイルがどのレイヤーに属するかをTailwindに通知することをお勧めします。

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer components {
  .btn-blue {
    @apply bg-blue-500 text-white font-bold py-2 px-4 rounded;
  }
  .btn-blue:hover {
    @apply bg-blue-700;
  }
}

Tailwindは、これらのスタイルを自動的に@tailwind componentsと同じ場所に移動します。 したがって、ソースファイルで順序を取得することを心配する必要はありません。

@layerディレクティブを使用すると、componentsレイヤーを除去するときに、除去するためにそれらのスタイルを考慮するようにTailwindに指示します。 詳細についてはControlling File Sizeをご覧ください。

Keeping things composable

例えば2つのボタンがあるとします。

<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
  Button
</button>

<button class="bg-gray-400 hover:bg-gray-500 text-gray-800 font-bold py-2 px-4 rounded">
  Button
</button>

次のように、これらボタンのコンポーネントクラスを実装したくなるかもしれません。

@layer components {
  .btn-blue {
    @apply bg-blue-500 text-white font-bold py-2 px-4 rounded;
  }
  .btn-blue:hover {
    @apply bg-blue-700;
  }

  .btn-gray {
    @apply bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded;
  }
  .btn-gray:hover {
    @apply bg-gray-500;
  }
}

このアプローチの問題は、まだ潜在的に痛みを伴う重複があるということです。

サイト上のすべてのボタンのパディング、フォントの太さ、または境界線の半径を変更する場合は、すべてのボタンクラスを更新する必要があります。

より良いアプローチは、同じ部分を別のクラスに抽出することです。

@layer components {
  .btn {
    @apply font-bold py-2 px-4 rounded;
  }

  .btn-blue {
    @apply bg-blue-500 text-white;
  }
  .btn-blue:hover {
    @apply bg-blue-700;
  }

  .btn-gray {
    @apply bg-gray-400 text-gray-800;
  }
  .btn-gray:hover {
    @apply bg-gray-500;
  }
}

これで、ボタンのスタイルを設定する必要があるときはいつでも、2つのクラスを適用できます。

<button class="btn btn-blue">
  Button
</button>

<button class="btn btn-gray">
  Button
</button>

これにより、.btnクラスを編集するだけで、共有スタイルを1か所で簡単に変更できます。

また、新しいコンポーネントクラスを作成したり、共有スタイルを複製したりすることなく、新しい1回限りのボタンスタイルを追加することもできます。

<button class="btn bg-green-500 hover:bg-green-400 text-white">
  Button
</button>

Writing a component plugin

CSSファイルにコンポーネントクラスを直接書き込むだけでなく、独自のプラグインを作成してコンポーネントクラスをTailwindに追加することもできます。

// tailwind.config.js
const plugin = require('tailwindcss/plugin')

module.exports = {
  plugins: [
    plugin(function({ addComponents }) {
      const buttons = {
        '.btn': {
          padding: '.5rem 1rem',
          borderRadius: '.25rem',
          fontWeight: '600',
        },
        '.btn-blue': {
          backgroundColor: '#3490dc',
          color: '#fff',
          '&:hover': {
            backgroundColor: '#2779bd'
          },
        },
        '.btn-red': {
          backgroundColor: '#e3342f',
          color: '#fff',
          '&:hover': {
            backgroundColor: '#cc1f1a'
          },
        },
      }

      addComponents(buttons)
    })
  ]
}

これは、Tailwindコンポーネントをライブラリとして公開する場合、または複数のプロジェクト間でコンポーネントを簡単に共有できるようにする場合に適しています。

詳細についてはcomponent plugin documentationをご覧ください。


Translated by T.Arai @ Entap,Inc. / スマホアプリ開発会社

Tailwind UI is now in early access!