Skip to content

sketch of configurable keyboard interaction patterns for tables¤

html only without interactive javascript. table roles can be quite flexible depending upon the contents of the data. when we get to role=grid and role=treegrid we need to implement keyboard shortcuts for navigation and selection. the keyboard shortcuts should be configurable.

from nbconvert_a11y.repr import get_table, new
from nbconvert_a11y.table import aria

grid and treegrid pattern appear the same

need patterns for navigation and selection

to design for accessibility we isolate every keyboard action as a button. each button element recieves an isolated function for a single action.

%%
use a random dataframe to seed our table region

    section = get_table(DataFrame(numpy.random.randn(3, 4)))
define navigation actions

    section.form.append(
        new("fieldset", new("legend", "navigation"),
        new("label", new("input", type="checkbox", checked=""), "allow navigation"),    
        new("button", "first cell in table", **aria(keyshortcuts="Ctrl+Home")),
        new("button", "left", **aria(keyshortcuts="ArrowLeft")),
        new("button", "right", **aria(keyshortcuts="ArrowRight")),
        new("button", "up", **aria(keyshortcuts="ArrowUp")),
        new("button", "down", **aria(keyshortcuts="ArrowDown")),
        new("button", "first cell in row", **aria(keyshortcuts="Home")),
        new("button", "last cell in row", **aria(keyshortcuts="End")),
        new("button", "next group", **aria(keyshortcuts="PageDown")),
        new("button", "prior group", **aria(keyshortcuts="PageUp")),
        new("button", "last cell in table", **aria(keyshortcuts="Ctrl+End")),
    ))

define selection actions

    section.form.append(
        new("fieldset", 
            new("legend", "selection"),
            new("label", new("input", type="checkbox", checked=""), "allow selection"),
            new("button", "select current column", **aria(keyshortcuts="Ctrl+End")),
            new("button", "select current row", **aria(keyshortcuts="Ctrl+End")),
            new("button", "select and focus cell to the left", **aria(keyshortcuts="Shift+ArrowLeft")),
            new("button", "select and focus cell to the right", **aria(keyshortcuts="Shift+ArrowRight")),
            new("button", "select and focus cell above", **aria(keyshortcuts="Shift+ArrowUp")),
            new("button", "select and focus cell below", **aria(keyshortcuts="Shift+ArrowDown")),
        ))
    HTML(section)
pandas dataframe with 3 rows, 4 columns with 1 index levels and 1 columns levels.
None0123
00.230-0.807-1.0920.011
10.3120.351-0.864-0.655
21.083-0.4790.0391.272
min0.230-0.807-1.092-0.655
max1.0830.3510.0391.272
diff0.8531.1571.1311.928
navigation
selection

use a random dataframe to seed our table region

section = get_table(DataFrame(numpy.random.randn(3, 4)))

define navigation actions

section.form.append(
    new("fieldset", new("legend", "navigation"),
    new("label", new("input", type="checkbox", checked=""), "allow navigation"),    
    new("button", "first cell in table", **aria(keyshortcuts="Ctrl+Home")),
    new("button", "left", **aria(keyshortcuts="ArrowLeft")),
    new("button", "right", **aria(keyshortcuts="ArrowRight")),
    new("button", "up", **aria(keyshortcuts="ArrowUp")),
    new("button", "down", **aria(keyshortcuts="ArrowDown")),
    new("button", "first cell in row", **aria(keyshortcuts="Home")),
    new("button", "last cell in row", **aria(keyshortcuts="End")),
    new("button", "next group", **aria(keyshortcuts="PageDown")),
    new("button", "prior group", **aria(keyshortcuts="PageUp")),
    new("button", "last cell in table", **aria(keyshortcuts="Ctrl+End")),
))

define selection actions

section.form.append(
    new("fieldset", 
        new("legend", "selection"),
        new("label", new("input", type="checkbox", checked=""), "allow selection"),
        new("button", "select current column", **aria(keyshortcuts="Ctrl+End")),
        new("button", "select current row", **aria(keyshortcuts="Ctrl+End")),
        new("button", "select and focus cell to the left", **aria(keyshortcuts="Shift+ArrowLeft")),
        new("button", "select and focus cell to the right", **aria(keyshortcuts="Shift+ArrowRight")),
        new("button", "select and focus cell above", **aria(keyshortcuts="Shift+ArrowUp")),
        new("button", "select and focus cell below", **aria(keyshortcuts="Shift+ArrowDown")),
    ))
HTML(section)
%%
when we consistently define `aria-keyshortcuts` we recieve canonical accessors
for defining a widget for summarizing and modifying the keyboard shortcuts.

it is most likely that this will be hidden in `dialog` to reduce the elements interfering with content.

    shortcuts = Series(section.select("[aria-keyshortcuts], [accesskey]"))

    table = DataFrame(
        index=Index(shortcuts.attrgetter("string"), name="action")
    ).assign(
        shortcut=shortcuts.attrgetter("attrs").series(
        )["aria-keyshortcuts"].apply(
            lambda x: new("kbd", x)
        ).values,
        update=list(new("button", "update") for x in range(len(shortcuts)))
    ).pipe(get_table, caption="keyboard shortcuts")
    table.table.caption.clear()
    HTML(table)
keyboard shortcuts
actionshortcutupdate
'first cell in table'Ctrl+Home
'left'ArrowLeft
'right'ArrowRight
'up'ArrowUp
'down'ArrowDown
'first cell in row'Home
'last cell in row'End
'next group'PageDown
'prior group'PageUp
'last cell in table'Ctrl+End
'select current column'Ctrl+End
'select current row'Ctrl+End
'select and focus cell to the left'Shift+ArrowLeft
'select and focus cell to the right'Shift+ArrowRight
'select and focus cell above'Shift+ArrowUp
'select and focus cell below'Shift+ArrowDown

when we consistently define aria-keyshortcuts we recieve canonical accessors for defining a widget for summarizing and modifying the keyboard shortcuts.

it is most likely that this will be hidden in dialog to reduce the elements interfering with content.

shortcuts = Series(section.select("[aria-keyshortcuts], [accesskey]"))

table = DataFrame(
    index=Index(shortcuts.attrgetter("string"), name="action")
).assign(
    shortcut=shortcuts.attrgetter("attrs").series(
    )["aria-keyshortcuts"].apply(
        lambda x: new("kbd", x)
    ).values,
    update=list(new("button", "update") for x in range(len(shortcuts)))
).pipe(get_table, caption="keyboard shortcuts")
table.table.caption.clear()
HTML(table)

marginalia¤

there likely needs to be a visual representation of the current cell.

collapse : tree :: select : grid