pkg-components
Version:
148 lines (126 loc) • 3.18 kB
CSS
/* ========================= ThemeToggle.module.css ========================= */
.toggle {
position: relative;
display: inline-block;
border-radius: 999px;
border: none;
cursor: pointer;
overflow: hidden;
padding: 8px;
background: linear-gradient(180deg, #111 0%, #0b0b0b 100%);
transition:
background-color 260ms ease,
box-shadow 200ms ease,
transform 120ms ease;
-webkit-tap-highlight-color: transparent;
}
/* Focus */
.toggle:focus-visible {
outline: 3px solid rgba(255, 255, 255, 0.14);
outline-offset: 4px;
box-shadow:
0 6px 18px rgba(0, 0, 0, 0.45),
0 0 0 6px rgba(255, 255, 255, 0.02);
}
/* States */
.light {
background: linear-gradient(180deg, #f6f7f9 0%, #eef0f2 100%);
}
.dark {
background: linear-gradient(180deg, #0b0b0b 0%, #060606 100%);
}
/* ========================= Knob ========================= */
.knob {
position: absolute;
top: 8px;
left: 8px;
width: calc(50% - 9px);
height: calc(100% - 16px);
border-radius: 999px;
background: linear-gradient(180deg, #ffffff 0%, #f0f0f0 100%);
box-shadow:
0 10px 30px rgba(2, 6, 23, 0.18),
inset 0 -6px 12px rgba(0, 0, 0, 0.06);
transform: translateX(0) scale(1);
transition:
transform 450ms cubic-bezier(0.22, 1, 0.36, 1),
box-shadow 220ms ease;
will-change: transform;
}
/* Press feedback */
.toggle:active {
transform: scale(0.985);
}
.toggle:active .knob {
transform: translateX(0) scale(0.985);
transition-duration: 120ms;
}
/* Dark slide with overshoot */
.dark .knob {
transform: translateX(calc(100% - 2px));
animation: knob-slide 450ms cubic-bezier(0.22, 1, 0.36, 1) both;
}
/* ========================= Icons ========================= */
.iconSun,
.iconMoon {
position: absolute;
top: 50%;
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
transform: translateY(-50%) scale(0.9);
opacity: 0;
pointer-events: none;
transition:
opacity 280ms ease,
transform 450ms cubic-bezier(0.22, 1, 0.36, 1);
}
.iconSun {
left: 20px;
color: #ffb84d;
}
.iconMoon {
right: 20px;
color: #2b2f36;
}
/* Icon states */
.light .iconSun {
opacity: 1;
transform: translateY(-50%) scale(1) rotate(0deg);
}
.dark .iconSun {
opacity: 0;
transform: translateY(-50%) scale(0.7) rotate(-35deg);
}
.light .iconMoon {
opacity: 0;
transform: translateY(-50%) scale(0.7) rotate(35deg);
}
.dark .iconMoon {
opacity: 1;
transform: translateY(-50%) scale(1) rotate(0deg);
}
/* ========================= Animations ========================= */
@keyframes knob-slide {
0% {
transform: translateX(0) scale(1);
}
60% {
transform: translateX(calc(100% + 6px)) scale(1.01);
}
100% {
transform: translateX(calc(100% - 2px)) scale(1);
}
}
/* ========================= Accessibility ========================= */
@media (prefers-reduced-motion: reduce) {
.toggle,
.knob,
.iconSun,
.iconMoon {
transition: none ;
animation: none ;
}
}