Anurag Deore
Anurag Deore

Reputation: 85

Tailwind CSS: change parent label style when a child checkbox is checked

I want to add a border to my checkbox container when the checkbox is checked.

Currently, I have this:

<label
  htmlFor="choose-me"
  className=
    'flex w-fit items-center justify-evely p-3 border-2 border-grey-4 text-grey-8 
     peer-checked:bg-light-indigo peer-checked:text-medium-indigo peer-checked:border-medium-indigo'>
  <input type="checkbox" id="choose-me" className="peer mr-3 " />
  Check me
</label>

I want something like the below:

checkbox - checked and unchecked

However, I am unable to change the label styles when I put my input inside the label.

Upvotes: 8

Views: 6262

Answers (4)

Parmeshwar Yadav
Parmeshwar Yadav

Reputation: 1

    STEP 1: Update checkbox-label in the label tag
    <label className="checkbox-label">
    
    STEP 2: add below code in index.css file
@tailwind base;
@tailwind components;
@tailwind utilities;
    
    @layer components {
      .checkbox-label:has(input:checked) {
        @apply text-gray-700;
      }
    }

Upvotes: 0

Ed Lucas
Ed Lucas

Reputation: 7305

A CSS-only solution would be to use the :has pseudo-selector (note: not supported yet in Firefox). This would apply the classes only if the label wraps a checkbox which has been checked.

index.css

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer components {
  .checkbox-label:has(input:checked) {
    @apply border-blue-800 bg-blue-400 text-blue-800;
  }
}

Your JSX: (with the unused classes stripped out)

<label className="checkbox-label justify-evely border-grey-4 text-grey-8 flex w-fit items-center gap-x-2 border-2 p-3">
  <input type="checkbox" id="choose-me" />
  Check me
</label>

You can test it here: https://play.tailwindcss.com/zidP4zm2Pq

The list of browsers supporting :has: https://developer.mozilla.org/en-US/docs/Web/CSS/:has#browser_compatibility

Upvotes: 6

TristanZimmerman
TristanZimmerman

Reputation: 616

First off, it's important to remember that the peer functionality in tailwind doesn't target parents (which is what you currently have). You can only target elements that are next to one another.

Additionally, because of how the CSS :peer combinator works you can only target previous components, meaning the checkbox MUST come before the peer who's state you want to affect when the checkbox is checked.

<label
  htmlFor="choose-me"
  class=
    'flex w-fit items-center justify-evely p-3 text-grey-8 relative'
>
  <input type="checkbox" id="choose-me" class="peer mr-3 relative z-10" />
  <div class="absolute inset-0 border-2 border-grey-4 peer-checked:bg-indigo-200 peer-checked:text-indigo-800 peer-checked:border-indigo-800 peer-checked:block z-0"></div>
  <span class="relative z-10">Check me</span>
</label>

Here's an example that works using pure tailwind/css, assuming you don't want to handle the state in your react component, as per @Vikesir's comment (though that was my first thought as well and it's a good idea).

You'll notice I'm fudging in an empty div and using that to simulate the background and border changing. I also wrapped the label text in a span to make sure I could change it's z-index so that both the checkbox and the text were visible above the div that handles the state change.

EDIT:

Here is a version using a pseudo-element built off of the span holding the label text if you don't want the empty div in your code:

<label
  htmlFor="choose-me"
  class=
    'flex w-fit items-center justify-evely text-grey-8 relative'
>
  <input type="checkbox" id="choose-me" class="peer mr-3 absolute left-2.5 z-20" />
  <span class="relative z-10 inset-0 py-3 pr-3 pl-8 before:-z-10 before:content-[''] before:absolute before:inset-0 before:h-full before:w-full before:border-2 before:border-grey-4 peer-checked:before:bg-indigo-200 peer-checked:before:text-indigo-800 peer-checked:before:border-indigo-800 peer-checked:before:block">Check me</span>
</label>

Upvotes: 1

VIKESIR
VIKESIR

Reputation: 231

use useState for handle active state:

const [isactive,setActive] = useState(false)

in JSX:

<label
  onClick={()=>setActive(isactive?false:true)}
  className= {`${isactive?'active-style':'normal-style'} extra-style-classes`}
>
  <input type="checkbox" id="choose-me" className= {`${isactive?'active-style':'normal-style'} extra-style-classes`} />
  Check me
</label>

Upvotes: -1

Related Questions