Comic Checkbox
A hand-inked checkbox whose check mark draws itself in with a starburst flash.
betaComicCheckbox.tsx
Examples
Basic
Check it: the mark draws in and a burst flashes behind the box.
comic-checkbox/basic.tsx
import { ComicCheckbox } from "@streamuilib/react";
export default function ComicCheckboxBasic() {
return <ComicCheckbox label="Remember this device" />;
}Group
comic-checkbox/group.tsx
import { ComicCheckbox } from "@streamuilib/react";
export default function ComicCheckboxGroup() {
return (
<fieldset className="flex flex-col gap-4">
<legend className="mb-4 font-bold">Today’s missions</legend>
<ComicCheckbox name="mission" value="cape" label="Iron the cape" defaultChecked />
<ComicCheckbox name="mission" value="cat" label="Rescue the cat" />
<ComicCheckbox name="mission" value="villain" label="Foil the villain" />
<ComicCheckbox name="mission" value="lunch" label="Lunch (required)" checked disabled readOnly />
</fieldset>
);
}Installation
bun add @streamuilib/reactusage.tsx
import { ComicCheckbox } from "@streamuilib/react";
<ComicCheckbox name="terms" label="I agree to the terms" />API
| Prop | Type | Default | Description |
|---|---|---|---|
| label | ReactNode | — | Text next to the box; clicking it toggles the box. |
| ...props | Omit<ComponentProps<"input">, "type"> | — | Native checkbox attributes: checked, defaultChecked, name, value, disabled. |
Accessibility
- A real checkbox, visually hidden, so forms, Space to toggle, and screen readers work unchanged.
- The focus ring shows on the box when the hidden input has keyboard focus.
- The draw-in and flash stop when the user prefers reduced motion.
Source
packages/react/src/ComicCheckbox.tsx
import { cn } from "./cn";
import { halftone } from "./comic";
import { SHAPES } from "./ComicBadge";
import type { CheckboxProps } from "./Checkbox";
export type ComicCheckboxProps = CheckboxProps;
/**
* A comic-book checkbox: a hand-inked box whose check mark draws itself in
* with a starburst flash behind it. The real `<input>` stays in the DOM
* (visually hidden), so forms, keyboard, and screen readers work unchanged.
* Motion stops for reduced motion.
*/
export function ComicCheckbox({ label, className, ...props }: ComicCheckboxProps) {
return (
<label
className={cn(
"sui-comic-check inline-flex cursor-pointer items-center gap-3 has-disabled:cursor-not-allowed has-disabled:opacity-50",
className,
)}
>
<input type="checkbox" className="peer sr-only" {...props} />
<span
aria-hidden
className={cn(
"sui-comic-check-box relative grid size-7 shrink-0 -rotate-3 place-items-center rounded-[7px_3px_8px_3px/3px_8px_3px_7px] border-[3px] border-ink bg-paper-raised shadow-[3px_3px_0_var(--ink)]",
"peer-checked:bg-lime peer-focus-visible:outline-3 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-blue",
)}
style={halftone(12)}
>
<svg viewBox="0 0 100 100" overflow="visible" className="sui-comic-check-flash absolute -inset-3 -z-10 size-[calc(100%+1.5rem)] opacity-0">
<path d={SHAPES.burst.d} className="fill-pink stroke-ink" strokeWidth={5} strokeLinejoin="round" />
</svg>
<svg viewBox="0 0 24 24" className="size-5 overflow-visible text-ink">
<path className="sui-comic-check-mark" d="M4 13 L10 18 L21 4" pathLength={1} fill="none" stroke="currentColor" strokeWidth={4} strokeLinecap="round" strokeLinejoin="round" />
</svg>
</span>
{label && <span className="font-bold">{label}</span>}
</label>
);
}