Schriftgröße linear mit CSS clamp() skalieren – basierend auf dem Viewport
Responsive Typografie wurde in der Vergangenheit mit einer Reihe von Methoden wie Media Queries und CSS ausprobiert calc()
.
Hier werden wir einen anderen Weg untersuchen, Text linear zwischen einer Reihe von minimalen und maximalen Größen zu skalieren, wenn die Breite des Darstellungsbereichs zunimmt, mit der Absicht, sein Verhalten bei verschiedenen Bildschirmgrößen vorhersehbarer zu machen – alles in einer einzigen CSS-Zeile , danke an clamp()
.
Die CSS-Funktion clamp()
ist ein Schwergewicht. Es ist für eine Vielzahl von Dingen nützlich, aber besonders gut für die Typografie. So funktioniert das. Es braucht drei Werte:
clamp(minimum, preferred, maximum);
Der zurückgegebene Wert ist der bevorzugte Wert, bis dieser bevorzugte Wert niedriger als der Mindestwert ist (an diesem Punkt wird der Mindestwert zurückgegeben) oder höher als der Höchstwert (an diesem Punkt wird der Höchstwert zurückgegeben).
Wäre es dann nicht immer der bevorzugte Wert, vorausgesetzt, Sie sind nicht komisch und setzen ihn zwischen Minimum und Maximum? Nun, es wird eher erwartet, dass Sie eine Formel für den bevorzugten Wert verwenden, wie zum Beispiel:
.banner {
width: clamp(200px, 50% + 20px, 800px); /* Yes, you can do math inside clamp()! */
}
Angenommen, Sie möchten das Minimum eines Elements font-size
auf 1 rem festlegen, wenn die Breite des Darstellungsbereichs 360 px oder weniger beträgt, und das Maximum auf 3,5 rem festlegen, wenn die Breite des Darstellungsbereichs 840 px oder mehr beträgt.
Mit anderen Worten:
1rem = 360px and below
Scaled = 361px - 839px
3.5rem = 840px and above
Jede Ansichtsfensterbreite zwischen 361 und 839 Pixel erfordert eine linear skalierte Schriftgröße zwischen 1 und 3,5 rem. Das ist eigentlich ganz einfach mit clamp()
! Bei einer Viewport-Breite von 600 Pixeln, also in der Mitte zwischen 360 und 840 Pixeln, würden wir beispielsweise genau den Mittelwert zwischen 1 und 3,5 rem erhalten, also 2,25 rem.

Was wir zu erreichen versuchen, wird als lineare Interpolationclamp()
bezeichnet : Zwischeninformationen zwischen zwei Datenpunkten erhalten.
Hier sind die vier Schritte dazu:
Schritt 1
Wählen Sie Ihre minimale und maximale Schriftgröße sowie Ihre minimale und maximale Breite des Darstellungsbereichs aus. In unserem Beispiel sind das 1rem und 3,5rem für die Schriftgrößen und 360px und 840px für die Breiten.
Schritt 2
Konvertieren Sie die Breiten in rem
. Da 1rem in den meisten Browsern standardmäßig 16px ist (dazu später mehr), werden wir das verwenden. Die minimale und maximale Viewport-Breite beträgt nun also 22,5 rem bzw. 52,5 rem.
Schritt 3
Hier werden wir uns ein bisschen auf die mathematische Seite lehnen. Zusammen bilden die Breiten des Ansichtsfensters und die Schriftgrößen zwei Punkte auf einem X- und Y-Koordinatensystem, und diese Punkte bilden eine Linie.

