24
loading...
This website collects cookies to deliver better user experience
Note: I've built this using the technologies I use the most: react
, typescript
and styled-components
. But the CSS can be applied to any frontend stack :)
import styled from "styled-components";
const Label = styled.label``;
const Input = styled.input``;
const Switch = styled.div``;
const ToggleSwitch = () => {
return (
<Label>
<span>Toggle is off</span>
<Input />
<Switch />
</Label>
);
};
<input>
. But we do want it to be of type="checkbox"
.<label>
to trigger the onChange
event, including our <span>
element.Note: It's important here to keep the input in the DOM by setting opacity: 0
and position: absolute
. Why?
opacity: 0
will hide it from the userposition: absolute
takes the element out of the normal doument flow.const Input = styled.input`
opacity: 0;
position: absolute;
`;
// Set type to be "checkbox"
<Input type="checkbox" />
<label>
component, it's wrapping everything, so I want it to be display: flex
to align the <span>
and <Switch />
vertically.gap
gives us a straight forward 10px gap between elements, and the cursor: pointer
gives the user visual feedback saying "Hey! 👋 you can click me!".<Switch />
element.const Label = styled.label`
display: flex;
align-items: center;
gap: 10px;
cursor: pointer;
`;
const Switch = styled.div`
width: 60px;
height: 32px;
background: #b3b3b3;
border-radius: 32px;
`
<Switch />
element. This will act as our switches "lever".const Switch = styled.div`
position: relative; /* <-- Add relative positioning */
width: 60px;
height: 32px;
background: #b3b3b3;
border-radius: 32px;
padding: 4px; /* <!-- Add padding
/* Add pseudo element */
&:before {
content: "";
position: absolute;
width: 28px;
height: 28px;
border-radius: 35px;
top: 50%;
left: 4px; /* <!-- Make up for padding
background: white;
transform: translate(0, -50%);
}
`;
const Switch = styled.div
variable declaration to be above the const Input = styled.input
variable. This is so we can reference the Switch
from within Input
.:checked
pseudo-class selector and the adjacent sibling combinator, we can make our switch turn green.const Input = styled.input`
display: none;
&:checked + ${Switch} {
background: green;
}
`;
:before
pseudo-element of the Switch
element:const Input = styled.input`
display: none;
&:checked + ${Switch} {
background: green;
&:before {
transform: translate(32px, -50%);
}
}
`;
transition: 300ms
to our Switch
and the Switch
:before
pseudo-elementconst Switch = styled.div`
position: relative;
width: 60px;
height: 28px;
background: #b3b3b3;
border-radius: 32px;
padding: 4px;
transition: 300ms all;
&:before {
transition: 300ms all;
content: "";
position: absolute;
width: 28px;
height: 28px;
border-radius: 35px;
top: 50%;
left: 4px;
background: white;
transform: translate(0, -50%);
}
`;
onChange
handler and useState
hook to allow us to store the value of the checked input and change the text depending on the value:const ToggleSwitch = () => {
const [checked, setChecked] = useState(false); // store value
const handleChange = (e: ChangeEvent<HTMLInputElement>) => setChecked(e.target.checked)
return (
<Label>
<span>Toggle is {checked ? 'on' : 'off'}</span>
<Input checked={checked} type="checkbox" onChange={handleChange} />
<Switch />
</Label>
);
};