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

Alternative take on hgroup #5002

Closed
annevk opened this issue Oct 11, 2019 · 25 comments
Closed

Alternative take on hgroup #5002

annevk opened this issue Oct 11, 2019 · 25 comments
Labels

Comments

@annevk
Copy link
Member

annevk commented Oct 11, 2019

#3499 got quite a few comments, mostly from @sideshowbarker (and at w3c/html-aam#123 from @stevefaulkner), about hgroup.

I've been thinking about this slight change to compute headings and heading levels to address the concerns raised:

  • hgroup is no longer a heading itself. It becomes a container that can influence heading levels. (It has no level itself.)
  • To determine the level of an h1 element, return the number of ancestor sections + 1 (as per current PR).
  • To determine the level of an hN element whereby N is in the range 2 to 6, inclusive: if parent is hgroup, then return the number of ancestor sections + N; otherwise, if parent is not hgroup, return N.

I think this would also be in line with the existing way these elements get rendered when used in combination and how hgroup is typically used.

cc @whatwg/a11y

@Crissov
Copy link

Crissov commented Oct 11, 2019

How about giving up the idea that heading levels must be integers?

  • To determine the level of an hgroup element,
    return the number of ancestor sections + 1.
  • To determine the level of an hN element
    whereby N is in the range 1 to 6, inclusive:
    if parent is hgroup,
    then return the parentʼs level + N/10;
    otherwise, if parent is not hgroup,
    return the number of ancestor sections + N.

This could be extended, e. g.:

  • To determine the level of an hr element,
    if it is preceded by any heading within its section,
    then return the floored level of the latest heading + 7/10,
    otherwise, if no heading precedes it,
    return the number of ancestor sections + 7/10.
  • To determine the level of a caption, figcaption or legend element,
    return the number of ancestor sections + 7/10.
  • To determine the level of a th element,
    if it has a scope attribute set to either colgroup or rowgroup,
    then return the number of ancestor sections + 8/10,
    otherwise, if it has either no scope attribute or one set to any other value,
    then return the number of ancestor sections + 9/10.

@annevk
Copy link
Member Author

annevk commented Oct 11, 2019

How about giving up the idea that heading levels must be integers?

Reinventing how headings work from scratch is unfortunately even less likely to succeed. We need to interoperate with the existing accessibility and general infrastructure for this stuff. Also, we need this to be extremely simple and efficient. There's a reason the text in the current standard hasn't been adopted in the past fifteen years.

@scottaohara
Copy link
Collaborator

scottaohara commented Oct 11, 2019

Am I correct in seeing this as a way to mitigate against authors not respecting the content model for hgroup, so the misuse that Steve noted about authors using non-heading / scripting elements would not try to then merge all that content into a single heading?

Heading level aside for a moment, would it then be correct of me to interpret that the following:

<hgroup>
  <h2>Primary text</h2>
  <p>Published: mm/yyyy</p>
  <h4>h4 because HTML for styling reasons!</h4>
</hgroup>

would combine the two headings into a single heading level N? (based on how i understand this text and again I know the p in there would be invalid) And as far as the accessibility tree is concerned, the p would come after the hN?
Essentially exposed like:

<hN>Primary text, h4 because....</hN>
<p>Published...</p>

@annevk
Copy link
Member Author

annevk commented Oct 11, 2019

@scottaohara yeah, Steve's concern about existing content and Mike's concern about the existing standard encouraging usage of h2-h6 inside hgroup of which the heading level would be bumped too much as a result.

As for your example, per the algorithm in OP it would end up with two headings, "Primary text" (with level 2, assuming no ancestors) and "h4 because HTML for styling reasons!" (with level 4, same assumption).

Per the algorithm in my PR it would indeed become a single heading "Primary text Published: mm/yyyy h4 because HTML for styling reasons!" (with level 1, same assumption), but this issue is specifically about changing the approach in that PR if we can find agreement that OP would be better.

@Crissov
Copy link

