De forma tradicional, en CSS no teníamos la capacidad de crear funciones como en los lenguajes de programación. Sin embargo, eso está cambiando gracias a la regla @function
de CSS.
La regla @function
Mediante la regla @function
podemos definir funciones reutilizables que podremos usar en nuestro código CSS, pasándole parámetros y devolviendo un valor como resultado.
Como puedes ver en el siguiente ejemplo, las funciones se definen con la sintaxis de las variables CSS, prefijadas con dos guiones. Por ejemplo, la función --darken
es una función que oscurece el color que le pasamos por parámetro --value
:
<div class="box"></div>
<style>
@function --darken(--value) {
result: color-mix(in srgb, var(--value), black 50%);
}
.box {
width: 100px;
height: 100px;
background: indigo;
transition: background 1s;
}
.box:hover { background: --darken(indigo) }
</style>
En este caso, se trata de un ejemplo muy sencillo, pero aún así se puede ver como hace que el código CSS sea mucho más legible si utilizamos nombres de funciones apropiadas. Observa que :hover
hace que al pasar el ratón por encima del elemento, se aplique la función --darken
, a la que le pasamos el color que queremos oscurecer. Esto, mediante el result
de la función CSS, devuelve el color oscurecido mediante la función color-mix, simplificando su uso en el código.
Otro ejemplo
Ahora que ya conocemos la funcionalidad básica de @function
, veamos otro ejemplo donde trabajamos con valores numéricos, lo que lo hace diferente al ejemplo anterior:
@function --negative(--value) {
result: calc(-1 * var(--value));
}
.box {
--offset: 50px;
translate: var(--offset) --negative(var(--offset)); /* 50px -50px */
}
Este ejemplo toma un valor numérico por parámetro y luego, result:
lo devuelve multiplicado por -1
, es decir, devolviendo su valor con signo cambiado. Luego, en la propiedad translate
lo utilizamos para invertir el signo de la variable --offset
.
Observa que cuando usamos una variable, utilizamos la función
var()
. Sin embargo, con las@function
no aplica. Sólo añadimos los paréntesis al final y le pasamos los parámetros (si los tiene).
Parámetros de la función
Vamos a centrarnos durante un momento en los parámetros de nuestra función y a observar las diferentes personalizaciones de las que podemos dotarlos.
Parámetros con valor por defecto
Es posible que nos interese indicar valores por defecto a los parámetros de nuestra función, de forma que si al llamar a la función, no le indicamos unos parámetros, use los valores por defecto.
La forma de hacerlo sería como se puede ver en este ejemplo:
@function --gradient(--color1: indigo, --color2: rebeccapurple) {
result: linear-gradient(75deg, var(--color1), var(--color2));
}
.box {
background: --gradient(blue, red); /* --gradient(blue, red) */
background: --gradient(green); /* --gradient(green, rebeccapurple) */
background: --gradient(); /* --gradient(indigo, rebeccapurple) */
background: --gradient(red, 10px); /* Se ignora */
}
Especial atención al último ejemplo, donde si añadimos un valor que no es adecuado (se esperan colores, no tamaños), se intentará aplicar 10px
al linear-gradient()
y como es un valor no válido se ignora el background
.
Ojo que la
@function
es correcta. Lo anterior ocurre debido al funcionamiento de CSS. Si le pasas un10px
a una propiedadbackground
, simplemente se ignora porque no es válido. Sin embargo, en la siguiente pestaña veremos como cambiar ese comportamiento, que en funciones podría ser algo más apropiado.
Parámetros con tipo de dato
Opcionalmente, podemos definir el tipo de dato de los parámetros de nuestra función añadiendo la función type()
al final del parámetro.
La siguiente función --half-size
nos devolverá la mitad del valor que le pasemos por parámetro:
@function --half-size(--size type(<length> | <percentage>)) {
result: calc(var(--size) / 2);
}
Observa, que tras el parámetro --size
hemos añadido type(<length> | <percentage>)
. Esto significa que nuestro parámetro --size
puede ser:
- 1️⃣ De tipo «longitud» (px, etc...)
- 2️⃣ De tipo «porcentaje» (%)
Respecto a la función type()
, solo es necesaria si necesitamos indicar más de un tipo de dato. Si en nuestra función CSS sólo requerimos un tipo de dato (por ej: length
), podríamos definir el tipo de dato de dos formas:
- 1️⃣ El que ya hemos visto:
type(<length>)
- 2️⃣ Sin usar
type()
, de una forma compacta:<length>
@function --half-size(--size <length>) {
result: calc(var(--size) / 2);
}
Definir los tipos de datos en CSS es algo muy útil cuando necesitamos que el navegador comprenda que tipo tiene una variable CSS, para poder actuar en consecuencia, como por ejemplo, al crear animaciones con variables CSS.
Si quieres saber más sobre tipos de datos en CSS, o que tipos de datos puedes utilizar, te aconsejo echar un vistazo a la tabla del artículo la propiedad @property.
Asegurar parámetros con tipos
Observa el siguiente ejemplo. Hemos retomado el ejemplo anterior, pero ahora vamos a asegurar con tipos de datos los parámetros --color1
y --color2
. Además, de forma opcional podemos añadirle valores por defecto:
@function --gradient(--color1 <color>: indigo, --color2 <color>: rebeccapurple) {
result: linear-gradient(75deg, var(--color1), var(--color2));
}
.box {
background: --gradient(green, yellow); /* Usa --gradient(green, yellow) */
background: --gradient(); /* Usa --gradient(indigo, rebeccapurple) */
background: --gradient(red, 10px); /* Usa --gradient(red, rebeccapurple) */
background: --gradient(default, red); /* Usa --gradient(indigo, red) */
}
Observa especialmente, los dos últimos ejemplos. En esta ocasión, como hemos definido que la función debe aceptar sólo colores por parámetro, en el caso de no cumplirse esta restricción, se descartará ese parámetro y se utilizará el valor por defecto designado. De esta forma no se ignora la propiedad.
En el último ejemplo, el primer parámetro tiene el valor default
(que no existe), por lo que descarta y se utilizará el color por defecto. En el segundo parámetro si hemos definido un color válido, por lo que se utilizará.
Valor devuelto de la función
Ya hemos visto que el valor devuelto por la funcion en @function
se hace con la propiedad result
, que es una especie de return
de programación. Sin embargo, también podemos definir, al cerrar los paréntesis de los parámetros, un returns
indicando el tipo de dato que se espera que devuelva la función.
Veámoslo con un ejemplo:
@function --gradient(--color1, --color2) returns type(<image>) {
result: linear-gradient(75deg, var(--color1), var(--color2));
}
Nuestra función espera dos parámetros: --color1
y --color2
(que también podrían estar tipados, no lo he hecho para simplificar el ejemplo). Esta función toma esos dos colores y los convierte en un gradiente linear-gradient()
, que es lo que devuelve la función.
En CSS los gradientes tienen el tipo de dato
<image>
, ya que son imágenes prerenderizadas por el navegador. En este caso, definimosreturns type(<image>)
para determinar que la función espera que se devuelva una imagen. Por defecto, si no definimosreturns
, se asume el tiporeturns type(<*>)
.
Funciones condicionales
Las funciones de CSS, combinadas con las reglas @media
nos permiten crear funciones condicionales muy potentes, que pueden ser muy interesantes para definir los estilos de nuestras páginas.
Por ejemplo, observa la siguiente función --my-font-size
, la cuál no tiene ningún parámetro. Esta función devuelve el valor 1rem
. Sin embargo, luego tiene definido un @media
query que nos indica que en el caso de que la pantalla o dispositivo sea más grande de 1000px
, devolverá el valor 2rem
:
@function --my-font-size() {
result: 1rem;
@media (width > 1000px) {
result: 2rem;
}
}
De esta forma, podemos hacer un poco más flexibles y autónomas nuestras funciones.
Ten mucho cuidado con el orden cuando usas múltiples
result
en una@function
. En CSS no puedes hacer early returns, ya que se aplican las reglas de CSS de herencia. Si en el ejemplo anterior, ponemos el@media
antes del primerresult
, el valor devuelto por la función siempre será1rem
, ya que el segundoresult
se aplica por herencia y sobreescribe al anterior.