Saltar al contenido principal

CSS Functions: La manera nativa de crear funciones en CSS

· 7 min de lectura
Oscar Adrian Ortiz Bustos
Contando lecturas...

Introduccion

CSS ha evolucionado mucho en los últimos años, de mis publicaciones recientes se encuentra la de NestingCSS que si no la recuerdas la puedes revisar. Hoy quiero hablarte de otra novedad que está generando bastante interés en la comunidad de desarrolladores web, y una de las novedades más interesantes es la introducción de la regla @function.

WelcomeBanner

Y tal como el nombre lo indica nos ayudara a crear funciones en CSS basadas en parametros.

info

De momento no todos los navegadores son compatibles con esta característica, por lo que es importante verificar la compatibilidad antes de usarla en producción.

Segun Can I use la compatibilidad actual es la siguiente:

image

Esta nueva de utilizar CSS renueva por completo y puede incluso poner en jaque a los preprocedadores como SASS o LESS. Ya que nos permite generar funciones completamentes reutilizables y parametrizables directamente en CSS.

Sintaxis

La siguiente función es un ejemplo de como podemos definir la opacidad de un color utilizando la nueva regla @function:

@function --opacity(--value) {
result color-mix(in srgb, var(--value), transparent 50%);
}
  • @function: Esta es la palabra clave que indica que estamos definiendo una función.
  • --opacity: Este es el nombre de la función que estamos definiendo.
  • (--value): Estos son los parámetros que la función acepta. En este caso, estamos aceptando un solo parámetro llamado --value.
  • result: Esta palabra clave indica el valor que la función devolverá.

Esto nos da la posibilidad de reutilizar esta función en cualquier parte de nuestro CSS, simplemente llamándola con el valor deseado:

