Diseños adaptables y propiedades personalizadas de CSS: creación de un sistema de cuadrícula flexible | Programar Plus

La última vez, analizamos algunos enfoques posibles para declarar y usar propiedades personalizadas de CSS en diseños adaptables. En este artículo, veremos más de cerca las variables CSS y cómo usarlas en componentes y módulos reutilizables. Aprenderemos cómo hacer que nuestras variables sean opcionales y establecer valores alternativos.

Como ejemplo, construiremos un sistema de cuadrícula simple basado en flexbox. Los sistemas de cuadrícula juegan un papel vital en los diseños receptivos. Sin embargo, construir un sistema de cuadrícula que sea flexible y liviano al mismo tiempo puede ser una tarea complicada. Veamos cuáles son los enfoques comunes hacia los sistemas de cuadrícula y cómo las propiedades personalizadas de CSS pueden ayudarnos a construirlos.

Serie de artículos:

  1. Definición de variables y puntos de interrupción

  2. Construyendo un Sistema de Red Flexible (Esta Publicación)

Un sistema de cuadrícula CSS simple

Comencemos con un sistema de cuadrícula de 12 columnas:

.container {
	max-width: 960px;
	margin: 0 auto;
	display: flex;
}

.col-1 { flex-basis: 8.333%; }
.col-2 { flex-basis: 16.666%; }
.col-3 { flex-basis: 25%; }
.col-4 { flex-basis: 33.333%; }
.col-5 { flex-basis: 41.666%; }
.col-6 { flex-basis: 50%; }
/* and so on up to 12... */

ver la pluma
#5 Creación de funciones receptivas con propiedades personalizadas de CSS por Mikołaj (@mikolajdobrucki)
en CodePen.

Hay bastantes repeticiones y valores codificados aquí. Sin mencionar cuántos más se generarán una vez que agreguemos más puntos de interrupción, clases de compensación, etc.

Construyendo un sistema de cuadrícula con Sass

Para hacer que nuestro ejemplo de cuadrícula sea más legible y fácil de mantener, usemos Sass para preprocesar nuestro CSS:

$columns: 12; // Number of columns in the grid system

.container {
	display: flex;
	flex-wrap: wrap;
	margin: 0 auto;
	max-width: 960px;
}

@for $width from 1 through $columns {
	.col-#{$width} {
		flex-basis: $width / $columns * 100%;
	}  
}

ver la pluma
#6 Creación de funciones receptivas con propiedades personalizadas de CSS por Mikołaj (@mikolajdobrucki)
en CodePen.

Definitivamente es mucho más fácil trabajar con esto. A medida que desarrollamos más nuestra cuadrícula y, digamos, nos gustaría cambiarla de 12 columnas a 16 columnas, todo lo que tenemos que hacer es actualizar una sola variable (en comparación con docenas de clases y valores). Pero… siempre que nuestro Sass sea más corto y fácil de mantener ahora, el código compilado es idéntico al del primer ejemplo. Todavía vamos a terminar con una gran cantidad de código en el archivo CSS final. Exploremos qué sucede si intentamos reemplazar las variables de Sass con propiedades personalizadas de CSS.

Creación de un sistema de cuadrícula con propiedades personalizadas de CSS

Antes de comenzar a jugar con las propiedades personalizadas de CSS, primero comencemos con algo de HTML. Este es el diseño que buscamos:

Consta de tres elementos: un encabezado, una sección de contenido y una barra lateral. Vamos a crear marcas para esta vista, dando a cada uno de los elementos una clase semántica única (header, content, sidebar) y un column class que indica que este elemento es parte de un sistema grid:

<div class="container">
	<header class="header column">
		header
	</header>
	<main class="content column">
		content
	</main>
	<aside class="sidebar column">
		sidebar
	</aside>
</div>

Nuestro sistema de cuadrícula, como antes, se basa en un diseño de 12 columnas. Puede visualizarlo como una superposición que cubra nuestras áreas de contenido:

Entonces .header toma las 12 columnas, .content toma ocho columnas (66.(6)% del ancho total) y .sidebar toma cuatro columnas (33.(3)% del ancho total). En nuestro CSS, nos gustaría poder controlar el ancho de cada sección cambiando una sola propiedad personalizada:

