MiHCM
primitives/@mihcm/ui·v0.21.0·stable

TransferList

Dual-pane shuttle: unassigned on the left, assigned on the right, four move buttons in the middle. Per-side filter and multi-select. Web only.

WCAG 2.1 AA — release blocker

Web (React DOM)

  • Each pane renders <ul role="listbox" aria-multiselectable> with aria-label matching the pane label.
  • Each row is <button role="option" aria-selected={isChecked}> — single click toggles per-side selection (which is internal state).
  • Move buttons are real <button> elements with action-describing aria-labels:
    • aria-label="Move selected right"
    • aria-label="Move all right"
    • aria-label="Move selected left"
    • aria-label="Move all left" And matching title for hover tooltips.
  • Buttons that would be no-ops are visibly disabled (cursor-not-allowed + reduced opacity) AND carry the disabled attribute so screen readers announce "dimmed" / "unavailable".

Keyboard

  • Tab enters the first pane → cycles through visible options → moves to the filter → next pane → move buttons.
  • Space / Enter toggles the focused option.
  • The move buttons are reachable via Tab; Enter / Space triggers them.

Empty states

  • "No items" / "None assigned" / "No matches" all render as plain text inside the listbox so screen readers announce the state when the user navigates into the empty pane.

Reduced motion

  • Hover background transitions are duration-short (200 ms). User agents honour prefers-reduced-motion.

Color is never the only signal

  • Selected rows have a fill on the checkbox plus a checkmark — colour change is reinforcement, not the only signal.
  • Move buttons gray out when disabled — opacity + cursor, not just colour.

Audit checklist

  • Each pane has a meaningful aria-label.
  • Each row has aria-selected reflecting state.
  • Every move button has an action-describing aria-label.
  • Disabled move buttons have the disabled attribute (not just aria-disabled).
  • 4.5:1 contrast on row text in selected AND unselected states.
  • Empty pane state announces via screen reader.