Como estilizar caixas de seleção

Use HTML semântico e acessível com cada caixa de seleção associada ao rótulo, seja aninhando o <input> dentro do <label> ou combinando o atributo for do rótulo com o id da entrada.

O CSS moderno oferece duas abordagens para estilizar caixas de seleção: personalizar a aparência nativa com accent-color ou substituir a apresentação padrão por appearance: none no elemento <input type="checkbox">.

Personalização nativa com accent-color

Para uma estilização simples e consistente com a marca que mantém a renderização nativa, o comportamento do teclado, a validação e os indicadores de foco do sistema operacional, use accent-color.

.checkbox-native {
  accent-color: #1a73e8;

  /* Sizing is controlled via relative units */
  inline-size: 1.25em;
  aspect-ratio: 1;
  margin: 0;
  cursor: pointer;
}

Estilo totalmente personalizado com appearance: none

Use appearance: none quando o design exigir controle sobre o formato, o tamanho, os estados ou os indicadores da caixa de seleção. Isso remove o estilo visual nativo, mas mantém o <input> como o destino interativo.

Defina tamanhos relativos explícitos para --checkbox-size e --checkbox-icon-size, porque appearance: none remove as dimensões padrão do navegador. Mantenha o ícone menor que a caixa de seleção para dar espaço à borda e ao padding visual.

/* Reusable SVG assets */
:root {
  --icon-check: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'></polyline></svg>");
  --icon-dash: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='4.5' stroke-linecap='round' stroke-linejoin='round'><line x1='5' y1='12' x2='19' y2='12'></line></svg>");
}

/* Custom checkbox */
.checkbox-custom {
  --checkbox-size: 1.25em;
  --checkbox-icon-size: 0.7em;

  appearance: none;
  display: inline-grid;
  place-content: center;
  inline-size: var(--checkbox-size);
  aspect-ratio: 1;
  margin: 0;
  border: 2px solid currentColor;
  cursor: pointer;

  &::before {
    content: "";
    inline-size: var(--checkbox-icon-size);
    aspect-ratio: 1;
    background-color: currentColor;
    mask: var(--checkbox-icon) no-repeat center / contain;
    scale: 0;

    @media (prefers-reduced-motion: no-preference) {
      transition: scale 150ms ease;
    }

    @media (forced-colors: active) {
      background-color: CanvasText;
    }
  }

  &:checked {
    --checkbox-icon: var(--icon-check);
  }

  &:indeterminate {
    --checkbox-icon: var(--icon-dash);
  }

  &:is(:checked, :indeterminate)::before {
    scale: 1;
  }

  &:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 2px;
  }

  &:disabled {
    cursor: not-allowed;
    opacity: 0.5;
  }
}

O estado indeterminado

O estado indeterminado não pode ser definido por um atributo HTML. Defina a propriedade DOM indeterminate em JavaScript (checkbox.indeterminate = true) e garanta que a pseudoclasse :indeterminate seja visualmente diferente dos estados :checked e desmarcado:

  • Nativo (accent-color): estilizado automaticamente pelo navegador usando o accent-color do elemento (não é necessária uma regra CSS :indeterminate extra).
  • Personalizado (appearance: none): troque --checkbox-icon por var(--icon-dash) em .checkbox-custom:indeterminate, conforme mostrado acima.
const selectAll = document.querySelector("#select-all");
selectAll.indeterminate = true;

Acessibilidade e interação

  • USE :focus-visible em vez de :focus para estilizar indicadores de foco em caixas de seleção personalizadas. Assim, um contorno diferente aparece para a navegação pelo teclado sem aparecer nos cliques do ponteiro.
  • NÃO use outline: none nem oculte os indicadores de foco nas caixas de seleção.
  • FAÇA com que a caixa de seleção ou o <label> que a envolve forneça uma área de toque interativa de pelo menos 44px x 44px, mantendo o tamanho da caixa de seleção visual em unidades relativas:

    label {
      display: inline-flex;
      align-items: center;
      gap: 0.65rem;
      min-block-size: 44px;
      cursor: pointer;
    }
    
  • RECOMENDAMOS fornecer uma variante de estilo :disabled que reduza a opacidade e defina cursor: not-allowed para transmitir estados inativos com clareza.

Problemas que podem ocorrer ao usar accent-color

A renderização de accent-color no navegador varia. Para indicadores geralmente pintados sobre um fundo de cor de destaque, como marcas de caixas de seleção e pontos de botões de opção, os navegadores podem aplicar tratamentos diferentes:

  • O Chromium e o Firefox geralmente selecionam uma cor de indicador contrastante.
  • O Safari pode preservar um indicador branco no modo claro ou um indicador preto no modo escuro, modificando a cor de destaque atrás dele. Esse ajuste nem sempre fornece contraste suficiente para todas as cores fornecidas. Por isso, teste combinações importantes no Safari, incluindo esquemas de cores claras e escuras. Não presuma que o Safari vai escolher automaticamente a cor do indicador que oferece o melhor contraste.

Estratégias de fallback

Alternativas e suporte do navegador para accent-color

A propriedade accent-color é um aprimoramento progressivo. Os navegadores que não são compatíveis com essa propriedade vão ignorá-la e usar as cores padrão da interface.

Máscaras CSS