Aplica CSS de forma condicional según la compatibilidad con las reglas arroba

Funciones web: at-rule()

Usa @supports at-rule(@<at-rule name>) {…} para aplicar de forma condicional ciertas reglas o declaraciones de CSS solo si el navegador admite una regla arroba determinada. Esto puede contener reglas completas o usarse junto con el anidamiento de CSS para aplicar de forma condicional ciertas declaraciones.

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

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

NO uses @supports at-rule() para aplicar de forma condicional la regla-at que se detecta y nada más.

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

Detecta la compatibilidad con reglas arroba cuando @supports at-rule() no es apropiado

Cuando at-rule() no es una opción, ya sea por la compatibilidad del navegador o porque se necesita una detección más profunda, puedes usar la siguiente guía.

Prueba primero con propiedades, valores o selectores

@supports ya puede detectar propiedades, valores y selectores (a través de @supports selector(...)). En muchos casos, puedes usarlos para detectar la compatibilidad con una función sin necesidad de verificar la regla-at en sí.

Por ejemplo, en lugar de @supports at-rule(@function) para detectar compatibilidad con funciones personalizadas, puedes usar @supports (color: --foo()).

En otros casos, puede haber pseudoclases o pseudoelementos que solo estén disponibles cuando se admita una determinada regla-at, que se puede usar para detectar la compatibilidad.

Detecta tipos de consultas de contenedor

at-rule() SOLO permite la detección de reglas arroba por nombre. NO permite la detección de otra sintaxis ni de otros descriptores. Por ejemplo, esto no funcionará:

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

Para muchos tipos de consultas de contenedores, puedes usar container-type con un valor adecuado. Por ejemplo, @supports (container-type: scroll-state) para consultas de estado de desplazamiento o @supports (container-type: size)container-type: anchoredforanchored()`.

Sin embargo, no hay un container-type para las consultas de estilo. IMPORTANTE: NO intentes usar @supports (container-type: style) para detectar la compatibilidad con las consultas de estilo, ya que no funcionará.

En su lugar, puedes probar una consulta de estilo que siempre sea verdadera:

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

Esto coincidirá en cualquier navegador que admita consultas de estilo, independientemente del nombre o valor de la propiedad que uses, siempre que sean iguales en ambos.

Propiedades personalizadas registradas (@property)

En algunos casos, si el CSS condicional que se aplicará se puede reducir a una sola propiedad, puedes usar una propiedad personalizada dedicada registrada para este propósito:

/* 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 obtener una solución de uso general, puedes registrar una propiedad que no se herede y establecerla en (un espacio) en el elemento raíz:

{# 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);
}

Notas:

  • Ambas opciones solo funcionan si el espacio en blanco es una alternativa aceptable. En otros casos, deberás usar JS como se describe a continuación.
  • La solución de uso general depende de la no herencia, por lo que no funcionará para las declaraciones en el elemento raíz.

Usa JavaScript como último recurso

Para reglas arroba completas, verifica la presencia de ciertas interfaces en JavaScript. Por ejemplo, si se define globalThis.CSSFunctionRule, se admite @function.

Si necesitas realizar pruebas más exhaustivas (p.ej., para ciertos preludios, reglas anidadas o descriptores), puedes configurar una prueba y verificar cómo se analizó:

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

IMPORTANTE:

  • Cuando uses JS para detectar la compatibilidad con las funciones de CSS, NO apliques los estilos de JS directamente. En su lugar, aplica una clase al elemento raíz y crea una rama a partir de ella en CSS.
  • Ten en cuenta que las consultas de contenedores se analizan incluso si no se reconoce el preludio, y no hay forma de detectar la compatibilidad con ciertos tipos de consultas de contenedores desde la estructura de CSSOM.
  • NO uses CSS.registerProperty para detectar la compatibilidad con @property, ya que su compatibilidad es un poco más amplia.

Estrategias de resguardo

A menos que esto esté dentro de tu objetivo de compatibilidad, usa @supports (at-rule()) SOLO para detectar reglas arroba cuya compatibilidad sea más limitada que la de la función @supports (at-rule()) en sí.

Para aplicar CSS de forma condicional según la compatibilidad con las reglas arroba que se enviaron antes de esta función, usa la guía de la sección anterior.

Para detectar la compatibilidad con la función @supports at-rule() en sí, puedes usar una regla arroba que se garantice que exista en todos los navegadores que admiten @supports at-rule(), como @supports at-rule(@media) { ... }.