[css-content][css-gcpm] building "bookmark-level: auto" with selectors

In Sapporo, we discussed whether it could be useful to have an auto value to the bookmark-level property, which would infer the right value from the structure of the document, in a way that depends on the document language. HTML would use the HTML 5 outline algorithm.

One argument against the idea was that selectors ought to be good enough to do this, and the there would be no need for an auto value. I got an action to try and write the set of selectors that would be a good alternative to an auto value to implement the HTML 5 outline algorithm.

I am not a selectors virtuoso by any mean, but here's me trying. (Spoilers: I fail).

Let's start simple with trivial things on trivial examples.

  <h1>Foo</h1>
  <h2>Bar</h2>
  <h3>Baz</h3>
Solution:
  h1 { bookmark-level: 1; }
  h2 { bookmark-level: 2; }
  h3 { bookmark-level: 3; }
  h4 { bookmark-level: 4; }
  h5 { bookmark-level: 5; }
  h6 { bookmark-level: 6; }

So far as good.

  <h1>Foo</h1>
  <section>
    <h1>Bar</h1>
    <section>
      <h1>Baz</h1>
    </section>
  </section>
Solution:
  h1 { bookmark-level: 1; }
  section h1 { bookmark-level: 2; }
  section section h1 { bookmark-level: 3; }

That doesn't work at arbitrary depth. This other solution (which uses the var-inherit function suggested for custom properties level 2) does:

  body { --outline: 1; }
  section { --outline: var-inherit(--outline) + 1; }
  h1 { bookmark-level: calc( var(--outline) ); }

That's fine, and it is even compatible with the first one that covered h1 to h6 at the top level, so you could have both in the same stylesheet, and work on either type of document.

However, HTML allows combinations of h1-h6 and of nesting sections, so we can get things like the following examples (intended level in /* comments */).

  <h1>Foo</h1> /*1*/
  <h2>Bar</h2> /*2*/
  <section>
      <h1>Baz</h1> /*2*/
  </section>

  <h1>Foo</h1> /*1*/
  <h2>Bar</h2> /*2*/
  <section>
      <h3>Baz</h3> /*2*/
  </section>

  <h1>Foo</h1> /*1*/
  <section>
    <h1>Bar</h1> /*2*/
    <h2>Baz</h2> /*3*/
  </section>

  <h1>Foo</h1> /*1*/
  <section>
    <h2>Bar</h2> /*2*/
    <h3>Baz</h3> /*3*/
  </section>

  <h2>Foo</h2> /*1*/
  <h3>Bar</h3> /*2*/
  <section>
      <h1>Baz</h1> /*2*/
  </section>

  <h2>Foo</h2> /*1*/
  <h4>Bar</h4> /*2*/
  <section>
      <h6>Baz</h6> /*3*/
  </section>

