Das dynamische Skalieren von CSS-Werten basierend auf der Viewport-Breite ist kaum ein neues Thema. Hier auf CSS-Tricks finden Sie zahlreiche ausführliche Berichte in Artikeln wie diesem oder diesem.
Die meisten dieser Beispiele verwenden zwar relative CSS-Einheiten und wertlose Werte, um eine flüssige Skalierung zu erreichen. Das geht auf Kosten der Pixelgenauigkeit und führt normalerweise zu Textumbrüchen und Layoutverschiebungen, sobald der Bildschirm unter oder über einen bestimmten Schwellenwert fällt.
Aber was, wenn wir wirklich Pixelgenauigkeit wollen? Was, wenn wir zum Beispiel ein komplexes Echtzeit-Analyse-Dashboard für große Fernseher in einem Konferenzraum entwickeln oder eine PWA, die ausschließlich auf mobilen Geräten und Tablets geöffnet wird, im Gegensatz zu textlastigen Blogs und Nachrichtenseiten? Das sind Fälle, in denen wir mehr Präzision benötigen.
Mit anderen Worten, was, wenn wir Designs einheitlich skalieren wollen? Natürlich kann man den Inhalt mit CSS-Transformationen basierend auf der verfügbaren Breite skalieren, wie in diesem Artikel beschrieben – so bleiben die richtigen Verhältnisse erhalten.
Wir können jedoch auch flüssige proportionale Skalierungs-Benutzeroberflächen mithilfe von Pixelwerten in CSS erzielen. Diese skalieren entsprechend der Bildschirmfläche des Geräts, während sie gleichzeitig ihre pixelgenauen Proportionen beibehalten. Darüber hinaus können wir weiterhin Pixelwerte verwenden und sie automatisch in relative CSS-Einheiten umwandeln, wenn die Arbeit in Pixeln angenehmer oder vertrauter ist.
Unsere Benutzeroberfläche skalieren
Versuchen wir, dieses fantastische Dashboard von Craftwork zu implementieren. Wir müssen es so gestalten, dass es perfekt skaliert und alle Textzeilenanzahlen, Abstände, Bildgrößen usw. beibehält.

