コアコンセプト
プリミティブなユーティリティの制約されたセットから複雑なコンポーネントを構築する。
Tailwind でスタイルを設定するには、多くの単一目的のプレゼンテーションクラス (ユーティリティクラス) をマークアップ内で直接組み合わせます。
新しいメッセージがあります!
<div class="mx-auto flex max-w-sm items-center gap-x-4 rounded-xl bg-white p-6 shadow-lg outline outline-black/5 dark:bg-slate-800 dark:shadow-none dark:-outline-offset-1 dark:outline-white/10"> <img class="size-12 shrink-0" src="/img/logo.svg" alt="ChitChat Logo" /> <div> <div class="text-xl font-medium text-black dark:text-white">ChitChat</div> <p class="text-gray-500 dark:text-gray-400">You have a new message!</p> </div></div>
たとえば、上記の UI では以下を使用しました。
flex
、shrink-0
、およびp-6
)max-w-sm
およびmx-auto
)bg-white
、rounded-xl
、およびshadow-lg
)size-12
)gap-x-4
)text-xl
、text-black
、font-medium
など)この方法でスタイルを設定することは、従来のベストプラクティスに多く反していますが、一度試してみると、非常に重要な利点にすぐに気付くでしょう。
これらの利点は、小規模なプロジェクトでは大きな違いを生み出しますが、長期にわたる大規模プロジェクトに取り組むチームにとってはさらに価値があります。
このアプローチに対する一般的な反応は、「これは単なるインラインスタイルではないか?」と疑問に思うことですが、ある意味ではそうです。クラス名を割り当ててそのクラスをスタイル設定する代わりに、要素に直接スタイルを適用しています。
しかし、ユーティリティクラスを使用することには、インラインスタイルよりも多くの重要な利点があります。たとえば、
このコンポーネントは完全にレスポンシブであり、ホバーおよびアクティブスタイルを持つボタンが含まれており、完全にユーティリティクラスで構築されています。
エリン・リンドフォード
プロダクトエンジニア
<div class="flex flex-col gap-2 p-8 sm:flex-row sm:items-center sm:gap-6 sm:py-4 ..."> <img class="mx-auto block h-24 rounded-full sm:mx-0 sm:shrink-0" src="/img/erin-lindford.jpg" alt="" /> <div class="space-y-2 text-center sm:text-left"> <div class="space-y-0.5"> <p class="text-lg font-semibold text-black">Erin Lindford</p> <p class="font-medium text-gray-500">Product Engineer</p> </div> <button class="border-purple-200 text-purple-600 hover:border-transparent hover:bg-purple-600 hover:text-white active:bg-purple-700 ..."> Message </button> </div></div>
ホバーやフォーカスなどの状態の要素をスタイル設定するには、ターゲットにする状態をユーティリティにプレフィックスとして追加します。たとえば、hover:bg-sky-700
などです。
このボタンにカーソルを合わせると、背景色が変化するのがわかります。
<button class="bg-sky-500 hover:bg-sky-700 ...">Save changes</button>
これらのプレフィックスは、Tailwind ではバリアントと呼ばれ、そのバリアントの条件が一致する場合にのみ、ユーティリティクラスのスタイルを適用します。
hover:bg-sky-700
クラスに対して生成されたCSSは次のようになります。
.hover\:bg-sky-700 { &:hover { background-color: var(--color-sky-700); }}
このクラスは、要素がホバーされない限り何も実行しないことに注意してください。その唯一の仕事は、ホバースタイルを提供することであり、それ以外には何もありません。
これは、従来のCSSの書き方とは異なります。従来のCSSでは、単一のクラスが通常、多くの状態のスタイルを提供します。
<button class="btn">Save changes</button><style> .btn { background-color: var(--color-sky-500); &:hover { background-color: var(--color-sky-700); } }</style>
Tailwind では、hover:
とdisabled:
を組み合わせるなど、複数の条件が一致した場合にユーティリティを適用するためにバリアントを積み重ねることもできます。
<button class="bg-sky-500 disabled:hover:bg-sky-500 ...">Save changes</button>
詳細については、ホバー、フォーカス、その他の状態の要素のスタイル設定に関するドキュメントをご覧ください。
ホバー状態やフォーカス状態と同様に、スタイルを適用するブレークポイントをユーティリティにプレフィックスとして追加することで、異なるブレークポイントで要素をスタイル設定できます。
この例のサイズを変更して、レイアウトの変化を確認してください。
<div class="grid grid-cols-2 sm:grid-cols-3"> <!-- ... --></div>
上記の例では、sm:
プレフィックスは、grid-cols-3
がsm
ブレークポイント以上でのみトリガーされるようにします。これは、デフォルトで40remです。
.sm\:grid-cols-3 { @media (width >= 40rem) { grid-template-columns: repeat(3, minmax(0, 1fr)); }}
詳細については、レスポンシブデザインのドキュメントをご覧ください。
ダークモードで要素をスタイル設定するには、ダークモードがアクティブなときに適用するユーティリティにdark:
プレフィックスを追加するだけです。
ライトモード
逆さまに書く
ゼログラビティペンは、逆さまを含め、あらゆる向きで書くことができます。宇宙空間でも機能します。
ダークモード
逆さまに書く
ゼログラビティペンは、逆さまを含め、あらゆる向きで書くことができます。宇宙空間でも機能します。
<div class="bg-white dark:bg-gray-800 rounded-lg px-6 py-8 ring shadow-xl ring-gray-900/5"> <div> <span class="inline-flex items-center justify-center rounded-md bg-indigo-500 p-2 shadow-lg"> <svg class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true" > <!-- ... --> </svg> </span> </div> <h3 class="text-gray-900 dark:text-white mt-5 text-base font-medium tracking-tight ">Writes upside-down</h3> <p class="text-gray-500 dark:text-gray-400 mt-2 text-sm "> The Zero Gravity Pen can be used to write in any orientation, including upside-down. It even works in outer space. </p></div>
ホバー状態やメディアクエリと同様に、理解しておくべき重要なことは、単一のユーティリティクラスには、ライトスタイルとダークスタイルの両方が決して含まれないということです。ダークモードでスタイルを設定するには、複数のクラスを使用します。1つはライトモードスタイル用、もう1つはダークモードスタイル用です。
.dark\:bg-gray-800 { @media (prefers-color-scheme: dark) { background-color: var(--color-gray-800); }}
詳細については、ダークモードのドキュメントをご覧ください。
Tailwind では、多くの場合、複数のクラスを使用して、単一のCSSプロパティの値を構築します。たとえば、複数のフィルターを要素に追加するなどです。
<div class="blur-sm grayscale"> <!-- ... --></div>
これらのエフェクトはどちらもCSSのfilter
プロパティに依存しているため、Tailwind はCSS変数を使用して、これらのエフェクトを一緒に構成できるようにしています。
.blur-sm { --tw-blur: blur(var(--blur-sm)); filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-grayscale,);}.grayscale { --tw-grayscale: grayscale(100%); filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-grayscale,);}
上記の生成されたCSSはわずかに簡略化されていますが、ここでの秘訣は、各ユーティリティが、適用することを意図したエフェクトのためだけにCSS変数を設定することです。次に、filter
プロパティはこれらの変数をすべて調べ、変数が設定されていない場合は何もしません。
Tailwind は、グラデーション、シャドウカラー、トランスフォームなどにも同じアプローチを使用しています。
Tailwind の多くのユーティリティは、bg-blue-500
、text-xl
、shadow-md
などのテーマ変数によって駆動されます。これらは、基盤となるカラーパレット、タイプスケール、およびシャドウにマッピングされます。
テーマ外の1回限りの値を使用する必要がある場合は、任意の値を指定するための特別な角かっこ構文を使用します。
<button class="bg-[#316ff6] ..."> Sign in with Facebook</button>
これは、カラーパレット外の1回限りの色 (上記の Facebook ブルーなど) に役立ちますが、非常に具体的なグリッドのような複雑なカスタム値が必要な場合にも役立ちます。
<div class="grid grid-cols-[24rem_2.5rem_minmax(0,1fr)]"> <!-- ... --></div>
テーマ値を使用している場合でも、calc()
などのCSS機能を使用する必要がある場合にも役立ちます。
<div class="max-h-[calc(100dvh-(--spacing(6))]"> <!-- ... --></div>
任意のプロパティ名を含む完全に任意のCSSを生成するための構文もあり、CSS変数を設定するのに役立ちます。
<div class="[--gutter-width:1rem] lg:[--gutter-width:2rem]"> <!-- ... --></div>
詳細については、任意の値の使用に関するドキュメントをご覧ください。
Tailwind CSS は、他のCSSフレームワークで使用されている可能性のある大きな静的スタイルシートではありません。CSSをコンパイルするときに実際に使用しているクラスに基づいて、必要なCSSを生成します。
これは、プロジェクト内のすべてのファイルをスキャンして、クラス名である可能性のあるシンボルを探すことによって行われます。
export default function Button({ size, children }) { let sizeClasses = { md: "px-4 py-2 rounded-md text-base", lg: "px-5 py-3 rounded-lg text-lg", }[size]; return ( <button type="button" className={`font-bold ${sizeClasses}`}> {children} </button> );}
Tailwind は、潜在的なクラスをすべて見つけた後、各クラスのCSSを生成し、実際に必要なスタイルのみを含む1つのスタイルシートにコンパイルします。
CSS はクラス名に基づいて生成されるため、Tailwind はbg-[#316ff6]
のような任意の値を使用するクラスを認識し、値がテーマの一部でなくても、必要なCSSを生成できます。
この仕組みの詳細については、ソースファイル内のクラスの検出をご覧ください。
要素を、ダークモード、特定のブレークポイント、ホバー時、および要素に特定のデータ属性がある場合など、条件の組み合わせでスタイル設定する必要がある場合があります。
Tailwind でそれがどのように見えるかの例を次に示します。
<button class="dark:lg:data-current:hover:bg-indigo-600 ..."> <!-- ... --></button>
@media (prefers-color-scheme: dark) and (width >= 64rem) { button[data-current]:hover { background-color: var(--color-indigo-600); }}
Tailwind は、group-hover
のようなものもサポートしており、特定の親がホバーされたときに要素をスタイル設定できます。
<a href="#" class="group rounded-lg p-8"> <!-- ... --> <span class="group-hover:underline">Read more…</span></a>
@media (hover: hover) { a:hover span { text-decoration-line: underline; }}
このgroup-*
構文は、group-focus
、group-active
、およびその他多数のような他のバリアントでも機能します。
本当に複雑なシナリオ (特に制御できないHTMLをスタイル設定する場合) の場合、Tailwind は任意のバリアントをサポートしており、クラス名に直接必要なセレクターを記述できます。
<div class="[&>[data-active]+span]:text-blue-600 ..."> <span data-active><!-- ... --></span> <span>This text will be blue</span></div>
div > [data-active] + span { color: var(--color-blue-600);}
インラインスタイルは、特に値がデータベースやAPIなどの動的なソースから来ている場合に、Tailwind CSS プロジェクトで依然として非常に役立ちます。
export function BrandedButton({ buttonColor, textColor, children }) { return ( <button style={{ backgroundColor: buttonColor, color: textColor, }} className="rounded-md px-3 py-1.5 font-medium" > {children} </button> );}
クラス名としてフォーマットすると読みにくい非常に複雑な任意の値をインラインスタイルで使用することもできます。
<div class="grid-[2fr_max(0,var(--gutter-width))_calc(var(--gutter-width)+10px)]"><div style="grid-template-columns: 2fr max(0, var(--gutter-width)) calc(var(--gutter-width) + 10px)"> <!-- ... --></div>
もう1つの便利なパターンは、インラインスタイルを使用して動的なソースに基づいてCSS変数を設定し、ユーティリティクラスでそれらの変数を参照することです。
export function BrandedButton({ buttonColor, buttonColorHover, textColor, children }) { return ( <button style={{ "--bg-color": buttonColor, "--bg-color-hover": buttonColorHover, "--text-color": textColor, }} className="bg-(--bg-color) text-(--text-color) hover:bg-(--bg-color-hover) ..." > {children} </button> );}
ユーティリティクラスだけでプロジェクト全体を構築すると、必然的に特定のパターンを繰り返して、異なる場所で同じデザインを再現することになります。
たとえば、ここでは、各アバター画像のユーティリティクラスが5回繰り返されています。
<div> <div class="flex items-center space-x-2 text-base"> <h4 class="font-semibold text-slate-900">Contributors</h4> <span class="bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-700 ...">204</span> </div> <div class="mt-3 flex -space-x-2 overflow-hidden"> <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="" /> <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="" /> <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2.25&w=256&h=256&q=80" alt="" /> <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="" /> <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1517365830460-955ce3ccd263?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="" /> </div> <div class="mt-3 text-sm font-medium"> <a href="#" class="text-blue-500">+ 198 others</a> </div></div>
パニックにならないでください!実際には、これは心配しているような問題ではなく、それに対処するための戦略は、すでに日常的に行っていることです。
レンダリングされたページに複数回表示されるデザイン要素の多くは、実際のマークアップがループでレンダリングされるため、実際には1回しか作成されていません。
たとえば、このガイドの冒頭にある重複するアバターは、実際のプロジェクトではほぼ確実にループでレンダリングされます。
<div> <div class="flex items-center space-x-2 text-base"> <h4 class="font-semibold text-slate-900">Contributors</h4> <span class="bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-700 ...">204</span> </div> <div class="mt-3 flex -space-x-2 overflow-hidden"> {#each contributors as user} <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src={user.avatarUrl} alt={user.handle} /> {/each} </div> <div class="mt-3 text-sm font-medium"> <a href="#" class="text-blue-500">+ 198 others</a> </div></div>
要素がこのようにループでレンダリングされる場合、実際のクラスリストは1回だけ記述されるため、解決すべき実際の重複問題はありません。
重複が単一ファイル内の要素のグループにローカライズされている場合、それに対処する最も簡単な方法は、マルチカーソル編集を使用して、各要素のクラスリストをすばやく選択して同時に編集することです。
これが最終的に最良のソリューションになることがどれほど多いかに驚かれることでしょう。重複するクラスリストすべてをすばやく同時に編集できる場合は、追加の抽象化を導入するメリットはありません。
<nav class="flex justify-center space-x-4"> <a href="/dashboard" class="font-medium rounded-lg px-3 py-2 text-gray-700 hover:bg-gray-100 hover:text-gray-900"> Home </a> <a href="/team" class="font-medium rounded-lg px-3 py-2 text-gray-700 hover:bg-gray-100 hover:text-gray-900"> Team </a> <a href="/projects" class="font-medium rounded-lg px-3 py-2 text-gray-700 hover:bg-gray-100 hover:text-gray-900"> Projects </a> <a href="/reports" class="font-medium rounded-lg px-3 py-2 text-gray-700 hover:bg-gray-100 hover:text-gray-900"> Reports </a></nav>
複数のファイルで一部のスタイルを再利用する必要がある場合、最良の戦略は、React、Svelte、Vueなどのフロントエンドフレームワークを使用している場合はコンポーネントを作成し、Blade、ERB、Twig、Nunjucksなどのテンプレート言語を使用している場合はテンプレートパーシャルを作成することです。
export function VacationCard({ img, imgAlt, eyebrow, title, pricing, url }) { return ( <div> <img className="rounded-lg" src={img} alt={imgAlt} /> <div className="mt-4"> <div className="text-xs font-bold text-sky-500">{eyebrow}</div> <div className="mt-1 font-bold text-gray-700"> <a href={url} className="hover:underline"> {title} </a> </div> <div className="mt-2 text-sm text-gray-600">{pricing}</div> </div> </div> );}
これで、このコンポーネントを必要な場所にいくつでも使用でき、スタイルを1つの場所で簡単に更新できるように、スタイルの単一の信頼できるソースを維持できます。
React や Vue のようなものを使用する代わりに、ERB や Twig のようなテンプレート言語を使用している場合、ボタンほど小さいものに対してテンプレートパーシャルを作成することは、btn
のような単純な CSS クラスと比較して、大げさな感じがするかもしれません。
より複雑なコンポーネントの場合は適切なテンプレートパーシャルを作成することを強くお勧めしますが、テンプレートパーシャルが重すぎると感じられる場合は、カスタム CSS を記述してもまったく問題ありません。
btn-primary
クラスがどのように見えるかを次に示します。テーマ変数を使用して、デザインの一貫性を維持しています。
<button class="btn-primary">Save changes</button>
@import "tailwindcss";@layer components { .btn-primary { border-radius: calc(infinity * 1px); background-color: var(--color-violet-500); padding-inline: --spacing(5); padding-block: --spacing(2); font-weight: var(--font-weight-semibold); color: var(--color-white); box-shadow: var(--shadow-md); &:hover { @media (hover: hover) { background-color: var(--color-violet-700); } } }}
ただし、HTML要素が1つだけの場合よりも複雑なものについては、スタイルと構造を1つの場所にカプセル化できるように、テンプレートパーシャルを使用することを強くお勧めします。
同じCSSプロパティをターゲットにする2つのクラスを追加すると、スタイルシートで後に出現するクラスが優先されます。したがって、この例では、flex
が実際のclass
属性で最後に記述されていても、要素はdisplay: grid
を受け取ります。
<div class="grid flex"> <!-- ... --></div>
.flex { display: flex;}.grid { display: grid;}
一般に、同じ要素に競合する2つのクラスを追加することは絶対に避けるべきです。実際に有効にしたいクラスのみを追加してください。
export function Example({ gridLayout }) { return <div className={gridLayout ? "grid" : "flex"}>{/* ... */}</div>;}
React や Vue のようなコンポーネントベースのライブラリを使用する場合、多くの場合、スタイルのカスタマイズのために特定のpropsを公開することを意味します。コンシューマーがコンポーネントの外部から追加のクラスを追加できるようにするのではなく、これらのスタイルがしばしば競合するためです。
特定のユーティリティクラスを強制的に有効にする必要があり、具体性を管理する他の手段がない場合は、クラス名の最後に!
を追加して、すべての宣言を!important
にすることができます。
<div class="bg-teal-500 bg-red-500!"> <!-- ... --></div>
.bg-red-500\! { background-color: var(--color-red-500) !important;}.bg-teal-500 { background-color: var(--color-teal-500);}
既存の複雑なCSSと高い具体性ルールを持つプロジェクトにTailwindを追加する場合は、Tailwind をインポートするときにimportant
フラグを使用して、すべてのユーティリティを!important
としてマークできます。
@import "tailwindcss" important;
@layer utilities { .flex { display: flex !important; } .gap-4 { gap: 1rem !important; } .underline { text-decoration-line: underline !important; }}
プロジェクトに Tailwind CSS ユーティリティと競合するクラス名がある場合は、prefix
オプションを使用して、Tailwind によって生成されたすべてのクラスと CSS 変数にプレフィックスを付けることができます。
@import "tailwindcss" prefix(tw);
@layer theme { :root { --tw-color-red-500: oklch(0.637 0.237 25.331); }}@layer utilities { .tw\:text-red-500 { color: var(--tw-color-red-500); }}