0

I have the following setup where a CSS counter works for slotted content but not in the shadow DOM.

import { LitElement, css, html } from 'lit-element';

class MyElement extends LitElement {

 static get properties() {
    return {
      counter: { type: Number },
    };
  }

  render() {
    return html`
      <div><slot></slot></div>
      <div class="foo">
        <h1>Hey</h1>
        <h1>Ho</h1>
      </div>
    `;
  }
}

MyElement.styles = [
  css`
    :host {
      counter-reset: partCounter afterCounter;
    }
    :host ::slotted(*):before {
      counter-increment: partCounter;
      content: 'Slotted ' counter(partCounter) '. ';
    }
    h1:after {
      counter-increment: afterCounter;
      content: ' Shadow ' counter(afterCounter) '. ';
    }
  `,
];
customElements.define('my-element', MyElement);
<my-element>
  <h1>one</h1>
  <h1>two</h1>
</my-element>

I see this output: Shadow 1 Shadow 1. Expected output: Shadow 1 Shadow 2.

Why is it behaving this way? I'm more interested in an explanation why, though a solution would be nice as well.

Working demo on Codesandbox: https://codesandbox.io/s/4j6n7xwmj7

P.S.: Some hints in this Github thread, but to me it suggests that it should actually be working: https://github.com/w3c/csswg-drafts/issues/2679

TylerH
  • 20,799
  • 66
  • 75
  • 101
montrealist
  • 5,593
  • 12
  • 46
  • 68

1 Answers1

2

It is all in where you place the counter-reset.

:host is needed for thing inside a slot and, in this case I added the other into .foo.

You can see from the example below that it works fine.

Yes, I removed all of LIT, but the principle is the same with or without LIT.

class MyElement extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode:'open'}).innerHTML = `
      <style>
      :host {
        counter-reset: partCounter;
      }
      :host ::slotted(*):before {
        counter-increment: partCounter;
        content: 'Slotted ' counter(partCounter) ': ';
      }
      .foo {
        counter-reset: afterCounter;
      }
      h1:before {
        counter-increment: afterCounter;
        content: ' Shadow ' counter(afterCounter) ' - ';
      }
      </style>
      <div><slot></slot></div>
      <div class="foo">
        <h1>Hey</h1>
        <h1>Ho</h1>
      </div>
    `;
  }
}

customElements.define('my-element', MyElement);
<my-element>
  <h1>one</h1>
  <h1>two</h1>
</my-element>

To see that each works independently I changed it to the following:

class MyElement extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode:'open'}).innerHTML = `
      <style>
      :host {
        counter-reset: partCounter -10;
      }
      :host ::slotted(*):before {
        counter-increment: partCounter;
        content: 'Slotted ' counter(partCounter) ': ';
      }
      .foo {
        counter-reset: afterCounter 30;
      }
      h1:before {
        counter-increment: afterCounter;
        content: ' Shadow ' counter(afterCounter) ' - ';
      }
      </style>
      <div><slot></slot></div>
      <div class="foo">
        <h1>Hey</h1>
        <h1>Ho</h1>
      </div>
    `;
  }
}

customElements.define('my-element', MyElement);
<my-element>
  <h1>one</h1>
  <h1>two</h1>
</my-element>
Intervalia
  • 10,248
  • 2
  • 30
  • 60
  • Thanks for the fix! A good explanation of why appears in the duplicate question: turns out `:host` selects the shadow DOM host, which "leaves" the shadow DOM: bad for me. I need to put my counter-reset into a selector inside the shadow DOM. – montrealist Apr 02 '19 at 15:37