0Apoyar

Playground de @function en CSS

Construye funciones CSS personalizadas, añade o elimina parámetros con tipos y valores por defecto, combina varios result con @media y copia un ejemplo completo listo para probar.

Leer la guía

Preguntar a la IA

Playground de @function en CSS

Respuesta generada con openai/gpt-5.4-nano. La IA puede cometer errores. Revisa siempre el resultado.
Llamada--responsive-size(1.35rem)
HTML
<article class="specimen">
  <p class="specimen-eyebrow">CSS custom function</p>
  <h1>Una fórmula, muchos resultados</h1>
  <p>El resultado de la función se aplica directamente sobre este componente de ejemplo.</p>
</article>
CSS
@function --responsive-size(--narrow <length>, --wide <length>: 2.65rem) returns <length> {
  result: var(--wide);

  @media (width < 500px) {
    result: var(--narrow);
  }
}

.specimen {
  padding: clamp(1.25rem, 6vw, 2.75rem);
  display: grid;
  gap: 0.75rem;
  background: #171a28e8;
  color: #f5f7ff;
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: --responsive-size(1.35rem);
}

.specimen-eyebrow {
  margin: 0;
  color: #ff9a6f;
  font-size: 0.72rem;
  font-weight: 800;
  text-transform: uppercase;
}

.specimen h1 {
  margin: 0;
  font-size: 1em;
  font-weight: 800;
  line-height: 1.05;
  text-wrap: balance;
}

.specimen > p:last-child {
  margin: 0;
  font-family: ui-sans-serif, system-ui, sans-serif;
  font-size: 0.9rem;
}

Guía educativa de @function

@function: fórmulas reutilizables con valores CSS

Una custom property guarda un valor; una función CSS recibe argumentos y calcula un valor en el punto de uso. Aprende a definir su firma, resolver result y conservar una alternativa cuando falte soporte.

Volver al playground

Modelo mental

Definir una función no aplica estilos: hay que llamarla dentro de un valor de propiedad.

  1. Firma y argumentos

    --guide-space(--step <number>: 2) declara un parámetro. --guide-space(1) le pasa un argumento.

  2. Resolución local

    var(--step) consulta el parámetro; los valores locales y las condiciones determinan result.

  3. Valor de propiedad

    El resultado sustituye la llamada en padding. Debe cumplir tanto returns como la gramática de padding.

Un valor, no un bloque de estilosEsquema estático: argumentos → cálculo → valor. La función no crea elementos ni aplica conjuntos de declaraciones como un mixin.

Los nombres de función y parámetro empiezan por -- y distinguen mayúsculas. La llamada usa paréntesis, no var(--nombre-de-función). Define @function fuera de las reglas de estilo y úsala donde aceptarías un valor CSS; puede no recibir parámetros.

Parámetros, tipos y valores por defecto

La firma describe las entradas y returns restringe el valor de salida.

Los parámetros se separan con comas. El tipo es opcional: sin él se acepta type(*). Un default va después de dos puntos y debe respetar el tipo. Aquí --step es <number>, pero el cálculo produce <length>. Los parámetros se leen con var(); la llamada recibe valores, no nombres de parámetros.

Una escala de espacio con argumento opcional

--guide-space(): default 2

--guide-space(1): argumento 1

Función CSS nativa: compara el espacio con el resultado esperado.

Fallback base: 1rem de padding; la sintaxis de llamada no está disponible.

Con soporte nativo, la primera caja tiene 1.5rem de padding y la segunda 0.75rem. Sin soporte, ambas conservan 1rem. El borde discontinuo marca el contenido para ver la separación.
Una escala de espacio con argumento opcional
<div class="function-scene-signature">
  <div class="sample"><p>--guide-space(): default 2</p></div>
  <div class="sample compact"><p>--guide-space(1): argumento 1</p></div>
  <p class="native">Función CSS nativa: compara el espacio con el resultado esperado.</p>
  <p class="fallback">Fallback base: 1rem de padding; la sintaxis de llamada no está disponible.</p>
</div>
@function --guide-space(--step <number>: 2) returns <length> {
  result: calc(var(--step) * 0.75rem);
}

.function-scene-signature {
  display: grid;
  gap: 1rem;
  min-inline-size: 0;
  font: 1rem/1.5 system-ui, sans-serif;
  overflow-wrap: anywhere;

  .sample {
    padding: 1rem;
    border: 1px solid currentColor;

    p {
      margin: 0;
      border: 1px dashed currentColor;
    }
  }

  > p {
    margin: 0;
  }

  .native {
    display: none;
  }

  @supports (padding: --guide-space()) {
    .sample {
      padding: --guide-space();
    }

    .compact {
      padding: --guide-space(1);
    }

    .native {
      display: block;
    }

    .fallback {
      display: none;
    }
  }
}

