twin.macro
twin.macro copied to clipboard
Issue with "peer" usage inside "styled"
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?
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 I'm not really sure what you mean.
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 Got it, will give it a try.
Hope you got this sorted 👍