A B C D F G I J K L M O P R S U W Z

Clamp

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).

In diesem Beispiel ist der bevorzugte Wert 50 %. Auf der linken Seite sind 50 % des 400 Pixel-Darstellungsbereichs 200 Pixel groß, was weniger als der Mindestwert von 300 Pixel ist, der stattdessen verwendet wird. Auf der rechten Seite entsprechen 50 % des 1400 Pixel-Darstellungsbereichs 700 Pixel, was größer als der Mindestwert und niedriger als der 800 Pixel-Höchstwert ist, also 700 Pixel entspricht.

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-sizeauf 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.

Liniendiagramm, bei dem die vertikale Achse in der Schriftgröße Rem Units von 0 bis 4 gemessen wird und die horizontale Achse die Ansichtsfensterbreite von 0 bis 1.060 Pixel misst.  Es gibt vier blaue Punkte auf dem Raster, die durch eine blaue Linie verbunden sind.

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.

Ein zweidimensionales Koordinatendiagramm mit zwei Punkten und einer roten Linie, die sie schneidet.
(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:

Klicken Sie auf den unteren Button, um den Inhalt von codepen.io zu laden.

Inhalt laden

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 remEinheiten 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 setIntervaljede 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.

Inhalt 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.

Es hat eine Reihe von Linien bei einer bestimmten Ansichtsfensterbreite …
…und wickelt seine Zeilen um, um sie auf eine andere Breite anzupassen

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.

Ansichtsfensterbreite = 400 Pixel
Ansichtsfensterbreite = 740px

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 chEinheit 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 Xdie Anzahl der chEinheiten (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 chGröß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 chGröß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`;
Ähm, wer hat dich zur Party eingeladen, Scrollbalken?

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- vwEinheit. vwDenken Sie daran, dass wir in verwenden, clamp()um die Schriftgröße zu steuern. Sie sehen, vwenthä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`;
Auf Wiedersehen, Bildlaufleiste!

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.

Ansichtsfensterbreite = 650 Pixel
Ansichtsfensterbreite = 670px

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-serifwird nicht garantiert, dass in jedem Browser dieselbe Schriftart verwendet wird. sans-serifsetzt 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:

Klicken Sie auf den unteren Button, um den Inhalt von codepen.io zu laden.

Inhalt laden

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.

Klicken Sie auf den unteren Button, um den Inhalt von codepen.io zu laden.

Inhalt laden

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 emBetrag ändern, und sie wird natürlich relativ zur Schriftgröße des Containers sein.

Klicken Sie auf den unteren Button, um den Inhalt von codepen.io zu laden.

Inhalt laden

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.

Klicken Sie auf den unteren Button, um den Inhalt von codepen.io zu laden.

Inhalt laden

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: 1remreicht 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!