日夜切换开关

说明 HTML CSS SVG

一个支持日夜模式切换的开关组件。活跃状态下为胶囊条,静止时为圆点。采用拟态风格设计

content_copy
<label class="theme-switch" title="切换日夜模式">
    <input type="checkbox" class="theme-toggle-input">
    <span class="slider-track"></span>
    <span class="slider-dot">
        <span class="icon sun"></span>                                    
        <span class="icon moon"></span>
    </span>
</label>
content_copy
/* 1. 总容器 */
.theme-switch {
    position: relative;
    display: block; 
    width: 60px;
    height: 34px;
    cursor: pointer;
    transition: none; 
}
.theme-switch input {
    opacity: 0;
    width: 0;
    height: 0;
}

/* 2. 背景轨道 */
.slider-track {
    position: absolute;
    top: 0; left: 0;
    width: 34px; height: 34px;
    border-radius: 17px;
    box-shadow: inset 2px 2px 4px rgba(0,0,0,0.2), inset -2px -2px 4px rgba(255,255,255,0.05);
    transition: width 0.3s ease, left 0.3s ease, border-radius 0.3s ease, box-shadow 0.3s ease;

    background-color: var(--switch-track-bg, var(--surface-container-highest));
}
.theme-switch:hover .slider-track { width: 60px; }
.theme-switch input:checked + .slider-track { left: 26px; }
.theme-switch:hover input:checked + .slider-track { left: 0; }

/* 3. 滑块圆点 */
.slider-dot {
    position: absolute;
    height: 26px; width: 26px;
    left: 4px; top: 4px;
    border-radius: 50%;
    box-shadow: 0 4px 8px rgba(0,0,0,0.3);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), background-color 0.3s ease, box-shadow 0.3s ease;

    background-color: var(--switch-dot-bg, var(--primary));
}
.theme-switch input:checked ~ .slider-dot {
    transform: translateX(26px);

    background-color: var(--switch-dot-checked-bg, var(--primary-container));
}

/* 4. 图标样式 (核心修改) */
.slider-dot .icon {
    position: absolute;
    width: 20px; height: 20px;
    -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
    -webkit-mask-position: center; mask-position: center;
    -webkit-mask-size: contain; mask-size: contain;
    transition: opacity 0.3s ease, transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);

    background-color: var(--switch-icon-color, var(--on-primary));
}
.icon.sun { -webkit-mask-image: url('/icons/sun.svg'); mask-image: url('/icons/sun.svg'); }
.icon.moon { -webkit-mask-image: url('/icons/moon.svg'); mask-image: url('/icons/moon.svg'); }

/* 控制动画和显隐 */
.sun { opacity: 1; transform: translateY(0) rotate(0deg); }
.moon { opacity: 0; transform: translateY(20px) rotate(-45deg); }

input:checked ~ .slider-dot .sun { opacity: 0; transform: translateY(-20px) rotate(45deg); }
input:checked ~ .slider-dot .moon {
    opacity: 1;
    transform: translateY(0) rotate(0deg);
    background-color: var(--switch-icon-checked-color, var(--on-surface));
}
content_copy
<!-- sun.svg -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>

<!-- moon.svg -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>