AniBTUI
✦ 组件@anibt/switchregistry JSON

Switch

36×20 的糖果开关:关是下沉的轨道,开是糖果粉,16px 的白色旋钮弹簧般滑过去。

安装

npx shadcn@latest add @anibt/switch

第一次用之前:在 components.json 里登记 @anibt,再单独运行一次 npx shadcn@latest add @anibt/theme(见快速开始)。只有单独安装主题时,CLI 才会把 shadcn 的标准颜色换成白樱 / 夜樱;随组件带进来的主题只补上 AniBT 自己的变量和配方。

手动安装
  1. 1. 先装主题组件只读 @anibt/theme 的 token 和配方,没有主题就没有样子。
    npx shadcn@latest add @anibt/theme
  2. 2. 安装依赖
    npm install radix-ui
  3. 3. 把下面的文件放进项目
    components/ui/switch.tsx
    "use client"
    
    import * as React from "react"
    import { Switch as SwitchPrimitive } from "radix-ui"
    
    import { cn } from "@/lib/utils"
    
    /**
     * Switch — A's candy switch (AniBT 「贴纸手账」).
     *
     * A 36×20 track, sunk and ringed off, candy on, with a 16px white knob that
     * springs across. The track carries the state; focus is a 2px `--ring`
     * outline outside it. `size="sm"` is a 28×16 track with a 12px knob for a
     * dense row. Disabled keeps the position readable: the sunk track in both
     * positions and a grey knob — never an opacity wash.
     *
     * The paint is the `anibt-switch` / `anibt-switch-on` recipe spelled out in
     * classes, so the disabled compounds win by order.
     */
    function Switch({
      className,
      size = "default",
      ...props
    }: React.ComponentProps<typeof SwitchPrimitive.Root> & {
      size?: "sm" | "default"
    }) {
      return (
        <SwitchPrimitive.Root
          data-slot="switch"
          data-size={size}
          className={cn(
            "group/switch peer relative inline-flex shrink-0 items-center rounded-full bg-muted p-0.5 shadow-[inset_0_0_0_1.5px_var(--input)] outline-hidden transition-[background-color,box-shadow] duration-200 after:absolute after:-inset-x-2 after:-inset-y-2",
            "data-[size=default]:h-5 data-[size=default]:w-9 data-[size=sm]:h-4 data-[size=sm]:w-7",
            "enabled:hover:data-[state=unchecked]:shadow-[inset_0_0_0_1.5px_var(--anibt-wash-line)]",
            "data-[state=checked]:bg-anibt-candy-edge data-[state=checked]:bg-(image:--anibt-candy) data-[state=checked]:shadow-[0_1px_0_var(--anibt-candy-edge)]",
            "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:outline-solid",
            "aria-invalid:outline-2 aria-invalid:outline-offset-2 aria-invalid:outline-anibt-danger-fill aria-invalid:outline-solid",
            "disabled:cursor-not-allowed disabled:shadow-[inset_0_0_0_1.5px_var(--border)] disabled:data-[state=checked]:bg-anibt-deep2 disabled:data-[state=checked]:bg-none disabled:data-[state=checked]:shadow-[inset_0_0_0_1.5px_var(--border)]",
            className
          )}
          {...props}
        >
          <SwitchPrimitive.Thumb
            data-slot="switch-thumb"
            className={cn(
              "pointer-events-none block rounded-full bg-white shadow-(--anibt-shadow-sticker) transition-transform duration-[350ms] ease-anibt-spring",
              "group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3",
              "data-[state=unchecked]:translate-x-0 group-data-[size=default]/switch:data-[state=checked]:translate-x-4 group-data-[size=sm]/switch:data-[state=checked]:translate-x-3",
              "group-disabled/switch:bg-input group-disabled/switch:shadow-none"
            )}
          />
        </SwitchPrimitive.Root>
      )
    }
    
    export { Switch }

用法

用法
import { Field, FieldLabel } from "@/components/ui/field"
import { Switch } from "@/components/ui/switch"

<Field orientation="horizontal">
  <Switch id="auto-push" />
  <FieldLabel htmlFor="auto-push">新剧集自动推送到下载器</FieldLabel>
</Field>

开关用于立即生效的设置;要点「保存」才生效的选项用勾选框。

状态

关开小号禁用 · 关禁用 · 开出错
  • 关:muted 底的下沉轨道、1.5px input 色内边;悬停边变粉。
  • 开:糖果渐变加深一档的底边;白色旋钮带贴纸阴影,弹簧曲线滑过去。
  • size="sm":28×16 的轨道、12px 的旋钮,放进紧凑的行。
  • 焦点是 2px ring 色轮廓;出错是同样的红色轮廓。
  • 禁用:开和关都是下沉的轨道,旋钮变灰,但位置仍然看得出开关。

设置列表

每条设置是一张井:标题 13/600、说明 12px,开关在右端。写法是 FieldLabel 包住横排的 Field,整张卡都能点。开着的设置仍是纸面——粉色淡底只留给「选中」。

属性

Switch
size"default" | "sm"默认 "default"36×20 / 28×16
checkedboolean默认 —受控的开关状态
defaultCheckedboolean默认 —非受控的初始状态
onCheckedChange(checked: boolean) => void默认 —切换时调用
disabledboolean默认 —禁用:轨道下沉、旋钮变灰

基于 Radix Switch,渲染成 button role="switch"。