Linting de Componentes Personalizados com o Endpoint REST
Um guia para usar Axe DevTools Linter na verificação de componentes personalizados com o endpoint REST
Este artigo mostra como usar o endpoint REST do Axe DevTools Linter para encontrar erros de acessibilidade em componentes personalizados.
Este artigo é para usuários do endpoint REST do Axe DevTools Linter. Se você usa a extensão Axe Accessibility Linter para o VS Code ou o plugin para o JetBrains, veja Linting de Componentes Personalizados com a Extensão Axe Accessibility Linter para VS Code ou o Plugin para JetBrains para mais informações.
Pré-requisitos
Você precisará de acesso à versão SaaS ou a uma versão local do Axe DevTools Linter. Veja Obtendo uma Chave de API SaaS do Axe DevTools Linter ou Configurando a Edição Local do Axe DevTools Linter para mais informações.
Você também precisará de uma ferramenta REST que:
- Possa enviar solicitações POST
- Pode adicionar cabeçalhos
Authorization(para a versão SaaS do Axe DevTools Linter) - Permita a criação de corpos de solicitação JSON
Um Tutorial de Linting de Componentes Personalizados
Quando você usa o Axe DevTools Linter para verificar o código-fonte, você fornece um corpo JSON contendo o código-fonte e a configuração na sua solicitação HTTP. Por exemplo, o seguinte HTML mostra o uso do elemento img:
<img src="path/to/image.jpg"/>(Este é um exemplo altamente simplificado apenas para demonstrar o linting, e não um exemplo real do mundo.)
O corpo JSON da solicitação a ser enviada para o Axe DevTools Linter seria assim:
{
"source": "<img src=\"path/to/image.jpg\"/>",
"filename": "image-demo.html"
}Você envia este JSON para o Axe DevTools Linter como uma solicitação REST POST para o endpoint /linter-source. Para mais informações, veja O Endpoint de Lint na documentação de referência.
Para acompanhar este tutorial, você pode usar qualquer ferramenta REST que possa enviar solicitações POST com corpos de solicitação JSON. Os exemplos a seguir mostram o corpo da solicitação enviado para o Axe DevTools Linter e o corpo da resposta JSON, que mostra os erros de acessibilidade encontrados pelo Axe DevTools Linter.
Como este elemento img não possui um atributo alt, você receberá um erro de acessibilidade do Axe DevTools Linter:
{
"report": {
"errors": [
{
"column": 1,
"description": "Ensures <img> elements have alternate text or a role of none or presentation",
"endColumn": 31,
"helpURL": "https://dequeuniversity.com/rules/axe/4.4/image-alt?application=axe-linter",
"lineContent": "<img src=\"path/to/image.jpg\"/>",
"lineNumber": 1,
"linterType": "html",
"ruleId": "image-alt"
}
]
}
}Um Componente de Imagem Personalizado
O seguinte exemplo mostra um componente personalizado custom-image:
<custom-image src="path/to/image.jpg"></custom-image>Para enviar o HTML para o Axe DevTools Linter usando uma solicitação POST, use o seguinte como corpo JSON:
{
"source": "<custom-image src=\"path/to/image.jpg\"></custom-image>",
"filename": "custom-image.html"
}O servidor responde sem erros de acessibilidade porque não há mapeamento entre custom-image e img, então o Axe DevTools Linter não pode sinalizar um atributo alt ausente:
{
"report": {
"errors": []
}
}Mapeando custom-image para img
Se você fornecer um mapeamento entre custom-image e img, o Axe DevTools Linter pode mapear seu componente personalizado como um elemento HTML padrão e localizar erros de acessibilidade. Você pode especificar o mapeamento usando a opção de configuração global-components (parte do objeto config):
{
"config": {
"global-components": {
"custom-image": "img"
}
},
"filename": "c-image.html",
"source": "<custom-image src=\"path/to/image.jpg\"></custom-image>\n\n"
}O Axe DevTools Linter agora responde o seguinte:
{
"report": {
"errors": [
{
"column": 1,
"description": "Ensures <img> elements have alternate text or a role of none or presentation",
"endColumn": 54,
"helpURL": "https://dequeuniversity.com/rules/axe/4.4/image-alt?application=axe-linter",
"lineContent": "<custom-image src=\"path/to/image.jpg\"></custom-image>",
"lineNumber": 1,
"linterType": "html",
"ruleId": "image-alt"
}
]
}
}Você também pode indicar o mesmo mapeamento acima com qualquer uma destas sintaxes:
{
"config": {
"global-components": {
"custom-image": {
"element": "img"
}
}
}
}Ou, alternativamente, abreviando element como el:
{
"config": {
"global-components": {
"custom-image": {
"el": "img"
}
}
}
}Quando você usa um mapeamento de elemento como mostrado acima, todos os atributos do componente personalizado são copiados para o elemento emitido, e esse elemento emitido é verificado.
Corrigindo o Problema de Acessibilidade
Você pode adicionar um atributo alt ao seu custom-image para corrigir o problema de acessibilidade (como mostrado abaixo com o corpo JSON da solicitação):
{
"config": {
"global-components": {
"custom-image": "img"
}
},
"filename": "c-image.html",
"source": "<custom-image src=\"path/to/image.jpg\" alt=\"alt text\"></custom-image>\n\n"
}O servidor responde com o seguinte array errors vazio porque seu componente personalizado tem o atributo alt necessário (que foi copiado—com *todos* outros atributos no componente custom-image—para o elemento img emitido):
{
"report": {
"errors": []
}
}Mapeando o Atributo alternative-text
Se seu componente de imagem personalizado usar um atributo diferente para indicar texto alternativo, você pode especificar esse atributo na configuração. Por exemplo, suponha que seu componente custom-image use um atributo alternative-text em vez de alt, como mostrado abaixo:
<custom-image src="path/to/image.jpg" alternative-text="alt text"></custom-image>Neste caso, você poderia especificar um mapeamento entre o atributo alternative-text e o atributo alt como mostrado com o array attributes no corpo da solicitação JSON mostrado abaixo:
{
"config": {
"global-components": {
"custom-image": {
"element": "img",
"attributes": [
{
"alternative-text": "alt"
}
]
}
}
},
"filename": "c-image.html",
"source": "<custom-image src=\"path/to/image.jpg\" alternative-text=\"alt text\"></custom-image>\n\n"
}Note que a configuração global-components difere ligeiramente do mapeamento anterior de um componente personalizado para um elemento HTML. Com apenas elementos, você usa um mapeamento de uma string ("custom-image") para outra string ("img"). Com a inclusão do array attributes, agora é necessário usar a propriedade element (ou el) para especificar o elemento HTML emitido.
O Axe DevTools Linter responde com o seguinte porque a regra alt-text foi atendida pelo seu atributo alternative-text:
{
"report": {
"errors": []
}
}Como você especificou o array attributes na configuração, quando o servidor mapeia de custom-image para img, somente os atributos especificados no array attributes são copiados para o elemento HTML emitido.
Você também pode abreviar attributes como attrs:
{
"config": {
"global-components": {
"custom-image": {
"attrs": [
{
"alternative-text": "alt"
}
],
"element": "img"
}
}
}
}Valores de Atributos Especiais: <text>, aria-* e <element>
Suponha que você use um componente custom-button da seguinte maneira:
<custom-button aria-controls="expand-region" aria-expanded="false" aria-colindex="1" message="Show Region"></custom-button>(O botão personalizado irá, usando JavaScript e CSS que não estão incluídos aqui, esconder e mostrar um div.)
Há dois problemas com esse uso:
- Se você mapear este componente
custom-buttondiretamente para um elementobutton, não haverá conteúdo textual para ser exibido no botão. A intenção do autor do componente, no entanto, é que o atributomessageseja usado como conteúdo textual:<button>valor do atributomessage</button> - O elemento
buttonemitido tem um papel implícito debutton, portanto o atributoaria-colindexestá incorreto e deve ser removido.
Se você enviar o código acima para o Axe DevTools Linter (sem mapeamento global-components), você receberá a seguinte resposta:
{
"report": {
"errors": []
}
}O Valor Especial <text>
Para abordar o primeiro problema (conteúdo de texto para o elemento button vindo de um atributo message), você pode usar o valor especial <text>, que mapeia um atributo para o conteúdo de texto do elemento emitido. Nesse caso, o texto do atributo message deve ser copiado para o conteúdo de texto do elemento button emitido.
Para configurar a solicitação para alertar o Axe DevTools Linter que o atributo message deve ser considerado como conteúdo de texto para o elemento HTML button, você pode usar o valor especial <text> e enviar a seguinte solicitação:
{
"config": {
"global-components": {
"custom-button": {
"attributes": [
{
"message": "<text>"
}
],
"element": "button"
}
}
},
"filename": "aria-button.html",
"source": "<custom-button aria-controls=\"expand-region\" aria-expanded=\"false\" aria-colindex=\"1\" message=\"Show Region\"></custom-button>\n"
}Because you defined the message attribute as <text>, you told Axe DevTools Linter to consider that attribute as replacing textual content of the HTML button element with the value of the message attribute.
Unfortunately, by using the attributes array, the only attribute that was passed through to the emitted button element was only the message attribute; any attributes not in the attributes array are not passed through. This means that the incorrect aria-colindex was not caught by the server.
Usando aria-*
Você pode usar o valor especial aria-* para passar todos os atributos ARIA, conforme mostrado abaixo:
{
"config": {
"global-components": {
"custom-button": {
"attributes": [
{
"message": "<text>"
},
"aria-*"
],
"element": "button"
}
}
},
"filename": "aria-button.html",
"source": "<custom-button aria-controls=\"expand-region\" aria-expanded=\"false\" aria-colindex=\"1\" message=\"Show Region\"></custom-button>\n"
}O servidor responde com:
{
"report": {
"errors": [
{
"column": 1,
"description": "Ensures ARIA attributes are allowed for an element's role",
"endColumn": 124,
"helpURL": "https://dequeuniversity.com/rules/axe/4.4/aria-allowed-attr?application=axe-linter",
"lineContent": "<custom-button aria-controls=\"expand-region\" aria-expanded=\"false\" aria-colindex=\"1\" message=\"Show Region\"></custom-button>",
"lineNumber": 1,
"linterType": "html",
"ruleId": "aria-allowed-attr"
}
]
}
}Este erro ocorre porque o elemento button tem um role="button" implícito e usar aria-colindex é inválido com botões. Com aria-*, todos os atributos ARIA são copiados para o elemento emitido; isso inclui copiar o atributo aria-colindex inválido.
<element>
Com componentes complexos, você pode querer emitir um elemento HTML diferente do elemento padrão em casos específicos. Por exemplo, você pode ter um componente de botão que normalmente se comporta como um botão e, em outros estados, como uma imagem de espaço reservado. O valor <element> permite especificar um atributo no seu componente personalizado que determina o elemento emitido.
{
"config": {
"global-components": {
"my-button": {
"element": "button",
"attributes": [
{
"use": "<element>"
},
'src',
'alt'
]
}
}
},
"filename": "aria-button.html",
"source": "<my-button use=\"img\" src=\"globe.jpg\"></my-button>"
}Neste caso, o atributo use no componente my-button indica o elemento a ser emitido. Como o elemento img emitido não contém um atributo alt, você receberá um erro:
{
"report": {
"errors": [
{
"ruleId": "image-alt",
"helpURL": "https://dequeuniversity.com/rules/axe/4.10/image-alt?application=axe-linter",
"description": "Images must have alternative text",
"lineNumber": 1,
"column": 1,
"linterType": "html",
"lineContent": "<my-button use=\"img\" src=\"globe.jpg\"></my-button>",
"endColumn": 50
}
]
}
}Passando Todos os Atributos Implicitamente
Note que se você tivesse usado apenas mapeamento de elemento (onde o mapeamento não usa o array attributes), todos os atributos seriam, por padrão, copiados para o elemento button como mostrado abaixo:
{
"config": {
"global-components": {
"custom-button": "button"
}
},
"filename": "aria-button.html",
"source": "<custom-button aria-controls=\"expand-region\" aria-expanded=\"false\" aria-colindex=\"1\" message=\"Show Region\"></custom-button>\n"
}Com esta resposta do servidor, você pode ver que todos os atributos de custom-button são verificados para problemas de acessibilidade, e dois erros são encontrados:
{
"report": {
"errors": [
{
"column": 1,
"description": "Ensures ARIA attributes are allowed for an element's role",
"endColumn": 124,
"helpURL": "https://dequeuniversity.com/rules/axe/4.4/aria-allowed-attr?application=axe-linter",
"lineContent": "<custom-button aria-controls=\"expand-region\" aria-expanded=\"false\" aria-colindex=\"1\" message=\"Show Region\"></custom-button>",
"lineNumber": 1,
"linterType": "html",
"ruleId": "aria-allowed-attr"
},
{
"column": 1,
"description": "Ensures buttons have discernible text",
"endColumn": 124,
"helpURL": "https://dequeuniversity.com/rules/axe/4.4/button-name?application=axe-linter",
"lineContent": "<custom-button aria-controls=\"expand-region\" aria-expanded=\"false\" aria-colindex=\"1\" message=\"Show Region\"></custom-button>",
"lineNumber": 1,
"linterType": "html",
"ruleId": "button-name"
}
]
}
}O exemplo acima mostra que um primeiro passo prático ao começar a verificar componentes personalizados seria começar com um mapeamento de elemento (copiando, assim, todos os atributos para o elemento HTML padrão emitido) e depois ver quais atributos precisam ser adicionados à configuração (seja os atributos do componente personalizado devem ser mapeados para outros atributos ou se você precisa usar <text> ou aria-*).
Atributos Padrão
Atributos padrão permitem que você defina valores para atributos no seu arquivo de configuração em vez de mapear um atributo para outro. Por exemplo, a seguinte configuração de exemplo mostra um componente custom-menu mapeado para um elemento li com um role de *menu*:
{
"config": {
"global-components": {
"custom-menu": {
"element": "li",
"attributes": [
{
"role": {
"name": null,
"default": "menu"
}
}
]
}
}
}
}Como o atributo role tem um valor padrão de *menu*, definido no arquivo de configuração, os usuários não precisam especificar um atributo role quando usam o componente custom-menu no código deles. A implicação é que a implementação do componente personalizado cria esses atributos no elemento de saída e define seus valores em vez de exigir que os usuários os definam quando usam seu componente.
Opcionalmente, o valor name é definido como null na configuração, o que faz com que o Axe DevTools Linter ignore quaisquer atributos role que os usuários tenham especificado em custom-menu no código verificado.
O valor especificado com default deve ser uma string.
Analisando Violações de Componentes Personalizados
Quando você tem muitos componentes personalizados configurados, pode ser difícil dizer quais violações na resposta vieram de mapeamentos de componentes personalizados versus HTML padrão. Adicionar "properties": ["customName"] ao corpo da sua solicitação faz com que o Axe DevTools Linter inclua uma propriedade customName em cada erro que se originou de um componente mapeado personalizado.
Com base no exemplo custom-image acima, adicionar "properties": ["customName"] à solicitação:
{
"properties": ["customName"],
"config": {
"global-components": {
"custom-image": "img"
}
},
"filename": "c-image.html",
"source": "<custom-image src=\"path/to/image.jpg\"></custom-image>\n\n"
}A resposta agora inclui uma propriedade customName no erro mostrando qual componente personalizado desencadeou a violação:
{
"report": {
"errors": [
{
"column": 1,
"customName": "custom-image",
"description": "Ensures <img> elements have alternate text or a role of none or presentation",
"endColumn": 54,
"helpURL": "https://dequeuniversity.com/rules/axe/4.4/image-alt?application=axe-linter",
"lineContent": "<custom-image src=\"path/to/image.jpg\"></custom-image>",
"lineNumber": 1,
"linterType": "html",
"ruleId": "image-alt"
}
]
}
}Erros de componentes que não fazem parte de um mapeamento personalizado não terão uma propriedade customName.
Veja Também
- Referência da API REST do Axe DevTools Linter contém mais informações sobre o uso dos vários endpoints REST fornecidos pelo Axe DevTools Linter.
- Obtendo uma Chave de API SaaS do Axe DevTools Linter mostra como obter uma chave de API para usar a versão Software como Serviço (SaaS) do Axe DevTools Linter.
