Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

landmark-unique violation at /examples/kitchen-sink/generic.html #1479

Closed
Tracked by #1428
gabalafou opened this issue Sep 26, 2023 · 5 comments
Closed
Tracked by #1428

landmark-unique violation at /examples/kitchen-sink/generic.html #1479

gabalafou opened this issue Sep 26, 2023 · 5 comments
Assignees
Labels
tag: accessibility Issues related to accessibility issues or efforts

Comments

@gabalafou
Copy link
Collaborator

gabalafou commented Sep 26, 2023

Sub-issue of #1428.

The landmark-unique rule violation shows up on the "Generic" page as a result of the <aside> tags within the <article> not having an accessible name (role + label/title).

(Note: same violation shows up in an aside on the Theme Elements page.)

Origin of the asides

How do these <aside> tags get generated? I believe they are created by Sphinx when converting a sidebar directive. For example, let's look at the following directive from the docs source:

.. sidebar:: Ch'ien / The Creative
Lorem ipsum dolor sit amet consectetur adipisicing elit.
.. image:: https://source.unsplash.com/200x200/daily?cute+puppy
Lorem ipsum dolor sit amet consectetur adipisicing elit.

This gets converted to the following HTML:

<aside class="sidebar">
<p class="sidebar-title">Ch’ien / The Creative</p>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
<img alt="https://source.unsplash.com/200x200/daily?cute+puppy" src="https://source.unsplash.com/200x200/daily?cute+puppy">
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
</aside>
@gabalafou gabalafou changed the title landmark-unique at /examples/kitchen-sink/generic.html landmark-unique violation at /examples/kitchen-sink/generic.html Sep 27, 2023
@trallard trallard added the tag: accessibility Issues related to accessibility issues or efforts label Sep 27, 2023
@gabalafou
Copy link
Collaborator Author

gabalafou commented Dec 22, 2023

So I did some digging into this issue and found some interesting results.

tl;dr-- Consider overriding Docutils sidebar-to-aside conversion with sidebar-to-div.

In order to understand this issue, I realized that I needed to keep two things separate and distinct in my head, ARIA versus HTML:

  • In ARIA, the complementary role creates a landmark and therefore should be at the same DOM level as the main landmark and should be used sparingly (to avoid landmark noise). It is used for content that is complementary to the main content, hence the name.
  • In HTML, the aside tag was also designed for complementary content -- tangentially related but could be considered separate.

The thing to keep in mind is that browsers have to perform a mapping between the aside tag and the complementary role; it doesn't just happen automatically.

And this is where the catch comes in. There is a standard that defines these kinds of mappings between HTML and ARIA, the HTML Accessibility API Mappings. It defines two separate mappings for the aside tag, depending on how it's used in the overall HTML document:

So when the aside tag is place at the same level as the body or main, it should become a landmark. But when it's inside, say, an article tag, as in the generic.html doc page, then it should not be made into a landmark.

But here's another catch. Based on my testing of Safari, Firefox and Chrome, only Chrome follows the spec. Safari and Firefox both create landmarks for an unlabeled/untitled aside within an article, even though the spec says they shouldn't.

The spec makes a lot of sense to me. At first, I thought that maybe it was wrong for Docutils to convert sidebar directives into HTML aside tags. But after some consideration, I changed my mind. Consider the following doc, for example:

A Long Entry About a Country
============================

Here there would be an intro paragraph about the country.

.. sidebar:: Facts and figures

    Here there could be important facts and figures about 
    the country, things like population, land area, etc.

And then the rest of the long article about the country continues.

If this RST doc were converted to a standalone HTML doc, then it would make sense for the sidebar content to exist in landmarked aside. It's only when the doc gets embedded into a larger site that has a segmented layout with header, footer, navigation, main area, left sidebar, right sidebar -- that's when you risk creating landmark noise if the .. sidebar:: directives get added to all the other landmarks needed for the site. I think the very fact that the directive is called "sidebar" is telling. I think it's meant to create a sidebar that's more like one you would find in a book. So when it's embedded into a website, you get an ambiguity between "article sidebar" versus "site sidebar."

So that's why Chrome's behavior, which follows the spec, makes sense to me.

