Aplicar CSS condicionalmente com base no suporte a regras at

Recursos da Web: at-rule()

Use @supports at-rule(@<at-rule name>) {…} para aplicar condicionalmente determinadas regras ou declarações CSS somente se uma determinada regra at for compatível com o navegador. Ela pode conter regras inteiras ou ser usada em conjunto com o aninhamento de CSS para aplicar condicionalmente determinadas declarações.

.foo {
  background: var(--accent-color);

  @supports at-rule(@property) {
    background-image: var(--rainbow-gradient);
    animation: sliding-rainbow 1s linear infinite;
  }
}

NÃO use @supports at-rule() para aplicar condicionalmente a regra @ que está sendo detectada e nada mais.

/* BAD: CSS already ignores rules the browser doesn't understand, so this is unnecessary */
@supports at-rule(@starting-style) {
  @starting-style {
    /* ... */
  }
}

Como detectar a compatibilidade com regras @ quando @supports at-rule() não é adequado

Quando at-rule() não é uma opção, seja devido à compatibilidade do navegador ou porque é necessária uma detecção mais profunda, use as orientações abaixo.

Tente usar propriedades, valores ou seletores primeiro

O @supports já pode detectar propriedades, valores e seletores (com @supports selector(...)). Em muitos casos, é possível usá-los para detectar o suporte a um recurso sem precisar verificar a regra @ em si.

Por exemplo, em vez de @supports at-rule(@function) para detectar compatibilidade com funções personalizadas, use @supports (color: --foo()).

Em outros casos, pode haver pseudoclasses ou pseudoelementos que só estão disponíveis quando uma determinada regra at é aceita, o que pode ser usado para detectar a compatibilidade.

Como detectar tipos de consulta de contêiner

at-rule() SÓ permite a detecção de regras @ por nome. Ele NÃO permite a detecção de outras sintaxes ou descritores. Por exemplo, isto não vai funcionar:

@supports at-rule(@container style(--a: 1)) {
  /* ... */
}

Para muitos tipos de consultas de contêiner, é possível usar container-type com um valor adequado. Por exemplo, @supports (container-type: scroll-state) para consultas de estado de rolagem ou @supports (container-type: size)container-type: anchoredforanchored()`.

No entanto, não há container-type para consultas de estilo. IMPORTANTE: NÃO tente usar @supports (container-type: style) para detectar a compatibilidade com consultas de estilo, porque isso não vai funcionar.

Em vez disso, tente uma consulta de estilo que seja sempre verdadeira:

@container style(--a: 1) or (not (style(--a: 1))) {
  /* Rules here will only be applied if style queries are supported */
}

Isso vai funcionar em qualquer navegador que ofereça suporte a consultas de estilo, independentemente do nome ou valor da propriedade que você usar, desde que sejam iguais em ambos.

Propriedades personalizadas registradas (@property)

Em alguns casos, se o CSS condicional a ser aplicado puder ser reduzido a uma única propriedade, use uma propriedade personalizada dedicada registrada para essa finalidade:

/* Use a custom property to conditionally include the dash animation */
@property --progress-dash-animation {
  syntax: "*";
  inherits: false;
  initial-value: , progress-dash 3s ease-in-out infinite;
}

.spinner {
  /* The dash animation is only included if @property is supported */
  animation:
    progress-spin var(--_used-spinner-duration) linear infinite
    var(--progress-dash-animation, );
}

Para uma solução de uso geral, registre uma propriedade não herdada e defina-a como (um espaço) no elemento raiz:

{# Testcase: https://codepen.io/leaverou/pen/KwWzpVr #}

@property --supports-atproperty {
    syntax: "*";
    inherits: false;
}

:root {
    --supports-atproperty: ;
}

body {
  /* The background is only applied if @property is supported */
    background: var(--supports-atproperty, green);
}

Observações:

  • Ambas só funcionam se o espaço em branco for uma alternativa aceitável. Para outros casos, use o JS conforme descrito abaixo.
  • A solução de uso geral depende da não herança, portanto, não funciona para declarações no elemento raiz.

Como usar o JavaScript como último recurso

Para regras at completas, verifique a presença de determinadas interfaces em JavaScript. Por exemplo, se globalThis.CSSFunctionRule estiver definido, @function será compatível.

Se você precisar de testes mais detalhados (por exemplo, para determinados prelúdios, regras aninhadas, descritores), configure um teste e verifique como ele foi analisado:

const sheet = new CSSStyleSheet();
sheet.replaceSync(`
  @page {
    @top-left {
      content: "test";
    }
  }
`);
const SUPPORTS_PAGE_MARGINS = Boolean(sheet.cssRules[0]?.cssRules?.[0]);

IMPORTANTE:

  • Ao usar JS para detectar suporte a recursos CSS, NÃO aplique os estilos diretamente do JS. Em vez disso, aplique uma classe ao elemento raiz e ramifique-a no CSS.
  • As consultas de contêiner são analisadas mesmo que o prelúdio não seja reconhecido, e não há como detectar suporte para determinados tipos de consultas de contêiner na estrutura do CSSOM.
  • NÃO use CSS.registerProperty para detectar a compatibilidade com @property, já que ela é um pouco mais ampla.

Estratégias de fallback

A menos que isso esteja dentro da sua meta de suporte, use @supports (at-rule()) APENAS para detectar regras @ com suporte mais restrito do que o próprio recurso @supports (at-rule()).

Para aplicar CSS condicionalmente com base no suporte a regras at que foram lançadas antes desse recurso, use as orientações na seção acima.

Para detectar a compatibilidade com o recurso @supports at-rule() em si, use uma regra at que certamente existe em todos os navegadores compatíveis com @supports at-rule(), como @supports at-rule(@media) { ... }.