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

Add heading-focused outlines and :heading #3499

Open
wants to merge 5 commits into
base: master
from
Open

Conversation

@annevk
Copy link
Member

@annevk annevk commented Feb 23, 2018

This makes a number of fairly big changes:

  • Introduces a heading and heading level concept.
  • Replaces the outline algorithm with a document headings concept.
  • Requires document headings to not skip heading levels and start
    with heading level 1.
  • Introduces a :heading pseudo-class selector.
  • Introduces a :heading(level) functional pseudo-class selector.
  • Does away with the section concept (except insofar it's needed to
    influence the heading level of h1/hgroup).
  • Does away with sectioning roots.

Tests: ...

Fixes #83.


/acknowledgements.html ( diff )
/dom.html ( diff )
/form-elements.html ( diff )
/grouping-content.html ( diff )
/index.html ( diff )
/indices.html ( diff )
/infrastructure.html ( diff )
/interactive-elements.html ( diff )
/links.html ( diff )
/sections.html ( diff )
/semantics-other.html ( diff )
/tables.html ( diff )

@js-choi
Copy link

@js-choi js-choi commented Feb 23, 2018

It’s wonderful that this is being, at last, concretely fleshed out.

I’m also interested in heading-level ranges in the pseudo-class, such as :heading(>=3). Should I raise this in a new issue, or would commenting in this pull request or in #83 be okay?

@annevk
Copy link
Member Author

@annevk annevk commented Feb 23, 2018

@js-choi I'd prefer new issues for enhancements on top #83 / this PR as they can be added later. It's always good for the initial take to be as simple as possible.

source Outdated Show resolved Hide resolved
source Outdated Show resolved Hide resolved
@cookiecrook
Copy link

@cookiecrook cookiecrook commented Mar 21, 2018

@stevefaulkner @alice for additional review.

source Outdated Show resolved Hide resolved
@sideshowbarker
Copy link
Member

@sideshowbarker sideshowbarker commented Apr 3, 2018

As far as the algorithm at https://whatpr.org/html/3499/sections.html#heading-level that assigns levels to headings, I think that for some normal cases of hgroup usage, it breaks author expectations by unintuitively assigning a level that’s different from what authors intend/assume.

For example, consider the following document:

<h1>Screenplays for Kubrick movies from the 1960s</h1>
  <p>The following are some details on movies Stanley Kubrick directed in the 1960s.
  <h2>Spartacus</h2>
    <p>1960; screenplay by Dalton Trumbo, based on a novel by Howard Fast.
  <h2>Lolita</h2>
    <p>1962; screenplay by Vladimir Nabokov, based on his own novel.
  <hgroup>
    <h2>Dr. Strangelove</h2>
    <h3>or: How I Learned to Stop Worrying and Love the Bomb</h3>
  </hgroup>
    <p>1964; screenplay by Kubrick, loosely based on a novel by Peter George.
  <h2>2001: A Space Odyssey</h2>
    <p>1968; screenplay by Kubrick and Arthur C. Clarke, based on a Clarke short story.

Given the above, the algorithm at https://whatpr.org/html/3499/sections.html#heading-level assigns a heading level of 1 to the h1 heading, and a heading level of 2 to every one of the h2 headings except the heading Dr. Strangelove (or: How I Learned to Stop Worrying and Love the Bomb) — which it instead assigns a heading level of 1 (due to the fact the author chose to put an hgroup element around the h2 title and h3 subtitle for that movie).

