I think the Django admin should have a skip link on all of its pages, so
keyboard users can bypass:
- The header area
- The sidebar
Here is a simple example to illustrate the problem – it currently takes 32
tab stops to reach the page’s main content on my demo site:
With a skip link, this is reduced to 2 tab stops (and one press of Enter
to navigate):
--
Ticket URL: <https://code.djangoproject.com/ticket/33726>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
* Attachment "2-tab-stops-one-enter.png" added.
* Attachment "32-tab-stops.png" added.
Old description:
> [https://www.a11yproject.com/posts/skip-nav-links/ Skip links] are a very
> common pattern to help keyboard go straight to a page’s main content.
> They aren’t technically a requirement to meet accessibility standards,
> but they’re recommended as nice-to-have way to help with issues such as
> those targeted by WCAG [https://www.w3.org/WAI/WCAG21/Understanding
> /bypass-blocks.html SC 2.4.1: Bypass Blocks].
>
> I think the Django admin should have a skip link on all of its pages, so
> keyboard users can bypass:
>
> - The header area
> - The sidebar
>
> Here is a simple example to illustrate the problem – it currently takes
> 32 tab stops to reach the page’s main content on my demo site:
>
> With a skip link, this is reduced to 2 tab stops (and one press of Enter
> to navigate):
New description:
[https://www.a11yproject.com/posts/skip-nav-links/ Skip links] are a very
common pattern to help keyboard go straight to a page’s main content. They
aren’t technically a requirement to meet accessibility standards, but
they’re recommended as nice-to-have way to help with issues such as those
targeted by WCAG [https://www.w3.org/WAI/WCAG21/Understanding/bypass-
blocks.html SC 2.4.1: Bypass Blocks].
I think the Django admin should have a skip link on all of its pages, so
keyboard users can bypass:
- The header area
- The sidebar
Here is a simple example to illustrate the problem – it currently takes 32
tab stops to reach the page’s main content on my demo site:
[[Image(https://code.djangoproject.com/raw-attachment/ticket/33726/32-tab-
stops.png)]]
With a skip link, this is reduced to 2 tab stops (and one press of Enter
to navigate):
[[Image(https://code.djangoproject.com/raw-attachment/ticket/33726/2-tab-
stops-one-enter.png)]]
--
--
Ticket URL: <https://code.djangoproject.com/ticket/33726#comment:1>
* stage: Unreviewed => Accepted
--
Ticket URL: <https://code.djangoproject.com/ticket/33726#comment:2>
Comment (by Thibaud Colas):
Here are reference acceptance criteria for a skip link, if someone is
interested in giving this a shot, as well as a reference implementation,
from [https://www.magentaa11y.com/checklist-web/skip-link/ MagentaA11y]:
{{{
How to test a skip link
1. Test keyboard only, then keyboard + screenreader
- Tab: Focus moves to the skip link
- Enter: Activates the link, focus/tabindex moves directly to the targeted
element
2. Test mobile screenreader gestures
- Swipe: Focus moves to the skip link
- Doubletap: Activates the link, focus/tabindex moves directly to the
targeted element
3. Listen to screenreader output on all devices
- Name: It describes which landmark it's targeting
- Role: It identifies itself as a link
- Group: It is typically the first element in the page
Full information: https://www.magentaa11y.com/checklist-web/skip-link/
}}}
For Django in particular,
- The skip link’s ("Skip to main content") text needs to be translate-
able.
- Screen reader tests can be done by our accessibility team if someone
wants to contribute to this and doesn’t know how to do this type of
testing (if you do, please test on either VoiceOver + Safari or NVDA +
Firefox for desktop, and either Android TalkBack or iOS VoiceOver on
mobile).
- Not sure if this is required but the keyboard support can likely be
demonstrated by writing an automated test for this with Selenium.
--
Ticket URL: <https://code.djangoproject.com/ticket/33726#comment:3>
* owner: nobody => Fab
* status: new => assigned
--
Ticket URL: <https://code.djangoproject.com/ticket/33726#comment:4>
* Attachment "input_focus.jpg" added.
Input being focused
--
Ticket URL: <https://code.djangoproject.com/ticket/33726>
Comment (by Fabian Jarrett):
When there is a search bar the focus already starts in the main content
area. Should skip links be hidden if they are not needed?
[[Image(input_focus.jpg)]]
--
Ticket URL: <https://code.djangoproject.com/ticket/33726#comment:5>
* owner: Fabian Jarrett => (none)
* status: assigned => new
--
Ticket URL: <https://code.djangoproject.com/ticket/33726#comment:6>
* owner: (none) => Marcelo Galigniana
* status: new => assigned
--
Ticket URL: <https://code.djangoproject.com/ticket/33726#comment:7>
* needs_better_patch: 0 => 1
* has_patch: 0 => 1
Comment:
[https://github.com/django/django/pull/15837 PR]
--
Ticket URL: <https://code.djangoproject.com/ticket/33726#comment:8>
* needs_better_patch: 1 => 0
--
Ticket URL: <https://code.djangoproject.com/ticket/33726#comment:9>
* needs_better_patch: 0 => 1
Comment:
Small issues remaining on PR
--
Ticket URL: <https://code.djangoproject.com/ticket/33726#comment:10>
* needs_better_patch: 1 => 0
Comment:
Changes applied!
--
Ticket URL: <https://code.djangoproject.com/ticket/33726#comment:11>
* needs_better_patch: 0 => 1
Comment:
Per Thibaud's
[https://github.com/django/django/pull/15837#pullrequestreview-1041060646
review].
--
Ticket URL: <https://code.djangoproject.com/ticket/33726#comment:12>
* needs_better_patch: 1 => 0
--
Ticket URL: <https://code.djangoproject.com/ticket/33726#comment:13>
* stage: Accepted => Ready for checkin
Comment:
I think this is good to go. Just waiting for a final review/OK from
Thibaud and a squash/rebase.
--
Ticket URL: <https://code.djangoproject.com/ticket/33726#comment:14>
* status: assigned => closed
* resolution: => fixed
Comment:
In [changeset:"564437f767eaa54bb6af86d2ebd2363e49a50421" 564437f7]:
{{{
#!CommitTicketReference repository=""
revision="564437f767eaa54bb6af86d2ebd2363e49a50421"
Fixed #33726 -- Added skip-link to admin for keyboard navigation.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/33726#comment:15>