Component
Checkbox Disabled
Checkbox variant focused on the disabled non-interactive state.
- Component
- React
- TSX
- Tailwind CSS
- MIT
Preview
Live component
9:41 100%
devsnips.dev/library/react/components/checkboxes/checkbox-disabled/ fluid · no fixed width 100%
Install
Add to your project
npx devsnips add React/Components/Checkboxes/checkbox-disabled React/Components/Checkboxes/checkbox-disabled import type { ChangeEvent, ReactNode } from "react";
import { useId, useState } from "react";
function cx(...parts: Array<string | false | null | undefined>): string {
return parts.filter(Boolean).join(" ");
}
export interface CheckboxDisabledProps {
label: ReactNode;
helperText?: ReactNode;
checked?: boolean;
defaultChecked?: boolean;
onChange?: (checked: boolean, event: ChangeEvent<HTMLInputElement>) => void;
disabled?: boolean;
name?: string;
value?: string | number | readonly string[];
id?: string;
className?: string;
}
/**
* Checkbox variant focused on the disabled (read-only non-interactive)
* state. The native `disabled` attribute carries the semantics; the visual
* treatment uses reduced opacity + muted foreground so the control stays
* perceivable without looking interactive. Built on the native
* `<input type="checkbox">`.
*/
export function CheckboxDisabled({
label,
helperText,
checked,
defaultChecked,
onChange,
disabled = true,
name,
value,
id,
className,
}: CheckboxDisabledProps) {
const generatedId = useId();
const inputId = id ?? `checkbox-${generatedId}`;
const helperId = `${inputId}-helper`;
const isControlled = checked !== undefined;
const [internal, setInternal] = useState<boolean>(defaultChecked ?? false);
const isChecked = isControlled ? checked : internal;
function handleChange(event: ChangeEvent<HTMLInputElement>) {
const next = event.target.checked;
if (!isControlled) setInternal(next);
onChange?.(next, event);
}
return (
<div className={cx("flex flex-col gap-1", className)}>
<label
htmlFor={inputId}
className="inline-flex cursor-not-allowed items-center gap-2.5 text-sm leading-5 text-[var(--ds-color-muted-foreground)] opacity-60"
>
<span className="relative inline-flex size-[18px] shrink-0 items-center justify-center">
<input
id={inputId}
type="checkbox"
aria-describedby={helperText ? helperId : undefined}
className="size-[18px] cursor-not-allowed appearance-none rounded-[var(--ds-radius-xs)] border border-[var(--ds-color-border)] bg-[var(--ds-color-muted)] opacity-50 checked:border-[var(--ds-color-muted-foreground)] checked:bg-[var(--ds-color-muted-foreground)] motion-reduce:transition-none"
checked={isControlled ? isChecked : undefined}
defaultChecked={isControlled ? undefined : defaultChecked}
disabled={disabled}
name={name}
value={value}
onChange={handleChange}
/>
<span
aria-hidden="true"
className={cx(
"pointer-events-none absolute inset-0 flex items-center justify-center text-[var(--ds-color-primary-foreground)] transition-opacity duration-150 motion-reduce:transition-none",
isChecked ? "opacity-100" : "opacity-0",
)}
>
<svg className="size-[12px]" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={3.5} strokeLinecap="round" strokeLinejoin="round">
<path d="M20 6 9 17l-5-5" />
</svg>
</span>
</span>
<span className="select-none">{label}</span>
</label>
{helperText ? (
<p id={helperId} className="pl-[26px] text-xs leading-4 text-[var(--ds-color-muted-foreground)]">
{helperText}
</p>
) : null}
</div>
);
}
export default CheckboxDisabled; /* DevSnips React — JavaScript parity build.
* Same API, behavior, and classes as code.tsx; TypeScript types removed.
* Regenerated from code.tsx — edit code.tsx and re-run the generator.
*/
import { useId, useState } from "react";
function cx(...parts) {
return parts.filter(Boolean).join(" ");
}
export function CheckboxDisabled({
label,
helperText,
checked,
defaultChecked,
onChange,
disabled = true,
name,
value,
id,
className
}) {
const generatedId = useId();
const inputId = id ?? `checkbox-${generatedId}`;
const helperId = `${inputId}-helper`;
const isControlled = checked !== undefined;
const [internal, setInternal] = useState(defaultChecked ?? false);
const isChecked = isControlled ? checked : internal;
function handleChange(event) {
const next = event.target.checked;
if (!isControlled) setInternal(next);
onChange?.(next, event);
}
return <div className={cx("flex flex-col gap-1", className)}>
<label
htmlFor={inputId}
className="inline-flex cursor-not-allowed items-center gap-2.5 text-sm leading-5 text-[var(--ds-color-muted-foreground)] opacity-60"
>
<span className="relative inline-flex size-[18px] shrink-0 items-center justify-center">
<input
id={inputId}
type="checkbox"
aria-describedby={helperText ? helperId : undefined}
className="size-[18px] cursor-not-allowed appearance-none rounded-[var(--ds-radius-xs)] border border-[var(--ds-color-border)] bg-[var(--ds-color-muted)] opacity-50 checked:border-[var(--ds-color-muted-foreground)] checked:bg-[var(--ds-color-muted-foreground)] motion-reduce:transition-none"
checked={isControlled ? isChecked : undefined}
defaultChecked={isControlled ? undefined : defaultChecked}
disabled={disabled}
name={name}
value={value}
onChange={handleChange}
/>
<span
aria-hidden="true"
className={cx(
"pointer-events-none absolute inset-0 flex items-center justify-center text-[var(--ds-color-primary-foreground)] transition-opacity duration-150 motion-reduce:transition-none",
isChecked ? "opacity-100" : "opacity-0"
)}
>
<svg className="size-[12px]" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={3.5} strokeLinecap="round" strokeLinejoin="round">
<path d="M20 6 9 17l-5-5" />
</svg>
</span>
</span>
<span className="select-none">{label}</span>
</label>
{helperText ? <p id={helperId} className="pl-[26px] text-xs leading-4 text-[var(--ds-color-muted-foreground)]">
{helperText}
</p> : null}
</div>;
}
export default CheckboxDisabled; # Checkbox Disabled
Checkbox variant focused on the disabled non-interactive state.
## Usage
```tsx
<CheckboxDisabled label="Inherited from team" checked defaultChecked />
```
## JavaScript
A `code.jsx` build is provided for projects that ship plain JSX. It exposes the same API and behavior as `code.tsx` — only the TypeScript types are removed.
```jsx
<CheckboxDisabled label="Inherited from team" checked defaultChecked />
```
## Props
| Name | Type | Default | Description |
|---|---|---:|---|
| `label` | `ReactNode` (required) | — | Visible label (omit for an icon-only / aria-label control). |
| `checked` / `defaultChecked` | `boolean` | `false` | Controlled / uncontrolled checked state. |
| `onChange` | `(checked, event) => void` | — | Change callback. |
| `disabled` | `boolean` | — | Disables the control. |
| `readOnly` | `boolean` | — | Read-only (blocks toggling). |
| `required` | `boolean` | — | Marks the field required (renders `*`). |
| `invalid` | `boolean` | — | Sets `aria-invalid` + destructive styling. |
| `name` / `value` | `string` | — | Native form name/value. |
| `id` | `string` | generated | Input id (also the label `htmlFor`). |
| `aria-label` / `aria-labelledby` / `aria-describedby` | `string` | — | Override association. |
| `helperText` | `ReactNode` | — | Helper text (still readable when disabled). |
| `disabled` | `boolean` | `true` | Defaults to disabled. |
## States
Native checkbox with `disabled` set (defaults to `true`). The visual treatment uses reduced opacity + muted foreground so the control stays perceivable without looking interactive. Native disabled semantics are preserved (excluded from form submission, not focusable).
## Accessibility
Native `disabled` attribute carries the semantics. Helper text stays associated via `aria-describedby`. Reduced opacity + muted color keeps it perceivable.
## Styling
Built with React, Tailwind CSS, and DevSnips design tokens. The component consumes the `--ds-*` semantic tokens via arbitrary values (for example `bg-[var(--ds-color-primary)]`). Define the tokens once in your theme — no component-specific CSS file is required.
## Design Tokens
See [React/DESIGN_TOKENS.md](../../../DESIGN_TOKENS.md) for the authoritative token specification. This checkbox uses the semantic color, radius, spacing, and motion tokens.
## Notes
Use `checkbox-disabled` for options that exist but cannot be changed in this context (e.g. a permission inherited from a team plan). For a value that is fixed but should still be focusable, use `checkbox-readonly`. 95 lines UTF-8 · LF · Spaces: 2
Continue browsing