弹性开关

开关圆钮在移动时会拉长成胶囊形,到另一侧后恢复圆形,轨道同时填满颜色。

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

AI 提示词

用 CSS 为 React 制作一个符合无障碍规范的开关。使用真正的 <button role="switch" aria-checked>,旁边显示可见文字“邮件通知”。轨道尺寸 48×28px;22px 的圆钮关闭时位于 translateX(3px),开启时位于 translateX(23px),移动时长 260ms,缓动为 cubic-bezier(0.16, 1, 0.3, 1)。按下时(:active),圆钮从即将离开的一侧做 scaleX(1.25) 拉伸,看起来像被拉长。轨道在 200ms 内从中性底色渐变为强调色。:focus-visible 时显示清晰的焦点环。点击文字也能切换。在 prefers-reduced-motion 下圆钮直接跳到位,不做拉伸。

源代码

Demo.tsx
'use client'

import { useId, useState } from 'react'
import s from './demo.module.css'

export default function ToggleSwitch({ labels }: { labels: Record<string, string> }) {
  const [on, setOn] = useState(true)
  const id = useId()

  return (
    <div className={s.row}>
      <label htmlFor={id} className={s.label}>
        {labels.label}
      </label>
      <button id={id} type="button" role="switch" aria-checked={on} className={s.track} onClick={() => setOn(!on)}>
        <span className={s.knob} />
      </button>
    </div>
  )
}
demo.module.css
.row {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px 18px;
  border: 1px solid var(--stage-rule);
  border-radius: 14px;
  background: var(--stage-surface);
}

.label { color: var(--stage-ink); font: 500 var(--text-base) / 1.2 var(--font-body); white-space: nowrap; cursor: pointer; }

.track {
  position: relative;
  flex: none;
  width: 48px;
  height: 28px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: var(--stage-rule);
  cursor: pointer;
  transition: background-color 200ms var(--ease-out);
}

.track[aria-checked='true'] { background: var(--stage-accent); }
.track:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 3px; }

.knob {
  position: absolute;
  top: 3px;
  left: 0;
  width: 22px;
  height: 22px;
  border-radius: 999px;
  background: var(--stage-ink);
  transform: translateX(3px);
  transform-origin: left;
  transition: transform 260ms var(--ease-out);
}

.track[aria-checked='true'] .knob { transform: translateX(23px); transform-origin: right; }
.track:active .knob { transform: translateX(3px) scaleX(1.25); }
.track[aria-checked='true']:active .knob { transform: translateX(23px) scaleX(1.25); }

@media (prefers-reduced-motion: reduce) {
  .knob, .track { transition: none; }
  .track:active .knob { transform: translateX(3px); }
  .track[aria-checked='true']:active .knob { transform: translateX(23px); }
}

投稿创意

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

你想投稿的是?

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