It doesn’t seem at all intuitive for the title to be assigned a heading level of 1 (effectively making it the same level as the title of the entire document) instead of being signed a heading level of 2 (keeping it at the same level as the headings with other movie titles.

I don’t think authors/developers would expect that title to be a level 1 heading — I think instead what they’d expect it to be a level 2 heading like the other titles in the list. And in fact that’s the level the old/existing outline algorithm in the spec would assign it.

@sideshowbarker
Copy link
Member

@sideshowbarker sideshowbarker commented Apr 3, 2018

As far as hgroup and what the heading-level algorithm should do with instead of what the current patch in this PR branch does: I think the algorithm should just ignore hgroup. That is, h2-h6 headings should always just get assigned their corresponding 2-6 heading level, with no regard for whether they have an hgroup parent and ancestor sectioning content elements.

The original/current purpose of hgroup is pretty closely bound to the existing/old outline algorithm — in that hgroup was created as a kind of necessary consequence of the fact the outline algorithm introduced the idea that headings create (conceptual) subsections (regardless of whether the headings happen to be marked up with section elements or other sectioning-content elements).

So in that model, it was necessary to ensure that a (conceptual) subsection would not be created by a heading an author intended as a subheading. Thus in the outline algorithm, that’s the effect hgroup has: It just prevents a new subsection from being created in the outline.

But if we dispense with the conceptual model of headings creating subsections (as the patch in this PR branch does), then we no longer need a way to prevent a heading from creating a subsection. So we need longer need hgroup to have any effect. It can (should) basically just become a no-op.

And if we don’t have hgroup actually doing anything, then logically the next question to consider is whether we should keep hgroup around at all. Personally, I think we shouldn’t — I think instead we should deprecate/obsolete it along with dropping the outline algorithm it was designed for. Use-counter data from the HTML checker shows that only around 0.2% of documents are using hgroup anyway.

And if we drop hgroup then the next question to consider is what authors should use to mark up subheadings. I think the answer to that is, they should use whatever they’re already using — because I think it’s clear that among the 99.8% of documents that aren’t using hgroup, there is some significant percentage that have subheadings but that the authors have chosen not to use hgroup to mark up.

In other words, instead of using hgroup, it seems that authors are largely either not putting any extra markup around heading+subheading groups at all or else they’re just a div or p around them.

So I think the vast majority of authors who are using subheadings in their documents aren’t going to care whether we drop hgroup — because they’re not using it anyway.

But all that said, I don’t feel strongly that we must drop hgroup. I guess keeping it around for continued use by the small number of authors who are using it would not do a lot of harm.

However, I do feel strongly that if we keep hgroup it must have not affect on the assignment of heading levels — for the reasons I give in #3499 (comment)

@sideshowbarker
Copy link
Member

@sideshowbarker sideshowbarker commented Apr 3, 2018

To reinforce the points I made in #3499 (comment) about hgroup having any purpose outside the context of the old/existing outline algorithm, I want to note the following specific change this patch makes; it takes the following (non-normative) text (emphasis added):

The point of using hgroup in these examples is to prevent the h2 element (which acts as a secondary title) from creating a separate section of its own in any outline

…and changes it to this:

The point of using hgroup in these examples is to prevent the h2 element (which acts as a secondary title) from creating a separate heading of its own.

On the face of it the phrase prevent the h2 element from creating a separate heading doesn’t make sense (in contrast to the phrase prevent the h2 element from creating a separate section of its own in any outline, which does make sense).

What I mean is, the h2 element does in fact create a separate displayed heading of its own in any visual rendering of the document (unless we’re end up deciding to have hgroup affect how UAs do the default visual rendering of headings, which I hope we’re won’t…).

So it’s unclear what that explanation prevent the h2 element from creating a separate heading of its own means. I guess one solution to that would be to just drop that explanation. But if we do that, then we’d need to come up with some other explanation for the point of using hgroup in the examples. However, as I pointed out in #3499 (comment), I don’t think there is any good point to using hgroup that we could explain in a way similar to the way we explained in the context of the (old) outline algorithm.

So I think that argues for dropping the hgroup examples, and I guess for dropping hgroup — since it doesn’t make much sense to have an element in the language that we can’t explain the purpose for well and that we can’t come up with good examples for that wouldn’t make just as much sense as examples where the hgroup is replaced with div or whatever).

