Tailwind CSS Interactive States: hover/focus/active State Prefixes and Cursor Styles

State Prefix Overview

Tailwind uses state prefixes to apply styles to elements in different states without writing pseudo-class CSS.

Prefix CSS Pseudo-class Description
hover: :hover Mouse hover
focus: :focus Focused
active: :active Mouse pressed
disabled: :disabled Disabled state
visited: :visited Visited link
first: :first-child First child element
last: :last-child Last child element
odd: :nth-child(odd) Odd child element
even: :nth-child(even) Even child element

Example

HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>State Prefix Demo</title>
  <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="min-h-screen bg-gray-100 p-8">
  <div class="max-w-4xl mx-auto space-y-8">
    <!-- hover state -->
    <div class="bg-white rounded-lg shadow p-6">
      <h3 class="font-semibold mb-4">hover state</h3>
      <div class="flex gap-4">
        <button class="px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-700 transition-colors">
          Darken on hover
        </button>
        <button class="px-6 py-3 bg-white border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 hover:border-gray-400 transition-colors">
          Border on hover
        </button>
        <button class="px-6 py-3 bg-green-500 text-white rounded-lg hover:shadow-lg hover:shadow-green-500/50 transition-all">
          Shadow on hover
        </button>
      </div>
    </div>

    <!-- focus state -->
    <div class="bg-white rounded-lg shadow p-6">
      <h3 class="font-semibold mb-4">focus state</h3>
      <div class="space-y-4">
        <input type="text" placeholder="Click the input to see focus effect"
               class="w-full p-3 border border-gray-300 rounded-lg focus:border-blue-500 focus:ring-2 focus:ring-blue-500/50 focus:outline-none transition-all">
        <input type="text" placeholder="Custom focus shadow"
               class="w-full p-3 border border-gray-300 rounded-lg focus:shadow-lg focus:shadow-purple-500/30 focus:border-purple-500 focus:outline-none transition-all">
      </div>
    </div>

    <!-- active state -->
    <div class="bg-white rounded-lg shadow p-6">
      <h3 class="font-semibold mb-4">active state</h3>
      <div class="flex gap-4">
        <button class="px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600 active:bg-blue-800 active:scale-95 transition-all">
          Darken and shrink on press
        </button>
        <button class="px-6 py-3 bg-white border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 active:bg-gray-100 active:scale-95 transition-all">
          Press effect
        </button>
      </div>
    </div>

    <!-- disabled state -->
    <div class="bg-white rounded-lg shadow p-6">
      <h3 class="font-semibold mb-4">disabled state</h3>
      <div class="flex gap-4">
        <button class="px-6 py-3 bg-blue-500 text-white rounded-lg disabled:opacity-50 disabled:cursor-not-allowed">
          Enabled button
        </button>
        <button disabled class="px-6 py-3 bg-blue-500 text-white rounded-lg disabled:opacity-50 disabled:cursor-not-allowed">
          Disabled button
        </button>
        <input type="text" placeholder="Enabled input"
               class="p-3 border border-gray-300 rounded-lg disabled:bg-gray-100 disabled:cursor-not-allowed">
        <input type="text" disabled placeholder="Disabled input"
               class="p-3 border border-gray-300 rounded-lg disabled:bg-gray-100 disabled:cursor-not-allowed">
      </div>
    </div>

    <!-- visited links -->
    <div class="bg-white rounded-lg shadow p-6">
      <h3 class="font-semibold mb-4">visited links</h3>
      <div class="flex gap-4">
        <a href="#visited1" class="text-blue-600 hover:text-blue-800 visited:text-purple-600">
          Turns purple after clicking
        </a>
        <a href="#visited2" class="text-green-600 hover:text-green-800 visited:text-gray-400">
          Turns gray after clicking
        </a>
      </div>
    </div>
  </div>
</body>
</html>
▶ Try it Yourself

Traditional CSS vs Tailwind The traditional approach requires writing pseudo-class rules like button:hover { background-color: ... }, while Tailwind uses prefixes like hover:bg-blue-700 to declare them directly.

