Skip to content

feat(ui): enlarge checkbox hit area and add hover state - #9867

Merged
joshistoast merged 3 commits into
mainfrom
fix/checkbox-hit-area
Oct 17, 2026
Merged

joshistoast merged 3 commits into
mainfrom
fix/checkbox-hit-area

Conversation

@joshistoast

@joshistoast joshistoast commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Checkboxes only toggled from their visible box, which is 12px in list rows such as the Model Manager. Every checkbox now accepts clicks within 6px of its box: an invisible absolutely positioned ::before on the control, inside the <label> root. The small list checkbox gets a 24px target (WCAG 2.5.8); 6px stays under the 8px minimum spacing between neighbouring controls.

Checkboxes also gain a hover state so the larger target reads as clickable. Hovering the label, the box, or the surrounding area strengthens an unchecked border or shifts a checked fill. Disabled and invalid controls are unchanged, and the transition follows reduced motion.

Both changes live in one theme-level checkbox slot recipe, so no call sites change.

Related Issues / Discussions

Closes #9785

QA Instructions

  • pnpm check:release: passed. The final change (hover excludes invalid controls, plus a comment) landed after that run; pnpm lint and the focused browser tests below passed on the final change.
  • pnpm test:browser src/platform/ui/list/ListItem.browser.test.tsx src/platform/ui/theme/CheckboxHover.browser.test.tsx: passes.
    • Clicks just left of and just above a list checkbox toggle it; a click past the extended area still presses the row.
    • Hover is checked in every theme, normal and high contrast: border contrast rises, the checked fill shifts, and the checkmark keeps at least 3:1 against it. Disabled and invalid checkboxes stay unchanged.
    • Each assertion fails with its fix removed.
  • Manual, Model Manager list against the mock backend:
    • A click 4px left of a row's checkbox selects the model, and a click 4px above deselects it; Model Details does not open.
    • Hovering beside the box strengthens its border; hovering a checked box shifts its fill.

Review

  • Checked every current checkbox usage for overlap: the extended area never reaches a neighbouring control or another checkbox's box.
  • Hover is defined for the solid variant only, the default every checkbox uses.
  • Known limits:
    • In a 52px list row, the 24px target leaves the very top and bottom of the row beside the checkbox inert.
    • On an active (accent-filled) row, a checked checkbox's hover is barely visible against the row fill.
    • At rest, unchecked checkbox borders are about 1.3–2.5:1 against the panel in normal themes, below the 3:1 guideline. That predates this PR; hover lifts it above 4.6:1.

Checklist

  • The PR has a short but descriptive title, suitable for a changelog
  • Meaningful regression coverage added / updated where needed; obsolete tests/code removed
  • Persisted-state and API changes include required migrations / compatibility validation
  • Relevant performance/efficiency opportunities considered; material claims have evidence
  • Material review findings resolved and relevant checks rerun
  • Documentation added / updated (if applicable)
  • Updated What's New copy (if doing a release after this PR)

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

Checkboxes take clicks within 6px of the visible box, giving the small list checkbox a 24px target.
Hovering the label, box, or surrounding area strengthens the border or shifts the checked fill; disabled and invalid controls are unchanged.
@github-actions github-actions Bot added the frontend PRs that change frontend files label Oct 15, 2026

@lstein lstein left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at a0b7767 and tested in the UI: the larger hit area and the hover state work as described. The two browser test files pass locally (20/20).

I tried to break it and found no blockers. These held up:

  • Disabled checkboxes: Chakra's _hover already excludes :disabled/[data-disabled]. A busy ListItem row just stops toggling from the surrounding area.
  • Clicks in the outset reach the input through the <label> root. In ListItem, a click about 8px past the box still goes to the primary button, and no row-level onClick sees the outset.
  • Row overlap: the smallest row is compact (minH: '7', 28px), taller than the 24px target. Spacing isn't rebased, so the outset really is 7px (checked in the browser).
  • The workflow node footer is nodrag, so the larger area can't start a node drag. ClearDialog's acknowledge checkbox stays 8px clear of the type-to-confirm label.
  • Reduced motion: the transition uses the fast token, which becomes 1ms.

Non-blocking findings:

  1. The outset can add scroll height. The ::before is absolutely positioned, so it counts toward a scroll container's scrollable overflow on the bottom and right edges. Repro: an overflow:auto box 40px tall holding a 28px spacer and a label-less sm checkbox gives scrollHeight 46 vs clientHeight 40, so a scrollbar appears that wasn't there before. No current call site hits this; the tightest is the OrphanedModelsDialog row (p="2"), with 1px to spare. Worth a line in the recipe comment for future checkboxes placed at the edge of a scroller with no padding.

  2. Checked + invalid still changes on hover. The checked-hover selector &:is([data-state=checked], [data-state=indeterminate]) has no :not([data-invalid]). Hovering a checked invalid checkbox shifts its fill from rgb(220,38,38) to the same red at 90% opacity, which contradicts "invalid controls are unchanged" in the description and the test file comment. No caller passes invalid to a checkbox today. Fix: add :not([data-invalid]) and a checked+invalid sample in CheckboxHover.browser.test.tsx.

  3. "Never reaches a neighbour" depends on paint order as well as spacing. When a neighbour is closer than 7px, a positioned element after the checkbox wins the overlap: a Chakra Button 4px away (gap="1") still got the click. A non-positioned element before the checkbox loses its edge pixels to the outset. Nothing is that close today; the closest is PositivePromptActions (image at 8px). It only matters if someone later puts a checkbox in a tight group.

Exclude invalid checked and indeterminate controls from hover styling.
Cover error appearance when hovering labels and surrounding hit areas.
@joshistoast
joshistoast enabled auto-merge (squash) October 17, 2026 07:44
@joshistoast
joshistoast merged commit 828f8fb into main Oct 17, 2026
16 checks passed
@joshistoast
joshistoast deleted the fix/checkbox-hit-area branch October 17, 2026 07:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

frontend PRs that change frontend files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[enhancement]: 7.0.0-alpha.1-post - UX/UI make (list) element checkboxes toggle with element body when there is enough space or no other event attached

2 participants