Here's an attempt to solves all that at arbitrary nesting levels (still using css variables functions from the future), with the handling of sectioning roots thrown in for good measure.

  body { --outline: 1; }
  section, article, nav, aside { --outline: var-inherit(--outline) + 1; }
  h1 { bookmark-level: calc( var(--outline) ); }
  h2 { bookmark-level: calc( var(--outline) ); }
  h3 { bookmark-level: calc( var(--outline) ); }
  h4 { bookmark-level: calc( var(--outline) ); }
  h5 { bookmark-level: calc( var(--outline) ); }
  h1 ~ h2 { bookmark-level: calc( var(--outline) + 1); }
  h2 ~ h3 { bookmark-level: calc( var(--outline) + 1); }
  h1 ~ h3  { bookmark-level: calc( var(--outline) + 1); }
  h1 ~ h2 ~ h3  { bookmark-level: calc( var(--outline) + 2); }
  h1 ~ h4 { bookmark-level: calc( var(--outline) + 1); }
  h2 ~ h4 { bookmark-level: calc( var(--outline) + 1); }
  h3 ~ h4  { bookmark-level: calc( var(--outline) + 1); }
  h1 ~ h2 ~ h4  { bookmark-level: calc( var(--outline) + 2); }
  h1 ~ h3 ~ h4  { bookmark-level: calc( var(--outline) + 2); }
  h2 ~ h3 ~ h4  { bookmark-level: calc( var(--outline) + 2); }
  h1 ~ h2 ~ h3 ~ h4  { bookmark-level: calc( var(--outline) + 3); }
  h1 ~ h5 { bookmark-level: calc( var(--outline) + 1); }
  h2 ~ h5 { bookmark-level: calc( var(--outline) + 1); }
  h3 ~ h5  { bookmark-level: calc( var(--outline) + 1); }
  h4 ~ h5  { bookmark-level: calc( var(--outline) + 1); }
  h1 ~ h2 ~ h5  { bookmark-level: calc( var(--outline) + 2); }
  h1 ~ h3 ~ h5  { bookmark-level: calc( var(--outline) + 2); }
  h1 ~ h4 ~ h5  { bookmark-level: calc( var(--outline) + 2); }
  h2 ~ h3 ~ h5  { bookmark-level: calc( var(--outline) + 2); }
  h2 ~ h4 ~ h5  { bookmark-level: calc( var(--outline) + 2); }
  h3 ~ h4 ~ h5  { bookmark-level: calc( var(--outline) + 2); }
  h1 ~ h2 ~ h3 ~ h5  { bookmark-level: calc( var(--outline) + 3); }
  h1 ~ h2 ~ h4 ~ h5  { bookmark-level: calc( var(--outline) + 3); }
  h1 ~ h3 ~ h4 ~ h5  { bookmark-level: calc( var(--outline) + 3); }
  h2 ~ h3 ~ h4 ~ h5  { bookmark-level: calc( var(--outline) + 3); }
  h1 ~ h2 ~ h3 ~ h4 ~ h5  { bookmark-level: calc( var(--outline) + 4); }
  h1 ~ h6 { bookmark-level: calc( var(--outline) + 1); }
  h2 ~ h6 { bookmark-level: calc( var(--outline) + 1); }
  h3 ~ h6  { bookmark-level: calc( var(--outline) + 1); }
  h4 ~ h6  { bookmark-level: calc( var(--outline) + 1); }
  h5 ~ h6  { bookmark-level: calc( var(--outline) + 1); }
  h1 ~ h2 ~ h6  { bookmark-level: calc( var(--outline) + 2); }
  h1 ~ h3 ~ h6  { bookmark-level: calc( var(--outline) + 2); }
  h1 ~ h4 ~ h6  { bookmark-level: calc( var(--outline) + 2); }
  h1 ~ h5 ~ h6  { bookmark-level: calc( var(--outline) + 2); }
  h2 ~ h3 ~ h6  { bookmark-level: calc( var(--outline) + 2); }
  h2 ~ h4 ~ h6  { bookmark-level: calc( var(--outline) + 2); }
  h2 ~ h5 ~ h6  { bookmark-level: calc( var(--outline) + 2); }
  h3 ~ h4 ~ h6  { bookmark-level: calc( var(--outline) + 2); }
  h3 ~ h5 ~ h6  { bookmark-level: calc( var(--outline) + 2); }
  h4 ~ h5 ~ h6  { bookmark-level: calc( var(--outline) + 2); }
  h1 ~ h2 ~ h3 ~ h6  { bookmark-level: calc( var(--outline) + 3); }
  h1 ~ h2 ~ h4 ~ h6  { bookmark-level: calc( var(--outline) + 3); }
  h1 ~ h2 ~ h5 ~ h6  { bookmark-level: calc( var(--outline) + 3); }
  h1 ~ h3 ~ h4 ~ h6  { bookmark-level: calc( var(--outline) + 3); }
  h1 ~ h3 ~ h5 ~ h6  { bookmark-level: calc( var(--outline) + 3); }
  h1 ~ h4 ~ h5 ~ h6  { bookmark-level: calc( var(--outline) + 3); }
  h2 ~ h3 ~ h4 ~ h6  { bookmark-level: calc( var(--outline) + 3); }
  h2 ~ h3 ~ h5 ~ h6  { bookmark-level: calc( var(--outline) + 3); }
  h2 ~ h4 ~ h5 ~ h6  { bookmark-level: calc( var(--outline) + 3); }
  h3 ~ h4 ~ h5 ~ h6  { bookmark-level: calc( var(--outline) + 3); }
  h1 ~ h2 ~ h3 ~ h4 ~ h6  { bookmark-level: calc( var(--outline) + 4); }
  h1 ~ h2 ~ h3 ~ h5 ~ h6  { bookmark-level: calc( var(--outline) + 4); }
  h1 ~ h2 ~ h4 ~ h5 ~ h6  { bookmark-level: calc( var(--outline) + 4); }
  h1 ~ h3 ~ h4 ~ h5 ~ h6  { bookmark-level: calc( var(--outline) + 4); }
  h2 ~ h3 ~ h4 ~ h5 ~ h6  { bookmark-level: calc( var(--outline) + 4); }
  h1 ~ h2 ~ h3 ~ h4 ~ h5 ~ h6  { bookmark-level: calc( var(--outline) + 5); }

  blockquote *, details *, fieldset *, figure *, td * {
    bookmark-level: none !important;
  }

Since I'm using var-inherit which isn't implemented anywhere, I can't check
that this does what I think it does, but I think it works. Even assuming I
did not screw up, it still misses the mark. For example in the following
example, "plop" is supposed to be level 2, and my machinery above would
give it level 3.
  <h1>Foo</h1>
  <h2>Bar</h2>  
  <h1>Baz</h1>
  <h3>plop</h3>

I don't know who to fix this. And I  have missed other subtleties in the
HTML 5 outline algo. And I was using a function that doesn't exist anyway.

Maybe there are much more effective / elegant ways to approach this, but
that's how far I got. If someone has a better idea, I'm very interested
to see how you'd approach that.


TL;DR: I do not know of a practical way to build using css the html outline
algo, or even how to approximate it if we restrict ourselves to non fictional
css.


 - Florian

Received on Tuesday, 24 November 2015 04:36:41 UTC