Tip: State prefixes can be combined, such as hover:focus:ring-2 to apply styles when both hovered and focused.

Structural Pseudo-classes (first/last/odd/even)

Structural pseudo-class prefixes are used to select child elements at specific positions.

Example

HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Structural Pseudo-class Demo</title>
  <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="min-h-screen bg-gray-100 p-8">
  <div class="max-w-4xl mx-auto space-y-8">
    <!-- first/last -->
    <div class="bg-white rounded-lg shadow p-6">
      <h3 class="font-semibold mb-4">first/last</h3>
      <ul class="space-y-2">
        <li class="p-3 bg-gray-50 rounded first:bg-blue-50 first:text-blue-700 last:bg-green-50 last:text-green-700">
          First item (first:bg-blue-50)
        </li>
        <li class="p-3 bg-gray-50 rounded first:bg-blue-50 first:text-blue-700 last:bg-green-50 last:text-green-700">
          Second item
        </li>
        <li class="p-3 bg-gray-50 rounded first:bg-blue-50 first:text-blue-700 last:bg-green-50 last:text-green-700">
          Third item
        </li>
        <li class="p-3 bg-gray-50 rounded first:bg-blue-50 first:text-blue-700 last:bg-green-50 last:text-green-700">
          Last item (last:bg-green-50)
        </li>
      </ul>
    </div>

    <!-- odd/even -->
    <div class="bg-white rounded-lg shadow p-6">
      <h3 class="font-semibold mb-4">odd/even</h3>
      <ul class="space-y-0">
        <li class="p-3 odd:bg-white even:bg-gray-50 border-b">Row 1 (odd:bg-white)</li>
        <li class="p-3 odd:bg-white even:bg-gray-50 border-b">Row 2 (even:bg-gray-50)</li>
        <li class="p-3 odd:bg-white even:bg-gray-50 border-b">Row 3 (odd:bg-white)</li>
        <li class="p-3 odd:bg-white even:bg-gray-50 border-b">Row 4 (even:bg-gray-50)</li>
        <li class="p-3 odd:bg-white even:bg-gray-50 border-b">Row 5 (odd:bg-white)</li>
        <li class="p-3 odd:bg-white even:bg-gray-50">Row 6 (even:bg-gray-50)</li>
      </ul>
    </div>

    <!-- Table example -->
    <div class="bg-white rounded-lg shadow p-6">
      <h3 class="font-semibold mb-4">Table zebra stripes</h3>
      <table class="w-full">
        <thead>
          <tr class="bg-gray-100">
            <th class="p-3 text-left">Name</th>
            <th class="p-3 text-left">Email</th>
            <th class="p-3 text-left">Role</th>
          </tr>
        </thead>
        <tbody>
          <tr class="odd:bg-white even:bg-gray-50 hover:bg-blue-50 transition-colors">
            <td class="p-3">Alice</td>
            <td class="p-3">alice@example.com</td>
            <td class="p-3">Admin</td>
          </tr>
          <tr class="odd:bg-white even:bg-gray-50 hover:bg-blue-50 transition-colors">
            <td class="p-3">Bob</td>
            <td class="p-3">bob@example.com</td>
            <td class="p-3">Editor</td>
          </tr>
          <tr class="odd:bg-white even:bg-gray-50 hover:bg-blue-50 transition-colors">
            <td class="p-3">Charlie</td>
            <td class="p-3">charlie@example.com</td>
            <td class="p-3">User</td>
          </tr>
          <tr class="odd:bg-white even:bg-gray-50 hover:bg-blue-50 transition-colors">
            <td class="p-3">Diana</td>
            <td class="p-3">diana@example.com</td>
            <td class="p-3">User</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</body>
</html>
▶ Try it Yourself

Traditional CSS vs Tailwind The traditional approach requires writing selectors like li:first-child { ... } or tr:nth-child(even) { ... }, while Tailwind uses prefixes like first:bg-blue-50 and even:bg-gray-50 to declare them directly.

