Lesson 6 of 8

Building Components

You'll learn how to avoid repeating the same long : extract components in React or Vue, or group utilities with `@apply` in plain projects.

<!-- 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>
Sandboxed page
<!-- 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>
Sandboxed page
Finished reading?Mark it done to track your progress.

Tip: use the ← and → keys to move between lessons.