Escalar dinámicamente los valores de CSS en función del ancho de la ventana gráfica no es un tema nuevo. Puede encontrar mucha cobertura en profundidad aquí mismo sobre Programar Plusen artículos como este o este.
La mayoría de esos ejemplos, sin embargo, usan unidades CSS relativas y valores sin unidades para lograr un escalado fluido. Eso pierde la perfección de píxeles y generalmente introduce ajuste de texto y cambios de diseño una vez que la pantalla pasa por debajo o por encima de un cierto umbral.
Pero, ¿y si realmente queremos la perfección de píxeles? ¿Qué pasa si, digamos, estamos desarrollando un panel de análisis complejo en tiempo real para ser visto en televisores grandes en una sala de conferencias o como alguna PWA para ser abierta exclusivamente en dispositivos móviles y tabletas, en lugar de blogs y sitios web de noticias con mucho texto? ? Son casos en los que necesitamos más precisión.
En otras palabras, ¿qué pasa si queremos escalar diseños de manera uniforme? Por supuesto, uno puede scale
el contenido con CSS se transforma en función del ancho disponible como se describe en este artículo; de esta manera, se conservan las proporciones correctas.
Sin embargo, también podemos lograr fluidos proporcional escalar las interfaces de usuario utilizando valores de píxeles en CSS. Se escalan de manera adecuada en función del espacio real de la pantalla del dispositivo, todo mientras preservando su píxel perfecto dimensiones. Además, todavía podemos usar valores de píxeles y convertirlos automáticamente a unidades CSS relativas si trabajar en píxeles es más cómodo o familiar.
Escalando nuestra interfaz de usuario
Intentemos implementar este impresionante panel, cortesía de Craftwork. Necesitamos hacerlo de tal manera que se escale perfectamente y conserve todos los textos, recuentos de líneas, márgenes, tamaños de imagen, etc.
Trabajemos en valores de píxeles de CSS y usemos SCSS para mayor velocidad y conveniencia. Entonces, si vamos a apuntar al título de uno de estos widgets de tarjeta, nuestro SCSS podría verse así:
.cardWidget {
.cardHeading {
font-size: 16px;
}
}
Nada lujoso. Nada que no hayamos visto antes. Al ser un valor de píxel, no se escalará.
Este diseño fue creado con un contenedor que es 1600px
ancho. Supongamos que en 1600px
, el tamaño de fuente ideal para los títulos de las tarjetas debe ser 16px
ya que así es como está diseñado.
Ahora que tenemos el tamaño de fuente de ancho de contenedor “ideal” para este ancho, escalemos nuestros valores de píxeles de CSS en consecuencia usando el ancho de ventana actual *:
/*
1600px is the ideal viewport width that the UI designers who
created the dashboard used when designing their Figma artboards
Please not we are not using pixel units here, treating it purely
as a numeric value.
*/
--ideal-viewport-width: 1600;
/*
The actual width of the user device
*/
--current-viewport-width: 100vw;
.cardWidget {
.cardHeading {
/*
16px is the ideal font size that the UI designers want for
1600px viewport width.
Please note that we are not using pixel units here,
treating it purely as a numeric value.
*/
--ideal-font-size: 16;
/*
Calculate the actual font size:
We take our idealFontSize and multiply it by the difference
between the current viewport width and the ideal viewport width.
*/
font-size: calc(
var(--ideal-font-size) * (var(--current-viewport-width) / var(--ideal-viewport-width)
);
}
}
Como puede ver, tratamos el tamaño de fuente ideal que obtuvimos del diseño como base y lo multiplicamos por la diferencia entre los anchos de ventana actuales e ideales. ¿Cómo se ve esto matemáticamente? Digamos que estamos viendo esta aplicación web en una pantalla con exactamente el mismo ancho que la maqueta:
--current-device-width: 100vw; // represents 1600px or full width of the screen
--ideal-viewport-width: 1600; // notice that the ideal and current width match
--ideal-font-size: 16;
// this evaluates to:
font-size: calc(16 * 1600px / 1600);
// same as:
font-size: calc(16 * 1px);
// final result:
font-size: 16px;
Entonces, dado que el ancho de nuestra ventana gráfica coincide perfectamente, nuestro font-size
termina siendo exactamente 16px
en el ancho ideal de la ventana gráfica de 1600px
.
Como otro ejemplo, digamos que estamos viendo la aplicación web en una pantalla de computadora portátil más pequeña que es 1366px
ancho. Aquí están las matemáticas actualizadas:
font-size: calc(16 * 1366px / 1600);
// same as:
font-size: calc(16 * 0.85375px);
// final result:
font-size: 13.66px;
O digamos que estamos viendo esto en una pantalla de alta definición completa en 1920px
ancho:
font-size: calc(16 * 1920px / 1600);
// same as:
font-size: calc(16 * 1.2px);
// final result:
font-size: 19.2px;
Puede ver por sí mismo cómo, aunque usamos valores de píxeles como referencia, en realidad podemos escalar proporcionalmente nuestros valores de CSS en función de la diferencia de ancho entre los tamaños de vista ideal y actual.
Aquí hay una pequeña demostración que construí para ilustrar la técnica:
Aquí hay un video para mayor comodidad:
Fijación del ancho mínimo y máximo de la ventana gráfica
Con este enfoque actual, el diseño se escala para coincidir con el tamaño de la ventana, sin importar cuán grande o pequeña sea la ventana. Podemos prevenir esto con CSS clamp()
lo que nos permite establecer un ancho mínimo de 350px
y ancho máximo de 3840px
. Esto significa que si vamos a abrir la aplicación web en un dispositivo con 5000px
ancho, nuestro diseño permanecerá bloqueado en 3840px
:
--ideal-viewport-width: 1600;
--current-viewport-width: 100vw;
/*
Set our minimum and maximum allowed layout widths:
*/
--min-viewport-width: 350px;
--max-viewport-width: 3840px;
.cardWidget {
.cardHeading {
--ideal-font-size: 16;
font-size: calc(
/*
The clamp() function takes three comma separated expressions
as its parameter, in the order of minimum value, preferred value
and maximum value:
*/
--clamped-viewport-width: clamp(var(--min-viewport-width), var(--current-viewport-width), var(--max-viewport-width);
/*
Use the clamped viewport width in our calculation
*/
var(--ideal-font-size) * var(--clamped-viewport-width) / var(--ideal-viewport-width)
);
}
}
Hagamos un ayudante para las conversiones de unidades.
Nuestro código es bastante detallado. Escribamos una función SCSS simple que convierta nuestros valores de píxeles a unidades relativas. De esa manera, podemos importar y reutilizar en cualquier lugar sin tanta duplicación:
/*
Declare a SCSS function that takes a value to be scaled and
ideal viewport width:
*/
@function scaleValue(
$value,
$idealViewportWidth: 1600px,
$min: 350px,
$max: 3840px
) {
@return calc(
#{$value} * (clamp(#{$min}, 100vw, #{$max}) / #{$idealViewportWidth})
);
}
/*
We can then apply it on any numeric CSS value.
Please note we are passing not pixel based, but numeric values:
*/
.myElement {
width: #{scaleValue(500)};
height: #{scaleValue(500)};
box-shadow: #{scaleValue(2)} #{scaleValue(2)} rgba(black, 0.5);
font-size: #{scaleValue(24)};
}
Portar esto a Javascript
A veces, CSS no lo corta y tenemos que usar JavaScript para dimensionar un componente. Digamos que estamos construyendo un SVG dinámicamente y necesitamos dimensionar su width
y height
propiedades basadas en un ancho de diseño ideal. Aquí está el JavaScript para que esto suceda:
/*
Our helper method to scale a value based on the device width
*/
const scaleValue = (value, idealViewportWidth = 1600) => {
return value * (window.innerWidth / idealViewportWidth)
}
/*
Create a SVG element and set its width, height and viewbox properties
*/
const IDEAL_SVG_WIDTH = 512
const IDEAL_SVG_HEIGHT = 512
const svgEl = document.createElement('svg')
/* Scale the width and height */
svgEl.setAttribute('width', scaleValue(IDEAL_SVG_WIDTH))
svgEl.setAttribute('height', scaleValue(IDEAL_SVG_WIDTH))
/*
We don't really need to scale the viewBox property because it will
perfectly match the ratio of the scaled width and height
*/
svg.setAttribute('viewBox', `0 0 ${IDEAL_SVG_WIDTH} ${IDEAL_SVG_HEIGHT}`)
Los inconvenientes de esta técnica.
Esta solución no es perfecta. Por ejemplo, un gran inconveniente es que las IU son ya no se puede ampliar. No importa cuánto haga zoom el usuario, los diseños permanecerán bloqueados como si se vieran con un zoom del 100%.
Dicho esto, podemos usar fácilmente las consultas de medios tradicionales, donde establecemos diferentes valores numéricos ideales en diferentes anchos de ventana gráfica:
.myElement {
width: #{scaleValue(500)};
height: #{scaleValue(500)};
box-shadow: #{scaleValue(2)} #{scaleValue(2)} rgba(black, 0.5);
font-size: #{scaleValue(24)};
@media (min-width: 64em) {
width: #{scaleValue(800)};
font-size: #{scaleValue(42)};
}
}
Ahora podemos beneficiarnos tanto de las consultas de medios como de nuestro escalado lineal perfecto en píxeles.
Terminando
Todo esto es una forma alternativa de implementar interfaces de usuario fluidas. Tratamos los valores de píxeles perfectos como valores numéricos puros y los multiplicamos por la diferencia entre el ancho de la ventana actual y el ancho de la ventana “ideal” de los diseños.
He utilizado esta técnica ampliamente en mi propio trabajo y espero que también la encuentres.