group-hover and peer

group-hover: and peer are used to handle interactive states between parent-child or sibling elements.

Example

HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>group-hover and peer Demo</title>
  <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="min-h-screen bg-gray-100 p-8">
  <div class="max-w-4xl mx-auto space-y-8">
    <!-- group-hover parent-child hover -->
    <div class="bg-white rounded-lg shadow p-6">
      <h3 class="font-semibold mb-4">group-hover parent-child hover</h3>
      <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
        <div class="group bg-gray-50 rounded-lg p-4 cursor-pointer transition-all hover:shadow-lg">
          <div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center mb-3 group-hover:bg-blue-500 transition-colors">
            <span class="text-blue-500 group-hover:text-white transition-colors">🎨</span>
          </div>
          <h4 class="font-semibold group-hover:text-blue-600 transition-colors">Design Tools</h4>
          <p class="text-gray-600 text-sm mt-1 group-hover:text-gray-900 transition-colors">Hover over the parent to see the effect</p>
        </div>
        <div class="group bg-gray-50 rounded-lg p-4 cursor-pointer transition-all hover:shadow-lg">
          <div class="w-12 h-12 bg-green-100 rounded-lg flex items-center justify-center mb-3 group-hover:bg-green-500 transition-colors">
            <span class="text-green-500 group-hover:text-white transition-colors">⚡</span>
          </div>
          <h4 class="font-semibold group-hover:text-green-600 transition-colors">Performance</h4>
          <p class="text-gray-600 text-sm mt-1 group-hover:text-gray-900 transition-colors">Hover over the parent to see the effect</p>
        </div>
        <div class="group bg-gray-50 rounded-lg p-4 cursor-pointer transition-all hover:shadow-lg">
          <div class="w-12 h-12 bg-purple-100 rounded-lg flex items-center justify-center mb-3 group-hover:bg-purple-500 transition-colors">
            <span class="text-purple-500 group-hover:text-white transition-colors">🔒</span>
          </div>
          <h4 class="font-semibold group-hover:text-purple-600 transition-colors">Security</h4>
          <p class="text-gray-600 text-sm mt-1 group-hover:text-gray-900 transition-colors">Hover over the parent to see the effect</p>
        </div>
      </div>
    </div>

    <!-- group combined usage -->
    <div class="bg-white rounded-lg shadow p-6">
      <h3 class="font-semibold mb-4">group combined usage</h3>
      <div class="space-y-4">
        <div class="group flex items-center gap-4 p-4 bg-gray-50 rounded-lg hover:bg-blue-50 transition-colors cursor-pointer">
          <div class="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center group-hover:bg-blue-500 transition-colors">
            <svg class="w-5 h-5 text-blue-500 group-hover:text-white transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
            </svg>
          </div>
          <div class="flex-1">
            <h4 class="font-medium group-hover:text-blue-600 transition-colors">Menu item 1</h4>
            <p class="text-sm text-gray-500 group-hover:text-blue-400 transition-colors">Description text</p>
          </div>
          <svg class="w-5 h-5 text-gray-400 group-hover:text-blue-500 group-hover:translate-x-1 transition-all" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
          </svg>
        </div>

        <div class="group flex items-center gap-4 p-4 bg-gray-50 rounded-lg hover:bg-green-50 transition-colors cursor-pointer">
          <div class="w-10 h-10 bg-green-100 rounded-full flex items-center justify-center group-hover:bg-green-500 transition-colors">
            <svg class="w-5 h-5 text-green-500 group-hover:text-white transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
            </svg>
          </div>
          <div class="flex-1">
            <h4 class="font-medium group-hover:text-green-600 transition-colors">Menu item 2</h4>
            <p class="text-sm text-gray-500 group-hover:text-green-400 transition-colors">Description text</p>
          </div>
          <svg class="w-5 h-5 text-gray-400 group-hover:text-green-500 group-hover:translate-x-1 transition-all" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
          </svg>
        </div>
      </div>
    </div>

    <!-- peer sibling elements -->
    <div class="bg-white rounded-lg shadow p-6">
      <h3 class="font-semibold mb-4">peer sibling elements</h3>
      <div class="space-y-4">
        <div>
          <input type="email" id="email" placeholder=" "
                 class="peer w-full p-3 pt-5 border border-gray-300 rounded-lg focus:border-blue-500 focus:ring-2 focus:ring-blue-500/50 focus:outline-none transition-all">
          <label for="email"
                 class="absolute left-3 top-1 text-gray-400 text-sm transition-all peer-placeholder-shown:top-3.5 peer-placeholder-shown:text-base peer-focus:top-1 peer-focus:text-sm peer-focus:text-blue-500">
            Email address
          </label>
        </div>
        <div class="relative">
          <input type="password" id="password" placeholder=" "
                 class="peer w-full p-3 pt-5 border border-gray-300 rounded-lg focus:border-purple-500 focus:ring-2 focus:ring-purple-500/50 focus:outline-none transition-all">
          <label for="password"
                 class="absolute left-3 top-1 text-gray-400 text-sm transition-all peer-placeholder-shown:top-3.5 peer-placeholder-shown:text-base peer-focus:top-1 peer-focus:text-sm peer-focus:text-purple-500">
            Password
          </label>
        </div>
        <div class="flex items-center gap-3">
          <input type="checkbox" id="agree" class="peer sr-only">
          <label for="agree"
                 class="w-5 h-5 border-2 border-gray-300 rounded peer-checked:bg-blue-500 peer-checked:border-blue-500 cursor-pointer transition-colors flex items-center justify-center">
            <svg class="w-3 h-3 text-white opacity-0 peer-checked:opacity-100" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M5 13l4 4L19 7"></path>
            </svg>
          </label>
          <span class="text-gray-700">I agree to the terms of service</span>
        </div>
      </div>
    </div>
  </div>
