MobTranslate styleguide

Checkbox

Choose independent options with a stable label, real state and a meaningful mixed aggregate when needed.

Choose an independent option

Use Checkbox when multiple independent choices may coexist or one explicit option is included/excluded. Use RadioGroup for one alternative and Switch for a binary feature setting. Do not use a checkbox to imply consent to a consequence that its label does not describe.

A mixed checkbox summarizes partial selection; it is not a third independent value for an ordinary item. CheckboxGroup owns the parent aggregate in the grouped example.

Anatomy and specifications

PartActual implementation
Label targetNative label wrapping the primitive and visible text; min 44px height/width
ControlDefault span with checkbox semantics and associated hidden input; public HTMLElement ref
Visual box20×20px, 2px readable boundary, --radius-sm
Checked / mixedPrimary fill; decorative check mark / horizontal dash
Namelabel string supplies visible text and default aria-label; native ARIA props can override
Focus / unavailableToken ring; disabled cursor/visual state; read-only remains distinct
TransitionToken duration; removed under reduced motion

Material choice, targets and source caveat

Use a checkbox set for related choices that can coexist, a radio group for one alternative, and a switch for a standalone on/off setting. The reference allows an immediately effective desktop checkbox setting; a form checkbox can instead remain draft until submission. State which policy the task uses. Related controls can move into a side sheet on a large screen without changing their labels, group question or selection model.

The captured checkbox accessibility keyboard table refers to chips, arrow movement and Delete/Backspace removal. That table is inconsistent with the checkbox topic; it is not copied as this control’s keyboard contract. The installed checkbox supports ordinary Tab focus and Space toggling, with native label activation as verified by the local examples and tests.

Reference pointLocal implementation
Anatomy and measurementsNative reference: 18dp box/icon, 2dp corner, 40dp state layer and 48dp target. Local box is nominally 20 CSS px at a 16px root within a minimum 44px labelled target. These are independent contracts, not a dp conversion.
Selection and errorCheck, dash and unchecked outline distinguish selected/mixed/empty. Required errors retain choices, use aria-invalid and a linked persistent explanation. The package does not claim separate Material error tokens for each selected/mixed state.
Parent aggregateNone/some/all follows actual group members. Selecting mixed chooses all; selecting all clears. The named parent is not an extra submitted member. Read the Checkbox Group guide for changing, unavailable or filtered membership.
Label and densityAdjacent text stays the foreground color when checked; click text or control to operate. Material recommends 48×48 CSS px targets and opt-in reversible density. This site uses a 44px baseline and provides no default dense mode or automatic density preference.
Interaction styleThe local ring, boundary and immediate role-color change distinguish interaction; there is no native state-layer/ripple or an asserted hardware feedback implementation.

Working required local option and form

No local form has been submitted.

tsx
'use client';
import { useRef, useState } from 'react';
import { Button, Checkbox, CheckboxGroup } from '@mobtranslate/ui';

export function CheckboxSelectionExample() {
  const [checked, setChecked] = useState(false);
  const [error, setError] = useState('');
  const [result, setResult] = useState('No local form has been submitted.');
  const control = useRef<HTMLElement>(null);
  return <div data-checkbox-lab className="space-y-4">
    <form noValidate className="space-y-3" onSubmit={event => {
      event.preventDefault();
      if (!checked) { setError('Include explanatory notes before submitting this local sample.'); control.current?.focus(); return; }
      setError(''); setResult(`Local FormData: notes=${new FormData(event.currentTarget).get('notes')}. No request was sent.`);
    }}>
      <Checkbox ref={control} label="Include explanatory notes" name="notes" value="include" uncheckedValue="omit" required checked={checked} onCheckedChange={next => { setChecked(next); setError(''); }} aria-invalid={!!error || undefined} aria-describedby={error ? 'local-checkbox-error' : undefined} className={state => state.checked ? 'local-checkbox-checked' : 'local-checkbox-unchecked'} />
      {error && <p id="local-checkbox-error" role="alert">{error}</p>}
      <Button type="submit">Submit local checkbox sample</Button>
    </form>
    <Checkbox label="Read-only checked sample" checked readOnly />
    <Checkbox label="Disabled unchecked sample" disabled />
    <p role="status">{result}</p>
  </div>;
}

