Explicando e codificando um verificador de contraste

Apr 26 2023
Um contraste ruim ocorre quando a diferença de brilho entre duas cores em uma página da Web é muito baixa, dificultando a distinção entre as cores por alguns usuários, principalmente aqueles com deficiência visual. Para ajudar designers e desenvolvedores a cumprir os padrões de acessibilidade, as Diretrizes de Acessibilidade de Conteúdo da Web (WCAG), publicadas pelo World Wide Web Consortium (W3C), especificam dois níveis de conformidade: AA e AAA.
meus olhos doem olhando essa foto

Um contraste ruim ocorre quando a diferença de brilho entre duas cores em uma página da Web é muito baixa, dificultando a distinção entre as cores por alguns usuários, principalmente aqueles com deficiência visual. Para ajudar designers e desenvolvedores a cumprir os padrões de acessibilidade, as Diretrizes de Acessibilidade de Conteúdo da Web (WCAG), publicadas pelo World Wide Web Consortium (W3C), especificam dois níveis de conformidade: AA e AAA. A conformidade com AA requer uma taxa de contraste de pelo menos 4,5:1 para texto normal e 3:1 para texto grande. A conformidade com AAA requer uma taxa de contraste de pelo menos 7:1 para texto normal e 4,5:1 para texto grande.

Para calcular a taxa de contraste, os verificadores de contraste normalmente usam a fórmula (L1 + 0,05) / (L2 + 0,05), onde L1 e L2 são os valores de luminância das duas cores que estão sendo comparadas. Adicionar 0,05 a cada valor de luminância é um pequeno ajuste que ajuda a evitar erros de divisão por zero e garante que a taxa de contraste sempre caia entre 1 e 21.

Colocando a teoria em prática, desenvolvi este simples verificador de contraste , que avalia o contraste das imagens enviadas pelos usuários.

const fileInputElement = document.getElementById('fileInput');
const imageElement = document.getElementById('image');
const resultElement = document.getElementById('result');


fileInputElement.addEventListener('change', () => {

  const file = fileInputElement.files[0];
  const reader = new FileReader();
  reader.readAsDataURL(file);

  reader.onload = () => {
    imageElement.src = reader.result;
    imageElement.onload = () => {
      const contrastRatio = calculateContrastRatio(imageElement);
      displayResult(contrastRatio);
    }
  }
});


function calculateContrastRatio(image) {

  const canvas = document.createElement('canvas');
  const context = canvas.getContext('2d');
  canvas.width = image.width;
  canvas.height = image.height;


  context.drawImage(image, 0, 0);
  const imageData = context.getImageData(0, 0, image.width, image.height);
  const pixels = imageData.data;


  let lightestColor = 0;
  let darkestColor = 255;


  for (let i = 0; i < pixels.length; i += 4) {
    const red = pixels[i];
    const green = pixels[i + 1];
    const blue = pixels[i + 2];
    const alpha = pixels[i + 3] / 255;
    const luminance = 0.2126 * red + 0.7152 * green + 0.0722 * blue;


    lightestColor = Math.max(luminance, lightestColor);
    darkestColor = Math.min(luminance, darkestColor);
  }

  const contrastRatio = (lightestColor + 0.05) / (darkestColor + 0.05);
  return contrastRatio.toFixed(2);
}

function displayResult(contrastRatio) {
  let color;
  let message;

  if (contrastRatio >= 4.5) {
    color = 'green';
    message = `Contrast Ratio: ${contrastRatio}:1 (Passes WCAG 2.0 AA)`;
  } else if (contrastRatio >= 3) {
    color = 'orange';
    message = `Contrast Ratio: ${contrastRatio}:1 (Passes WCAG 2.0 AA for large text)`;
  } else {
    color = 'red';
    message = `Contrast Ratio: ${contrastRatio}:1 (Fails WCAG 2.0 AA)`;
  }


  resultElement.style.color = color;
  resultElement.textContent = message;
}

A calculateContrastRatiofunção usa uma imagem como argumento, cria uma tela e um contexto e define a largura e a altura da tela para corresponder à imagem. Em seguida, desenha a imagem na tela e recupera um objeto de dados de imagem do contexto. Ele itera sobre os pixels dos dados da imagem, calculando a luminância de cada pixel e acompanhando as cores mais claras e mais escuras. Por fim, calcula e retorna a taxa de contraste entre as duas cores.

A displayResultfunção usa uma taxa de contraste como argumento e define a cor e o conteúdo do texto de um elemento especificado com base no valor da taxa. Se a proporção for maior ou igual a 4,5, considera-se que atende às diretrizes de acessibilidade WCAG 2.0 AA e a mensagem é colorida em verde. Se a proporção for maior ou igual a 3, considera-se que passa por texto grande e é colorido de laranja. Se a proporção for menor que 3, ele falha e fica vermelho.

Obviamente, esse código pode ser aprimorado e dimensionado para atender a outras demandas relacionadas à acessibilidade. Verifique o código completo.