.element {
background-color: var(--opacity(#ff0000));
}

Esto aplicará un color rojo con una opacidad del 50% al fondo del elemento.

Ejemplos prácticos

El ejemplo de arriba es una forma sencilla de ver cómo funcionan las funciones en CSS. Pero tal como sucede en la programacion tradicional, las funciones pueden ser mucho más complejas y útiles. Aquí les dejo algunos ejemplos adicionales:

Tipografía Fluida

¿Cuantas veces hemos usado JavaScript o preprocesadores para crear tipografía fluida? es decir, que el tamaño de la fuente se ajuste dinámicamente según el tamaño de la pantalla. Con las funciones en CSS, podemos lograr esto de manera nativa:

@function --fluid-type(--font-min, --font-max, --type: 'header') {
--scalar: if(
style(--type: 'header'): 4vw;
style(--type: 'copy'): 0.5vw
);
result: clamp(var(--font-min), var(--scalar) + var(--font-min), var(--font-max));
}
h1 {
/* Headers escalan de 24px a 36px con ratio 4vw */
font-size: --fluid-type(24px, 36px);
}

p {
/* Copy escala de 16px a 24px con ratio 0.5vw (más lento) */
font-size: --fluid-type(16px, 24px, 'copy');
}

¿Notaron que al igual que en las funciones de programacion tradicional, podemos tener valores por defecto en los parámetros? En este caso, el parámetro --type tiene un valor por defecto de 'header'. Dios, no puedo con tanto.

Border-Radius Condicional

Supongo que no soy el unico que le encanta jugar con los valores del border-radius para crear diseños interesantes que a veces rayan en lo bizarro jaja, con las funciones en CSS podemos crear una función que ajuste el border-radius basado en el tamaño del elemento:

La siguiente funcion elimina automaticamente el border-radius cuando un elemento se acerca a los bordes del viewport, evitando efectos visualos extraños en pantallas pequeñas.

@function --conditional-radius(--radius, --edge-dist: 4px) {
result: clamp(0px, ((100vw - var(--edge-dist)) - 100%) * 1e5, var(--radius));
}

Quiza no sepas lo que hace clamp en este caso, te lo explico rapidamente, el valor minimo es 0px, el valor preferido es una formula que reduce el border-radius a medida que el elemento se acerca al borde del viewport, y el valor maximo es el valor original del border-radius. O sea:

  • Minimo: 0px (sin border-radius)
  • Preferido: formula que reduce el border-radius segun la distancia al borde
  • Maximo: valor original del border-radius
  • --radius: El valor original del border-radius que queremos aplicar.
  • --edge-dist: La distancia desde el borde del viewport en la que queremos empezar a reducir el border-radius (por defecto es 4px).
  • result: El valor calculado del border-radius.
.card {
/* Radio de 1rem, se elimina a 4px del borde */
border-radius: --conditional-radius(1rem);
}

.hero {
/* Radio de 2rem, se elimina justo en el borde (0px) */
border-radius: --conditional-radius(2rem, 0px);
}

Z-Index por Prioridad

Si pensaste que las funciones anteriores eran interesantes, espera a ver esta. Es imposible que sea el unico al que manejar el z-index le da dolores de cabeza, siempre termino usando un z-index de 100000 para asegurarme de que un elemente este por encima de todos los demas.

En este funcion haremos uso de otra cosita que me gusta mucho en css y que es poco conocida, me refiero al if y else.

Esta funcion nos va a evitar un millar de dolores de cabeza creando un sistema jerarquico basado en roles de componentes en lugar de numeros arbitrarios.

@function --z-index(--priority: 'default') {
result: if(
style(--priority: 'modal'): 1000;
style(--priority: 'popover'): 900;
style(--priority: 'dropdown'): 800;
style(--priority: 'header'): 700;
else: 1
);
}
  • --priority: El nivel de prioridad del elemento. Puede ser 'modal', 'popover', 'dropdown', 'header' o 'default'.
  • if y else: Estas palabras clave permiten crear condiciones dentro de la función para devolver diferentes valores basados en el parámetro --priority.
.modal {
z-index: --z-index('modal'); /* 1000 */
}

.dropdown {
z-index: --z-index('dropdown'); /* 800 */
}

.default-element {
z-index: --z-index(); /* 1 */
}

Focus Outline Condicional

No es un secreto que soy un fiel creyente de la filosofía de "Keyboard-driven" por ende muchas veces prefiero usar el teclado antes que el raton.

Teniendo esto en mente surge una funcion que hara focus visible en los elementos interactivos, lo que directamente mejorara la accesibilidad de las aplicaciones web.

Aunque admito que aqui la parte de detectar el modo de interacción (teclado o raton) tendra que ser manejada por JavaScript, la funcion en CSS nos ayudara a definir el estilo del outline basado en ese modo.

@function --focus-outline(--mode: 'default') {
result: if(
style(--mode: 'keyboard'): 2px dashed #254EDF;
style(--mode: 'mouse'): none;
else: 2px solid #888
);
}
  • --mode: El modo de enfoque. Puede ser 'default', 'keyboard' o 'mouse'.
/* JavaScript detectaría el modo de interacción */
:root {
--interaction-mode: 'keyboard'; /* o 'mouse' */
}

a:focus, button:focus {
outline: --focus-outline(var(--interaction-mode));
}

El codigo JavaScript necesario para detectar el modo de interacción puede ser algo como esto:

// Detecta si el usuario está usando teclado o mouse
class FocusMode {
constructor() {
this.mode = 'default';
this.init();
}

init() {
// Detecta cuando el usuario usa el teclado
document.addEventListener('keydown', (e) => {
// Solo Tab y Shift+Tab indican navegación por teclado
if (e.key === 'Tab') {
this.setMode('keyboard');
}
});

// Detecta cuando el usuario usa el mouse
document.addEventListener('mousedown', () => {
this.setMode('mouse');
});

// Opcional: detecta cuando se mueve el mouse después de usar teclado
document.addEventListener('mousemove', () => {
if (this.mode === 'keyboard') {
this.setMode('mouse');
}
});
}

setMode(newMode) {
if (this.mode !== newMode) {
this.mode = newMode;
// Actualiza la variable CSS
document.documentElement.style.setProperty('--interaction-mode', `'${newMode}'`);

// Opcional: añade clase al body para debugging
document.body.dataset.interactionMode = newMode;
}
}
}

// Inicializa al cargar la página
new FocusMode();

Conclusión

Así es, Cibernícolas: CSS sigue demostrando que ya no es solo un lenguaje de estilos, sino una herramienta cada vez más poderosa y modular. Con la llegada de @function, ahora podemos crear lógica reutilizable directamente en CSS, acercándolo más que nunca a un lenguaje de programación declarativo.

Esta nueva capacidad nos libera en parte de preprocesadores, mejora la mantenibilidad del código y abre la puerta a sistemas de diseño más inteligentes y coherentes.

Aún falta compatibilidad total, pero vale la pena experimentar desde ahora. Porque el futuro del front-end está en aprovechar cada nueva función nativa, y @function es solo el comienzo de una nueva era para el CSS moderno.

Nos leemos en la próxima exploración, Cibernícolas.

"La Web no es una cosa que haces, es algo que haces crecer."

Tim Berners-Lee
Escrito por un humano