</body>
</html>
▶ Try it Yourself

Tip: Add the group class to a parent element, then use the group-hover: prefix on child elements. Add the peer class to a preceding sibling element, then use prefixes like peer-focus: on subsequent sibling elements.

Cursor Styles (cursor-*)

cursor-* classes control the cursor style when the mouse hovers over an element.

Class CSS Value Description
cursor-auto cursor: auto Auto (default)
cursor-default cursor: default Default arrow
cursor-pointer cursor: pointer Hand pointer
cursor-wait cursor: wait Wait
cursor-text cursor: text Text selection
cursor-move cursor: move Move
cursor-not-allowed cursor: not-allowed Not allowed
cursor-grab cursor: grab Grab
cursor-grabbing cursor: grabbing Grabbing
cursor-crosshair cursor: crosshair Crosshair
cursor-zoom-in cursor: zoom-in Zoom in
cursor-zoom-out cursor: zoom-out Zoom out

Example

HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Cursor Styles Demo</title>
  <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="min-h-screen bg-gray-100 p-8">
  <div class="max-w-4xl mx-auto">
    <div class="bg-white rounded-lg shadow p-6">
      <h3 class="font-semibold mb-4">Cursor Styles</h3>
      <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
        <div class="p-4 bg-gray-50 rounded-lg text-center cursor-auto">
          <div class="text-2xl mb-2">👆</div>
          <div class="text-sm">cursor-auto</div>
        </div>
        <div class="p-4 bg-gray-50 rounded-lg text-center cursor-default">
          <div class="text-2xl mb-2">👆</div>
          <div class="text-sm">cursor-default</div>
        </div>
        <div class="p-4 bg-gray-50 rounded-lg text-center cursor-pointer">
          <div class="text-2xl mb-2">👆</div>
          <div class="text-sm">cursor-pointer</div>
        </div>
        <div class="p-4 bg-gray-50 rounded-lg text-center cursor-wait">
          <div class="text-2xl mb-2">⏳</div>
          <div class="text-sm">cursor-wait</div>
        </div>
        <div class="p-4 bg-gray-50 rounded-lg text-center cursor-text">
          <div class="text-2xl mb-2">📝</div>
          <div class="text-sm">cursor-text</div>
        </div>
        <div class="p-4 bg-gray-50 rounded-lg text-center cursor-move">
          <div class="text-2xl mb-2">✋</div>
          <div class="text-sm">cursor-move</div>
        </div>
        <div class="p-4 bg-gray-50 rounded-lg text-center cursor-not-allowed">
          <div class="text-2xl mb-2">🚫</div>
          <div class="text-sm">cursor-not-allowed</div>
        </div>
        <div class="p-4 bg-gray-50 rounded-lg text-center cursor-grab">
          <div class="text-2xl mb-2">✊</div>
          <div class="text-sm">cursor-grab</div>
        </div>
        <div class="p-4 bg-gray-50 rounded-lg text-center cursor-grabbing">
          <div class="text-2xl mb-2">✊</div>
          <div class="text-sm">cursor-grabbing</div>
        </div>
        <div class="p-4 bg-gray-50 rounded-lg text-center cursor-crosshair">
          <div class="text-2xl mb-2">➕</div>
          <div class="text-sm">cursor-crosshair</div>
        </div>
        <div class="p-4 bg-gray-50 rounded-lg text-center cursor-zoom-in">
          <div class="text-2xl mb-2">🔍</div>
          <div class="text-sm">cursor-zoom-in</div>
        </div>
        <div class="p-4 bg-gray-50 rounded-lg text-center cursor-zoom-out">
          <div class="text-2xl mb-2">🔍</div>
          <div class="text-sm">cursor-zoom-out</div>
        </div>
      </div>
    </div>
  </div>