(22.5, 1)
Und (52.5, 3.5)
Wir brauchen irgendwie diese Linie – oder besser gesagt ihre Neigung und ihren Schnittpunkt mit der Y-Achse, um genauer zu sein. So berechnen Sie das:
slope = (maxFontSize - minFontSize) / (maxWidth - minWidth)
yAxisIntersection = -minWidth * slope + minFontSize
Das ergibt einen Wert von 0,0833 für die Steigung und -0,875 für den Schnittpunkt auf der Y-Achse.
Schritt 4
Jetzt bauen wir die clamp()
Funktion. Die Formel für den Vorzugswert lautet:
preferredValue = yAxisIntersection[rem] + (slope * 100)[vw]
Die Funktion endet also so:
.header {
font-size: clamp(1rem, -0.875rem + 8.333vw, 3.5rem);
}
Das Ergebnis können Sie in der folgenden Demo visualisieren:
Gehen Sie voran und spielen Sie damit. Wie Sie sehen können, wächst die Schriftgröße nicht mehr, wenn die Ansichtsfensterbreite 840 Pixel beträgt, und schrumpft nicht mehr bei 360 Pixel. Alles dazwischen ändert sich linear.
Was ist, wenn der Benutzer die Schriftgröße des Stammverzeichnisses ändert?
Möglicherweise ist Ihnen bei diesem ganzen Ansatz ein kleiner Fehler aufgefallen: Er funktioniert nur, solange die Schriftgröße des Stamms diejenige ist, die Sie denken – im vorherigen Beispiel 16 Pixel – und sich nie ändert.
Wir wandeln die Breiten, 360px und 840px, in rem
Einheiten um, indem wir sie durch 16 dividieren, weil wir davon ausgehen, dass dies die Schriftgröße des Stamms ist. Wenn der Benutzer seine Einstellungen auf eine andere Stammschriftgröße eingestellt hat, z. B. 18 Pixel anstelle der standardmäßigen 16 Pixel, dann ist diese Berechnung falsch und die Textgröße wird nicht so geändert, wie wir es erwarten würden.
Es gibt nur einen Ansatz, den wir hier verwenden können, und dieser besteht darin, (1) die erforderlichen Berechnungen im Code beim Laden der Seite durchzuführen, (2) auf Änderungen an der Schriftgröße des Stammverzeichnisses zu warten und (3) alles neu zu berechnen, wenn Änderungen stattfinden .
Hier ist eine nützliche JavaScript-Funktion, um die Berechnungen durchzuführen:
// Takes the viewport widths in pixels and the font sizes in rem
function clampBuilder( minWidthPx, maxWidthPx, minFontSize, maxFontSize ) {
const root = document.querySelector( "html" );
const pixelsPerRem = Number( getComputedStyle( root ).fontSize.slice( 0,-2 ) );
const minWidth = minWidthPx / pixelsPerRem;
const maxWidth = maxWidthPx / pixelsPerRem;
const slope = ( maxFontSize - minFontSize ) / ( maxWidth - minWidth );
const yAxisIntersection = -minWidth * slope + minFontSize
return `clamp( ${ minFontSize }rem, ${ yAxisIntersection }rem + ${ slope * 100 }vw, ${ maxFontSize }rem )`;
}
// clampBuilder( 360, 840, 1, 3.5 ) -> "clamp( 1rem, -0.875rem + 8.333vw, 3.5rem )"
Ich lasse absichtlich aus, wie die zurückgegebene Zeichenfolge in das CSS eingefügt wird, da es eine Menge Möglichkeiten gibt, dies zu tun, je nach Ihren Anforderungen und ob Sie Vanilla-CSS, eine CSS-in-JS-Bibliothek oder etwas anderes verwenden. Außerdem gibt es kein natives Ereignis für Änderungen der Schriftgröße, daher müssten wir dies manuell überprüfen. Wir könnten setInterval
jede Sekunde überprüfen, aber das könnte zu Leistungseinbußen führen.
Das ist eher ein Grenzfall. Nur sehr wenige Menschen ändern die Schriftgröße ihres Browsers und noch weniger werden sie genau während des Besuchs Ihrer Website ändern. Aber wenn Sie möchten, dass Ihre Website so reaktionsschnell wie möglich ist, dann ist dies der richtige Weg.
Für diejenigen, denen dieser Grenzfall nichts ausmacht
Du denkst, du kannst leben, ohne dass es perfekt ist? Dann habe ich etwas für dich. Ich habe ein kleines Tool erstellt, um die Berechnungen schnell und einfach zu machen.
Klicken Sie auf den unteren Button, um den Inhalt von codesandbox.io zu laden.
Sie müssen lediglich die Breiten und Schriftgrößen in das Tool eingeben, und die Funktion wird für Sie berechnet. Kopieren Sie das Ergebnis und fügen Sie es in Ihr CSS ein. Es ist nicht schick und ich bin sicher, dass vieles davon verbessert werden kann, aber für den Zweck dieses Artikels ist es mehr als genug. Fühlen Sie sich frei, nach Herzenslust zu forken und zu modifizieren.
So vermeiden Sie umfließenden Text
Eine so feinkörnige Kontrolle über die Dimensionen der Typografie ermöglicht es uns, andere coole Dinge zu tun – wie z.
So verhält sich Text normalerweise.


