动态复选框

勾选待办时方框带着挤压感填满,画出对勾,任务文字也从左到右被划掉。

  • CSS
  • 表单与控件
  • 切换
  • 收录于

AI 提示词

用纯 CSS 在原生复选框上制作带动画的待办事项。把原生 input 在视觉上隐藏(保持可聚焦),在文字旁画一个 22px 的圆角方框。:checked 时用主色填满方框,并播放一个 300ms 的挤压 keyframe(scale 1 → 0.85 → 1.08 → 1)。对勾用 SVG path 绘制,stroke-dasharray 等于路径长度,stroke-dashoffset 从完整长度过渡到 0,时长 260ms,延迟 120ms。用 ::after 画一条删除线,transform-origin 为 left,在 360ms 内从 scaleX(0) 变为 scaleX(1),同时文字淡化为弱化色。input 处于 :focus-visible 时在方框外显示焦点框。在 prefers-reduced-motion 下直接应用勾选状态,不做动画。任务为“画好分镜”“选好缓动”“发布演示”。

源代码

Demo.tsx
'use client'

import s from './demo.module.css'

export default function AnimatedCheckbox({ labels }: { labels: Record<string, string> }) {
  return (
    <ul className={s.list}>
      {[labels.a, labels.b, labels.c].map((t, i) => (
        <li key={t}>
          <label className={s.row}>
            <input type="checkbox" className={s.input} defaultChecked={i === 0} />
            <span className={s.box} aria-hidden="true">
              <svg viewBox="0 0 24 24">
                <path d="M6 12.5l4 4 8-9" />
              </svg>
            </span>
            <span className={s.text}>{t}</span>
          </label>
        </li>
      ))}
    </ul>
  )
}
demo.module.css
.list {
  display: grid;
  gap: 14px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.row {
  display: flex;
  align-items: center;
  gap: 12px;
  cursor: pointer;
}

.input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.box {
  display: grid;
  place-items: center;
  flex: none;
  width: 22px;
  height: 22px;
  border: 2px solid var(--stage-rule);
  border-radius: 6px;
  transition: background-color 200ms var(--ease-out), border-color 200ms var(--ease-out);
}

.box svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: var(--stage-accent-ink);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 20;
  stroke-dashoffset: 20;
  transition: stroke-dashoffset 260ms var(--ease-out);
}

.text {
  position: relative;
  font: 500 var(--text-base) / 1.3 var(--font-body);
  color: var(--stage-ink);
  transition: color 300ms var(--ease-out);
}

.text::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 52%;
  height: 2px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 360ms var(--ease-out);
}

.input:checked + .box {
  border-color: var(--stage-accent);
  background: var(--stage-accent);
  animation: squish 300ms var(--ease-out);
}

.input:checked + .box svg { stroke-dashoffset: 0; transition-delay: 120ms; }
.input:checked ~ .text { color: var(--stage-muted); }
.input:checked ~ .text::after { transform: scaleX(1); }
.input:focus-visible + .box { outline: 2px solid var(--color-focus); outline-offset: 3px; }

@keyframes squish {
  35% { transform: scale(0.85); }
  70% { transform: scale(1.08); }
}

@media (prefers-reduced-motion: reduce) {
  .box, .box svg, .text, .text::after { transition: none; }
  .input:checked + .box { animation: none; }
}

投稿创意

看到想收录的动效或主题?简单描述一下,会打开一个预先填好的 GitHub Issue,确认后即可提交。

你想投稿的是?

需要 GitHub 账号,投稿内容会公开。