Aguarde...

13 de abril de 2024

Pseudoclasses personalizadas para componentes da web com a API CustomStateSet

Pseudoclasses personalizadas para componentes da web com a API CustomStateSet

CSS tem pseudoclasses para estilizar elementos HTML com base no estado – há :invalid:hover:checked:placeholder-shown, etc. De forma semelhante, com a CustomStateSetAPI, os componentes da web podem expor suas próprias pseudoclasses como :state(loading):state(done), etc.

Suporte ao navegador

Safari 17.4 adicionou suporte para a API CustomStateSet. Também está disponível no Firefox Nightly. O Chrome suportava uma sintaxe obsoleta mais antiga. O Chrome suporta a nova sintaxe a partir da versão 125.


Vejamos um exemplo: um botão que copia texto para a área de transferência do usuário. O usuário clica no botão, o texto é copiado e o botão exibe temporariamente uma confirmação de que a ação foi bem-sucedida. Enquanto a confirmação estiver sendo exibida, direi que o elemento personalizado está em seu estado de confirmação .

Ser capaz de estilizar com base no estado é útil tanto para o criador quanto para o consumidor do elemento personalizado. Nos exemplos abaixo, o autor do elemento personalizado está usando o estado para alterar o ícone de um símbolo de cópia para um símbolo de escala. O usuário do elemento personalizado está usando o estado para personalizar o background-colortom de verde de sua preferência.

Estilizando de dentro do shadow DOM:

:host(:state(confirmation)) button {
    background-image: url("/tick.svg");
}

Estilizando fora do shadow DOM:

copy-button:state(confirmation) {
        --bg: #34d399;
    }

O seletor de estado personalizado pode ser combinado ::partpara selecionar uma parte específica do elemento personalizado para estilizar fora do shadow DOM:

copy-button:state(confirmation)::part(button) {
        background-color: #34d399;
    }

Essa é a sintaxe CSS, mas como configuramos isso?

Adicionando estado a um elemento personalizado

.attachInternals()método retorna um ElementInternals objeto. ElementInternalstem uma .statespropriedade que retorna um CustomStateSet objeto.

class CopyButton extends HTMLElement {
    #internals = this.attachInternals();
    #states = this.#internals.states;

    // more code...
}

Ao usar a sintaxe do campo de classe privada ( #), o estado não pode ser alterado por código fora da classe.

CustomStateSettem um método .add()and .delete()para adicionar e excluir estados.

const styles = new CSSStyleSheet();
styles.replaceSync(`
button {
    background-color: var(--bg, rgb(240,240,240));
    background-image: url(/copy.svg);
}   

:host(:state(confirmation)) button {
    background-image: url("/tick.svg");
}`);

class CopyButton extends HTMLElement {
  #internals = this.attachInternals();
  #states = this.#internals.states;
  #shadowRoot = this.attachShadow({ mode: "open" });

  connectedCallback() {
    this.addEventListener("click", this.#onClick.bind(this));
    this.#shadowRoot.adoptedStyleSheets.push(styles);
    this.#shadowRoot.innerHTML = `<button part="button"></button>`;
  }

  get confirmation() {
    return this.#states.has("confirmation");
  }

  #onClick(event) {
    navigator.clipboard.writeText(this.getAttribute("value"));
    this.#states.add("confirmation");
    setTimeout(() => {
      this.#states.delete("confirmation");
    }, 2000);
  }
}

customElements.define("copy-button", CopyButton);

Selecionando componentes com base no estado personalizado comquerySelector

Você não apenas pode estilizar componentes com base no estado, mas também selecionar componentes com JavaScript com base no estado. querySelectorquerySelectorAllaceite qualquer seletor CSS válido como argumento, para que possamos usar a mesma sintaxe. Se você criar um <toggle-button>componente da web com um estado selecionado personalizado , por exemplo, o seguinte selecionaria apenas os elementos nesse estado:

const selectedButtons = document.querySelectorAll('toggle-button:state(selected)');
Postado em BlogTags:
Escreva um comentário