TwTailwind CSS · Lesson 6 of 8

Building Components

Real-world Tailwind means building reusable component patterns. In a framework like React or Vue you extract these into components. In plain HTML, `@apply` in CSS can package groups of utilities.

HTML
<!-- Button variants — the pattern -->
<!-- Primary -->
<button class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg text-sm transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">
  Primary
</button>

<!-- Secondary -->
<button class="px-4 py-2 bg-white hover:bg-gray-50 text-gray-900 font-medium rounded-lg text-sm border border-gray-300 transition-colors focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2">
  Secondary
</button>

<!-- Danger -->
<button class="px-4 py-2 bg-red-600 hover:bg-red-700 text-white font-medium rounded-lg text-sm transition-colors focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2">
  Delete
</button>

<!-- Ghost -->
<button class="px-4 py-2 hover:bg-gray-100 text-gray-700 font-medium rounded-lg text-sm transition-colors">
  Ghost
</button>

<!-- Alert variants -->
<div class="flex gap-3 p-4 bg-blue-50 border border-blue-200 rounded-lg text-blue-800 text-sm">
  <span>ℹ️</span>
  <p>Informational message here.</p>
</div>

<div class="flex gap-3 p-4 bg-red-50 border border-red-200 rounded-lg text-red-800 text-sm">
  <span>⚠️</span>
  <p>Something went wrong. Please try again.</p>
</div>

<div class="flex gap-3 p-4 bg-green-50 border border-green-200 rounded-lg text-green-800 text-sm">
  <span>✅</span>
  <p>Successfully saved your changes.</p>
</div>
HTML
<!-- Badge component -->
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">New</span>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">Active</span>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">Error</span>

<!-- Input with label -->
<div class="flex flex-col gap-1.5">
  <label class="text-sm font-medium text-gray-700" for="email">Email</label>
  <input
    id="email"
    type="email"
    class="px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent placeholder:text-gray-400"
    placeholder="you@example.com"
  />
  <p class="text-xs text-gray-500">We'll never share your email.</p>
</div>

<!-- Card with image -->
<div class="max-w-xs rounded-2xl overflow-hidden bg-white shadow-md hover:shadow-xl transition-shadow">
  <img class="w-full h-48 object-cover" src="image.jpg" alt="Card image" />
  <div class="p-5">
    <span class="text-xs font-semibold text-blue-600 uppercase tracking-wider">Category</span>
    <h3 class="mt-1 font-bold text-gray-900 text-lg leading-snug">Card Title Here</h3>
    <p class="mt-2 text-gray-500 text-sm line-clamp-3">
      Description text that might be long and gets clamped after three lines.
    </p>
    <div class="mt-4 flex items-center justify-between">
      <span class="text-sm text-gray-400">5 min read</span>
      <a href="#" class="text-sm font-medium text-blue-600 hover:text-blue-700">Read more →</a>
    </div>
  </div>
</div>
✦ Tip
If you're using React, Vue, or Svelte, extract repeated utility strings into components instead of `@apply`. Component abstraction is the right level — not CSS class abstraction. `@apply` is mainly useful for integrating Tailwind with plain HTML pages or third-party component slots.