Resumo
Um <howto-checkbox> representa uma opção booleana em um formulário. O tipo mais comum
de caixa de seleção é um tipo duplo que permite ao usuário alternar entre duas
opções: marcada e desmarcada.
O elemento tenta aplicar os atributos role="checkbox" e
tabindex="0" quando é criado pela primeira vez. O atributo role ajuda a tecnologia
auxiliar, como um leitor de tela, a informar ao usuário que tipo de controle é esse.
O atributo tabindex opta pelo elemento na ordem de guias, tornando-o focado e operável
pelo teclado. Para saber mais sobre esses dois tópicos, consulte
O que a ARIA pode fazer? e Usar tabindex.
Quando a caixa de seleção está marcada, ela adiciona um atributo booleano checked e define
uma propriedade checked correspondente como true. Além disso, o elemento define um
atributo aria-checked como "true" ou "false", dependendo do
estado. Clicar na caixa de seleção com o mouse ou a barra de espaço alterna esses
estados marcados.
A caixa de seleção também oferece suporte a um estado disabled. Se a propriedade disabled
for definida como verdadeira ou o atributo disabled for aplicado, a caixa de seleção vai definir
aria-disabled="true", remover o atributo tabindex e retornar o foco
ao documento se a caixa de seleção for o activeElement atual.
A caixa de seleção é associada a um elemento howto-label para garantir que ela tenha um
nome acessível.
Referência
- HowTo: Componentes no GitHub
- Padrão de caixa de seleção nas práticas de criação de ARIA 1.1
- O que a ARIA pode fazer?
- Como usar o tabindex
Demonstração
Conferir a demonstração ao vivo no GitHub
Exemplo de uso
<style>
  howto-checkbox {
    vertical-align: middle;
  }
  howto-label {
    vertical-align: middle;
    display: inline-block;
    font-weight: bold;
    font-family: sans-serif;
    font-size: 20px;
    margin-left: 8px;
  }