.header {
	--width: 12;
}

.content {
	--width: 8;
}

.sidebar {
	--width: 4;
}

Para que funcione, todo lo que tenemos que hacer es escribir una regla para el .column clase. Por suerte para nosotros, ¡la mayor parte del trabajo ya está hecho! Podemos reutilizar el Sass del capítulo anterior y reemplazar las variables de Sass con propiedades personalizadas de CSS:

.container {
	display: flex;
	flex-wrap: wrap;
	margin: 0 auto;
	max-width: 960px;
}

.column {
	--columns: 12; /* Number of columns in the grid system */
	--width: 0; /* Default width of the element */

	flex-basis: calc(var(--width) / var(--columns) * 100%);
}

Observe dos cambios importantes aquí:

  1. El --columns La variable ahora se declara dentro de la .column regla. La razón es que no se supone que esta variable se use fuera del alcance de esta clase.
  2. La ecuación matemática que realizamos en el flex-basis la propiedad ahora está encerrada dentro de un calc() función. Los cálculos matemáticos escritos en Sass son compilados por el preprocesador y no necesitan sintaxis adicional. calc(), por otro lado, nos permite realizar cálculos matemáticos en CSS en vivo. La ecuación siempre necesita estar envuelta dentro de un calc() función.

En un nivel muy básico, eso es todo! Acabamos de crear un sistema de cuadrícula de 12 columnas con propiedades personalizadas de CSS. ¡Felicidades! Podríamos dar por terminado el día y felizmente terminar este artículo ahora mismo, pero… por lo general, necesitamos un sistema de cuadrícula que sea un poco más sofisticado. Y aquí es cuando las cosas se ponen realmente interesantes.

ver la pluma
#8 Creación de funciones receptivas con propiedades personalizadas de CSS por Mikołaj (@mikolajdobrucki)
en CodePen.

Adición de un punto de interrupción a la cuadrícula

La mayoría de las veces, necesitamos que los diseños se vean diferentes en varios tamaños de pantalla. Digamos que, en nuestro caso, queremos que el diseño permanezca como está en una ventana grande (por ejemplo, una computadora de escritorio) pero que los tres elementos se vuelvan de ancho completo en pantallas más pequeñas (por ejemplo, un dispositivo móvil).

Entonces, en este caso, nos gustaría que nuestras variables se vean de la siguiente manera:

.header {
	--width-mobile: 12;
}

.content {
	--width-mobile: 12;
	--width-tablet: 8; /* Tablet and larger */
}

.sidebar {
	--width-mobile: 12;
	--width-tablet: 4; /* Tablet and larger */
}

.content y .sidebar cada uno tiene dos variables ahora. La primera variable (--width-mobile) es un número de columnas que un elemento debería tomar por defecto, y la segunda (--width-tablet) es el número de columnas que debe tener un elemento en pantallas más grandes. El .header el elemento no cambia; siempre ocupa todo el ancho. En pantallas más grandes, el encabezado simplemente debe heredar el ancho que tiene en el móvil.

Ahora, actualicemos nuestro .column clase.

Variables CSS y respaldo

Para que la versión móvil funcione como se espera, necesitamos modificar la .column clase de la siguiente manera:

.column {
	--columns: 12; /* Number of columns in the grid system */
	--width: var(--width-mobile, 0); /* Default width of the element */
	
	flex-basis: calc(var(--width) / var(--columns) * 100%);
}

Básicamente, reemplazamos el valor de la --width variable con --width-mobile. Note que el var() función toma dos argumentos ahora. El primero de ellos es un valor por defecto. Dice: “Si un --width-mobile existe una variable en un ámbito dado, asigne su valor a la --width variable.” El segundo argumento es un retroceso. En otras palabras: “Si un --width-mobile variable no está declarada en un ámbito determinado, asigne este valor de reserva al --width variable.” Configuramos este respaldo para prepararnos para un escenario en el que algunos elementos de la cuadrícula no tendrán un ancho específico.