@prlbr
Copy link

@prlbr prlbr commented Apr 3, 2018

As a note to @sideshowbarker's comment:

<hgroup> would have been interesting to me if It had not forced me to use <hx> as a sub-heading.

I understand that using <hx> as a sub-heading was a pattern that had been found in the wild, but as far as I can remember it has been a pattern that accessibility experts have always criticized – similar to using <table> for purely presentational reasons instead of for tabular data. Standardizing this as the correct way to do it has been an unfortunate choice.

In my opinion what we have now is a badly designed <hgroup> element besides an overloaded <header> element which serves two different purposes with different semantics, depending on whether it is “scoped to the <body>” or not.

“Scoped to the <body>”, the <header> element has an implied role=banner, meaning that it represents content that is rather site-oriented than page-specific. But when it’s not scoped to the <body>, it represents a group of introductory stuff to the nearest main/section/article/etc. it resides in, so it is specific for where it is “scoped to”.

What I would have liked to have: An element for site-oriented content, say <header>, and an element that groups the heading of a page or section with other introductory stuff, say <hgroup>.

@annevk
Copy link
Member Author

@annevk annevk commented Apr 5, 2018

@sideshowbarker @prlbr I think we need some kind of answer for subheadings though.

@prlbr
Copy link

@prlbr prlbr commented Apr 11, 2018

I think we need some kind of answer for subheadings though.

w3c chose to add a section on subheadings etc. for common idioms without dedicated elements.
https://www.w3.org/TR/html52/common-idioms-without-dedicated-elements.html#subheadings-subtitles-alternative-titles-and-taglines

A new element <hsub> was a favorite of some people in the past:
https://www.w3.org/html/wg/wiki/ChangeProposals/hSub

source Outdated Show resolved Hide resolved
zcorpan added a commit to web-platform-tests/wpt that referenced this pull request Jun 8, 2018
Part of whatwg/html#3499.

This does not yet test :heading().
@JoshTumath JoshTumath mentioned this pull request Jan 3, 2019
2 of 6 tasks complete
@Dan503

This comment has been hidden.

@hober
hober approved these changes Oct 9, 2019
Copy link
Collaborator

@hober hober left a comment

Looks good.

source Outdated
@@ -16117,7 +16104,7 @@ interface <dfn>HTMLHeadingElement</dfn> : <span>HTMLElement</span> {};</code></p

<p>The two styles can be combined, for compatibility with legacy tools while still
future-proofing for when that compatibility is no longer needed. This third snippet again has the
same outline as the previous two:</p>
same <span data-x="heading level">heading levels</span> as the previous two:</p>

This comment has been minimized.

@hober

hober Oct 9, 2019
Collaborator

I think this would read better in the singular: "This third snippet again has the same heading level as the previous two:"

This comment has been minimized.

@annevk

annevk Oct 11, 2019
Author Member

I'm not sure I follow this one as there are multiple headers and therefore multiple levels.

source Outdated Show resolved Hide resolved
annevk added 2 commits Feb 23, 2018
This makes a number of fairly big changes:

* Introduces a heading and heading level concept.
* Replaces the outline algorithm with a document headings concept.
* Requires document headings to not skip heading levels and start
  with heading level 1.
* Introduces a :heading pseudo-class selector.
* Introduces a :heading(level) functional pseudo-class selector.
* Does away with the section concept (except insofar it's needed to
  influence the heading level of h1/hgroup).
* Does away with sectioning roots.

Tests: ...

Fixes #83.
@annevk annevk force-pushed the annevk/heading-level branch from f13faa7 to 16fc3b3 Oct 11, 2019
@annevk
Copy link
Member Author

@annevk annevk commented Oct 11, 2019

Thanks everyone for your feedback! Given the concerns raised regarding hgroup I've proposed a slight change to the model in #5002 that I'd appreciate review on within that issue.