</style>
<howto-checkbox id="join-checkbox"></howto-checkbox>
<howto-label for="join-checkbox">Join Newsletter</howto-label>
Código
(function() {
Define códigos de tecla para ajudar a processar eventos de teclado.
  const KEYCODE = {
    SPACE: 32,
  };
Clonar o conteúdo de um elemento <template> tem melhor desempenho do que usar innerHTML porque evita custos de análise de HTML adicionais.
  const template = document.createElement('template');
  template.innerHTML = `
    <style>
      :host {
        display: inline-block;
        background: url('../images/unchecked-checkbox.svg') no-repeat;
        background-size: contain;
        width: 24px;
        height: 24px;
      }
      :host([hidden]) {
        display: none;
      }
      :host([checked]) {
        background: url('../images/checked-checkbox.svg') no-repeat;
        background-size: contain;
      }
      :host([disabled]) {
        background:
          url('../images/unchecked-checkbox-disabled.svg') no-repeat;
        background-size: contain;
      }
      :host([checked][disabled]) {
        background:
          url('../images/checked-checkbox-disabled.svg') no-repeat;
        background-size: contain;
      }
    </style>
  `;
  class HowToCheckbox extends HTMLElement {
    static get observedAttributes() {
      return ['checked', 'disabled'];
    }
O construtor do elemento é executado sempre que uma nova instância é criada. As instâncias são criadas analisando HTML, chamando document.createElement('howto-checkbox') ou chamando new HowToCheckbox(). O construtor é um bom lugar para criar DOMs sombra, mas evite tocar em atributos ou filhos leves do DOM, porque eles ainda não estão disponíveis.
    constructor() {
      super();
      this.attachShadow({mode: 'open'});
      this.shadowRoot.appendChild(template.content.cloneNode(true));
    }
connectedCallback() é acionado quando o elemento é inserido no DOM. É um bom lugar para definir o role inicial, o tabindex, o estado interno e instalar listeners de eventos.
    connectedCallback() {
      if (!this.hasAttribute('role'))
        this.setAttribute('role', 'checkbox');
      if (!this.hasAttribute('tabindex'))
        this.setAttribute('tabindex', 0);
Um usuário pode definir uma propriedade em uma instância de um elemento antes que o protótipo seja conectado a essa classe. O método _upgradeProperty() vai verificar se há propriedades de instância e executá-las nos setters de classe adequados. Consulte a seção Propriedades lazy para mais detalhes.
      this._upgradeProperty('checked');
      this._upgradeProperty('disabled');
      this.addEventListener('keyup', this._onKeyUp);
      this.addEventListener('click', this._onClick);
    }
    _upgradeProperty(prop) {
      if (this.hasOwnProperty(prop)) {
        let value = this[prop];
        delete this[prop];
        this[prop] = value;
      }
    }
disconnectedCallback() é acionado quando o elemento é removido do DOM. É um bom lugar para fazer a limpeza, como liberar referências e remover listeners de eventos.
    disconnectedCallback() {
      this.removeEventListener('keyup', this._onKeyUp);
      this.removeEventListener('click', this._onClick);
    }
As propriedades e os atributos correspondentes precisam ser espelhados. O setter de propriedade para "checked" lida com valores verdadeiros/falsos e os reflete no estado do atributo. Consulte a seção evitar reentrada para mais detalhes.
    set checked(value) {
      const isChecked = Boolean(value);
      if (isChecked)
        this.setAttribute('checked', '');
      else
        this.removeAttribute('checked');
    }
    get checked() {
      return this.hasAttribute('checked');
    }
    set disabled(value) {
      const isDisabled = Boolean(value);
      if (isDisabled)
        this.setAttribute('disabled', '');
      else
        this.removeAttribute('disabled');
    }
    get disabled() {
      return this.hasAttribute('disabled');
    }
attributeChangedCallback() é chamado quando qualquer um dos atributos na matriz observedAttributes é alterado. É um bom lugar para lidar com efeitos colaterais, como definir atributos ARIA.
    attributeChangedCallback(name, oldValue, newValue) {
      const hasValue = newValue !== null;
      switch (name) {
        case 'checked':
          this.setAttribute('aria-checked', hasValue);
          break;
        case 'disabled':
          this.setAttribute('aria-disabled', hasValue);
O atributo tabindex não oferece uma maneira de remover totalmente a capacidade de foco de um elemento. Elementos com tabindex=-1 ainda podem ser focados com um mouse ou chamando focus(). Para garantir que um elemento esteja desativado e não seja focalizável, remova o atributo tabindex.
          if (hasValue) {
            this.removeAttribute('tabindex');
Se o foco estiver nesse elemento, remova-o chamando o método HTMLElement.blur().
            this.blur();
          } else {
            this.setAttribute('tabindex', '0');
          }
          break;
      }
    }
    _onKeyUp(event) {
Não processe atalhos de modificador normalmente usados por tecnologia adaptativa.
      if (event.altKey)
        return;
      switch (event.keyCode) {
        case KEYCODE.SPACE:
          event.preventDefault();
          this._toggleChecked();
          break;
Qualquer outra tecla pressionada é ignorada e transmitida de volta ao navegador.
        default:
          return;
      }
    }
    _onClick(event) {
      this._toggleChecked();
    }
_toggleChecked() chama o setter de verificado e inverte o estado dele. Como _toggleChecked() é causado apenas por uma ação do usuário, ele também envia um evento de mudança. Esse evento é transmitido para imitar o comportamento nativo de <input type=checkbox>.
    _toggleChecked() {
      if (this.disabled)
        return;
      this.checked = !this.checked;
      this.dispatchEvent(new CustomEvent('change', {
        detail: {
          checked: this.checked,
        },
        bubbles: true,
      }));
    }
  }
  customElements.define('howto-checkbox', HowToCheckbox);
})();
 
 
        
        