Omitir el argumento usa 2; pasar 1 lo sustituye. Un argumento incompatible también puede activar el default; sin default, un argumento obligatorio ausente o incompatible invalida la llamada. <length> no admite porcentajes: usa <length-percentage> si necesitas ambos. type(<number> | <percentage>) permite una unión, pero tu fórmula aún debe manejar ambos tipos.

Ámbito: parámetros, variables locales y elemento

La función se evalúa en el contexto de la llamada, no como un valor fijado en :root.

var() busca primero una variable local, después un parámetro y después las custom properties disponibles en el punto de llamada. Una variable local oculta un parámetro del mismo nombre; el parámetro oculta la propiedad del elemento. Los locales no se publican como propiedades en el DOM.

Misma función, distinta unidad por componente

--unit: 0.5rem; parámetro --step: 2

--unit: 1rem; parámetro --step: 2

Función CSS nativa: compara el espacio con el resultado esperado.

Fallback base: 1rem de padding; la sintaxis de llamada no está disponible.

Con soporte, el parámetro --step sigue siendo 2 aunque las cajas tengan --step: 99. --unit sí viene de cada caja: el padding es 1rem y 2rem. Sin soporte, ambas usan 1rem.
Misma función, distinta unidad por componente
<div class="function-scene-scope">
  <div class="sample"><p>--unit: 0.5rem; parámetro --step: 2</p></div>
  <div class="sample large"><p>--unit: 1rem; parámetro --step: 2</p></div>
  <p class="native">Función CSS nativa: compara el espacio con el resultado esperado.</p>
  <p class="fallback">Fallback base: 1rem de padding; la sintaxis de llamada no está disponible.</p>
</div>
@function --guide-local-space(--step <number>: 2) returns <length> {
  --computed-space: calc(var(--step) * var(--unit, 0.5rem));
  result: var(--computed-space);
}

.function-scene-scope {
  display: grid;
  gap: 1rem;
  min-inline-size: 0;
  font: 1rem/1.5 system-ui, sans-serif;
  overflow-wrap: anywhere;

  .sample {
    padding: 1rem;
    border: 1px solid currentColor;

    p {
      margin: 0;
      border: 1px dashed currentColor;
    }
  }

  > p {
    margin: 0;
  }

  .native {
    display: none;
  }

  .sample {
    --unit: 0.5rem;
    --step: 99;
  }

  .large {
    --unit: 1rem;
  }

  @supports (padding: --guide-local-space()) {
    .sample {
      padding: --guide-local-space();
    }

    .native {
      display: block;
    }

    .fallback {
      display: none;
    }
  }
}

--computed-space solo existe durante la evaluación. Evita nombres locales que oculten parámetros por accidente y pasa argumentos explícitos cuando quieras que las dependencias sean visibles. El ámbito de las variables no convierte la definición de @function en una regla local a un selector; usa nombres propios para evitar colisiones entre definiciones.

result y ramas condicionales

El último result aplicable gana; no existe un return anticipado como en JavaScript.

Coloca primero el result general y después el condicional. En este ejemplo @media cambia el valor cuando el viewport mide menos de 700px. Si mueves el result general al final, sobrescribe la rama incluso cuando la condición se cumple. También puedes variar variables locales y resolver un único result al final.

Espacio responsive con dos resultados

Viewport < 700px: 0.75rem; desde 700px: 1.5rem

Función CSS nativa: compara el espacio con el resultado esperado.

Fallback base: 1rem de padding; la sintaxis de llamada no está disponible.

Con soporte nativo, el padding pasa de 1.5rem a 0.75rem por debajo de 700px de viewport. Sin soporte, permanece en 1rem. Redimensiona la ventana para comparar.
Espacio responsive con dos resultados
<div class="function-scene-conditions">
  <div class="sample"><p>Viewport &lt; 700px: 0.75rem; desde 700px: 1.5rem</p></div>
  <p class="native">Función CSS nativa: compara el espacio con el resultado esperado.</p>
  <p class="fallback">Fallback base: 1rem de padding; la sintaxis de llamada no está disponible.</p>
</div>
@function --guide-responsive-space(--narrow <length>, --wide <length>: 1.5rem) returns <length> {
  result: var(--wide);

  @media (width < 700px) {
    result: var(--narrow);
  }
}

.function-scene-conditions {
  display: grid;
  gap: 1rem;
  min-inline-size: 0;
  font: 1rem/1.5 system-ui, sans-serif;
  overflow-wrap: anywhere;

  .sample {
    padding: 1rem;
    border: 1px solid currentColor;

    p {
      margin: 0;
      border: 1px dashed currentColor;
    }
  }

  > p {
    margin: 0;
  }

  .native {
    display: none;
  }

  @supports (padding: --guide-responsive-space(0.75rem)) {
    .sample {
      padding: --guide-responsive-space(0.75rem);
    }

    .native {
      display: block;
    }

    .fallback {
      display: none;
    }
  }
}

