Word-style Tab navigation in tables with an accessible exit (#338) #343

Merged
fable-5 merged 1 commits from issue-338-tab-navigation into main 2026-08-16 05:22:18 +02:00
Collaborator

Tab/Shift-Tab now move cell-wise inside tables; Tab in the last cell appends a new row and moves into it (Word behavior). Outside tables Tab keeps its browser default, so the editor stays a single tab stop.

Accessible exit (WCAG 2.1.2): Escape places the cursor after the table. The arrow keys reach the gap cursor (#335) only from the table's edge cells (ProseMirror searches gap positions only at the end of the parent), so Escape is the exit that works from every cell. Announced via an aria-describedby hint on the editor surface (visually hidden, de+en).

The keyboard-trap proof lives in the editor pack rather than a11y.spec.ts: adding a login to the a11y pack shifts the CI's rate-limit budget (see the #301 lesson in the handoff), and the editor pack already owns the table specs.

Also stabilizes the table specs: right after the insert the collab sync can swallow a click's selection update -- the specs now settle briefly and verify cursor placement with typed markers.

Stacked on #342 (fast-forward chain #340 -> #341 -> #342 -> this).

Closes #338

Tab/Shift-Tab now move cell-wise inside tables; Tab in the last cell appends a new row and moves into it (Word behavior). Outside tables Tab keeps its browser default, so the editor stays a single tab stop. **Accessible exit (WCAG 2.1.2):** Escape places the cursor after the table. The arrow keys reach the gap cursor (#335) only from the table's edge cells (ProseMirror searches gap positions only at the end of the parent), so Escape is the exit that works from every cell. Announced via an `aria-describedby` hint on the editor surface (visually hidden, de+en). The keyboard-trap proof lives in the editor pack rather than a11y.spec.ts: adding a login to the a11y pack shifts the CI's rate-limit budget (see the #301 lesson in the handoff), and the editor pack already owns the table specs. Also stabilizes the table specs: right after the insert the collab sync can swallow a click's selection update -- the specs now settle briefly and verify cursor placement with typed markers. Stacked on #342 (fast-forward chain #340 -> #341 -> #342 -> this). Closes #338
fable-5 added 4 commits 2026-08-15 19:06:30 +02:00
Gap cursor for block-edge positions (#335)
Some checks failed
CI / Lint, typecheck, test (pull_request) Successful in 6m52s
CI / Build container images (pull_request) Successful in 1m13s
CI / Auth e2e pack (pull_request) Successful in 9m25s
CI / Import/export fidelity gate (pull_request) Successful in 58s
CD / Deploy to Test (push) Blocked by required conditions
CD / Smoke tests against Test (push) Blocked by required conditions
CD / Promote to Int (push) Blocked by required conditions
CI / Auth e2e pack (push) Blocked by required conditions
CI / Import/export fidelity gate (push) Blocked by required conditions
CI / Build container images (push) Blocked by required conditions
CI / Lint, typecheck, test (push) Has been cancelled
CD / Build and push images (push) Has been cancelled
69563348ca
A table (or any other block node without a text position of its own) as
the page's first, last, or only block was unreachable from before/after:
neither mouse nor arrow keys could place the cursor there, so no
paragraph could be created around it.

- add the prosemirror-gapcursor plugin as a TipTap extension (via
  @tiptap/pm, no new dependency; schema-neutral, so the editorSchema
  drift fence is unaffected)
- style the gap cursor bar in base.css -- the upstream package does not
  ship its stylesheet through our import path; the blink animation
  honors prefers-reduced-motion
- e2e: keyboard-only round trip that creates paragraphs before and
  after a lone table

Closes #335

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aoPvnakfBP28nAfijgUY9
Unambiguous delete row/column toolbar icons (#336)
Some checks failed
CI / Lint, typecheck, test (pull_request) Successful in 6m52s
CI / Build container images (pull_request) Successful in 1m12s
CI / Auth e2e pack (pull_request) Successful in 9m24s
CI / Import/export fidelity gate (pull_request) Successful in 58s
CD / Deploy to Test (push) Blocked by required conditions
CD / Smoke tests against Test (push) Blocked by required conditions
CD / Promote to Int (push) Blocked by required conditions
CI / Auth e2e pack (push) Blocked by required conditions
CI / Import/export fidelity gate (push) Blocked by required conditions
CI / Build container images (push) Blocked by required conditions
CD / Build and push images (push) Has been cancelled
CI / Lint, typecheck, test (push) Has been cancelled
b1165a37e6
The delete buttons paired the minus-box with a double arrow (bidirectional
arrows next to the symbol) which reads as "resize/expand", not "delete".
Replace them with axis stripes plus the x delete marker that deleteTable
already established: vertical stripes with x for delete column, horizontal
stripes with x for delete row. Labels/tooltips were correct all along and
stay unchanged.

Closes #336

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aoPvnakfBP28nAfijgUY9
Merge and split table cells (#337)
Some checks failed
CI / Lint, typecheck, test (pull_request) Successful in 6m51s
CI / Build container images (pull_request) Successful in 3m57s
CI / Auth e2e pack (pull_request) Failing after 5m22s
CI / Import/export fidelity gate (pull_request) Has been skipped
d76c731473
prosemirror-tables already ships mergeCells/splitCell and the schema
(tableNodes) already carries colspan/rowspan -- only the controls were
missing. Adds the two commands, toolbar buttons whose enabled state
follows the selection (merge needs a multi-cell selection, split a
merged cell), and de+en labels.

Both render paths now carry the spans: docToHtml emits colspan/rowspan
(read mode, exports via the HTML path), and the markdown serializer pads
a colspan with empty cells so every row keeps the table's column count
-- rowspan stays lossy there, GFM cannot express it.

e2e drives merge and split through the toolbar; the cell selection is
made per Shift+Click because a keypress in the same tick as the
preceding click races the editor's post-click rendering (keyboard cell
selection itself works, verified interactively with a settled editor).

Closes #337

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aoPvnakfBP28nAfijgUY9
Word-style Tab navigation in tables with an accessible exit (#338)
Some checks failed
CI / Lint, typecheck, test (pull_request) Successful in 6m56s
CI / Auth e2e pack (pull_request) Failing after 2m26s
CI / Import/export fidelity gate (pull_request) Has been skipped
CI / Build container images (pull_request) Successful in 5m47s
753338f1be
Tab used to fall through to the browser's focus navigation everywhere.
Inside tables it now moves cell-wise (Shift-Tab backwards) and appends a
new row from the last cell, Word-style. Outside tables every branch
returns false, so Tab keeps leaving the editor.

Capturing Tab inside tables needs a documented way out (WCAG 2.1.2):
Escape places the cursor after the table -- unlike the arrow keys, which
reach the gap cursor (#335) only from the table's edge cells, it works
from every cell, including from a cell selection. When no textblock
follows the table it falls back to the gap cursor position. The
mechanism is announced to assistive tech via an aria-describedby hint
on the editor surface (visually hidden, de+en).

e2e: cell round trip per Tab/Shift-Tab with typed markers, row append
from the last cell, and the full keyboard-only exit (Escape, then Tab
leaves the editor). The table specs now settle briefly after the insert
-- right after it the collab sync can swallow a click's selection
update, which had the markers landing in stale selections.

Closes #338

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aoPvnakfBP28nAfijgUY9
fable-5 force-pushed issue-338-tab-navigation from 753338f1be to c17ab41a33 2026-08-15 19:32:32 +02:00 Compare
fable-5 merged commit c17ab41a33 into main 2026-08-16 05:22:18 +02:00
Sign in to join this conversation.
No description provided.