Crissov commented Oct 11, 2019

Well, you could probably express something equivalent without fractional levels by introducing global (= traditional) and local levels, both integers:

  • To determine the (global) level of an hgroup element,
    return the number of ancestor sections + 1.
    Its local level is always 0.
  • To determine the global level of an hN element
    whereby N is in the range 1 to 6, inclusive:
    if parent is hgroup,
    then return the parentʼs level;
    otherwise, if parent is not hgroup,
    return the number of ancestor sections + N.
  • To determine the local level of an hN element
    whereby N is in the range 1 to 6, inclusive:
    if parent is hgroup,
    then return N;
    otherwise, if parent is not hgroup,
    return 0.

@Alohci
Copy link

Alohci commented Oct 14, 2019

@Crissov How would that help? How could you expose the local level to a11y?

@Crissov
Copy link

Crissov commented Oct 14, 2019

I would not (within existing frameworks). I would expose all hgroup children as having either the same (global, main, …) heading level, i.e. that of its parent, or none at all.

I believe this is unproblematic for the Avengers and Dr. Strangelove examples in the spec, but it might become a problem for others, e. g. for ledes and taglines – some authors may even put full abstracts into hgroup instead of header.

@aardrian
Copy link

@Crissov

some authors may even put full abstracts into hgroup instead of header.

IME doing audits, those devs who use <hgroup> put all sorts of structured and long-form content into it.

Anecdata aside, there are techniques to review sites, as WebAIM did with its review of the top million web pages (and discovered that in standard, boring, current headings today, 1 in 20 are wrong). Might be good to look at real-world <hgroup> use.

@scottaohara's example above would result in a heading too wordy to be of much use to those who rely on them most.

@scottaohara
Copy link
Collaborator

@annevk thank you for clarifying.

I personally think a mix of the two ideas might work best. I agree with this idea in that hgroup should not be the heading itself, but rather modify the headings within. As @aardrian notes, creating overly wordy headings is not particularly helpful.

Where I have concerns about this idea is that two (or more) exposed headings that introduce a single section of content are also problematic, as it makes navigating by headings to get to new sections of content less reliable.

So per this idea forhgroup, might it instead modify the heading of the highest importance to an appropriate heading level. And then the heading element(s) that serve as subheading be exposed "as subheadings" so as not to create one overly long heading, but also to mitigate against the navigation issues created by grouped headings?

The issue with all of this though is handling author misuse of hgroup. Could that be rectified by browsers treating hgroup with invalid children like <p>s handle nested divs? For example...

<p>
  my text 
  <div>div text</div> 
  more text
</p>

becomes:

<p>my text </p>
<div>div text</div> 
more text

Might not invalid code like:

<hgroup>
  <h2>Primary text</h2>
  <h3>a subheading</h3>
  <p>I'm not supposed to be here</p>
  <h3>Another section of content</h3>
  <p>Clearly I don't validate my code</p>
</hgroup>

become:

<hgroup>
  <h2>Primary text</h2>
  <h3>a subheading</h3>
</hgroup>
  <p>I'm not supposed to be here</p>
  <h3>Another section of content</h3>
  <p>Clearly I don't validate my code</p>

Or if that's out of the question, could invalid children within the hgroup cause the process of modifying nested headings be aborted all together, and thus not breaking current misuse in the wild anymore than it already is?

I realize this was a bit long and deviates into more things to consider, so i appreciate your time in giving it thought. Thanks.

@annevk
Copy link
Member Author

annevk commented Oct 15, 2019

I think we could probably do something differently with subsequent headings inside hgroup, but as far as I know "subheading" is currently not a thing that exists in the accessibility tree. It could certainly be a further enhancement though once the necessary infrastructure is there.

Changing the tree structure of hgroup is not an option and ignoring hgroup once it becomes invalid is also somewhat expensive as you would not know whether it is invalid until you see </hgroup> so assistive technology might initially see something that then changes due to more bytes appearing over the network. Which can happen for various reasons, but is not ideal.