Wir arbeiten mit CSS-Pixelwerten und verwenden SCSS für Geschwindigkeit und Komfort. Wenn wir also den Titel eines dieser Karten-Widgets ansprechen wollen, könnte unser SCSS so aussehen:
.cardWidget {
.cardHeading {
font-size: 16px;
}
}
Nichts Besonderes. Nichts, was wir nicht schon gesehen hätten. Als Pixelwert wird dies nicht skaliert.
Dieses Design wurde mit einem Container mit einer Breite von 1600px erstellt. Nehmen wir an, dass bei 1600px die ideale Schriftgröße für die Titel der Karten 16px sein sollte, da es so entworfen wurde.
Nachdem wir nun die ideale Schriftgröße für den Container für diese Breite haben, skalieren wir unsere CSS-Pixelwerte entsprechend der aktuellen* Ansichtsbreite.
/*
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)
);
}
}
Wie Sie sehen, behandeln wir die ideale Schriftgröße, die wir aus dem Design erhalten haben, als Basis und multiplizieren sie mit der Differenz zwischen der aktuellen und der idealen Ansichtsbreite. Wie sieht das mathematisch aus? Nehmen wir an, wir betrachten diese Webanwendung auf einem Bildschirm mit exakt derselben Breite wie das Mockup.
--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;
Da unsere Ansichtsbreite perfekt übereinstimmt, beträgt unsere Schriftgröße bei der idealen Ansichtsbreite von 1600px genau 16px.
Als weiteres Beispiel nehmen wir an, wir betrachten die Webanwendung auf einem kleineren Laptop-Bildschirm mit einer Breite von 1366px. Hier ist die aktualisierte Mathematik:
font-size: calc(16 * 1366px / 1600);
// same as:
font-size: calc(16 * 0.85375px);
// final result:
font-size: 13.66px;
Oder nehmen wir an, wir betrachten dies auf einem Full-HD-Display mit 1920px Breite.
font-size: calc(16 * 1920px / 1600);
// same as:
font-size: calc(16 * 1.2px);
// final result:
font-size: 19.2px;
Sie sehen selbst, dass wir, obwohl wir Pixelwerte als Referenz verwenden, unsere CSS-Werte proportional zur Differenz der Breite zwischen der idealen und der aktuellen Ansichtsgröße skalieren können.
Hier ist eine kleine Demo, die ich zur Veranschaulichung der Technik erstellt habe:
Hier ist ein Video zur Bequemlichkeit:
Begrenzung der minimalen und maximalen Ansichtsbreite
Mit dem aktuellen Ansatz skaliert das Design, um der Ansichtsgröße zu entsprechen, egal wie groß oder klein die Ansicht wird. Wir können dies mit dem CSS-Befehl clamp() verhindern, der es uns ermöglicht, eine minimale Breite von 350px und eine maximale Breite von 3840px festzulegen. Das bedeutet, dass, wenn wir die Webanwendung auf einem Gerät mit 5000px Breite öffnen, unser Layout bei 3840px gesperrt bleibt.
--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)
);
}
}
Machen wir einen Helfer für die Einheitenumrechnungen
Unser Code ist ziemlich ausführlich. Schreiben wir eine einfache SCSS-Funktion, die unsere Werte von Pixeln in relative Einheiten umwandelt. So können wir sie importieren und überall wiederverwenden, ohne viel Duplikation.
/*
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)};
}
Portierung auf JavaScript
Manchmal reicht CSS nicht aus und wir müssen JavaScript verwenden, um eine Komponente zu dimensionieren. Nehmen wir an, wir erstellen dynamisch eine SVG und müssen ihre Breite und Höhe basierend auf einer idealen Designbreite festlegen. Hier ist das JavaScript, um das zu erreichen:
/*
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}`)
Die Nachteile dieser Technik
Diese Lösung ist nicht perfekt. Ein großer Nachteil ist zum Beispiel, dass die Benutzeroberflächen nicht mehr zoombar sind. Egal wie viel der Benutzer zoomt, die Designs bleiben gesperrt, als würden sie mit 100% Zoom betrachtet.
Dennoch können wir traditionelle Media-Queries verwenden, bei denen wir für verschiedene Ansichtsbreiten unterschiedliche ideale numerische Werte festlegen.
.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)};
}
}
Jetzt können wir sowohl von Media-Queries als auch von unserer pixelgenauen linearen Skalierung profitieren.
Zusammenfassung
Dies alles ist eine alternative Methode zur Implementierung von flüssigen Benutzeroberflächen. Wir behandeln die pixelgenauen Werte als reine numerische Werte und multiplizieren sie mit der Differenz zwischen der aktuellen Ansichtsbreite und der "idealen" Ansichtsbreite aus den Designs.
Ich habe diese Technik in meiner Arbeit ausgiebig genutzt und hoffe, dass Sie sie auch nützlich finden werden.
Man weiß, dass es eine großartige Lösung ist, wenn man sie ansieht und denkt: "Verdammt, warum bin ich nicht selbst darauf gekommen?!"
Vielen Dank, wirklich ein interessanter Ansatz.
Wie ist die Performance dieser Technik? Werden die Werte nur einmal ausgewertet?
Sie wird bei jeder Größenänderung ausgewertet, d. h. wenn sich die numerische Darstellung von
100vwändert.Alle Berechnungen werden an CSS ausgelagert, daher würde ich sagen, dass die Leistung ziemlich gut ist.
Interessant! Ich wäre nicht auf die Idee gekommen, calc so zu verwenden, und ich finde die Funktion scaleValue elegant. Persönlich verwende ich tendenziell einfach em-Einheiten für alles und setze die Schriftgröße des Body auf VW-Einheiten. Ich bin mir nicht sicher, ob ich die Nachteile meiner Methode sehe… Kann mich jemand aufklären?
Hallo Koen, ich denke, der Hauptnachteil der Verwendung von "flüssigen" Einheiten wie em / vw ist, dass man unweigerlich die Pixelgenauigkeit verliert. Man kann sehr nah herankommen, aber Ihr Layout wird sich bei bestimmten Auflösungen immer verschieben / neu anordnen.
Diese Technik versucht, dieses Problem zu minimieren und bewahrt die Größen in allen möglichen verschiedenen Auflösungen.
Meinst du, dass, wenn ich die Schriftgröße auf 1rem im Wurzelelement setze und sie mit der Ansichtsbreite skalieren lasse, das Design nicht zu einer pixelgenauen Version bei einer Auflösung wird, die du von deinem Designteam erhältst? Ich stimme dem nicht zu, aber du sagst uns nicht, wie es die "Pixelgenauigkeit" verliert, also kann ich nicht argumentieren.
Das obige Zitat beschreibt eine Differenz als Ergebnis einer Division.
Ich glaube, dass eine Differenz das Ergebnis einer Subtraktion ist und ein Quotient das Ergebnis einer Division.
Mit diesem Ansatz werden Texte auf kleinen Bildschirmen tendenziell sehr klein und unleserlich. Ich finde das schön für Diagramme und so etwas, aber nicht für das gesamte Seitendesign.
Hallo Raphael, wir können diese Technik mit Media-Queries mischen und sicherstellen, dass wir für kleinere Auflösungen größere Werte verwenden!
Ich stimme zu. Zwingt den Benutzer, Ihre App im Vollbildmodus zu verwenden. Auf dem Desktop wird es unbrauchbar, wenn er den Bildschirm verkleinert, wie im Video gezeigt.
Ich liebe das!
Am Ende sagst du, dass es nur "zoombar" wäre, wenn man Media-Queries verwendet?
Ziemlich nett und elegant!
Das Einzige, was ich hier hinzufügen würde, ist vielleicht ein weiteres Argument, das mir erlaubt, einen Minimalwert von der Funktion zurückzugeben, z. B. ich möchte einen minimalen Schriftgrößenwert.
Aber das würde das Gesamtbild des
Widgetsstören und ich würde in diesem Szenario vielleicht sogar überlaufenden Text sehen. Haben Sie Vorschläge, wenn wir so vorgehen?Schöner Artikel und Implementierung, aber meine Bedenken gelten der Lesbarkeit und Barrierefreiheit. Sollten wir die Schriftgröße wirklich skalieren, um diesen pixelgenauen Look zu erhalten? Was passiert mit der Lesbarkeit?
Ich sehe zu viele Nachteile bei der Verwendung.
Der Code wird schwerer zu warten.
Die CSS-Größe erhöht sich bei größeren Projekten.
Auf kleineren Bildschirmen wird die App unbrauchbar, zum Beispiel auf mobilen Auflösungen hat das Design zu viele Änderungen bei der Größe von Abständen, Rändern, Schriften usw. Sie müssen also Media-Queries mit all dieser Skalierungslogik schreiben, was manchmal noch schwerer zu warten sein kann.
Versuchen Sie, all diese Dinge zu kombinieren: Schreiben Sie ein Großprojekt, das auf Mobilgeräten, Tablets und Desktops verwendbar ist und eine JS-CSS-Handhabung hat.
Ich verwende seit einigen Monaten eine ähnliche Technik und freue mich, dass dieser Artikel meine Arbeit endlich bestätigt hat.
Im Gegensatz zum Artikel fand ich es einfacher, REM-Werte zu verwenden, anstatt calc() + SCSS-Funktionen zu verwenden.
Die Technik, die ich verwende, ist wie folgt:
html { font-size: 1.6vw }
p { font-size: 1rem; } /* 16px */
div { width: 3.75rem } /* 60px */
Es gibt einige Haken, die ich noch nicht herausgefunden habe, und es gibt ein Upgrade dieser Technik, das ich kürzlich verwendet habe: Wenn die Ansicht über 1680px liegt, verwende ich absolute PX-Werte, da ein größerer Bildschirm einem größeren Design entspricht und das Konsequenzen hat. Alles zwischen 960 und 1440 verwende ich skaliert, damit ich nicht mehrere Media-Queries verwenden muss, um das Layout auf allen Bildschirmgrößen gut aussehen zu lassen.
Auf Mobilgeräten und Tablets verwende ich lieber auch Pixelwerte!
Obwohl die Idee hier ziemlich raffiniert ist, sehe ich mehrere Probleme mit diesem Ansatz.
Ein Problem ist, dass es keine echte Pixelgenauigkeit ist, da Sie Präzision durch Fließkomma-Berechnungen verlieren. Bei bestimmten Ansichtsbreiten ergeben Ihre Berechnungen ungenaue Werte, die gerundet/abgeschnitten werden, und somit sieht die Seite anders aus als bei anderen Breiten. Im Codepen, den Sie verlinkt haben, zum Beispiel, wenn Sie die Seite auf etwa 600px verkleinern, werden die Tags einiger Karten auf die nächste Zeile umgebrochen. Und wenn Sie die Seite langsam vergrößern/verkleinern, werden Sie feststellen, dass bei bestimmten Ansichtsbreiten die Bilder verzerrt werden und breiter als hoch sind. Die Seite wackelt beim Vergrößern/Verkleinern, was darauf hindeutet, dass sie ihre Proportionen nicht richtig beibehält.
Das offensichtlichere Problem mit diesem Ansatz ist, dass Ihre Schriftgrößen unleserlich klein sind. Auf einem 360px breiten Mobilgerät erhalten Sie eine winzige Basisschriftgröße von
3,6px. Sie können Media-Queries verwenden, um dies zu beheben, aber dann haben Sie den Zweck der Verwendung dieser Technik zunichte gemacht.In ähnlicher Weise ignoriert dies die Schriftgrößeneinstellungen der Benutzer. Auf einem
1600pxbreiten Gerät erhalten Benutzer immer eine Basisschriftgröße von16px, auch wenn sie ihre Browser so konfiguriert haben, dass sie eine andere Wurzelschriftgröße haben.Stattdessen empfehle ich die Verwendung des 62,5%-Wurzelschriftgröße-Trick zusammen mit einer Body-Schriftgröße von
1,6rem. Dies ermöglicht es Ihnen, in Pixeln zu denken, aber in responsiven Einheiten zu codieren, die die Schriftgrößeneinstellungen der Benutzer respektieren. Sie können dies sogar mit clamp kombinieren, um eine flüssige Typografie zu erstellen.Die Technik, die ich verwende, ist wie folgt:
`html { font-size: 1.6vw`
p { font-size: 1rem; } /* 16px */
div { width: 3.75rem } /* 60px */`
Es gibt einige Haken, die ich noch nicht herausgefunden habe, und es gibt ein Upgrade dieser Technik, das ich kürzlich verwendet habe: Wenn die Ansicht über 1680px liegt, verwende ich absolute PX-Werte, da ein größerer Bildschirm einem größeren Design entspricht und das Konsequenzen hat. Alles zwischen 960 und 1440 verwende ich skaliert, damit ich nicht mehrere Media-Queries verwenden muss, um das Layout auf allen Bildschirmgrößen gut aussehen zu lassen.
Gute Arbeit! Aber was, wenn meine Ansicht nicht skaliert, sondern mein Container? Ich habe standardmäßig einen Container-Div in einer gegebenen Ansicht. Aber mit JS kann ich ein- und ausblenden zwei weitere Container-Divs. Und innerhalb dieser Divs möchte ich den Text skalieren, der, wenn 3 Divs nebeneinander angezeigt werden, kleiner wäre, wenn nur ein Container-Div angezeigt würde. Keine Änderung der Ansicht, nur der relativen Größe des Container-Divs.
Das habe ich auch schon gemacht, aber ich habe festgestellt, dass es für die Barrierefreiheit nicht gut funktioniert. Der Text wird beim Vergrößern/Verkleinern kleiner und sehr schwer zu lesen. Was man bedenken sollte, ist, dass nicht jeder Ihre Website im Vollbildmodus betrachtet.
Zu diesem Zeitpunkt, da Sie Media-Queries einführen, erfordert diese Technik leider mehr Aufwand bei der Einrichtung, ohne einen Vorteil zu bringen, da Sie bereits Größen in verschiedenen Skalierungen definieren.
Es gibt einige Anwendungsfälle dafür, wie z. B. isolierte Komponenten, die auf die Größe der Ansicht reagieren und entsprechend skalieren sollten, aber für eine ganze Website würde ich das nicht tun.
Das ist großartig, besonders wenn Sie eine reine CSS-Lösung benötigen. Aber wenn Sie bereit sind, JavaScript zu verwenden, finde ich es überkompliziert und leicht, es nicht ganz richtig hinzubekommen (Beachten Sie im Beispiel, dass einige Dinge tatsächlich auf eine neue Zeile fallen, da die Skalierung nicht perfekt ist).
Ich habe dieses Codepen-Beispiel erstellt, das viel einfacher und wahrscheinlich wartbarer und zuverlässiger sein sollte, ohne die genannten Nachteile zu haben.
https://codepen.io/daledesilva/pen/dyejOMe
Ich hoffe, es hilft jemandem!