As I outlined in w3c/csswg-drafts#1008 (comment) there's also a valid concern with :heading that needs more consideration and is best discussed there.

I'm hopeful the change to hgroup is agreeable at which point I want to get the accessibility mapping part of this proposal prototyped in Firefox. And perhaps :heading will have to be postponed to ensure we get the design right. The downside is that there's then less of carrot to get the heading levels right, but at this point I'll settle for getting there incrementally.

@@ -15735,8 +15729,8 @@ interface <dfn>HTMLBodyElement</dfn> : <span>HTMLElement</span> {
<p class="note" id="use-div-for-wrappers">The <code>section</code> element is not a generic
container element. When an element is needed only for styling purposes or as a convenience for
scripting, authors are encouraged to use the <code>div</code> element instead. A general rule is
that the <code>section</code> element is appropriate only if the element's contents would be
listed explicitly in the document's <span>outline</span>.</p>
that the <code>section</code> element is appropriate when used in conjunction with a <span

This comment has been minimized.

@stevefaulkner

stevefaulkner Oct 16, 2019
Contributor

Can this not be a SHOULD rule so that it is raised as a warning in the checker?

@domenic
Copy link
Member

@domenic domenic commented Oct 17, 2019

/cc @muan who has in the past raised concerns that this would not suffice for GitHub's use case.

If I recall correctly, the ask was to be able to wrap user-generated content in an <article> or similar, and thus adjust all its heading levels. Something like:

<h1>GitHub</h1>
<h2>jsdom/jsdom</h2>

<article>
  <h1>jsdom</h1>
  <h2>Basic usage</h2>
  <h2>Customizing jsdom</h2>
  <h3>Simple options</h3>
  ...
</article>

generating

  • (1) GitHub
  • (2) jsdom/jsdom
  • (3) jsdom
  • (4) Basic usage
  • (4) Customizing jsdom
  • (5) Simple options

I'm unsure whether that's feasible given the constraints on simplicity that this PR is working toward, but I want to ensure that if we're excluding this use case, that we do so on purpose.

@annevk
Copy link
Member Author

@annevk annevk commented Oct 18, 2019

Going through previous sibling might not be that bad (though it is certainly worse than counting ancestors), but I suspect the need is even more complicated and would need to cover cases such as

<article>
  <h1>jsdom</h1>
  <h2>Basic usage</h2>
  <div><h2>Customizing jsdom</h2></div>
  <h3>Simple options</h3>
  ...
</article>

as well, at which point it becomes unworkable. (Also known as the unstated algorithm in the standard today, roughly.)

@domenic
Copy link
Member

@domenic domenic commented Oct 18, 2019

I'm not familiar enough with the details of what's happening to tell why inserting a <div> would make things difficult. I was just envisioning that wrappers like article would cause the heading level of all their children to increase by the most-recently-seen heading level, or something similar.

Edit: hmm, defining "most-recently-seen heading level" in a timeless way makes the difficulties more apparent. E.g. in cases like

<h1>GitHub</h1>
<h2>jsdom/jsdom</h2>

<div>
  <article>
    <h1>jsdom</h1>
    <h2>Basic usage</h2>
    <h2>Customizing jsdom</h2>
    <h3>Simple options</h3>
    ...
  </article>
</div>

(which I believe is more realistic with regard to GitHub's actual markup) do not provide any easy way of finding <h2>jsdom/jsdom</h2> from <article>. It's certainly not "previous sibling heading element". So I can see how if our goal is simplicity + support for h1-only pages, we might have to drop support for wrapping user-generated content.

@Dan503
Copy link

@Dan503 Dan503 commented Oct 18, 2019

If I recall correctly, the ask was to be able to wrap user-generated content in an <article> or similar, and thus adjust all its heading levels.

I'm a little bit concerned about if this idea breaks the meaning of the <article> element.

Article is meant to be a self contained piece of content that can make sense on its own when taken out of context.

Does this idea go against the fundamental meaning of what the <article> element is meant to represent?

I do like the idea in general though so maybe <section> would be better for this functionality. Or maybe a new element if necessary.

@muan
Copy link
Member

@muan muan commented Oct 21, 2019

Thanks @domenic for mentioning our issue. I've been quietly following the thread and can see how this is all complicated, so haven't been able to add anything.

I don't know if introducing a new element will solve this since what algorithmically couldn't be done for <article> would be applied to this new element too, wouldn't it?

I might be way off– could introducing a new attribute, to make "most-recently-seen heading level" explicit, work?

<h1>GitHub</h1>
<h2>jsdom/jsdom</h2>
<div>
  <article headinglevelstart="3">
    <h1>jsdom</h1>
    <h2>Basic usage</h2>
    <h2>Customizing jsdom</h2>
    <h3>Simple options</h3>
    ...
  </article>
</div>

Since the alternative is generating markup that has the explicit <h3>/<h4>/<h5>, the developers would need to have the starting level information either way. An attribute would save us from altering user generated content.

@Dan503
Copy link

@Dan503 Dan503 commented Oct 22, 2019

I like your idea @muan it is reminiscent of how <ul> and <ol> work with their start attribute. That would also make it easy to teach since parallels can be made with how <ul> and <ol> works.

This also might help solve the problem of nested sections labelled with aria-label increasing the heading level past what we want.

We don't have to move aria-label into a visually hidden heading if we can just override the base heading level that will be output 😁

This is how I imagine it working:

Without the attribute:

<body>
  <h1>Read as h1 :)</h1> 

  <p>content</p>

  <aside>

    <section>
      <h1>This is read as a h3 :(</h1>
      <p>content</p>
    </section>

    <section>
      <h1>Also read as a h3 :(</h1>
      <p>content</p>
    </section>

  </aside>
</body>

With the attribute:

<body>
  <h1>Read as h1 :)</h1> 

  <p>content</p>

  <aside headinglevelstart="1">

    <section>
      <h1>This is read as a h2 :)</h1>
      <p>content</p>
    </section>

    <section>
      <h1>Also read as a h2 :)</h1>
      <p>content</p>
    </section>

  </aside>
