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

Convert admonition blockquotes to sections for screen reader users #1938

Merged
merged 16 commits into from
Aug 16, 2024

Conversation

voughtdq
Copy link
Contributor

@voughtdq voughtdq commented Aug 10, 2024

Some screen readers will announce that an element is a blockquote. It could be potentially surprising to hear something announced as a blockquote despite the inner contents not being a quote.

References:

Closes #1939

Some screen readers will announce that an element is a blockquote.
It could be potentially surprising to hear something announced as a
blockquote despite the inner contents  not being a quote.
|> String.split(" ")
|> Enum.filter(&(&1 in @admonition_classes))
|> Enum.join(" ")
end)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to keep everything self-contained instead of adding helper functions, so that's why this is a little messy. The "coercion" to "" is there since the call to Enum.join/2 will return an empty string for an empty list.

@voughtdq
Copy link
Contributor Author

Might not need this javascript anymore.

@whatyouhide
Copy link
Member

Would it make sense to turn this into a <section> instead? That's what the first blog post you linked to does.

@voughtdq
Copy link
Contributor Author

I don't see why not! I'll push some changes.

@voughtdq voughtdq changed the title Convert admonition blockquotes to divs for screen reader users Convert admonition blockquotes to sections for screen reader users Aug 12, 2024
@josevalim
Copy link
Member

/cc @DavidOliver, who, iirc, worked on this feature.

/cc @wojtekmach, who, iirc, worked on fixup.

@voughtdq can you please take a look at tabset.css and print.css? They also reference blockquotes and I think they should be addressed as well. There may be others.

@josevalim
Copy link
Member

Might not need this javascript anymore.

That looks like the case as well. We should give it a try. Thank you!

@voughtdq
Copy link
Contributor Author

I think we're good to go. @DavidOliver can you take a look?

assets/css/print.css Outdated Show resolved Hide resolved
@DavidOliver
Copy link
Contributor

DavidOliver commented Aug 12, 2024

@voughtdq, so far I've only looked through the latest CSS changes - I haven't tried out your branch yet - but I've only found one thing remaining, which I've commented on directly.

I would usually tend not to rely on section.admonition > :first-child and stick to section.admonition :is(h3, h4) (without the additional error, info, etc. classes), to avoid being dependent on markup order, but I appreciate the headings will almost certainly be the first children and that the markup is controlled.

Thanks.

@voughtdq
Copy link
Contributor Author

(Saw your other comment about the selector I missed, pushed those changes - sorry about that).

I agree that using :first-child is not ideal.

The problem I'm running into with :is(h3, h4) is that it will affect all child heading elements. So something like this:

> ### Warning {: .warning}
> Don't feed your dog too much peanut butter.
>
> #### What about my cats?
> They don't really like peanut butter.

will end up having the second h4 given the styles applied to :is(h3, h4). It can be discriminated further with > :is(h3, h4):first-child, but at that point, since we already have control of the markup, > :first-child is basically equivalent.

In this case, I think it's pragmatic to use > :first-child to avoid applying styles to subsequent heading elements. There's a real possibility that someone might want additional headings in their admonition.

That said, I never have strong opinions on these things, so I'm open to alternatives.

The alternative I can think of is adding an additional class (e.g. .admonition) to the heading element that gets an admonition class and then select on section.admonition .admonition.

@DavidOliver
Copy link
Contributor

@voughtdq, I see. Thanks!

@josevalim
Copy link
Member

@voughtdq given that we control the markup, we can adjust the markup by adding a .header or title to the h3/h4. As you prefer. Whatever leads to the best combination of code + CSS.

However, I am remembering that we explicitly chose to not go down this path. We didn't want to change the generated Markdown because that would further couple us to the underlying markdown engine and we didn't want to have a subset/superset of Markdown. So it was very important that our rules were expressed with only CSS. Perhaps it is fine to deviate at this point, but I wanted to write down why we didn't go with this approach early on.

This is a common practice with other markdown/documentation generators.
@voughtdq
Copy link
Contributor Author

I took some time to consult how other markdown/documentation implementations do it. There is a consensus of setting a class for the admonition title, but not on what elements/classes to use. I see a lot of implementations that use .admonition-title with a few others using less common names. I've decided to go ahead and add the .admonition-title class.

There is a lot of fragmentation in markdown admonition syntax and then further fragmentation in what HTML is generated for it. I anticipate that any new engines added, markdown or otherwise, will require transformation of the admonition markup simply because there's no true consensus or standard.

blockquote_meta}
)
when tag in ["h3", "h4"] do
{h_classes, h_attrs} = Enum.split_with(h_attrs, &match?({"class", _}, &1))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also use List.keytake and similar. :) Similar below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'm glad to hear that 😅
I think I can make it look better with keytake/keystore

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you take, you probably don't need keystore, you can just prepend it: [{"class", val} | attrs]

@josevalim
Copy link
Member

@voughtdq I dropped one minor nitpick. Also, can you please go ahead and delete the JavaScript function you linked to? https://github.com/elixir-lang/ex_doc/blob/v0.34.2/assets/js/content.js#L37-L45 Then I will merge it! Thank you!

@voughtdq
Copy link
Contributor Author

voughtdq commented Aug 15, 2024

Working on that when I wake up.

I have a clearer implementation with List.keyfind/List.keystore, but it also means different ordering of attrs in some cases. Is that ok? I will go back to keytake/append if not ok.

@josevalim
Copy link
Member

Yes, it is ok!

keyfind/keystore didn't really improve things that much, so using
keytake/prepend as originally suggested
@josevalim josevalim merged commit 95056f7 into elixir-lang:main Aug 16, 2024
5 checks passed
@josevalim
Copy link
Member

💚 💙 💜 💛 ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Admonition blockquotes should be converted to divs
4 participants