@scottaohara
Copy link
Collaborator

Correct, the concept of a subheading would need to be created.

Your response about handling authoring errors is unfortunate, but not unexpected. Thank you for clarifying that as well.

@aardrian
Copy link

Alternative proposal:

  1. Declare that <hgroup> on its own does nothing;
  2. Mint <h>.

Then <hgroup> does not modify <h#>, thereby leaving existing structures intact and not modifying author intent for explicitly-chosen heading levels.

Given use of <hgroup> I have seen in the wild, this approach will not make any existing heading structures less accessible.

If the effort here is to justify <hgroup>, and by extension try again at a Document Outline Algorithm, then let's mint a new required child element, <h>. The <h> element can then get its nesting level from the algorithm proposed here.

This has the advantage of keeping existing heading parsing logic in place and compartmentalizing the logic of this new effort at a Document Outline Algorithm without blowing up 30 years of existing content and rules. It may also make uptake in user agents a bit easier to swallow.

We can lean on a previous effort to mint <h> to kick this off.

For the sub-heading concept, we can argue that any non-<h> non-phrasing-content-element child of <hgroup> is a de facto sub-head, whether it is a <div> or a <p> (probably more thought required there).

@annevk
Copy link
Member Author

annevk commented Oct 15, 2019

Adding a new element for this is too costly in my opinion. It wouldn't have the correct styling, it wouldn't have the correct parsing, it wouldn't map at all to assistive technology in any existing user agent, and it doesn't have any mindshare. If we end up there I rather admit defeat and leave it to custom elements…

@aardrian
Copy link

It wouldn't have the correct styling,

That can be added as part of this overall process. I suggest bold by default.

it wouldn't have the correct parsing,

As far as I know, <hgroup> does not now either. This would be part of the larger effort to make <hgroup> also have correct parsing.

it wouldn't map at all to assistive technology in any existing user agent,

That is part of this process (exposing it to accessibility APIs, not mapping to AT). If UAs are going to already open up the guts to implement <hgroup> and the DOA as suggested above, then is it that much more of an effort to fold <h> into that logic?

and it doesn't have any mindshare.

With who? The <h> element has mindshare going back to 2004 and has been discussed extensively in WICG and with W3C.

If we end up there I rather admit defeat and leave it to custom elements…

If we leave it to custom elements, then we have a fragmented approach for as many custom elements as developers decide to make. I feel that is the wrong approach.

@stevefaulkner
Copy link
Contributor

a suggestion:

  • the highest level heading in a hgroup is the heading and is assigned a heading role and level via the accessibility APIs.
  • headings of lower rank within hgroup are not assigned a heading role or level
  • If the are multiple headings of the highest level then only the first in the DOM order is assigned a heading role and level via the accessibility APIs.

@annevk
Copy link
Member Author

annevk commented Oct 15, 2019

@stevefaulkner interesting. What if we only made that work if the first element child is h1 and ignore the hgroup otherwise? Allowing an arbitrary child of the hgroup element to be the highest would not be ideal and perhaps it's safer to ignore hgroup if h1 isn't used with it.

@zcorpan
Copy link
Member

zcorpan commented Oct 16, 2019

@annevk I think requiring h1 to use hgroup is not ideal for graceful degradation.

If the constraint is "don't change accessibility tree of earlier nodes based on later nodes", I think this could work:

  • The first h1-h6 direct child of hgroup gets heading and level as appropriate.
  • Later h1-h6 direct children of hgroup do not get heading or level.
  • Other descendant h1-h6 of hgroup (which are invalid) work normally, as if there's no hgroup.

A document conformance requirement could be that the first h1-h6 child of hgroup must not have lower rank than other h1-h6 in the hgroup.

@annevk
Copy link
Member Author

annevk commented Oct 16, 2019

The idea behind requiring h1 for hgroup's first h1-h6 element is mostly to avoid adjusting the level of non-h1 elements in a way that is unexpected. A further refinement could be that only h1 gets level adjustment, but all later h1-h6 elements become non-headings, provided the first element is a h1-h6 (i.e., even if the first element was not h1).