La condición mira el viewport del documento que ejecuta la función, no la anchura de la tarjeta. En el playground, la función vive dentro de un iframe: @media consulta ese iframe. @supports y otras reglas condicionales admitidas tienen su propia compatibilidad; este ejemplo solo demuestra @media. if() es otra forma de elegir un valor y no se necesita para esta receta.

Comparativas

Elige la abstracción según lo que realmente se repite.

Usa una custom property para un token o un valor configurable por cascada y herencia. Para una cuenta sencilla, calc() con variables en el propio componente suele bastar. @function aporta una firma reutilizable con argumentos, tipos, valores por defecto y lógica que puede devolver resultados distintos en cada llamada.

Alternativa simple con custom properties

Alternativa simple con custom properties
CSS
.spacing-component {
  --step: 2;
  padding: calc(var(--step) * 0.75rem);

  &.compact {
    --step: 1;
  }
}

En esta alternativa la fórmula se calcula en el elemento que recibe padding. Si defines un valor compuesto en :root y lo heredas, sus referencias se resuelven allí: cambiar una variable dependiente en un descendiente no recalcula automáticamente aquel valor heredado. Una función calcula en el punto de llamada. Ninguna de las dos opciones necesita JavaScript.

Un fallback protegido

Separa falta de sintaxis de una función que se acepta, pero devuelve un valor inválido.

Las tres previews definen padding: 1rem fuera del bloque de mejora. Solo aplican la llamada dentro de @supports (padding: --nombre()). En un motor sin sintaxis de funciones personalizadas, la condición no se cumple y la base queda intacta. Este test comprueba aceptación sintáctica: no verifica que ese nombre exista, sus argumentos ni su resultado.

Antiejemplo: la declaración anterior no siempre te salva

Ejemplo incorrecto deliberado, sin preview: si el motor acepta la llamada pero la función no existe, padding queda inválido al calcularse y usa su valor inicial, 0; no vuelve a 1rem.

Antiejemplo: la declaración anterior no siempre te salva
CSS
.unprotected {
  padding: 1rem;
  padding: --missing-function();
}

El guard tampoco rescata una llamada rota en un navegador compatible. Comprueba el valor computado y prueba argumentos y ramas reales. var(--token, alternativa) no es un try/catch de cualquier error en una propiedad. Los ejemplos usan @supports sobre padding para no exigir además soporte de at-rule(@function).

Errores frecuentes

Diagnostica firma, resolución y propiedad de destino por separado.

La rama condicional nunca cambia el resultado

Resultado esperado
El valor se adapta cuando coincide @media.
Causa
Un result general posterior sobrescribe el condicional; result no detiene la evaluación.
Solución
Coloca primero la base y luego las excepciones, o cambia locales y calcula result al final.

La función existe, pero la propiedad no se aplica

Resultado esperado
El resultado cumple el tipo declarado y la propiedad receptora.
Causa
Un argumento obligatorio, returns o la gramática de destino no encaja; devolver una longitud no la hace válida para color.
Solución
Revisa argumentos/defaults, tipo de salida y valor computado. Prueba una llamada mínima antes de añadir condiciones.

La variable devuelve otro valor o no aparece fuera

Resultado esperado
Cada nombre se resuelve en el ámbito previsto.
Causa
Un local o parámetro oculta la propiedad del elemento, o intentas leer un local desde fuera de la función.
Solución
Renombra variables para evitar colisiones y devuelve el dato mediante result. No esperes que un local se herede en el DOM.

El fallback desaparece en un navegador compatible

Resultado esperado
La interfaz mantiene un valor útil con y sin la mejora.
Causa
La llamada supera @supports, pero falla al calcularse: el navegador no recupera la declaración previa.
Solución
Mantén una base fuera del guard y verifica también la llamada, los valores y las ramas en el motor que la acepta.

Soporte y compatibilidad

El estado general no certifica cada firma, rama ni valor devuelto.

@function tiene disponibilidad limitada. En la consulta del 22 de octubre de 2026, Web Platform DX registra Chrome y Edge desde 139, y ningún soporte estable en Firefox ni Safari. MDN BCD marca Safari como preview; eso no equivale a una versión estable disponible.

La tarjeta Baseline informa de la feature general. El aviso de cada ejemplo usa un test de sintaxis local; las medidas descritas requieren que su función se evalúe correctamente. Los fallbacks mantienen espacio legible, sin simular funciones con JavaScript. Revisa los datos enlazados antes de usarlo en producción: el borrador y las implementaciones pueden cambiar.