Components/Checkbox

Checkbox

Accessible boolean selection control with keyboard toggling, label placement, fill state, and SVG tick variant.

VerifiedSince 1.0.0

Checkbox

Basic Usage

Click the box or its label to toggle the state. The checked state follows the selected visual variant with either fill or tick motion.

Checkbox

Loading demo...

Variants

The default checkmark variant shows the inner tick. Use variant="fill" when you want a quieter pure filled square.

Checkbox variants

Loading demo...

Label First

Use label-placement="start" to render the visible label before the checkbox box.

Checkbox (label start)

Loading demo...

No Label

When there is no label or default slot, pass aria-label so assistive technology can identify the control.

Checkbox (no label)

Loading demo...

Disabled State

disabled removes focusability and prevents click or keyboard toggles while preserving the current checked state.

Checkbox (disabled)

Loading demo...

Loading State

Use loading when the checked state needs server confirmation: the box becomes a spinning ring, the tick, dash, and fill all stand down, and the ring's color carries the state instead — neutral grey when unchecked, primary when checked or mixed. Pointer and keyboard toggles are blocked.

Checkbox (loading)

Loading demo...

Using Slots

The default slot replaces label as the visible label. Use it for rich text, helper copy, icons, or emphasis.

Checkbox (slot)

Loading demo...

API

Props

PropDescriptionTypeDefault
modelValue / v-modelChecked state. Updates through click, Enter, or Space when enabled.booleanfalse
disabledDisables focus and prevents click or keyboard toggles.booleanfalse
loadingPending async commit: the box becomes a spinning ring, adds is-loading plus aria-busy="true", and blocks toggling the way disabled does — without the disabled palette.booleanfalse
labelVisible text label rendered next to the checkbox.string-
labelPlacementPlaces the visible label before or after the checkbox box.'start' | 'end''end'
variantVisual variant: fill uses a pure filled square; checkmark shows the inner tick.'checkmark' | 'fill''checkmark'
ariaLabelAccessible label used only when neither label nor the default slot is visible.string-
indeterminatePartial selection: some but not all of the governed items are checked. Renders the dash and reports aria-checked="mixed"; activating it resolves to checked rather than flipping the boolean, matching a native input's indeterminate behaviour.booleanfalse

Events

EventDescriptionParams
update:modelValuev-model update emitted with the next checked state after an enabled toggle.(value: boolean) => void
changeEmitted with the next checked state after an enabled toggle.(value: boolean) => void

Slots

SlotDescription
defaultCustom visible label content. When present, it replaces label rendering.

Best Practices

  • Prefer a visible label or default slot. Use ariaLabel only for icon-only or visually label-less controls.
  • Keep disabled as the single source for inert state; disabled checkboxes are removed from tab order and emit neither update:modelValue nor change.
  • loading and disabled say different things: "committing" versus "unavailable". Using disabled for a busy control makes it look permanently inert.
  • Do not write modelValue back optimistically while loading is set. Leave the box on the old value, commit on success, and simply clear loading on failure so the state stays where it was.
  • Use labelPlacement="start" for compact settings rows where the text should align before the control.

Review Notes

  • Reviewed against packages/tuffex/packages/components/src/checkbox/src/TxCheckbox.vue and checkbox.test.ts.
  • Loading contract: loading is a purely visual and blocking state; the component owns no async logic. The ring's color still follows modelValue / indeterminate, so it sits on the old value to show that this toggle has not landed yet.
  • Known loading trade-off: checked and mixed look identical while loading (both draw a primary ring); aria-checked="mixed" is what preserves the difference. Keeping the fill and drawing a white ring on it was tried first, but the white ring is close to invisible against a light page, so ring-coloring won.
  • Accessibility note: the root element owns role="checkbox", aria-checked, and keyboard handling; provide a visible label or default slot whenever possible, and reserve ariaLabel for controls with no visible text.
  • Motion fallback: The ring stops rotating under prefers-reduced-motion: reduce; its gapped border is a static glyph on its own, so the busy cue survives.
  • Verified coverage: checkbox.test.ts covers rendered labels, aria-label fallback behavior, label placement, the default checkmark variant plus explicit fill and checkmark variants, click emits, disabled blocking, plus loading blocking, is-loading staying separate from is-disabled, a partial selection still reporting mixed while loading, and the aria-busy lifecycle.

Source

  • Component source: packages/tuffex/packages/components/src/checkbox/src/TxCheckbox.vue.
  • Export alias: packages/tuffex/packages/components/src/checkbox/index.ts exports both TuffCheckbox and TxCheckbox.
  • Coverage: packages/tuffex/packages/components/src/checkbox/__tests__/checkbox.test.ts verifies click toggles, ARIA labelling, slot precedence, variant classes, and disabled blocking; keyboard activation is inherited from the native <button> element.
查看源码
packages/tuffex/packages/components/src/checkbox/index.ts