Agents (llms.txt)
Octocat

Checkbox

A simple native checkbox prepared for indeterminate states

import { Checkbox } from "@/components/checkbox";

export default function CheckboxPreview() {
  return <Checkbox />;
}

Dependencies

Source Code

import { useEffect, useRef } from "react";

import { composeRefs } from "@/lib/compose-refs";
import { cva } from "@/lib/utils/classnames";

interface CheckboxProps extends Omit<React.ComponentPropsWithRef<"input">, "type"> {
  indeterminate?: boolean;
}

const checkboxStyle = cva({
  base: [
    "focus-visible:ring-(length:--ring-width) relative flex size-5 shrink-0 appearance-none items-center justify-center rounded-sm border border-border bg-background shadow-xs outline-none ring-ring transition enabled:cursor-pointer enabled:not-checked:hover:border-mix-border/8",
    // checked
    "checked:enabled:hover:mix-with-accent-foreground checked:enabled:border-accent checked:enabled:bg-accent checked:enabled:hover:border-mix-accent/8",
    // indeterminate
    "indeterminate:enabled:hover:mix-with-accent-foreground indeterminate:enabled:border-accent indeterminate:enabled:bg-accent indeterminate:enabled:hover:border-mix-accent/8",
    // checked checkmark
    "before:absolute before:text-accent-foreground checked:before:font-bold checked:before:text-xs checked:before:content-['✓']",
    // indeterminate dash
    "indeterminate:before:h-0.5 indeterminate:before:w-1.5 indeterminate:before:bg-accent-foreground",
    // disabled
    "disabled:cursor-not-allowed disabled:border-foreground/5 disabled:bg-foreground/10 disabled:indeterminate:before:bg-foreground/50 disabled:checked:before:text-foreground/50",
  ],
});

const Checkbox = ({ ref, indeterminate, className, ...props }: CheckboxProps) => {
  const internalRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    if (internalRef.current) {
      internalRef.current.indeterminate = !!indeterminate;
    }
  }, [indeterminate]);

  return (
    <input
      type="checkbox"
      ref={composeRefs(ref, internalRef)}
      className={checkboxStyle({ className })}
      {...props}
    />
  );
};

export { Checkbox };

API Reference

Extends the native input element with type="checkbox".

Examples

Simple

import { Checkbox } from "@/components/checkbox";

export default function CheckboxPreview() {
  return <Checkbox />;
}

Disabled

import { Checkbox } from "@/components/checkbox";

export default function CheckboxDisabledPreview() {
  return (
    <div className="flex flex-col space-y-4">
      <Checkbox disabled />
      <Checkbox checked disabled />
    </div>
  );
}

All states

import { Checkbox } from "@/components/checkbox";

const labelClass = "font-medium text-base text-foreground";

export default function CheckboxAllStatesPreview() {
  return (
    <div className="flex flex-col space-y-4">
      <div className="flex items-center space-x-2">
        <Checkbox id="unchecked" />
        <label className={labelClass} htmlFor="unchecked">
          Unchecked
        </label>
      </div>

      <div className="flex items-center space-x-2">
        <Checkbox id="checked" checked onChange={() => {}} />
        <label className={labelClass} htmlFor="checked">
          Checked
        </label>
      </div>

      <div className="flex items-center space-x-2">
        <Checkbox id="indeterminate" indeterminate />
        <label className={labelClass} htmlFor="indeterminate">
          Indeterminate
        </label>
      </div>

      <div className="flex items-center space-x-2">
        <Checkbox id="disabled" disabled />
        <label className={labelClass} htmlFor="disabled">
          Disabled
        </label>
      </div>

      <div className="flex items-center space-x-2">
        <Checkbox id="disabled-checked" checked disabled />
        <label className={labelClass} htmlFor="disabled-checked">
          Checked Disabled
        </label>
      </div>

      <div className="flex items-center space-x-2">
        <Checkbox id="disabled-indeterminate" indeterminate disabled />
        <label className={labelClass} htmlFor="disabled-indeterminate">
          Indeterminate Disabled
        </label>
      </div>
    </div>
  );
}

Best Practices

  1. Labels:

    • Always provide clear labels
    • Make labels clickable
    • Use consistent label positioning
  2. Accessibility:

    • Ensure keyboard navigation
    • Use fieldset for groups

Previous

Calendar

Next

Color Picker