Appearance
Switch Component Code ​
Dependencies
This component requires:
- Vue 3 with Composition API
Full Component Code ​
vue
<script setup>
import { computed, ref, watch } from "vue";
const emit = defineEmits(["update:modelValue", "change"]);
const props = defineProps({
modelValue: {
type: Boolean,
},
value: {
type: Boolean,
},
disabled: {
type: Boolean,
default: false,
},
readonly: {
type: Boolean,
default: false,
},
variant: {
type: String,
default: "default", // default, success
},
size: {
type: String,
default: "xl", //xs, sm, md, lg, xl
},
label: {
type: String,
default: "",
},
labelPosition: {
type: String,
default: "right", //left, right
},
inset: {
type: Boolean,
default: false,
},
dotLabels: {
type: Object,
default: () => null,
// {
// true: 'On',
// false: 'Off',
// }
},
});
const internalValue = ref(props.modelValue || props.value || false);
watch(
[() => props.modelValue, () => props.value],
([newModelValue, newValue]) => {
internalValue.value = newModelValue ?? newValue;
}
);
const handleSwitchChange = () => {
if (props.readonly || props.disabled) return;
const newValue = !internalValue.value;
internalValue.value = newValue;
if (props.modelValue !== undefined) {
emit("update:modelValue", newValue);
}
emit("change", newValue);
};
</script>
<template>
<label
class="switch-container"
:class="{
disabled: disabled,
readonly: readonly,
checked: internalValue,
[size]: true,
inset: inset,
[variant]: true,
}"
@click.prevent="handleSwitchChange"
>
<slot name="left-label" v-if="label && labelPosition === 'left'">
{{ label }}
</slot>
<input
type="checkbox"
:checked="internalValue"
hidden
:readonly="readonly"
:disabled="disabled"
/>
<span class="switch-slider">
{{ dotLabels?.[internalValue] }}
<span class="switch-slider-dot"> </span>
</span>
<slot name="right-label" v-if="label && labelPosition === 'right'">
{{ label }}
</slot>
</label>
</template>
<style lang="scss" scoped>
.switch-container {
flex-shrink: 0;
display: inline-flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
*,
::before *,
::after * {
box-sizing: border-box;
}
input {
display: none;
&:checked + .switch-slider {
justify-content: flex-start;
.switch-slider-dot {
transform: translateX(-100%);
margin: 0;
}
}
}
.switch-slider {
position: relative;
min-width: 4rem;
height: 2rem;
border-radius: 1rem;
transition: all 0.3s ease;
display: flex;
align-items: center;
cursor: pointer;
justify-content: flex-end;
.switch-slider-dot {
position: absolute;
left: 0;
border-radius: 50%;
transition: all 0.3s ease;
transform: translateX(0);
display: flex;
align-items: center;
justify-content: center;
font-size: 0.5rem;
overflow: hidden;
}
}
// Variant styles
&.default {
.switch-slider {
background-color: grey;
color: #000;
}
input:checked + .switch-slider {
background-color: grey;
}
&:not(.inset) {
.switch-slider-dot {
background-color: white;
}
input:checked + .switch-slider .switch-slider-dot {
background-color: white;
}
}
&.inset {
.switch-slider-dot {
background-color: white;
}
input:checked + .switch-slider .switch-slider-dot {
background-color: white;
}
}
}
&.success {
.switch-slider {
background-color: #ccffcc;
color: #000;
}
input:checked + .switch-slider {
background-color: #44ff44;
}
&:not(.inset) {
.switch-slider-dot {
background-color: white;
}
input:checked + .switch-slider .switch-slider-dot {
background-color: white;
}
}
&.inset {
.switch-slider-dot {
background-color: #ccffcc;
}
input:checked + .switch-slider .switch-slider-dot {
background-color: #44ff44;
}
}
}
&.inset {
input + .switch-slider .switch-slider-dot {
transform: translateX(-50%);
left: 0;
margin: 0;
}
input:checked + .switch-slider .switch-slider-dot {
left: calc(100%);
margin: 0;
}
}
// Size variations
&.xs {
font-size: 0.75rem;
.switch-slider {
min-width: 2.5rem;
height: 1.25rem;
font-size: 0.375rem;
padding-inline: 0.25rem;
.switch-slider-dot {
width: 1rem;
height: 1rem;
margin: 0 0.1875rem;
}
}
input:checked + .switch-slider .switch-slider-dot {
left: calc(100% - 0.1875rem);
}
&.inset {
.switch-slider {
min-width: 1.75rem;
height: 0.75rem;
margin-right: 0.5rem;
font-size: 0.375rem;
.switch-slider-dot {
width: 1rem;
height: 1rem;
margin: 0 0.1875rem;
box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2),
0px 1px 1px 0px rgba(0, 0, 0, 0.14),
0px 1px 3px 0px rgba(0, 0, 0, 0.12);
}
}
}
}
&.sm {
font-size: 0.875rem;
.switch-slider {
min-width: 3rem;
height: 1.5rem;
font-size: 0.4375rem;
padding-inline: 0.3125rem;
.switch-slider-dot {
width: 1.25rem;
height: 1.25rem;
margin: 0 0.1875rem;
}
}
input:checked + .switch-slider .switch-slider-dot {
left: calc(100% - 0.1875rem);
}
&.inset {
.switch-slider {
min-width: 2.125rem;
height: 0.875rem;
margin-right: 0.5rem;
font-size: 0.4375rem;
.switch-slider-dot {
width: 1.125rem;
height: 1.125rem;
margin: 0 0.1875rem;
box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2),
0px 1px 1px 0px rgba(0, 0, 0, 0.14),
0px 1px 3px 0px rgba(0, 0, 0, 0.12);
}
}
}
}
&.md {
font-size: 1rem;
.switch-slider {
min-width: 4rem;
height: 2rem;
font-size: 0.5625rem;
padding-inline: 0.375rem;
.switch-slider-dot {
width: 1.5rem;
height: 1.5rem;
margin: 0 0.25rem;
font-size: 0.5625rem;
}
}
input:checked + .switch-slider .switch-slider-dot {
left: calc(100% - 0.25rem);
}
&.inset {
.switch-slider {
min-width: 2.5rem;
height: 1rem;
margin-right: 0.625rem;
font-size: 0.5625rem;
padding-inline: 0.25rem;
.switch-slider-dot {
width: 1.25rem;
height: 1.25rem;
margin: 0 0.25rem;
box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2),
0px 1px 1px 0px rgba(0, 0, 0, 0.14),
0px 1px 3px 0px rgba(0, 0, 0, 0.12);
}
}
}
}
&.lg {
font-size: 1.125rem;
.switch-slider {
min-width: 5rem;
height: 2.5rem;
border-radius: 1.25rem;
font-size: 0.6875rem;
padding-inline: 0.5rem;
.switch-slider-dot {
width: 2rem;
height: 2rem;
margin: 0 0.375rem;
}
}
input:checked + .switch-slider .switch-slider-dot {
left: calc(100% - 0.375rem);
}
&.inset {
.switch-slider {
min-width: 3.125rem;
height: 1rem;
margin-right: 0.625rem;
padding-inline: 0.375rem;
font-size: 0.6875rem;
.switch-slider-dot {
width: 1.5rem;
height: 1.5rem;
margin: 0 0.375rem;
box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2),
0px 1px 1px 0px rgba(0, 0, 0, 0.14),
0px 1px 3px 0px rgba(0, 0, 0, 0.12);
}
}
}
}
&.xl {
font-size: 1.25rem;
.switch-slider {
min-width: 6rem;
height: 3rem;
border-radius: 1.5rem;
font-size: 0.875rem;
padding-inline: 0.5rem;
.switch-slider-dot {
width: 2.5rem;
height: 2.5rem;
margin: 0 0.375rem;
}
}
input:checked + .switch-slider .switch-slider-dot {
left: calc(100% - 0.375rem);
}
&.inset {
.switch-slider {
min-width: 3.5rem;
height: 1.25rem;
margin-right: 0.625rem;
font-size: 0.75rem;
.switch-slider-dot {
width: 1.75rem;
height: 1.75rem;
margin: 0 0.375rem;
box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2),
0px 1px 1px 0px rgba(0, 0, 0, 0.14),
0px 1px 3px 0px rgba(0, 0, 0, 0.12);
}
}
}
}
&.disabled {
opacity: 0.6;
pointer-events: none;
}
&.readonly {
pointer-events: none;
}
}
</style>