</body>
</html>
▶ Try it Yourself

Traditional CSS vs Tailwind The traditional approach requires writing CSS properties like cursor: pointer, while Tailwind uses class names like cursor-pointer to declare them directly.

Selection Control (select-*)

select-* classes control whether users can select text.

Class CSS Value Description
select-none user-select: none Not selectable
select-text user-select: text Selectable (default)
select-all user-select: all Click to select all
select-auto user-select: auto Auto

Example

HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Selection Control Demo</title>
  <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="min-h-screen bg-gray-100 p-8">
  <div class="max-w-4xl mx-auto space-y-8">
    <div class="bg-white rounded-lg shadow p-6">
      <h3 class="font-semibold mb-4">Selection Control</h3>
      <div class="space-y-4">
        <div class="p-4 bg-gray-50 rounded-lg">
          <p class="text-sm text-gray-500 mb-2">select-none: Not selectable</p>
          <p class="select-none">This text cannot be selected, suitable for buttons, labels, and other interactive elements.</p>
        </div>
        <div class="p-4 bg-gray-50 rounded-lg">
          <p class="text-sm text-gray-500 mb-2">select-text: Selectable (default)</p>
          <p class="select-text">This text can be selected, suitable for body content.</p>
        </div>
        <div class="p-4 bg-gray-50 rounded-lg">
          <p class="text-sm text-gray-500 mb-2">select-all: Click to select all</p>
          <p class="select-all bg-blue-50 p-2 rounded font-mono">const apiKey = "abc123";</p>
        </div>
      </div>
    </div>

    <!-- Practical application -->
    <div class="bg-white rounded-lg shadow p-6">
      <h3 class="font-semibold mb-4">Practical Application</h3>
      <div class="space-y-4">
        <!-- Code block -->
        <div class="bg-gray-900 text-green-400 p-4 rounded-lg font-mono text-sm select-all">
          npm install tailwindcss
        </div>
        <!-- Button text not selectable -->
        <div class="flex gap-4">
          <button class="px-6 py-3 bg-blue-500 text-white rounded-lg select-none cursor-pointer hover:bg-blue-600">
            Button text not selectable
          </button>
          <button class="px-6 py-3 bg-green-500 text-white rounded-lg select-none cursor-pointer hover:bg-green-600">
            Confirm Submit
          </button>
        </div>
      </div>
    </div>
  </div>
