AniBTUI
✦ 组件@anibt/checkboxregistry JSON

Checkbox

18px 的糖果勾选框:选中是糖果粉加一个弹出来的白色对勾,部分选中是一道横线。

安装

npx shadcn@latest add @anibt/checkbox

第一次用之前:在 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 lucide-react radix-ui
  3. 3. 把下面的文件放进项目
    components/ui/checkbox.tsx
    "use client"
    
    import * as React from "react"
    import { CheckIcon, MinusIcon } from "lucide-react"
    import { Checkbox as CheckboxPrimitive } from "radix-ui"
    
    import { cn } from "@/lib/utils"
    
    /**
     * Checkbox — A's candy box (AniBT 「贴纸手账」).
     *
     * 18px on radius 6: paper inside a 1.5px `--input` ring when empty (pink
     * under the pointer); checked is candy with a deeper bottom edge and a white
     * tick that pops in; indeterminate is the same candy with a dash. A ticked
     * box always shows its tick — the state never rides on colour alone.
     *
     * Focus is a 2px `--ring` outline 2px outside, so it never replaces the
     * box's own ring; invalid is the same outline in red. Disabled is the sunk
     * well under the weak ink, checked or not — still readable, never an
     * opacity wash. The hit area reaches 8px past the box.
     *
     * The paint is the `anibt-check` / `anibt-check-on` recipe spelled out in
     * classes, so the disabled compounds below win by order, not by luck.
     */
    function Checkbox({ className, ...props }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
      return (
        <CheckboxPrimitive.Root
          data-slot="checkbox"
          className={cn(
            "group/checkbox peer relative inline-flex size-[18px] shrink-0 items-center justify-center rounded-[6px] bg-card text-anibt-candy-ink shadow-[inset_0_0_0_1.5px_var(--input)] outline-hidden transition-[background-color,box-shadow] duration-150 after:absolute after:-inset-2",
            "enabled:hover: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)]",
            "data-[state=indeterminate]:bg-anibt-candy-edge data-[state=indeterminate]:bg-(image:--anibt-candy) data-[state=indeterminate]: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:bg-muted disabled:text-muted-foreground 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)]",
            "disabled:data-[state=indeterminate]:bg-anibt-deep2 disabled:data-[state=indeterminate]:bg-none disabled:data-[state=indeterminate]:shadow-[inset_0_0_0_1.5px_var(--border)]",
            className
          )}
          {...props}
        >
          <CheckboxPrimitive.Indicator
            data-slot="checkbox-indicator"
            className="grid place-content-center text-current transition-none"
          >
            <CheckIcon
              className="size-3 animate-anibt-pop group-data-[state=indeterminate]/checkbox:hidden"
              strokeWidth={3.5}
              aria-hidden="true"
            />
            <MinusIcon
              className="hidden size-3 group-data-[state=indeterminate]/checkbox:block"
              strokeWidth={3.5}
              aria-hidden="true"
            />
          </CheckboxPrimitive.Indicator>
        </CheckboxPrimitive.Root>
      )
    }
    
    export { Checkbox }

用法

用法
import { Checkbox } from "@/components/ui/checkbox"
import { Field, FieldLabel } from "@/components/ui/field"

<Field orientation="horizontal">
  <Checkbox id="only-chs" defaultChecked />
  <FieldLabel htmlFor="only-chs">只看简体中文</FieldLabel>
</Field>

勾选框旁的文字是选项,不是字段标签:13/400 墨色。用 Field orientation="horizontal" 会自动排成这样。

状态

未选已选部分出错禁用禁用已选
  • 未选:纸面、1.5px input 色内边;悬停边变粉。
  • 已选:糖果渐变、深一档的底边、白色对勾弹入。多选里选中的项一定露出对勾,不只换颜色。
  • 部分:checked="indeterminate",同样的糖果底,中间一道白色横线。
  • 焦点:2px ring 色轮廓,外偏 2px;出错(aria-invalid)是同样的红色轮廓。
  • 禁用:下沉的浅底、弱色的勾,选没选都读得出来。
  • 点击区域比方块大一圈(每边 8px),手指也好点。

全选

2 / 4

属性

Checkbox
checkedboolean | "indeterminate"默认 —受控的选中状态
defaultCheckedboolean | "indeterminate"默认 —非受控的初始状态
onCheckedChange(checked: boolean | "indeterminate") => void默认 —状态改变时调用
disabledboolean默认 —禁用:浅底弱色
aria-invalidboolean默认 —出错:红色轮廓

基于 Radix Checkbox,渲染成 button role="checkbox";放进 <form> 时 Radix 会补一个隐藏的原生 input,name / value / required 照常提交。