Aber jetzt, mit der Kontrolle, die wir haben, können wir dafür sorgen, dass der Text die gleiche Anzahl von Zeilen behält und immer beim gleichen Wort umbricht, egal welche Ansichtsfensterbreite wir darauf werfen.


Wie machen wir das? Zunächst muss das Verhältnis zwischen Schriftgröße und Breite des Ansichtsfensters gleich bleiben. In diesem Beispiel gehen wir von 1rem bei 320px zu 3rem bei 960px.
320 / 1 = 320
960 / 3 = 320
clampBuilder()
Wenn wir die zuvor erstellte Funktion verwenden , wird das zu:
const text = document.querySelector( "p" );
text.style.fontSize = clampBuilder( 320, 960, 1, 3 );
Es behält das gleiche Verhältnis von Breite zu Schriftart bei. Der Grund dafür ist, dass wir sicherstellen müssen, dass der Text bei jeder Breite die richtige Größe hat, damit er die gleiche Anzahl von Zeilen beibehalten kann. Es wird immer noch in verschiedenen Breiten umfließen, aber dies ist für das, was wir als nächstes tun werden, notwendig.
Jetzt müssen wir etwas Hilfe von der CSS-Zeicheneinheit ( ch
) bekommen, weil es nicht ausreicht, die richtige Schriftgröße zu haben. Eine ch
Einheit entspricht der Breite der Glyphe „0“ in der Schriftart eines Elements. Wir möchten den Textkörper so breit wie das Ansichtsfenster machen, nicht durch die Einstellung, width: 100%
sondern mit width: Xch
, wobei X
die Anzahl der ch
Einheiten (oder 0s) ist, die erforderlich ist, um das Ansichtsfenster horizontal zu füllen.
Um zu finden X
, müssen wir die minimale Viewport-Breite, 320 Pixel, durch die ch
Größe des Elements teilen, unabhängig von der Schriftgröße, wenn das Viewport 320 Pixel breit ist. Das ist in diesem Fall 1rem.
Nicht schwitzen, hier ist ein Ausschnitt, um die ch
Größe eines Elements zu berechnen:
// Returns the width, in pixels, of the "0" glyph of an element at a desired font size
function calculateCh( element, fontSize ) {
const zero = document.createElement( "span" );
zero.innerText = "0";
zero.style.position = "absolute";
zero.style.fontSize = fontSize;
element.appendChild( zero );
const chPixels = zero.getBoundingClientRect().width;
element.removeChild( zero );
return chPixels;
}
Jetzt können wir fortfahren, die Breite des Textes festzulegen:
function calculateCh( element, fontSize ) { ... }
const text = document.querySelector( "p" );
text.style.fontSize = clampBuilder( 320, 960, 1, 3 );
text.style.width = `${ 320 / calculateCh(text, "1rem" ) }ch`;

Wow, warte. Etwas Schlimmes ist passiert. Es gibt eine horizontale Bildlaufleiste, die Dinge vermasselt!
Wenn wir über 320px sprechen, sprechen wir über die Breite des Ansichtsfensters, einschließlich der vertikalen Bildlaufleiste. Die Breite des Textes wird also auf die Breite des sichtbaren Bereichs plus die Breite der Bildlaufleiste gesetzt, wodurch er horizontal überläuft.
Warum dann nicht eine Metrik verwenden, die die Breite der vertikalen Bildlaufleiste nicht enthält? Das können wir nicht, und das liegt an der CSS- vw
Einheit. vw
Denken Sie daran, dass wir in verwenden, clamp()
um die Schriftgröße zu steuern. Sie sehen, vw
enthält die Breite der vertikalen Bildlaufleiste, wodurch die Schriftart entlang der Breite des Ansichtsfensters einschließlich der Bildlaufleiste skaliert wird. Wenn wir Reflow vermeiden wollen, muss die Breite proportional zur Breite des Ansichtsfensters sein, einschließlich der Bildlaufleiste.
Also, was machen wir? Wenn wir dies tun:
text.style.width = `${ 320 / calculateCh(text, "1rem") }ch`;
… wir können das Ergebnis verkleinern, indem wir es mit einer Zahl kleiner als 1 multiplizieren. 0,9 reicht aus. Das bedeutet, dass die Breite des Textes 90 % der Breite des Ansichtsfensters beträgt, was den geringen Platzbedarf der Bildlaufleiste mehr als berücksichtigt. Wir können es enger machen, indem wir eine noch kleinere Zahl verwenden, z. B. 0,6.
function calculateCh( element, fontSize ) { ... }
const text = document.querySelector( "p" );
text.style.fontSize = clampBuilder( 20, 960, 1, 3 );
text.style.width = `${ 320 / calculateCh(text, "1rem" ) * 0.9 }ch`;

