单选按钮动画列表
单选按钮动画列表
带有选中动画效果的 CSS 单选按钮列表,Selection 边框会跟随选中项平滑移动。
▼ 效果展示
纯 CSS 实现的单选按钮动画效果,利用
:has() 伪类实现选中状态的视觉反馈。这是一个精美的单选按钮列表动画效果。当选中某个选项时,会出现一个内阴影边框跟随选中项移动,同时单选按钮本身也会有缩放动画。整个效果完全使用 CSS 实现,无需 JavaScript。
核心特性
- 纯 CSS 实现:无需 JavaScript,完全使用 CSS 伪类和动画
- 平滑动画:Selection 边框跟随选中项平滑过渡移动
- 按钮缩放:选中时单选按钮有缩放弹跳效果
- 暗色主题:支持根据系统主题自动切换明暗模式
- 悬停效果:未选中项悬停时也有背景色变化
完整代码
以下是完整的代码,分为 HTML、CSS 两部分,点击各部分查看:
🎨 CSS 样式
* {
border: 0;
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--hue: 223;
--bg: hsl(var(--hue),90%,90%);
--fg: hsl(var(--hue),90%,10%);
--primary: hsl(var(--hue),90%,50%);
--trans-dur: 0.3s;
--trans-timing: cubic-bezier(0.65,0,0.35,1);
font-size: calc(16px + (24 - 16) * (100vw - 320px) / (2560 - 320));
}
body,
input {
color: var(--fg);
font: 1em/1.5 "DM Sans", sans-serif;
}
body {
background-color: var(--bg);
display: flex;
height: 100vh;
transition: background-color var(--trans-dur), color var(--trans-dur);
}
main {
margin: auto;
padding: 1.5em 0;
width: 100%;
}
form {
margin: auto;
max-width: 20em;
position: relative;
width: calc(100% - 3em);
}
label,
input[type="radio"] {
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
label {
background-color: hsla(0,0%,100%,0);
border-radius: 0.75em;
display: flex;
align-items: center;
padding: 1.5em;
transition: background-color var(--trans-dur);
}
input[type="radio"] {
background-color: hsl(0,0%,100%);
border-radius: 50%;
box-shadow:
0 0 0 0.0625em hsl(var(--hue),90%,80%),
0 0.125em 0.125em 0.0625em hsla(var(--hue),90%,10%,0.3);
flex-shrink: 0;
margin-inline-end: 1em;
outline: transparent;
position: relative;
width: 1.5em;
height: 1.5em;
transition: background-color var(--trans-dur), box-shadow var(--trans-dur);
-webkit-appearance: none;
appearance: none;
}
input[type="radio"]:before,
input[type="radio"]:after {
border-radius: 50%;
content: "";
display: block;
position: absolute;
transform: scale(0);
transition: transform var(--trans-dur) var(--trans-timing);
}
input[type="radio"]:before {
box-shadow:
0 0 0 0.5em var(--primary) inset,
0 0 0 0.0625em var(--primary);
top: -0.0625em;
left: -0.0625em;
width: calc(100% + 0.125em);
height: calc(100% + 0.125em);
}
input[type="radio"]:after {
background-color: hsl(0,0%,100%);
top: 25%;
left: 25%;
width: 50%;
height: 50%;
}
input[type="radio"]:checked:before,
input[type="radio"]:checked:after {
transform: scale(1);
}
label:has(input[type="radio"]:checked),
label:has(input[type="radio"]:not(:checked)):hover {
background-color: hsla(0,0%,100%,0.5);
}
.selection {
border-radius: 0.75em;
box-shadow: 0 0 0 0.125em var(--primary) inset;
display: none;
pointer-events: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 4.5em;
transition: transform var(--trans-dur) var(--trans-timing);
}
label:has(input[type="radio"]:checked) ~ .selection {
animation: fade-in var(--trans-dur) var(--trans-timing);
display: inherit;
}
label:nth-of-type(2):has(input[type="radio"]:checked) ~ .selection {
transform: translateY(100%);
}
label:nth-of-type(3):has(input[type="radio"]:checked) ~ .selection {
transform: translateY(200%);
}
.truncate {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Dark theme */
@media (prefers-color-scheme: dark) {
:root {
--bg: hsl(var(--hue),90%,10%);
--fg: hsl(var(--hue),90%,90%);
}
label {
background-color: hsla(var(--hue),90%,30%,0);
}
label:has(input[type="radio"]:checked),
label:has(input[type="radio"]:not(:checked)):hover {
background-color: hsla(var(--hue),90%,30%,0.5);
}
input[type="radio"] {
background-color: hsl(var(--hue),90%,30%);
box-shadow:
0 0 0 0.0625em hsl(var(--hue),90%,50%),
0 0.125em 0.125em 0.0625em hsla(var(--hue),90%,10%,0.3);
}
}
/* Animations */
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
技术要点
| 技术点 | 说明 |
|---|---|
| :has() 伪类 | 父选择器,根据子元素状态改变样式 |
| CSS 自定义属性 | 使用 CSS 变量控制主题色和动画时长 |
| transform scale | 实现单选按钮选中时的缩放弹跳效果 |
| transition | cubic-bezier 缓动函数实现弹性动画 |
| prefers-color-scheme | 检测系统主题,自动切换明暗模式 |
| box-shadow inset | 使用内阴影创建 selection 边框效果 |
使用说明
- 将 HTML 结构复制到你的页面中
- 添加对应的 CSS 样式
- 根据需要修改
--hue变量调整主色调 - 点击选项即可看到动画效果
评论