@zcorpan
Copy link
Member

zcorpan commented Oct 16, 2019

Sounds good to me!

@stevefaulkner
Copy link
Contributor

@annevk Is it possible to see some example code illustrating effect/requirements for the latest idea? since my aged head is having difficulties understanding.

@zcorpan
Copy link
Member

zcorpan commented Oct 16, 2019

My understanding in examples:

Scott's example above

<hgroup>
  <h2>Primary text</h2>
  <h3>a subheading</h3>
  <p>I'm not supposed to be here</p>
  <h3>Another section of content</h3>
  <p>Clearly I don't validate my code</p>
</hgroup>

->

<hgroup>
  <h2 role=heading aria-level=2>Primary text</h2>
  <h3 role=generic>a subheading</h3>
  <p>I'm not supposed to be here</p>
  <h3 role=generic>Another section of content</h3>
  <p>Clearly I don't validate my code</p>
</hgroup>

Automatic level for h1

<body>
 <h1>foo</h1>
 <section>
  <h1>bar</h1>
  ...
  <hgroup>
   <h1>baz</h1>
   <h1>subheading</h1>
   <div>
    <h1>invalid</h1>
   </div>
  </hgroup>
 </section>
</body>

->

<body>
 <h1 role=heading aria-level=1>foo</h1>
 <section>
  <h1 role=heading aria-level=2>bar</h1>
  ...
  <hgroup>
   <h1 role=heading aria-level=2>baz</h1>
   <h1 role=generic>subheading</h1>
   <div>
    <h1 role=heading aria-level=2>invalid</h1>
   </div>
  </hgroup>
 </section>
</body>

First child is not h1-h6 (invalid)

<hgroup>
 <br>
 <h2>foo</h2>
 <h2>bar</h2>
</hgroup>

->

<hgroup>
 <br>
 <h2 role=heading aria-level=2>foo</h2>
 <h2 role=heading aria-level=2>bar</h2>
</hgroup>

@stevefaulkner
Copy link
Contributor

@zcorpan thanks that helps a lot, LGTM

@prlbr
Copy link

prlbr commented Nov 21, 2019

My understanding in examples:

Scott's example above

<hgroup>
  <h2>Primary text</h2>
  <h3>a subheading</h3>
  <p>I'm not supposed to be here</p>
  <h3>Another section of content</h3>
  <p>Clearly I don't validate my code</p>
</hgroup>

->

<hgroup>
  <h2 role=heading aria-level=2>Primary text</h2>
  <h3 role=generic>a subheading</h3>
  <p>I'm not supposed to be here</p>
  <h3 role=generic>Another section of content</h3>
  <p>Clearly I don't validate my code</p>
</hgroup>

(@zcorpan)

If everything but the first heading element in <hgroup> shall be interpreted as generic content, then consider allowing only one <h1><h6> element in <hgroup> plus generic elements (such as <div>) from now on. Make things simple and clear for future users.

It feels conceptually overcomplicated to allow only <h1></h6> elements, but multiple ones of these, in <hgroup> and then define all but the first one as generic content.

Of course a decision has to be made for these cases so that clients know how to interpret legacy content. But please do not force the people of the future to continue to use a syntax whose vocabulary contradicts its meaning (such as having to use <hgroup><h1>…<h2>… which is at the same time defined to be interpreted as <h1>…<div>…).

@zcorpan
Copy link
Member

zcorpan commented Nov 21, 2019

@prlbr not everything, only h1-h6 elements (which are the only allowed elements). Changing them so subheadings are not represented as actual headings is the point. Only allowing one h1-h6 in hgroup and then other content would make hgroup itself useless.

@prlbr
Copy link

prlbr commented Jul 1, 2022

I think this issue can be closed now that <hgroup>was updated in #7829.

@domenic domenic closed this as completed Jul 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

9 participants