export function CheckboxGroupSelectionExample() {
  const options = ['text','audio','images'];
  const [selected, setSelected] = useState<string[]>(['text']);
  const [disabled, setDisabled] = useState(false);
  const [result, setResult] = useState('No local grouped form submitted.');
  return <div data-checkbox-group-lab className="space-y-4">
    <Button type="button" variant="outline" aria-pressed={disabled} onClick={() => setDisabled(current => !current)}>Disable local checkbox group</Button>
    <form className="space-y-3" onSubmit={event => { event.preventDefault(); setResult(`Local FormData resources: ${new FormData(event.currentTarget).getAll('resource').join(', ') || 'none'}.`); }}>
      <fieldset><legend className="font-semibold">Local resource format filters</legend><p>Selection describes this sample, not permission to use any recording or image.</p>
        <CheckboxGroup value={selected} allValues={options} onValueChange={setSelected} disabled={disabled} aria-label="Local resource format filters" className={state => state.disabled ? 'local-group-disabled' : 'local-group-enabled'}>
          <Checkbox parent label="Select all local formats" />
          {options.map(value => <Checkbox key={value} value={value} name="resource" label={`Include ${value} samples`} />)}
        </CheckboxGroup>
      </fieldset>
      <Button type="submit">Submit grouped sample</Button>
    </form>
    <p role="status">Selected local formats: {selected.join(', ') || 'none'}. {result}</p>
  </div>;
}

API reference

PropContract
labelOptional visible string and default accessible name; provide a meaningful name when omitted.
checked / defaultCheckedControlled boolean / initial uncontrolled boolean; default unchecked.
onCheckedChange(boolean, eventDetails); details.cancel() can reject the change. Use this rather than native onChange.
disabled / readOnly / requiredfalse defaults; unavailable, immutable and required are different states.
name / uncheckedValueForm field name; unchecked is omitted unless an explicit uncheckedValue is supplied.
inputRefRef to hidden HTMLInputElement. The public control ref is HTMLElement, a span by default.
aria-label / aria-labelledby / aria-describedby / aria-invalidConnect a stable name, concise explanation and current error.
className / style / render / native propsInstalled primitive props; styled wrappers retain string-or-state class callbacks. A custom render must preserve semantics, events and refs.
childrenWrapper owns the indicator/thumb. Supply visible text through label and supporting content nearby.
valueString submitted when checked.
indeterminatefalse default; exposes mixed state and a visible dash. Caller-controlled mixed state must be updated coherently.
parentfalse default; use inside CheckboxGroup with allValues to control its aggregate.

State and keyboard contract

  • Space changes a focused mutable checkbox. The label is also a pointer target; do not nest another label around this wrapper.
  • Keep checked, mixed graphic and aria-checked consistent. A caller-controlled indeterminate prop is not automatically cleared by changing another state value.
  • Disabled prevents interaction; read-only retains the current value. Do not treat either as a permission decision.
  • For a required local option, retain the form and focus the control on error; connect the explanation through aria-describedby.
  • Names/value/uncheckedValue describe actual submission. An unchecked native checkbox is ordinarily omitted; a boolean React state alone is not FormData.

Failure cases

  • A mixed aggregate drawn as an ordinary check.
  • An unnamed span checkbox or an incorrectly button-typed public ref.
  • Changing disabled/read-only values through a click handler outside the primitive guard.
  • A label that changes its meaning after selection, or a form error with no focused recovery.
  • Recording consent because an unrelated preference was checked.

Verification and release scope

  • Check actual names, roles, state and focus with pointer and keyboard. Inspect the retained value and submitted FormData when relevant.
  • Exercise empty, selected, mixed, disabled, read-only, invalid and pending/recovery states that apply to the control. Styling alone does not prove enforcement.
  • Check both themes, long labels, 320px reflow, meaningful graphic/text contrast, reduced motion, forced colors and the actual target device.
  • Use a named screen-reader/browser pair to verify spoken state. DOM assertions and local examples do not prove announcements or backend persistence.
  • Selection never establishes language quality, source authority, consent, qualified review or permission. Keep that evidence explicit in the product task.