But that still leaves us with the question of what we should do. Like I said, I don't think Docutils is wrong to output aside tags when converting RST to HTML. It can't possibly know ahead of time what context the HTML will be embedded into, and the html5 spec itself provides examples that justify this kind of usage. But on the other hand, it seems that a lot of browsers automatically asign role=complementary to all aside tags, regardless of what level they appear in the overall web page.

So we can't change Docutils. Fixing Firefox, Safari and other browsers is hard. One thing I think we could consider: since we know ahead of time that sites that use our theme will be building the docs in the context of a website, we could consider overriding the converter so that instead of outputting <aside>s for the sidebar directives, it could output a div instead, which is what the classic html Docutils writer does.

Resources:

@drammock
Copy link
Collaborator

very interesting! Thanks for the clear summary of the issue. Having just spent a week trying to wrestle sphinx/docutils into submission on a different issue, I feel both daunted by your proposed solution and also kindof think I know how one would do it. It probably would involve writing a SphinxPostTransform to modify doctree prior to writing pages. Something like

from docutils import nodes
from sphinx.transforms.post_transforms import SphinxPostTransform

class Foo(SphinxPostTransform):

    default_priority = 400
    builders = ("html", "dirhtml")
    formats = ("html",)

    def run(self, **kwargs):
        for node in self.document.findall(nodes.aside):
            # do what is needed here, possibly with:
            foo = nodes.Node()  # edit as appropriate
            # ↑↑↑ populate with the content from `node`.
            #     May need to be nodes.Text() or something else.
            #     Not sure without researching how to get docutils
            #     to just write a bare `div`
            node.replace_self(foo)
            # ...or if there's a simpler way to just change the node
            #    type without recreating/replacing, do that instead?

and then in the setup() func in our __init__.py

    app.add_post_transform(Foo)

Hopefully that gives you a nice running start... I won't have time to look at this until February probably (January is heavily committed for me).

@gabalafou
Copy link
Collaborator Author

gabalafou commented Apr 24, 2024

@drammock I took a stab at this (#1783). I wasn't sure about your proposed approach because it seems to rely on the idea that there is such a thing as a Docutils nodes.aside. By my accounting, there is no such node, but Docutils recognizes the following nodes and converts these to <aside> tags with its HTML5 writer: nodes.sidebar, nodes.footnote, nodes.admonition, and nodes.system_message. Interestingly, Sphinx throws a bit of a wrench into this by rewiring nodes.admonition so that it outputs a div instead of an aside, and also remaps the nodes.topic to (in most cases) output an aside instead of a div.

If you think your approach might still work, then I'm missing something and I'll need a bit more help.

@gabalafou
Copy link
Collaborator Author

@trallard, I started to file an issue with Sphinx about this, as we discussed, but in the time since I last investigated browser behavior (in December), this issue has apparently been fixed in other browsers. I cannot reproduce the problem in Firefox or Safari on Mac with VoiceOver.

I tracked down the issue in Firefox: Conditional role mapping for aside element (WPT html-aam/roles-contextual.html el-aside-in-section-without-name). It looks like it's been fixed.

Same story for WebKit (Safari): Assign generic role to aside tag within the sectioning content elements #20013

On Windows, I got correct behavior with NVDA when testing with Chrome and Edge, but not with Firefox.

With the latest version of NVDA (2024.1) and Firefox (125.0.3) on Windows, when I navigate landmarks via the d key on the Kitchen Sink Generic Items page, I still get taken to the sidebars and footnotes, which is super weird because when I inspect the accessibility tree with the Firefox dev tools, it's clear that Firefox correctly gives those asides role=generic, so I'm not exactly sure how NVDA ends up interpreting them as landmarks.

But given that this seems fixed in most browsers now, I don't think it makes sense to try to implement a browser workaround either in our theme or in Sphinx.

There's really only one thing left to do here, which is to make sure that there's an issue on the Axe-core repository to update the landmark-unique test, which does not seem to be aware that sectioned asides are no longer landmarks in most browsers.

@gabalafou
Copy link
Collaborator Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tag: accessibility Issues related to accessibility issues or efforts
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants