Opened 21 months ago

Last modified 9 days ago

#35874 assigned Bug

The console directive is not accessible for screenreaders.

Reported by: Sarah Boyce Owned by: Mike Edmunds
Component: Documentation Version: dev
Severity: Normal Keywords: accessibility, sphinx, sphinx-theme
Cc: accessibility Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by James Beard)

docs._ext.djangodocs.visit_console_html generates the HTML for the console directive in docs and has buttons for Linux/MacOS and Windows

This is not very screen reader friendly.
The main issues are that the labels are not read out properly (it should read out the current title but is reading the content of &#xf17c/&#xf179).
That these are clickable and what happens when you click them are also not clear.

I think it might be better to turn this into tabs: https://www.w3.org/WAI/ARIA/apg/patterns/tabs/examples/tabs-manual/

Some examples in docs: https://docs.djangoproject.com/en/5.1/intro/contributing/#getting-a-copy-of-django-s-development-version
Example html:

<div class="console-block" id="console-block-0">
<input class="c-tab-unix" id="c-tab-0-unix" type="radio" name="console-0" checked="">
<label for="c-tab-0-unix" title="Linux/macOS">/</label>
<input class="c-tab-win" id="c-tab-0-win" type="radio" name="console-0">
<label for="c-tab-0-win" title="Windows"></label>
<section class="c-content-unix" id="c-content-0-unix" style="display: block;">
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>clone<span class="w"> </span>https://github.com/YourGitHubName/django.git
</pre></div>
</div>
</section>
<section class="c-content-win" id="c-content-0-win" style="display: none;">
<div class="highlight"><pre><span></span><span class="gp">...\&gt;</span> git clone https://github.com/YourGitHubName/django.git
</pre></div>
</section>
</div>

Change History (13)

comment:1 by Sarah Boyce, 21 months ago

Type: UncategorizedBug

comment:2 by Tom Carrick, 21 months ago

Keywords: accessibility added
Triage Stage: UnreviewedAccepted

comment:3 by Johanan Oppong Amoateng, 21 months ago

Owner: set to Johanan Oppong Amoateng
Status: newassigned

comment:4 by Ahmed Nassar, 15 months ago

Owner: changed from Johanan Oppong Amoateng to Ahmed Nassar

Due to the owner's inactivity. I will be happy to work on this ticket.

comment:5 by James Beard, 7 weeks ago

Description: modified (diff)
Owner: changed from Ahmed Nassar to James Beard

Will make some improvements. Brief game plan:

  1. make the tabs look much more tab like
  2. make the generated HTML more screen reader friendly
  3. change the hover cursor to provide some visual feedback that tabs are clickable
  4. leave tabs as form inputs to retain css only presentation of the active block
  5. tidy up the layout of the block to match plain python pre-formatted code (padding on all sides, etc)

R.e. item 4, am going to assume if moving to something more semantic like <button> for the tabs required Javascript (think it might) we'd prefer not to do that.

comment:6 by Mike Edmunds, 6 weeks ago

Keywords: sphinx sphinx-theme added

If any CSS changes are needed, note that docs.djangoproject.com uses its own _console-tabs.scss, not the CSS from django/docs/_theme/djangodocs/static. (In #37138 I'm creating an updated "djangoproject" Sphinx theme that shares the scss from djangoproject.)

comment:7 by Mike Edmunds, 2 weeks ago

I'd suggest dropping the icons altogether, and changing to text labels "Unix/macOS" and "Windows".

  • Cleanly solves one of the accessibility issues
  • Removes need to vendor the FontAwesome 5 Brands font into Django
  • The current Linux penguin icon does not universally represent "Unix"
  • The current Windows logo icon is outdated (applies to Windows 8-10 only; Windows 11 removed the perspective from the windowpanes)

I've implemented this in the first commit of PR https://github.com/django/django/pull/21585, along with some other fixes and portability improvements to the Sphinx console-tabs extension. Note that this is only a partial solution to the accessibility concerns in this issue.

in reply to:  7 ; comment:8 by Natalia Bidart, 2 weeks ago

Replying to Mike Edmunds:

I'd suggest dropping the icons altogether, and changing to text labels "Unix/macOS" and "Windows".

I'm +1 to this. Two extra notes/questions:

  1. Would replacing the FA glyph code points break the website? If not, would this allows us to drop FA from the website?
  2. if we want the icons, I would strongly advice to use the FA icons instead the fonts, see potential diff:
    • docs/_static/console-tabs.css

      diff --git a/docs/_static/console-tabs.css b/docs/_static/console-tabs.css
      index 67fa0fcc502a..d24c0177f1ec 100644
      a b  
      2323    font-size: 0; /* Hide the directive's Font Awesome glyph placeholders. */
      2424}
      2525
      26 .console-block > label:before {
      27     content: attr(title); /* "Linux/macOS" or "Windows". */
      28     font-size: 0.8rem;
       26/* Tab icons (Font Awesome brand SVGs as background images). */
       27.console-block > label[for$="-unix"]:before,
       28.console-block > label[for$="-unix"]:after,
       29.console-block > label[for$="-win"]:before {
       30    content: "";
       31    display: inline-block;
       32    width: 14px;
       33    height: 14px;
       34    vertical-align: middle;
       35    background-position: center;
       36    background-size: contain;
       37    background-repeat: no-repeat;
       38}
       39
       40.console-block > label[for$="-unix"]:before {
       41    background-image: url("icons/linux.svg");
       42}
       43
       44.console-block > label[for$="-unix"]:after {
       45    margin-left: 5px;
       46    background-image: url("icons/apple.svg");
       47}
       48
       49.console-block > label[for$="-win"]:before {
       50    background-image: url("icons/windows.svg");
      2951}
      3052
      3153.console-block > input:checked + label {
    • new file docs/_static/icons/apple.svg

      diff --git a/docs/_static/icons/apple.svg b/docs/_static/icons/apple.svg
      new file mode 100644
      index 000000000000..0481dd6d5768
      - +  
       1<svg fill="#888" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc. --><path d="M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-61.9 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 49.9-11.4 69.5-34.3z"/></svg>
       2 No newline at end of file
    • new file docs/_static/icons/linux.svg

      diff --git a/docs/_static/icons/linux.svg b/docs/_static/icons/linux.svg
      new file mode 100644
      index 000000000000..22d278a4754e
      - +  
       1<svg fill="#888" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc. --><path d="M220.8 123.3c1 .5 1.8 1.7 3 1.7 1.1 0 2.8-.4 2.9-1.5.2-1.4-1.9-2.3-3.2-2.9-1.7-.7-3.9-1-5.5-.1-.4.2-.8.7-.6 1.1.3 1.3 2.3 1.1 3.4 1.7zm-21.9 1.7c1.2 0 2-1.2 3-1.7 1.1-.6 3.1-.4 3.5-1.6.2-.4-.2-.9-.6-1.1-1.6-.9-3.8-.6-5.5.1-1.3.6-3.4 1.5-3.2 2.9.1 1 1.8 1.5 2.8 1.4zM420 403.8c-3.6-4-5.3-11.6-7.2-19.7-1.8-8.1-3.9-16.8-10.5-22.4-1.3-1.1-2.6-2.1-4-2.9-1.3-.8-2.7-1.5-4.1-2 9.2-27.3 5.6-54.5-3.7-79.1-11.4-30.1-31.3-56.4-46.5-74.4-17.1-21.5-33.7-41.9-33.4-72C311.1 85.4 315.7.1 234.8 0 132.4-.2 158 103.4 156.9 135.2c-1.7 23.4-6.4 41.8-22.5 64.7-18.9 22.5-45.5 58.8-58.1 96.7-6 17.9-8.8 36.1-6.2 53.3-6.5 5.8-11.4 14.7-16.6 20.2-4.2 4.3-10.3 5.9-17 8.3s-14 6-18.5 14.5c-2.1 3.9-2.8 8.1-2.8 12.4 0 3.9.6 7.9 1.2 11.8 1.2 8.1 2.5 15.7.8 20.8-5.2 14.4-5.9 24.4-2.2 31.7 3.8 7.3 11.4 10.5 20.1 12.3 17.3 3.6 40.8 2.7 59.3 12.5 19.8 10.4 39.9 14.1 55.9 10.4 11.6-2.6 21.1-9.6 25.9-20.2 12.5-.1 26.3-5.4 48.3-6.6 14.9-1.2 33.6 5.3 55.1 4.1.6 2.3 1.4 4.6 2.5 6.7v.1c8.3 16.7 23.8 24.3 40.3 23 16.6-1.3 34.1-11 48.3-27.9 13.6-16.4 36-23.2 50.9-32.2 7.4-4.5 13.4-10.1 13.9-18.3.4-8.2-4.4-17.3-15.5-29.7zM223.7 87.3c9.8-22.2 34.2-21.8 44-.4 6.5 14.2 3.6 30.9-4.3 40.4-1.6-.8-5.9-2.6-12.6-4.9 1.1-1.2 3.1-2.7 3.9-4.6 4.8-11.8-.2-27-9.1-27.3-7.3-.5-13.9 10.8-11.8 23-4.1-2-9.4-3.5-13-4.4-1-6.9-.3-14.6 2.9-21.8zM183 75.8c10.1 0 20.8 14.2 19.1 33.5-3.5 1-7.1 2.5-10.2 4.6 1.2-8.9-3.3-20.1-9.6-19.6-8.4.7-9.8 21.2-1.8 28.1 1 .8 1.9-.2-5.9 5.5-15.6-14.6-10.5-52.1 8.4-52.1zm-13.6 60.7c6.2-4.6 13.6-10 14.1-10.5 4.7-4.4 13.5-14.2 27.9-14.2 7.1 0 15.6 2.3 25.9 8.9 6.3 4.1 11.3 4.4 22.6 9.3 8.4 3.5 13.7 9.7 10.5 18.2-2.6 7.1-11 14.4-22.7 18.1-11.1 3.6-19.8 16-38.2 14.9-3.9-.2-7-1-9.6-2.1-8-3.5-12.2-10.4-20-15-8.6-4.8-13.2-10.4-14.7-15.3-1.4-4.9 0-9 4.2-12.3zm3.3 334c-2.7 35.1-43.9 34.4-75.3 18-29.9-15.8-68.6-6.5-76.5-21.9-2.4-4.7-2.4-12.7 2.6-26.4v-.2c2.4-7.6.6-16-.6-23.9-1.2-7.8-1.8-15 .9-20 3.5-6.7 8.5-9.1 14.8-11.3 10.3-3.7 11.8-3.4 19.6-9.9 5.5-5.7 9.5-12.9 14.3-18 5.1-5.5 10-8.1 17.7-6.9 8.1 1.2 15.1 6.8 21.9 16l19.6 35.6c9.5 19.9 43.1 48.4 41 68.9zm-1.4-25.9c-4.1-6.6-9.6-13.6-14.4-19.6 7.1 0 14.2-2.2 16.7-8.9 2.3-6.2 0-14.9-7.4-24.9-13.5-18.2-38.3-32.5-38.3-32.5-13.5-8.4-21.1-18.7-24.6-29.9s-3-23.3-.3-35.2c5.2-22.9 18.6-45.2 27.2-59.2 2.3-1.7.8 3.2-8.7 20.8-8.5 16.1-24.4 53.3-2.6 82.4.6-20.7 5.5-41.8 13.8-61.5 12-27.4 37.3-74.9 39.3-112.7 1.1.8 4.6 3.2 6.2 4.1 4.6 2.7 8.1 6.7 12.6 10.3 12.4 10 28.5 9.2 42.4 1.2 6.2-3.5 11.2-7.5 15.9-9 9.9-3.1 17.8-8.6 22.3-15 7.7 30.4 25.7 74.3 37.2 95.7 6.1 11.4 18.3 35.5 23.6 64.6 3.3-.1 7 .4 10.9 1.4 13.8-35.7-11.7-74.2-23.3-84.9-4.7-4.6-4.9-6.6-2.6-6.5 12.6 11.2 29.2 33.7 35.2 59 2.8 11.6 3.3 23.7.4 35.7 16.4 6.8 35.9 17.9 30.7 34.8-2.2-.1-3.2 0-4.2 0 3.2-10.1-3.9-17.6-22.8-26.1-19.6-8.6-36-8.6-38.3 12.5-12.1 4.2-18.3 14.7-21.4 27.3-2.8 11.2-3.6 24.7-4.4 39.9-.5 7.7-3.6 18-6.8 29-32.1 22.9-76.7 32.9-114.3 7.2zm257.4-11.5c-.9 16.8-41.2 19.9-63.2 46.5-13.2 15.7-29.4 24.4-43.6 25.5s-26.5-4.8-33.7-19.3c-4.7-11.1-2.4-23.1 1.1-36.3 3.7-14.2 9.2-28.8 9.9-40.6.8-15.2 1.7-28.5 4.2-38.7 2.6-10.3 6.6-17.2 13.7-21.1.3-.2.7-.3 1-.5.8 13.2 7.3 26.6 18.8 29.5 12.6 3.3 30.7-7.5 38.4-16.3 9-.3 15.7-.9 22.6 5.1 9.9 8.5 7.1 30.3 17.1 41.6 10.6 11.6 14 19.5 13.7 24.6zM173.3 148.7c2 1.9 4.7 4.5 8 7.1 6.6 5.2 15.8 10.6 27.3 10.6 11.6 0 22.5-5.9 31.8-10.8 4.9-2.6 10.9-7 14.8-10.4s5.9-6.3 3.1-6.6-2.6 2.6-6 5.1c-4.4 3.2-9.7 7.4-13.9 9.8-7.4 4.2-19.5 10.2-29.9 10.2s-18.7-4.8-24.9-9.7c-3.1-2.5-5.7-5-7.7-6.9-1.5-1.4-1.9-4.6-4.3-4.9-1.4-.1-1.8 3.7 1.7 6.5z"/></svg>
       2 No newline at end of file
    • new file docs/_static/icons/windows.svg

      diff --git a/docs/_static/icons/windows.svg b/docs/_static/icons/windows.svg
      new file mode 100644
      index 000000000000..da4cae6152ae
      - +  
       1<svg fill="#888" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc. --><path d="M0 93.7l183.6-25.3v177.4H0V93.7zm0 324.6l183.6 25.3V268.4H0v149.9zm203.8 28L448 480V268.4H203.8v177.9zm0-380.6v180.1H448V32L203.8 65.7z"/></svg>
       2 No newline at end of file

in reply to:  8 comment:9 by Mike Edmunds, 2 weeks ago

Replying to Natalia Bidart:

  1. Would replacing the FA glyph code points break the website? If not, would this allows us to drop FA from the website?

Either option here (text or svg console-tabs labels) would allow us to drop the vendored fa-brands v5.0.4 fonts from the Django docs source. We'd remove the private-area FA glyphs from the rendered html.

djangoproject.com has its own vendored copy of the full FontAwesome v4.2.0 fonts. It uses those for icons throughout the site, including admonition icons on docs.djangoproject.com, so that wouldn't be affected. (djangoproject.com also has its own copy of the console-tabs stylesheet, which would need to be updated to track changes here—and I guess to somehow be simultaneously compatible with both old and new tabs html. 🤔)

[I'm tempted to suggest migrating to a third-party, generalized sphinx tabs package like sphinx-inline-tabs or sphinx-design. That would support, e.g., a third console tab for PowerShell, or breaking apart macOS and Unix if that's ever necessary, or even tabbed Jinja and Django template examples or sync+async examples. But that would be a separate discussion and ticket.]

comment:10 by Natalia Bidart, 2 weeks ago

Mike, thanks for the details! also, shall we assign this issue to you given your two PRs?

Last edited 2 weeks ago by Natalia Bidart (previous) (diff)

in reply to:  10 comment:11 by Mike Edmunds, 2 weeks ago

Replying to Natalia Bidart:

Mike, thanks for the details! also, shall we assign this issue to you given your two PRs?

Let's leave this ticket with James for now.

There are a bunch of accessibility issues with the console tabs:

  1. The FontAwesome glyphs as reported here
  2. Screen reader behavior in general (e.g., incorrect roles)
  3. No keyboard navigation support (selecting a tab currently requires a pointer device)
  4. Choice of tab is not synchronized/persistent (so Windows users have to activate their preferred tabs repeatedly on pages with multiple console examples, or after navigating; solution would require JS)
  5. The use of text-align: right and text-align: left probably causes problems for rtl

My PRs for #37138 only solve the first problem right now, and only because I needed to get rid of the fa-brands fonts while adapting the djangodocs extension to the other Sphinx themes. I'd defer to any progress James has made on tab accessibility in general. (But if the scope of console-tabs changes in the other ticket starts to grow, I'll come back and claim this ticket too.)

Here are a couple of tab implementations that are meant to be accessible and work without JS enabled (though may be progressively enhanced when JS is available). The first one has a DOM structure pretty close to our own, so adapting it might be easier. But I'm not qualified to evaluate the screenreader support (and am mindful of "no ARIA is better than bad ARIA"):

One other thought: if we're making substantial changes to the DOM and CSS, we might want to change the class names at the same time (e.g., change to .dj-console-tabs). That would help djangoproject.com keep a single CSS file that supports old and new DOM across multiple Django versions.

Last edited 2 weeks ago by Mike Edmunds (previous) (diff)

comment:12 by Mike Edmunds, 12 days ago

Owner: changed from James Beard to Mike Edmunds

After more investigation, I think we should handle this ticket in two stages. I'm claiming it to work on the first stage because that's also related to #37138.

There seems to be widespread agreement that fully accessible, screen-reader friendly tabs require JavaScript. We can make our CSS-only, hidden-radio-button tabs work better than they are now with keyboard nav and screen readers, but there's no way to make them truly behave correctly as tabs without using JS. (Attempts to assign ARIA tab roles to radio buttons and surrounding DOM—like the post I referenced in an earlier comment—fall into the "bad ARIA is worse than no ARIA" category. They claim to be tabs, but the behavior is not quite right.)

So my proposal is:

Stage 1: Make the current (CSS-only, hidden radio button) tabs support standard keyboard navigation for radio buttons and announce themselves as radio buttons in screen readers. Use text labels, eliminate the use of FontAwesome brand icons, and drop the vendored fa-brands font. (We might also try to follow the ARIA guidelines around high-contrast compatibility.)

Stage 2: Add JS that progressively enhances the console tabs to follow the ARIA tabs with automatic activation pattern, announce themselves as tabs, and implement all recommended keyboard navigation for tabs. While we're adding JS, also make the console tab selection synchronized within a page and persistent across pages.

I've started work on stage 1, but I'll leave the stage 2 JS for someone else later. That's a bigger project that requires either finding a tab component that's been extensively tested for accessibility¹ or developing our own JS and carefully testing it with a variety of screen readers and browsers. (Note that the WAI-ARIA example code says it shouldn't be used in production because it hasn't had that testing.)

Also, a big caveat on all of this is I'm not a screen reader user, so I can't speak to what regular users would or wouldn't find acceptable. And though I've done some exploration with macOS VoiceOver, Windows Narrator, and NVDA while researching this, I haven't fully tested all browser and screen reader combinations, or tried anything on mobile.

¹ Some possible, accessible tab components: Spectrum Web Components sp-tabs, GitHub tab-container-element, Web Awesome tab-group.

comment:13 by Mike Edmunds, 9 days ago

Has patch: set

Here's "stage 1" that tries to improve accessibility on the current, CSS-only, hidden radio-button implementation: https://github.com/django/django/pull/21614 [for review].

And a proof-of-concept "stage 2" that uses JS to replace the CSS-only tabs with an ARIA-compliant tab component: https://github.com/django/django/pull/21615 [AI-generated, not intended for merge].

Last edited 9 days ago by Mike Edmunds (previous) (diff)
Note: See TracTickets for help on using tickets.
Back to Top