Tip 13/72

Use of `has` Variant

Use `has-{modifier}` to style an element based on the state of its descendants.

TailwindCSS’s has-{modifier} variant is a game-changer for scenarios where you want to apply styles to a parent element based on the state of its child elements. Here are some practical use cases demonstrating how to leverage this feature effectively.

Disabled input

Make a parent container visually indicate when its child input field is disabled.

<div class="space-y-2.5 has-disabled:opacity-50 has-disabled:pointer-events-none">
    <label 
        for="email" 
        class="text-gray-950 text-sm dark:text-white"
    >
        Your Email
    </label>
    <input 
        disabled 
        type="email" 
        id="email" 
        class="block outline-hidden w-full px-3 h-9 text-sm rounded-lg border shadow-sm shadow-gray-950/5 focus:ring-2 focus:ring-primary-600 dark:bg-gray-800/25 dark:border-gray-800" 
        placeholder="Enter your email"
    />
</div>

Input validation

Display an error message dynamically when an invalid value is detected.

Please enter a valid email
Type an invalid email and click away to see the error message.
<div class="space-y-2.5 [--error-display:none] has-[:user-invalid]:[--error-display:block]">
    <label 
        for="email" 
        class="text-gray-950 text-sm dark:text-white"
    >
        Your Email
    </label>
    <input 
        type="email" 
        id="email" 
        class="block outline-hidden w-full px-3 h-9 text-sm rounded-lg border shadow-sm shadow-gray-950/5 focus:ring-2 focus:ring-primary-600 dark:bg-gray-800/25 dark:border-gray-800" 
        placeholder="Enter your email" 
    />
    <div class="[display:var(--error-display)] text-sm text-danger-600 dark:text-danger-400">Please enter a valid email</div>
</div>

Radio Cards

Highlight a card-like UI when its associated radio input is checked.

<form>
    <label class="block space-y-2.5 px-4 rounded-2xl border border-gray-200 shadow-sm shadow-gray-950/5 py-3 hover:bg-gray-50 [--icon-display:none] has-checked:border-transparent has-checked:[--icon-display:block] has-checked:outline has-checked:outline-2 has-checked:outline-indigo-600 dark:hover:bg-gray-800/50 dark:border-gray-800" for="personal2">
        <input type="radio" id="personal2" name="plan" hidden />
        <div class="flex justify-between">
            <p class="text-sm text-gray-950 font-medium dark:text-white">4-Core CPU</p>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-5 text-primary-600 [display:var(--icon-display)]">
                <path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z" clip-rule="evenodd" />
            </svg>
        </div>
        <p class="mt-1 text-gray-500 text-sm">For personal projects</p>
        <p class="mt-4 text-gray-600 text-sm dark:text-gray-400">$9.99</p>
    </label>

    <label class="block space-y-2.5 px-4 rounded-2xl border border-gray-200 shadow-sm shadow-gray-950/5 py-3 hover:bg-gray-50 [--icon-display:none] has-checked:border-transparent has-checked:[--icon-display:block] has-checked:outline has-checked:outline-2 has-checked:outline-indigo-600 dark:hover:bg-gray-800/50 dark:border-gray-800" for="business2">
        <input type="radio" id="business2" name="plan" hidden />
        <div class="flex justify-between">
            <p class="text-sm text-gray-950 font-medium dark:text-white">6-Core CPU</p>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-5 text-primary-600 [display:var(--icon-display)]">
                <path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z" clip-rule="evenodd" />
            </svg>
        </div>
        <p class="mt-1 text-gray-500 text-sm">For startups and small businesses</p>
        <p class="mt-4 text-gray-600 text-sm dark:text-gray-400">$39.99</p>
    </label>
</form>

Subscribe form

Apply a focus effect on the container when the input is focused or focus-visible.

<div class="grid grid-cols-[1fr_auto] items-center rounded-full w-full max-w-sm pr-1 text-sm border border-gray-200 shadow-sm shadow-gray-950/5 has-[input:focus]:ring-2 has-[input:focus]:ring-primary-600 has-[input:focus]:border-transparent dark:bg-gray-800/25 dark:border-gray-800">
    <input 
        type="email" 
        class="h-10 pl-4 text-sm bg-transparent outline-hidden peer" 
        placeholder="Enter your email" 
        required
    />
    <button class="h-8 px-3.5 rounded-full flex items-center justify-center bg-indigo-600 text-white hover:brightness-90 transition-[filter] duration-300">Subscribe</button>
</div>

Each example demonstrates the has-{modifier} variant’s utility for dynamic state styling, simplifying parent-child state interactions.