</body>
@annevk
Copy link
Member Author

@annevk annevk commented Oct 22, 2019

I filed #5033 on that suggestion. Seems like a reasonable follow-up to me once we have header level infrastructure in place and as it only requires going through ancestors shouldn't pose much of an issue implementation-wise.

@Dan503
Copy link

@Dan503 Dan503 commented Oct 22, 2019

This headinglevelstart attribute could also be a good way of telling browsers to use this heading levels algorithm.

<body>
  <!-- heading levels ignore sectioning elements -->
</body>
<body headinglevelstart="1">
  <!-- heading levels are affected by sectioning elements -->
</body>

Just implementing this algorithm directly into browsers across all websites across the whole world would break backwards compatibility.

@annevk

This comment has been hidden.

@Dan503
Copy link

@Dan503 Dan503 commented Oct 22, 2019

This also might help solve the problem of nested sections labelled with aria-label increasing the heading level past what we want.

I've had a better idea about this. It can be explicitly written in the spec that if a sectioning element does not have a heading element associated with it, then it does not affect heading levels. That would probably be much easier and also much less likely to break backwards compatibility 🤔

<body>
  <h1>Read as h1</h1> 

  <p>content</p>

  <!-- no heading element association so heading levels are not incremented -->
  <aside>

    <section>
      <h1>This is read as a h2 :)</h1>
      <p>content</p>
    </section>

    <section>
      <h1>Also read as a h2 :)</h1>
      <p>content</p>
    </section>

  </aside>
</body>

I still like the headinglevelstart attribute. This addition to the spec would mainly mean that we don't have to use the attribute as often and it would help prevent backwards compatibility issues.

@MarcoZehe
Copy link