Sie könnten versucht sein, einfach ein paar Pixel von 320 abzuziehen, um die Bildlaufleiste zu ignorieren, wie hier:
text.style.width = `${ ( 320 - 30 ) / calculateCh( text, "1rem" ) }ch`;
Das Problem dabei ist, dass es das Reflow-Problem zurückbringt! Das liegt daran, dass das Subtrahieren von 320 das Darstellungsfenster-zu-Schriftart-Verhältnis aufhebt.


Die Breite des Textes muss immer ein Prozentsatz der Breite des Ansichtsfensters sein. Eine andere zu beachtende Sache ist, dass wir sicherstellen müssen, dass wir auf jedem Gerät, das die Website nutzt, dieselbe Schriftart laden. Das klingt offensichtlich, nicht wahr? Nun, hier ist ein kleines Detail, das Ihren Text verwirren könnte. Wenn Sie so etwas tun, font-family: sans-serif
wird nicht garantiert, dass in jedem Browser dieselbe Schriftart verwendet wird. sans-serif
setzt Arial auf Chrome für Windows, aber Roboto auf Chrome für Android. Außerdem kann die Geometrie einiger Schriftarten einen Reflow verursachen, selbst wenn Sie alles richtig machen. Monospaced-Schriftarten erzielen in der Regel die besten Ergebnisse. Stellen Sie also immer sicher, dass Ihre Schriftarten auf dem Punkt sind.
Sehen Sie sich dieses nicht umfließende Beispiel in der folgenden Demo an:
Nicht umfließender Text in einem Container
Jetzt müssen wir nur noch die Schriftgröße und -breite anstelle der Textelemente direkt auf den Container anwenden. Der Text darin muss nur auf gesetzt werden width: 100%
. Bei Absätzen und Überschriften ist dies nicht erforderlich, da es sich sowieso um Block-Level-Elemente handelt, die automatisch die Breite des Containers ausfüllen.
Ein Vorteil der Anwendung in einem übergeordneten Container besteht darin, dass seine untergeordneten Container automatisch reagieren und die Größe ändern, ohne dass ihre Schriftgrößen und -breiten einzeln festgelegt werden müssen. Wenn wir die Schriftgröße eines einzelnen Elements ändern müssen, ohne die anderen zu beeinflussen, müssen wir nur die Schriftgröße auf einen beliebigen em
Betrag ändern, und sie wird natürlich relativ zur Schriftgröße des Containers sein.
Nicht umfließender Text ist knifflig, aber es ist ein subtiler Effekt, der einem Design eine nette Note verleihen kann!
Einpacken
Um das Ganze abzurunden, habe ich eine kleine Demonstration zusammengestellt, wie all dies in einem realen Szenario aussehen könnte.
In diesem letzten Beispiel können Sie auch die Grundschriftgröße ändern und die clamp()
Funktion wird automatisch neu berechnet, damit der Text in jeder Situation die richtige Größe hat.
Obwohl das Ziel dieses Artikels die Verwendung clamp()
von Schriftgrößen ist, könnte dieselbe Technik in jeder CSS-Eigenschaft verwendet werden, die eine Längeneinheit erhält. Nun, ich sage nicht, dass Sie dies überall verwenden sollten . Oft font-size: 1rem
reicht ein gutes altes aus. Ich versuche nur, Ihnen zu zeigen, wie viel Kontrolle Sie haben können, wenn Sie sie brauchen.
Persönlich glaube ich, clamp()
dass es eines der besten Dinge ist, die in CSS ankommen, und ich kann es kaum erwarten zu sehen, welche anderen Verwendungen die Leute sich einfallen lassen, wenn es immer weiter verbreitet wird!