Keyboard-accessible widget move and resize #43

Open
opened 2026-07-28 19:48:10 +00:00 by claude-bot · 0 comments
Collaborator

Problem

Rearranging the dashboard requires a mouse. The drag grip is pointer-only — gridstack's drag interaction has no keyboard equivalent — so a keyboard or assistive-tech user can view the dashboard but never change it.

The close button already does this correctly and sets the standard to match:

closeButton.getElement().setAttribute("role", "button");
closeButton.getElement().setAttribute("tabindex", "0");
closeButton.getElement().addEventListener("keydown", e -> close(true))
        .setFilter("event.key === 'Enter' || event.key === ' '");

The grip has title and aria-label but no tabindex and no key handling.

Proposal

Make the grip a real control:

  • role="button", tabindex="0", focus-visible styling from the --dialect-* token layer.
  • Enter/Space enters a "move mode" on that widget (announce it; a visual outline is not enough).
  • Arrow keys move the widget by one grid cell; Shift+Arrow resizes; Escape/Enter exits move mode.
  • Drive the changes through gridstack's update() API — the same call clearStorage() already uses — so the engine handles collisions and the existing change event fires, which means persistence (#22) works with no extra wiring.

Acceptance criteria

  • A widget can be moved and resized to any reachable position using only the keyboard.
  • Mode changes are announced to assistive tech, not just visually indicated.
  • Keyboard-driven changes persist identically to mouse-driven ones.
  • Focus is never lost when a widget moves.
  • Playwright e2e driving move mode via keyboard only.

Depends on

Nothing hard. Should follow #27, so the action menu and the grip end up with one consistent keyboard model rather than two.

Scope

Medium — the interaction design is the work, not the code.

## Problem Rearranging the dashboard requires a mouse. The drag grip is pointer-only — gridstack's drag interaction has no keyboard equivalent — so a keyboard or assistive-tech user can view the dashboard but never change it. The close button already does this correctly and sets the standard to match: ```java closeButton.getElement().setAttribute("role", "button"); closeButton.getElement().setAttribute("tabindex", "0"); closeButton.getElement().addEventListener("keydown", e -> close(true)) .setFilter("event.key === 'Enter' || event.key === ' '"); ``` The grip has `title` and `aria-label` but no `tabindex` and no key handling. ## Proposal Make the grip a real control: - `role="button"`, `tabindex="0"`, focus-visible styling from the `--dialect-*` token layer. - Enter/Space enters a "move mode" on that widget (announce it; a visual outline is not enough). - Arrow keys move the widget by one grid cell; Shift+Arrow resizes; Escape/Enter exits move mode. - Drive the changes through gridstack's `update()` API — the same call `clearStorage()` already uses — so the engine handles collisions and the existing `change` event fires, which means persistence (#22) works with no extra wiring. ## Acceptance criteria - A widget can be moved and resized to any reachable position using only the keyboard. - Mode changes are announced to assistive tech, not just visually indicated. - Keyboard-driven changes persist identically to mouse-driven ones. - Focus is never lost when a widget moves. - Playwright e2e driving move mode via keyboard only. ## Depends on Nothing hard. Should follow #27, so the action menu and the grip end up with one consistent keyboard model rather than two. ## Scope Medium — the interaction design is the work, not the code.
claude-bot added the enhancement label 2026-07-28 19:48:10 +00:00
pitfriedrich added this to the Fix dashboard imporvements milestone 2026-07-29 05:58:39 +00:00
pitfriedrich added this to the Dashboard improvements project 2026-07-29 06:00:00 +00:00
Sign in to join this conversation.