Tailwind Group Hover Explained: Cards, Nested Groups & Fixes

Tailwind group-hover illustrated by a card whose title and styling highlight together under the cursor.
Spread the love

Tailwind group-hover lets a child element react when its parent is hovered. Add group to the parent and a class such as group-hover:text-blue-700 to the child. The parent must contain the child in the HTML; no JavaScript is needed.

This is useful for cards, navigation items and buttons where several pieces should respond together. We’ll start with a small example, build a project card, then cover nested groups, keyboard focus and the common reasons group-hover seems not to work. The setup below uses Tailwind CSS 4.

Step 1: Set up a small Tailwind project

If your project already compiles Tailwind, you can use the HTML examples directly. For a fresh local experiment, create an empty folder and run:

npm init -y
npm install tailwindcss@4 @tailwindcss/cli@4

Create input.css in that folder with the following line:

@import "tailwindcss";

Next, create index.html with a viewport meta tag and a link to the generated CSS. Put the examples from this guide inside the main element.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Tailwind group-hover examples</title>
  <link rel="stylesheet" href="./output.css">
</head>
<body class="bg-slate-100 text-slate-900">
  <main class="mx-auto max-w-2xl space-y-8 p-6">
    <!-- Put an example here. -->
  </main>
</body>
</html>

Start the compiler from the same folder. It will rebuild the output when you change the HTML:

npx @tailwindcss/cli -i ./input.css -o ./output.css --watch

Open index.html in your browser after the first build finishes. Keep the compiler running while you experiment. This setup uses Tailwind 4’s CLI package; older Tailwind 3 projects have different setup instructions. The basic group and group-hover pattern also exists in Tailwind 3.

Step 2: Understand parent and child states

Here is the smallest useful example. Hovering anywhere inside the padded parent changes the child text colour:

<div class="group rounded-lg bg-white p-6">
  <p class="text-slate-700 group-hover:text-blue-700">
    Hover over this card to change my colour.
  </p>
</div>

group marks the parent. It doesn’t add any visible styling by itself. The child’s group-hover: variant tells Tailwind to apply that utility when an ancestor with group is hovered. By comparison, hover:text-blue-700 responds to hovering the element carrying that class.

This first example is only a visual demonstration. If the card performs an action or opens another page, use a real button or link, as we’ll do next. Don’t make a plain div behave like a link just to get a hover effect.

Example: A project card with a title and arrow

Let’s make the whole card a link. Its title changes colour and its arrow moves slightly when the card is hovered. We also mirror those states for keyboard focus.

<a href="/projects/website-redesign"
  class="group block rounded-xl border border-slate-300 bg-white p-6
         hover:border-blue-600 focus-visible:outline-2
         focus-visible:outline-offset-4 focus-visible:outline-blue-600">
  <h2 class="text-xl font-semibold text-slate-900
             group-hover:text-blue-700 group-focus-visible:text-blue-700">
    Website redesign
  </h2>
  <p class="mt-2 text-slate-600">View the brief, timeline and project files.</p>
  <span class="mt-4 inline-flex items-center gap-2 font-medium text-blue-700">
    View project
    <span aria-hidden="true"
      class="inline-block motion-safe:transition-transform
             group-hover:translate-x-1 group-focus-visible:translate-x-1
             motion-reduce:transform-none">→</span>
  </span>
</a>

Replace the sample href with a real project URL. The anchor wraps all the content, so the whole card is clickable. Avoid putting a second link or button inside that anchor; if you need several actions, use a non-interactive card container with separate controls.

The title uses group-focus-visible: as well as group-hover:. That means a keyboard user receives the same visual cue. The outline belongs to the link itself, while the arrow is hidden from assistive technology because it repeats the link’s meaning.

The arrow transition uses motion-safe:. We also remove the transform when reduced motion is requested, so the movement is optional rather than necessary to understand the card.

Example: Use named groups in nested layouts

A generic group can become confusing when groups sit inside other groups. Name them so each child responds to the intended ancestor. Here the outer card gets its own name, and the button inside gets another:

<article class="group/card rounded-xl border border-slate-300 bg-white p-6">
  <h2 class="font-semibold group-hover/card:text-blue-700">Project files</h2>
  <p class="mt-2 text-slate-600">Manage the files used in this project.</p>
  <button type="button"
    class="group/action mt-4 inline-flex min-h-11 items-center gap-2
           rounded-lg border border-slate-400 px-4
           focus-visible:outline-2 focus-visible:outline-offset-2">
    <span>Upload a file</span>
    <span aria-hidden="true"
      class="group-hover/action:text-blue-700
             group-focus-visible/action:text-blue-700">↑</span>
  </button>
</article>

Hovering the card changes its heading through group-hover/card:. The arrow responds specifically to group/action, so hovering elsewhere on the card does not activate the arrow’s hover style. This is a styling example; connect the upload button to your own file picker before using it as a working control.

Example: React when a field inside a group is focused

Use group-focus-within: when the focus belongs to a descendant, such as an input, rather than to the wrapper itself. This works well for a label and field that should share a visual state:

<label class="group block">
  <span class="font-medium text-slate-700 group-focus-within:text-blue-700">
    Project name
  </span>
  <input type="text" name="project-name" autocomplete="off"
    class="mt-2 block min-h-11 w-full rounded-lg border border-slate-400
           bg-white px-3 focus:border-blue-700 focus:outline-2
           focus:outline-offset-2 focus:outline-blue-700">
</label>

The label contains the input, so it provides an accessible name without a separate for attribute. The label text changes when the input receives focus. Use group-focus-visible: for a focusable parent like the earlier link, and group-focus-within: when focus may be anywhere inside the parent.

Why is Tailwind group-hover not working?

The parent is missing group: check that the child sits inside an element carrying the exact group name used by the variant. A sibling is not an ancestor. For sibling relationships, look at Tailwind’s peer variants instead.

The class is assembled dynamically: Tailwind needs to detect complete utility names in your source. Avoid creating a class by joining "group-hover:text-" with a colour name at runtime. Map choices to complete strings:

const titleStyles = {
  blue: "group-hover:text-blue-700 group-focus-visible:text-blue-700",
  emerald: "group-hover:text-emerald-700 group-focus-visible:text-emerald-700"
};
// Select one complete string in your template or rendering code.
const titleClass = titleStyles.blue;

The generated CSS is stale: check that the compiler is running, your HTML is included in source detection, and the page loads the expected output.css. After changing the HTML, wait for compilation and reload the example.

The effect works with a mouse but not on a phone: Tailwind 4’s hover styles are scoped to devices that support hovering. Keep essential text and controls visible without hover, and use click or focus states when interaction requires them. Don’t hide the only available action behind a hover effect.

Several nested groups trigger the same effect: use names such as group/card and group/action to make the relationship explicit.

Taking the pattern further

Start with one clear state change, then add detail only where it helps the interface. Check the card with a mouse, the Tab key and a touch device. The content should remain understandable before any hover effect runs.

For the full variant reference, see Tailwind’s state documentation and the Tailwind CLI setup guide. You can combine these card states with our Tailwind gradient examples, or explore the Tailwind templates in our collection for a larger starting point.

Leave a Reply

Your email address will not be published. Required fields are marked *

Sign up for our Newsletter

Get the latest updates, free stuff, and more delivered right to your inbox.