@MarcoZehe MarcoZehe commented Oct 22, 2019

I don't think that's true. We've implemented the proposed algorithm in Firefox Nightly and there's been some changes observed that could be considered breakage, but overall it seems reasonable thus far.

I disagree with this assessment. Lots of WordPress themes I|ve visited encompass a post inside a main inside an article, and the comments are outisde the article, but inside the main. The h1 that contains the blog post title is in all these cases remapped to a level 2, which is wrong, since it is then put in line with h2s inside the post that denounce sub sections. And those pages then no longer have an h1 alltogether.

Further, one of the major news sites for IT topics in Germanz, Heise, uses articles as well, putting their h1 out of service.

The Moyilla instance of Bugyilla|s view bug page breaks, no longer has an h1 as well.

And I|ve seen others, which I forgot to take notes about, but to me, this seems prettz significant. The breakage inflicted on WordPress blogs alone is prettz substantial.

@Comandeer
Copy link

@Comandeer Comandeer commented Oct 22, 2019

I agree with @MarcoZehe. I raised this before in #83 (comment): main headings in default WordPress themes, e.g. Twenty Fifteen, are inside main > article on subpages dedicated to a single blog post. They are used as both page and blog post main headings:

<body>
  […]
  <main>
  	<article>
    	<h1>Blog post heading</h1>
    </article>
  </main>
  […]
</body>

I downloaded the newest Firefox Nightly and tested it on several WP sites (including mine) and Firefox treats such heading as level 2 one – so the main heading of the page is removed in the process.

However this pattern is not common only in the context of WordPress and its default themes, it is also present in many tutorials about basics of HTML and accessibility (including mine ;)) – at least in Poland.

I see two possible solutions for this issue:

  • mechanism for opting-in for the new algorithm. The proposed attribute ([headinglevelstart]) seems sensible.
  • always treat the first h1 on a page as level 1 heading or treat the first :is(article, section) h1 as level 1 heading if there is no h1 element directly in body. However, it would make the whole algorithm much more convoluted.
@Dan503
Copy link

@Dan503 Dan503 commented Oct 22, 2019

I see two possible solutions for this issue:

  • mechanism for opting-in for the new algorithm. The proposed attribute ([headinglevelstart]) seems sensible.
  • always treat the first h1 on a page as level 1 heading or treat the first :is(article, section) h1 as level 1 heading if there is no h1 element directly in body. However, it would make the whole algorithm much more convoluted.

I would much rather an opt-in mechanism. It is far less likely to result in bugs that are impossible for the developer to fix outside of forcing it with an aria-level attribute on the heading element.

The second option you gave doesn't really work if there is a left sidebar on the page with a heading in it. The sidebar heading would be encountered first so it would become the <h1> heading for the page. The main page content heading would remain at <h2> level.

@annevk
Copy link
Member Author

@annevk annevk commented Oct 22, 2019

@MarcoZehe yeah, I clearly wrote that too quickly. If reporting some h1s as level 2 is considered too much breakage we should make the standard reflect the status quo of h1-h6, even if it makes the default styling for some elements rather weird. Not sure what it means for hgroup, I guess it'll remain a container of sorts.

@domenic
Copy link
Member

@domenic domenic commented Jan 23, 2020

@annevk it appears from https://groups.google.com/d/msg/mozilla.dev.platform/SdnMKYwWxzU/U-v_b8c2BwAJ that this was not able to be implemented. Should we abandon this approach, and instead update the outline algorithm to just assemble an outline based on h1-hN, and update all the examples in the spec accordingly?

I guess we may still want to build it on top of this PR, since you've done a lot of the work to remove concepts like sectioning roots, etc.

@annevk
Copy link
Member Author

@annevk annevk commented Jan 24, 2020

Yeah, though you'll run into "what to do with hgroup" pretty quickly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Linked issues

Successfully merging this pull request may close these issues.

You can’t perform that action at this time.