por ejemplo, nuestro .header elemento tiene un declarado --width-mobile variable que significa --width la variable será igual a ella y la flex-basis propiedad de este elemento se calculará para 100%:

.header {
	--width-mobile: 12;
}

.column {
	--columns: 12;
	--width: var(--width-mobile, 0); /* 12, takes the value of --width-mobile */
	
	flex-basis: calc(var(--width) / var(--columns) * 100%); /* 12 ÷ 12 × 100% = 100% */
}

Si quitamos el --width-mobile variable de la .header regla, entonces la --width variable utilizará un valor de reserva:

.header {
	/* Nothing here... */
}

.column {
	--columns: 12;
	--width: var(--width-mobile, 0); /* 0, takes the the fallback value */
	
	flex-basis: calc(var(--width) / var(--columns) * 100%); /* 0 ÷ 12 × 100% = 0% */
}

Ahora, a medida que entendemos cómo configurar el respaldo para las propiedades personalizadas de CSS, podemos crear un punto de interrupción agregando una consulta de medios a nuestro código:

.column {
	--columns: 12; /* Number of columns in the grid system */
	--width: var(--width-mobile, 0); /* Default width of the element */
	
	flex-basis: calc(var(--width) / var(--columns) * 100%);
}

@media (min-width: 576px) {
	.column {
		--width: var(--width-tablet); /* Width of the element on tablet and up */
	}
}

Esto funciona exactamente como se esperaba, pero solo para el contenido y la barra lateral, es decir, para los elementos que han especificado ambos. --width-mobile y --width-tablet. ¿Por qué?

La consulta de medios que creamos se aplica a todos .column elementos, incluso aquellos que no tienen --width-tablet variable declarada en su alcance. ¿Qué pasa si usamos una variable que no está declarada? La referencia a la variable no declarada en un var() Entonces, la función se considera no válida en el momento del valor calculado, es decir, no válida en el momento en que un agente de usuario intenta calcularla en el contexto de una declaración dada.

Idealmente, en tal caso, nos gustaría que el --width: var(--width-tablet); declaración a ignorar y la anterior declaración de --width: var(--width-mobile, 0); para ser utilizado en su lugar. ¡Pero no es así como funcionan las propiedades personalizadas! De hecho, el inválido --width-tablet la variable se seguirá utilizando en el flex-basis declaración. Una propiedad que contiene un inválido var() La función siempre calcula su valor inicial. Así como flex-basis: calc(var(--width) / var(--columns) * 100%); contiene un inválido var() función toda la propiedad calculará para auto (el valor inicial de flex-basis).

¿Qué más podemos hacer entonces? ¡Establece una reserva! Como aprendimos antes, un var() que contiene una referencia a la variable no declarada, calcula su valor alternativo, siempre que se especifique. Entonces, en este caso, podemos simplemente establecer un respaldo para el --width-tablet variable:

.column {
	--columns: 12; /* Number of columns in the grid system */
	--width: var(--width-mobile, 0); /* Default width of the element */
	
	flex-basis: calc(var(--width) / var(--columns) * 100%);
}

@media (min-width: 576px) {
	.column {
		--width: var(--width-tablet, var(--width-mobile, 0));
	}
}

ver la pluma
#9 Creación de funciones receptivas con propiedades personalizadas de CSS por Mikołaj (@mikolajdobrucki)
en CodePen.

Esto creará una cadena de valores alternativos, haciendo que el --width uso de la propiedad --width-tablet cuando esté disponible, entonces --width-mobile Si --width-tablet no se declara, y eventualmente, 0 si no se declara ninguna de las variables. Este enfoque nos permite realizar numerosas combinaciones:

.section-1 {
	/* Flexible on all resolutions */
}

.section-2 {
	/* Full-width on mobile, half of the container's width on tablet and up */
	--width-mobile: 12;
	--width-tablet: 6;
}
	
.section-3 {
	/* Full-width on all resolutions */
	--width-mobile: 12;
}
	
.section-4 {
	/* Flexible on mobile, 25% of the container's width on tablet and up */
	--width-tablet: 3;
}

Una cosa más que podemos hacer aquí es convertir el valor predeterminado 0 valor a otra variable más para evitar la repetición. Hace que el código sea un poco más largo pero más fácil de actualizar:

.column {
	--columns: 12; /* Number of columns in the grid system */
	--width-default: 0; /* Default width, makes it flexible */
	--width: var(--width-mobile, var(--width-default)); /* Width of the element */
	
	flex-basis: calc(var(--width) / var(--columns) * 100%);
}

@media (min-width: 576px) {
	.column {
		--width: var(--width-tablet, var(--width-mobile, var(--width-default)));
	}
}

ver la pluma
N.º 10 Creación de funciones receptivas con propiedades personalizadas de CSS por Mikołaj (@mikolajdobrucki)
en CodePen.

¡Ahora tenemos una red flexible y completamente funcional! ¿Qué tal agregar algunos puntos de interrupción más?

Agregar más puntos de interrupción

Nuestra red ya es bastante potente, pero a menudo necesitamos más de un punto de interrupción. Afortunadamente, agregar más puntos de interrupción a nuestro código no podría ser más fácil. Todo lo que tenemos que hacer es reutilizar el código que ya tenemos y agregar una variable más:

.column {
	--columns: 12; /* Number of columns in the grid system */
	--width-default: 0; /* Default width, makes it flexible */
	--width: var(--width-mobile, var(--width-default)); /* Width of the element */
	
	flex-basis: calc(var(--width) / var(--columns) * 100%);
}

@media (min-width: 576px) {
	.column {
		--width: var(--width-tablet, var(--width-mobile, var(--width-default)));
	}
}

@media (min-width: 768px) {
	.column {
		--width: var(--width-desktop, var(--width-tablet, var(--width-mobile, var(--width-default))));
	}
}

ver la pluma
#11 Creación de funciones receptivas con propiedades personalizadas de CSS por Mikołaj (@mikolajdobrucki)
en CodePen.

Reducción de las cadenas de respaldo

Una cosa que no se ve muy bien en nuestro código es que las cadenas de comentarios se hacen cada vez más largas con cada punto de interrupción. Si queremos abordar este problema, podemos cambiar nuestro enfoque a algo como esto:

.column {
	--columns: 12; /* Number of columns in the grid system */
	--width: var(--width-mobile, 0); /* Width of the element */
	
	flex-basis: calc(var(--width) / var(--columns) * 100%);
}

@media (min-width: 576px) {
	.column {
		--width-tablet: var(--width-mobile);
		--width: var(--width-tablet);
	}
}

@media (min-width: 768px) {
	.column {
		--width-desktop: var(--width-tablet);
		--width: var(--width-desktop);
	}
}

ver la pluma
#12 Creación de funciones receptivas con propiedades personalizadas de CSS por Mikołaj (@mikolajdobrucki)
en CodePen.

Este código está haciendo exactamente el mismo trabajo pero de una manera un poco diferente. En lugar de crear una cadena de respaldo completa para cada punto de interrupción, establecemos un valor de cada variable para la variable del punto de interrupción anterior como valor predeterminado.

¿Por qué tan complicado?

Parece que hemos trabajado bastante para completar una tarea relativamente simple. ¿Por qué? La respuesta principal es: hacer que el resto de nuestro código sea más simple y fácil de mantener. De hecho, podríamos construir el mismo diseño utilizando las técnicas descritas en la parte anterior de este artículo:

.container {
	display: flex;
	flex-wrap: wrap;
	margin: 0 auto;
	max-width: 960px;
}

.column {
	--columns: 12; /* Number of columns in the grid system */
	--width: 0; /* Default width of the element */

	flex-basis: calc(var(--width) / var(--columns) * 100%);
}

.header {
	--width: 12;
}

.content {
	--width: 12;
}

.sidebar {
	--width: 12;
}

@media (min-width: 576px) {
	.content {
		--width: 6;
	}
	
	.sidebar {
		--width: 6;
	}
}

@media (min-width: 768px) {
	.content {
		--width: 8;
	}
	
	.sidebar {
		--width: 4;
	}
}

En un proyecto pequeño, este enfoque podría funcionar perfectamente bien. Sin embargo, para las soluciones más complejas, sugeriría considerar una solución más escalable.