</body>
</html>
▶ Try it Yourself

Tip: select-none is commonly used for buttons, labels, and other elements where text selection is unnecessary. select-all is commonly used for code blocks and command lines where one-click copying is desired.

Resize Control (resize-*)

resize-* classes control whether an element can be resized.

Class CSS Value Description
resize-none resize: none Not resizable
resize-y resize: vertical Vertical resize only
resize-x resize: horizontal Horizontal resize only
resize resize: both Resizable

Example

HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Resize Control Demo</title>
  <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="min-h-screen bg-gray-100 p-8">
  <div class="max-w-4xl mx-auto space-y-8">
    <div class="bg-white rounded-lg shadow p-6">
      <h3 class="font-semibold mb-4">Resize Control</h3>
      <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
        <div>
          <p class="text-sm text-gray-500 mb-2">resize-none: Not resizable</p>
          <textarea class="w-full p-3 border border-gray-300 rounded-lg resize-none h-24 focus:outline-none focus:border-blue-500"
                    placeholder="Non-resizable textarea"></textarea>
        </div>
        <div>
          <p class="text-sm text-gray-500 mb-2">resize-y: Vertical resize only</p>
          <textarea class="w-full p-3 border border-gray-300 rounded-lg resize-y h-24 focus:outline-none focus:border-blue-500"
                    placeholder="Vertically resizable textarea"></textarea>
        </div>
        <div>
          <p class="text-sm text-gray-500 mb-2">resize-x: Horizontal resize only</p>
          <textarea class="w-full p-3 border border-gray-300 rounded-lg resize-x h-24 focus:outline-none focus:border-blue-500"
                    placeholder="Horizontally resizable textarea"></textarea>
        </div>
        <div>
          <p class="text-sm text-gray-500 mb-2">resize: Resizable</p>
          <textarea class="w-full p-3 border border-gray-300 rounded-lg resize h-24 focus:outline-none focus:border-blue-500"
                    placeholder="Freely resizable textarea"></textarea>
        </div>
      </div>
    </div>
  </div>
</body>
</html>
▶ Try it Yourself

Traditional CSS vs Tailwind The traditional approach requires writing CSS properties like resize: vertical, while Tailwind uses class names like resize-y to declare them directly.

Scroll Behavior (scroll-*)

scroll-* classes control scroll behavior.

Class CSS Value Description
scroll-auto scroll-behavior: auto Auto (default)
scroll-smooth scroll-behavior: smooth Smooth scrolling
snap-start scroll-snap-align: start Snap to start
snap-center scroll-snap-align: center Snap to center
snap-end scroll-snap-align: end Snap to end
snap-none scroll-snap-type: none No snapping
snap-x scroll-snap-type: x mandatory Horizontal snapping
snap-y scroll-snap-type: y mandatory Vertical snapping

Example

HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Scroll Behavior Demo</title>
  <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="min-h-screen bg-gray-100 p-8">
  <div class="max-w-4xl mx-auto space-y-8">
    <!-- Smooth scrolling -->
    <div class="bg-white rounded-lg shadow p-6">
      <h3 class="font-semibold mb-4">Smooth Scrolling</h3>
      <div class="flex gap-4 mb-4">
        <a href="#section1" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600">Jump to Section 1</a>
        <a href="#section2" class="px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600">Jump to Section 2</a>
        <a href="#section3" class="px-4 py-2 bg-purple-500 text-white rounded-lg hover:bg-purple-600">Jump to Section 3</a>
      </div>
      <div class="h-48 overflow-y-auto scroll-smooth border rounded-lg">
        <div id="section1" class="p-4 bg-blue-50 h-48 flex items-center justify-center">
          <h4 class="text-xl font-bold text-blue-700">Section 1</h4>
        </div>
        <div id="section2" class="p-4 bg-green-50 h-48 flex items-center justify-center">
          <h4 class="text-xl font-bold text-green-700">Section 2</h4>
        </div>
        <div id="section3" class="p-4 bg-purple-50 h-48 flex items-center justify-center">
          <h4 class="text-xl font-bold text-purple-700">Section 3</h4>
        </div>
      </div>
    </div>

    <!-- Scroll snapping -->
    <div class="bg-white rounded-lg shadow p-6">
      <h3 class="font-semibold mb-4">Scroll Snapping</h3>
      <div class="flex overflow-x-auto snap-x snap-mandatory gap-4 pb-4">
        <div class="snap-center flex-shrink-0 w-64 h-40 bg-blue-100 rounded-lg flex items-center justify-center">
          <span class="text-blue-700 font-semibold">Card 1</span>
        </div>
        <div class="snap-center flex-shrink-0 w-64 h-40 bg-green-100 rounded-lg flex items-center justify-center">
          <span class="text-green-700 font-semibold">Card 2</span>
        </div>
        <div class="snap-center flex-shrink-0 w-64 h-40 bg-purple-100 rounded-lg flex items-center justify-center">
          <span class="text-purple-700 font-semibold">Card 3</span>
        </div>
        <div class="snap-center flex-shrink-0 w-64 h-40 bg-orange-100 rounded-lg flex items-center justify-center">
          <span class="text-orange-700 font-semibold">Card 4</span>
        </div>
        <div class="snap-center flex-shrink-0 w-64 h-40 bg-red-100 rounded-lg flex items-center justify-center">
          <span class="text-red-700 font-semibold">Card 5</span>
        </div>
      </div>
    </div>

    <!-- Hidden scrollbar -->
    <div class="bg-white rounded-lg shadow p-6">
      <h3 class="font-semibold mb-4">Hidden Scrollbar</h3>
      <div class="flex overflow-x-auto scrollbar-hide gap-4 pb-4">
        <div class="flex-shrink-0 w-48 h-32 bg-gradient-to-br from-blue-400 to-blue-600 rounded-lg flex items-center justify-center text-white">
          Hidden scrollbar
        </div>
        <div class="flex-shrink-0 w-48 h-32 bg-gradient-to-br from-green-400 to-green-600 rounded-lg flex items-center justify-center text-white">
          Using scrollbar-hide
        </div>
        <div class="flex-shrink-0 w-48 h-32 bg-gradient-to-br from-purple-400 to-purple-600 rounded-lg flex items-center justify-center text-white">
          Horizontally scrollable
        </div>
        <div class="flex-shrink-0 w-48 h-32 bg-gradient-to-br from-orange-400 to-orange-600 rounded-lg flex items-center justify-center text-white">
          Keep scrolling
        </div>
      </div>
    </div>
  </div>
</body>
</html>
▶ Try it Yourself

Tip: Adding scroll-smooth to the <html> element enables smooth scrolling for the entire page. Combining snap-x snap-mandatory with snap-center creates a carousel effect.

❓ FAQ

Q What's the difference between group-hover and hover?
A hover: is the element's own hover state. group-hover: applies styles to child elements when the parent element (with the group class) is hovered. It's ideal for coordinated hover effects between parent and child elements.
Q How does peer work?
A Add the peer class to an element, then use prefixes like peer-focus: and peer-valid: to style subsequent sibling elements. Commonly used for floating labels and form validation.
Q Why isn't disabled:opacity-50 working?
A Make sure the disabled: prefix is used with the actual disabled attribute. Tailwind generates the .disabled\:opacity-50:disabled selector, so the element must have the disabled attribute for it to take effect.
Q How do I implement scroll snapping?
A Add snap-x snap-mandatory (horizontal) or snap-y snap-mandatory (vertical) to the scroll container, and add snap-center or snap-start to child elements.

📖 Summary

📝 Exercises

  1. ⭐ Create a button component using hover:, focus:, active:, and disabled: state prefixes to implement full interactive state styling

  2. ⭐⭐ Create a navigation menu using group and group-hover: to implement coordinated hover changes for icons, text, and arrows

  3. ⭐⭐⭐ Create a form using peer and peer-focus: to implement floating label effects, combined with focus: and invalid: states for form validation styling

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