<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<title>煎鸡蛋 Switch</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1, viewport-fit=cover"
/>
<style>
* {
border: 0;
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--hue1: 223;
--hue2: 123;
--hue3: 38;
--hue4: 63;
--bg: hsl(var(--hue1), 90%, 90%);
--fg: hsl(var(--hue1), 90%, 10%);
--primary: hsl(var(--hue1), 90%, 50%);
--trans-dur: 0.4s;
--trans-timing1: cubic-bezier(0.65, 0.05, 0.36, 1);
--trans-timing2: cubic-bezier(0.65, -0.05, 0.36, 1.1);
font-size: calc(30px + (60 - 30) * (100vw - 320px) / (2560 - 320));
}
body,
input {
font: 1em/1.5 sans-serif;
}
body {
background-color: var(--bg);
color: var(--fg);
display: flex;
height: 100vh;
perspective: 480px;
transition: background-color var(--trans-dur), color var(--trans-dur);
}
.switch {
animation: switchOff var(--trans-dur) var(--trans-timing1) forwards;
border-radius: 1.5em;
box-shadow: 0 0 0 0.125em hsla(var(--hue1), 90%, 50%, 0);
margin: auto;
position: relative;
transform-origin: 0 50%;
transition: box-shadow 0.15s linear;
}
.switch:before,
.switch:after {
content: "";
display: block;
position: absolute;
}
.switch:before {
background-color: hsl(var(--hue1), 10%, 60%);
box-shadow: 0.0625em 0 0.0625em hsla(0, 0%, 10%, 0.8) inset;
top: calc(50% - 0.125em);
right: 100%;
width: 0.5em;
height: 0.25em;
transition: background-color calc(var(--trans-dur) / 2)
calc(var(--trans-dur) / 2) var(--trans-timing1);
}
.switch:after {
background-image: radial-gradient(
0.3em 0.3em at 25% 50%,
hsla(var(--hue1), 10%, 20%, 0) 30%,
hsl(var(--hue1), 10%, 20%) 35% 45%,
hsl(var(--hue1), 10%, 30%) 50%
);
border-radius: 0.25em 0.125em 0.125em 0.25em;
box-shadow: 0 0 0.125em hsl(var(--hue1), 10%, 10%) inset;
top: calc(50% - 0.25em);
right: calc(100% + 0.5em);
width: 1.5em;
height: 0.5em;
}
.switch__input,
.switch__input:before,
.switch__input:after {
display: block;
}
.switch__input {
animation: switchOffInput var(--trans-dur) var(--trans-timing1) forwards;
background-color: hsl(var(--hue1), 10%, 30%);
border-radius: 1.5em;
box-shadow: 0 0 0 0.125em hsl(var(--hue1), 10%, 40%) inset,
0 0 0.125em 0.375em hsl(var(--hue1), 10%, 25%) inset,
0 0 0 0.375em hsl(var(--hue1), 10%, 55%) inset;
outline: transparent;
position: relative;
width: 6em;
height: 3em;
-webkit-appearance: none;
appearance: none;
-webkit-tap-highlight-color: transparent;
}
.switch__input:before,
.switch__input:after {
content: "";
position: absolute;
transition: background-color var(--trans-dur) var(--trans-timing1),
box-shadow var(--trans-dur) var(--trans-timing1),
transform var(--trans-dur) var(--trans-timing1);
}
.switch__input:before {
background-color: hsla(var(--hue4), 90%, 50%, 0.15);
box-shadow: 0.0625em 0.0625em 0 hsla(var(--hue4), 0%, 100%, 0.15) inset;
border-radius: 50% 40%;
top: 0.625em;
left: 0.5em;
width: 2em;
height: 1.75em;
}
.switch__input:after {
background-color: hsl(var(--hue3), 90%, 50%);
background-image: radial-gradient(
12.5% 12.5% at 20% 25%,
hsl(0, 0%, 100%) 45%,
hsla(0, 0%, 100%, 0) 50%
),
radial-gradient(
15% 30% at 15% 50%,
hsl(0, 0%, 100%) 45%,
hsla(0, 0%, 100%, 0) 50%
);
border-radius: 50%;
box-shadow: -0.0625em 0 0 hsla(var(--hue3), 10%, 95%, 0.9),
-0.125em 0 0 hsl(var(--hue3), 90%, 45%) inset,
0.125em 0 0.125em hsla(0, 0%, 10%, 0.3);
top: 1em;
left: 1em;
width: 1em;
height: 1em;
transform: rotate(45deg);
transition-timing-function: var(--trans-timing1), var(--trans-timing1),
var(--trans-timing2);
}
.switch__sr {
overflow: hidden;
position: absolute;
width: 1px;
height: 1px;
}
.switch:has([data-pristine="true"]),
.switch__input[data-pristine="true"] {
animation: none;
}
.switch:has(:focus-visible) {
box-shadow: 0 0 0 0.125em var(--primary);
}
.switch:has(:checked) {
animation-name: switchOn;
}
.switch:has(:checked):before {
background-color: hsl(var(--hue2), 90%, 60%);
}
.switch__input:checked {
animation-name: switchOnInput;
}
.switch__input:checked:before {
background-color: hsl(var(--hue4), 10%, 95%);
box-shadow: -0.0625em -0.0625em 0 hsl(var(--hue4), 0%, 100%) inset;
transform: translateX(3em) rotate(180deg);
}
.switch__input:checked:after {
background-color: hsl(var(--hue3), 90%, 50%);
box-shadow: -0.0625em -0.0625em 0 hsla(var(--hue3), 10%, 95%, 0.6),
-0.125em 0 0 hsl(var(--hue3), 90%, 45%) inset,
0.0625em 0.0625em 0.125em hsla(0, 0%, 10%, 0);
transform: translateX(3em) rotate(45deg);
}
@media (prefers-color-scheme: dark) {
:root {
--bg: hsl(var(--hue1), 90%, 10%);
--fg: hsl(var(--hue1), 90%, 90%);
}
}
@keyframes switchOff {
from,
to {
transform: rotateY(0);
}
50% {
transform: rotateY(-15deg);
}
}
@keyframes switchOffInput {
from {
background-color: hsl(var(--hue2), 90%, 30%);
box-shadow: 0 0 0 0.125em hsl(var(--hue2), 90%, 40%) inset,
0 0 0.125em 0.375em hsl(var(--hue2), 90%, 25%) inset,
0 0 0 0.375em hsl(var(--hue2), 90%, 55%) inset;
}
50% {
background-color: hsl(var(--hue2), 90%, 30%);
box-shadow: -0.0625em 0 0 0.125em hsl(var(--hue2), 90%, 40%) inset,
0.0625em 0 0.125em 0.375em hsl(var(--hue2), 90%, 25%) inset,
0.0625em 0 0 0.375em hsl(var(--hue2), 90%, 55%) inset;
}
to {
background-color: hsl(var(--hue1), 10%, 30%);
box-shadow: 0 0 0 0.125em hsl(var(--hue1), 10%, 40%) inset,
0 0 0.125em 0.375em hsl(var(--hue1), 10%, 25%) inset,
0 0 0 0.375em hsl(var(--hue1), 10%, 55%) inset;
}
}
@keyframes switchOn {
from,
to {
transform: rotateY(0);
}
50% {
transform: rotateY(15deg);
}
}
@keyframes switchOnInput {
from {
background-color: hsl(var(--hue1), 10%, 30%);
box-shadow: 0 0 0 0.125em hsl(var(--hue1), 10%, 40%) inset,
0 0 0.125em 0.375em hsl(var(--hue1), 10%, 25%) inset,
0 0 0 0.375em hsl(var(--hue1), 10%, 55%) inset;
}
50% {
background-color: hsl(var(--hue1), 10%, 30%);
box-shadow: 0.0625em 0 0 0.125em hsl(var(--hue1), 10%, 40%) inset,
-0.0625em 0 0.125em 0.375em hsl(var(--hue1), 10%, 25%) inset,
-0.0625em 0 0 0.375em hsl(var(--hue1), 10%, 55%) inset;
}
to {
background-color: hsl(var(--hue2), 90%, 30%);
box-shadow: 0 0 0 0.125em hsl(var(--hue2), 90%, 40%) inset,
0 0 0.125em 0.375em hsl(var(--hue2), 90%, 25%) inset,
0 0 0 0.375em hsl(var(--hue2), 90%, 55%) inset;
}
}
</style>
</head>
<body>
<label class="switch">
<input
id="switch"
class="switch__input"
type="checkbox"
role="switch"
data-pristine="true"
/>
<span class="switch__sr">Power</span>
</label>
<script>
window.addEventListener("DOMContentLoaded", () => {
const c = new Switch("#switch");
});
class Switch {
constructor(el) {
this.el = document.querySelector(el);
this.el?.addEventListener("change", this.removePristine);
}
removePristine() {
this.removeAttribute("data-pristine");
}
}
</script>
</body>
</html>