twin.macro icon indicating copy to clipboard operation
twin.macro copied to clipboard

Issue with "peer" usage inside "styled"

Open Shaker-Pelcro opened this issue 1 year ago • 4 comments

I'm trying to replicate this toggle checkbox with emotion and twin.macro using "styled" ...

<div class="relative">
    <input type="checkbox" class="peer appearance-none cursor-pointer border border-gray-300 rounded-full
    checked:border-gray-900 w-12 h-6"/>

    <span class="peer-checked:left-7 peer-checked:bg-gray-900 transition-all duration-200 pointer-events-none w-4 h-4 block absolute top-1 left-1
    rounded-full bg-gray-300"></span>
</div>

And here's my code ...

import tw, { styled } from 'twin.macro';

const Div = styled.div<any>`
  ${tw`relative`}

  input {
    ${tw`appearance-none cursor-pointer border border-gray-300 rounded-full checked:border-gray-900 w-6 h-3`}
  }

  span {
    ${tw`peer-checked:left-7 peer-checked:bg-gray-900 transition-all duration-200 pointer-events-none w-2 h-2 block absolute top-1 left-1 rounded-full bg-gray-300`}
  }
`;

type Iprops = {
  id: string;
  name: string;
  label: string;
  clickHandler: (e: any) => void;
  isChecked: boolean;
};

const InputCheckboxToggle: React.FC<Iprops> = ({
  id,
  name,
  label,
  clickHandler,
  isChecked,
}) => {
    <Div>
      <input
        type="checkbox"
        className="peer"
        id={id}
        name={name}
        onChange={clickHandler}
        checked={isChecked}
      />
      <span></span>
    </Div>
  );
};

export default InputCheckboxToggle;

However, the "peer-checked" here doesn't work, because when I inspected the HTML, I found out that the generated CSS was wrong ...

HTML ...

<div class="css-f1j2xp">
    <input type="checkbox" class="peer" id="remember-toggle" name="remember" />
    <span></span>
</div>

CSS ...

<style data-emotion="css" data-s="">
.peer:checked~.css-f1j2xp span{left:70px;--tw-bg-opacity:1;background-color:rgba(17, 24, 39, var(--tw-bg-opacity));}
</style>

As you can see, instead of ".peer:checked ~ span" I get ".peer:checked ~ .css-f1j2xp span".

So, instead of targeting the <span> that is sibling to ".peer", it targets the parent <div> that is sibling to ".peer" Is that normal?

Shaker-Pelcro avatar Aug 26 '22 13:08 Shaker-Pelcro

Looks about right to me - emotion adds those hashed selector styles - try adding the peer classes directly on the element within the tw prop rather than within a separate container component.

ben-rogerson avatar Sep 10 '22 00:09 ben-rogerson

@ben-rogerson I'm not really sure what you mean.

Shaker-Pelcro avatar Sep 10 '22 13:09 Shaker-Pelcro

I believe Ben is referring to a solution that looks like this:

const Div = styled.div<any>`
  ${tw`relative`}
`;

...

const InputCheckboxToggle: React.FC<Iprops> = ({
  id,
  name,
  label,
  clickHandler,
  isChecked,
}) => {
    <Div>
      <input
        type="checkbox"
        // Substituting className prop for tw prop
        tw="peer appearance-none cursor-pointer border border-gray-300 rounded-full checked:border-gray-900 w-6 h-3"
        id={id}
        name={name}
        onChange={clickHandler}
        checked={isChecked}
      />
      <span tw="peer-checked:left-7 peer-checked:bg-gray-900 transition-all duration-200 pointer-events-none w-2 h-2 block absolute top-1 left-1 rounded-full bg-gray-300"></span>
    </Div>
  );
};

robmisasi avatar Sep 21 '22 16:09 robmisasi

@robmisasi Got it, will give it a try.

Shaker-Pelcro avatar Sep 21 '22 16:09 Shaker-Pelcro