¿Por qué debería molestarme de todos modos?

Si el código presentado está haciendo un trabajo muy similar al que podemos lograr con preprocesadores como Sass, ¿por qué deberíamos molestarnos en absoluto? ¿Las propiedades personalizadas son mejores? La respuesta, como siempre, es: depende. Una ventaja de usar Sass es una mejor compatibilidad con el navegador. Sin embargo, el uso de propiedades personalizadas también tiene algunas ventajas:

  1. Es CSS simple. En otras palabras, es una solución más estandarizada y confiable, independiente de terceros. Sin compilación, sin versiones de paquetes, sin problemas extraños. Simplemente funciona (aparte de los navegadores donde simplemente no funciona).
  2. Es más fácil de depurar. Eso es cuestionable, ya que se puede argumentar que Sass proporciona comentarios a través de mensajes de consola y CSS no. Sin embargo, no puede ver y depurar código preprocesado directamente en un navegador, mientras trabaja con variables CSS, todo el código está disponible (¡y en vivo!) directamente en DevTools.
  3. Es más mantenible. Las propiedades personalizadas nos permiten hacer cosas simplemente imposibles con cualquier preprocesador. Nos permite hacer nuestras variables más contextuales y, por tanto, más mantenibles. Además, son seleccionables por JavaScript, algo que las variables de Sass no son.
  4. Es más flexible. Tenga en cuenta que el sistema de cuadrícula que hemos construido es extremadamente flexible. ¿Le gustaría usar una cuadrícula de 12 columnas en una página y una cuadrícula de 15 columnas en otra? Sea mi invitado, se trata de una sola variable. Se puede utilizar el mismo código en ambas páginas. Un preprocesador requeriría generar código para dos sistemas de cuadrícula separados.
  5. Se necesita menos espacio. Siempre que el peso de los archivos CSS no sea el principal cuello de botella del rendimiento de carga de la página, no hace falta decir que debemos intentar optimizar los archivos CSS cuando sea posible. Para dar una mejor imagen de cuánto se puede ahorrar, hice un pequeño experimento. Tomé el sistema de cuadrícula de Bootstrap y lo reconstruí desde cero con propiedades personalizadas. Los resultados son los siguientes: la configuración básica de la cuadrícula Bootstrap genera más de 54 KB de CSS, mientras que una cuadrícula similar hecha con propiedades personalizadas es de solo 3 KB. ¡Esa es una diferencia del 94%! Además, agregar más columnas a la cuadrícula de Bootstrap hace que el archivo sea aún más grande. Con las variables CSS, podemos usar tantas columnas como queramos sin afectar el tamaño del archivo en absoluto.

Los archivos se pueden comprimir para minimizar un poco la diferencia. La cuadrícula de Bootstrap comprimida con gzip ocupa 6,4 KB en comparación con los 0,9 KB de la cuadrícula de propiedades personalizadas. ¡Esto sigue siendo una diferencia del 86%!

Rendimiento de las variables CSS

En resumen, el uso de propiedades personalizadas de CSS tiene muchas ventajas. Pero, si estamos haciendo que el navegador haga todos los cálculos que habían hecho los preprocesadores, ¿estamos afectando negativamente el rendimiento de nuestro sitio? Es cierto que usar propiedades personalizadas y calc() funciones utilizarán más poder de cómputo. Sin embargo, en casos similares a los ejemplos que discutimos en este artículo, la diferencia generalmente será imperceptible. Si desea obtener más información sobre este tema, le recomiendo leer este excelente artículo de Lisi Linhart.

No solo sistemas de red

Después de todo, comprender los entresijos de las propiedades personalizadas puede no ser tan fácil como parece. Definitivamente tomará tiempo, pero vale la pena. Las variables CSS pueden ser de gran ayuda cuando se trabaja en componentes reutilizables, sistemas de diseño, temas y soluciones personalizables. Saber cómo manejar los valores alternativos y las variables no declaradas puede resultar muy útil en ese momento.

¡Gracias por leer y buena suerte en su propio viaje con las propiedades personalizadas de CSS!

(Visited 12 times, 1 visits today)