I'm working on a Vue.js project and I'm trying to figure out how to replicate this toggle button from TailGrids:
Sadly, this kind of switch isn't among the examples that TailGrids provides.
I was able to reproduce the general style of the toggle switch with Tailwind, that's rather easy. I was also able to find working code for similar switches.
* { font-family: sans-serif; }
.switchbox label {
display: inline-block;
cursor: pointer;
position: relative;
width: 70px;
height: 20px;
border: 1px solid #c1c1c1;
border-radius: 3px;
}
.switchbox label span.on,
.switchbox label span.off {
position: absolute;
top: 0;
width: 50%;
line-height: 1.4;
font-size: 12px;
font-weight: 600;
height: 16px;
padding: 2px 0;
text-align: center;
}
.switchbox span.on {
color: black;
right: inherit;
left: 0;
border-bottom-right-radius: 2px;
border-top-right-radius: 2px;
}
.switchbox span.off {
right:0;
color: black;
border-bottom-right-radius: 2px;
border-top-right-radius: 2px;
}
.switchbox input {
display: none;
}
.switchbox input:checked + label:before {
color: black;
}
.switchbox input:checked + label:after {
color: #737373;
}
label{
position:relative;
}
.switchbox input + label:before {
transition:0.3s;
content:""; display:block;
width:50%;
height:20px;
position:absolute;
left:0;
top:0;
background: #a5a5a5; /* W3C */
}
.switchbox input:checked + label:before {
left:50%;
}
<div class="switchbox">
<input type="checkbox" id="switch" name="switch" />
<label for="switch" data-on="ON" data-off="OFF">
<span class="on">ON</span>
<span class="off">OFF</span>
</label>
</div>
The problem is that these switches always make the coloured background 50% of the width of the switch. That doesn't work for my project, as one label has to be roughly double the size of the other.
I understand that this implementation animates the label:before pseudo-element and moves its absolute position 50% to the right when the input is checked. But I don't have any clue how I could make this work with two texts of different lengths.
Any help is highly appreciated. I've been researching and fiddling around for roughly 2 hours, but it seems that I'm simply missing the required css expertise.
You can do this in pure CSS
Just by moving a background on each text...
body
{
background : lavender;
font-family : Geneva, sans-serif;
font-size : 16px;
padding : 1em;
}
label.switchbox
{
border : .4em solid white;
border-radius : .5em;
display : flex;
width : fit-content;
overflow : hidden;
cursor : pointer;
& > input[type="checkbox"]
{
display: none;
}
& > span
{
display : inline-block;
position : relative;
color : black;
padding : .3em .7em;
overflow : hidden;
}
& > span::after
{
content : "";
position : absolute;
top : 0;
left : -100%;
display : block;
width : 200%;
height : 100%;
z-index : -1;
transition : all 0.3s ease;
}
& > span:first-of-type::after
{
background : linear-gradient(to left, #a5a5a5 50%, white 50%);
}
& > span:last-of-type::after
{
background : linear-gradient(to left, white 50%, #a5a5a5 50%);
}
}
label.switchbox:has(input[type="checkbox"]:checked) > span::after
{
left: 0;
}
<label class="switchbox">
<input type="checkbox">
<span> Preview </span>
<span> Vue </span>
</label>
Other way in pure CSS;
(with no span::after
)
body
{
background : lavender;
font-family : Geneva, sans-serif;
font-size : 16px;
padding : 1em;
}
label.switchbox
{
border : .4em solid white;
border-radius : .5em;
display : flex;
width : fit-content;
overflow : hidden;
cursor : pointer;
--txtOn : white;
--txtOff : black;
--bgiOn : #253342; /* off black */
--bgiOff : white;
& > input[type="checkbox"]
{
display: none;
}
& > span
{
font-weight : bold;
padding : .3em .7em;
background-repeat : no-repeat;
background-size : 0% 100%;
transition : all .4s ease-in-out;
}
& > span:nth-of-type(1)
{
color : var(--txtOn);
background-color : var(--bgiOn);
background-image : linear-gradient(var(--bgiOff),var(--bgiOff));
}
& > span:nth-of-type(2)
{
color : var(--txtOff);
background-color : var(--bgiOff);
background-image : linear-gradient(var(--bgiOn),var(--bgiOn));
}
&:has(input[type="checkbox"]:checked)
{
& > span { background-size : 100% 100%; }
& > span:nth-of-type(1) { color : var(--txtOff); }
& > span:nth-of-type(2) { color : var(--txtOn); }
}
}
<label class="switchbox">
<input type="checkbox">
<span> Preview </span>
<span> Vue </span>
</label>