In CSS ist line-height wahrscheinlich eines der am meisten missverstandenen und doch am häufigsten verwendeten Attribute. Als Designer und Entwickler denken wir bei line-height vielleicht an das Konzept des Durchschusses aus dem Printdesign – ein Begriff, der interessanterweise vom buchstäblichen Einlegen von Bleistücken zwischen die Schriftzeilen stammt.
Durchschuss und line-height, so ähnlich sie auch sein mögen, haben einige wichtige Unterschiede. Um diese Unterschiede zu verstehen, müssen wir zuerst ein wenig mehr über Typografie erfahren.
Ein Überblick über typografische Begriffe
In der traditionellen westlichen Schriftgestaltung besteht eine Textzeile aus mehreren Teilen:
- Grundlinie: Dies ist die imaginäre Linie, auf der die Schrift sitzt. Wenn Sie in einem Linealheft schreiben, ist die Grundlinie die Linie, auf der Sie schreiben.
- Unterlänge: Diese Linie liegt knapp unter der Grundlinie. Sie ist die Linie, die einige Zeichen – wie Kleinbuchstaben
g,j,q,yundp– unterhalb der Grundlinie berühren. - x-Höhe: Dies ist (wenig überraschend) die Höhe eines normalen Kleinbuchstabens
xin einer Textzeile. Im Allgemeinen ist dies die Höhe anderer Kleinbuchstaben, obwohl einige Teile ihrer Zeichen die x-Höhe überschreiten können. Für alle praktischen Zwecke dient sie als wahrgenommene Höhe von Kleinbuchstaben. - Versalhöhe: Dies ist die Höhe der meisten Großbuchstaben in einer gegebenen Textzeile.
- Oberlänge: Eine Linie, die oft knapp über der Versalhöhe erscheint, wo einige Zeichen wie ein Kleinbuchstabe
hoderbdie normale Versalhöhe überschreiten können.

Jeder der oben beschriebenen Textteile ist intrinsisch für die Schriftart selbst. Eine Schriftart wird mit jedem dieser Teile im Hinterkopf entworfen. Es gibt jedoch einige Teile der Typografie, die dem Setzer (wie Ihnen und mir!) und nicht dem Designer überlassen werden. Einer davon ist der Durchschuss.
Durchschuss ist definiert als der Abstand zwischen zwei Grundlinien in einem Schriftsatz.

Ein CSS-Entwickler könnte denken: „OK, Durchschuss ist line-height, machen wir weiter.“ Während die beiden zusammenhängen, unterscheiden sie sich auch auf sehr wichtige Weise.
Nehmen wir ein leeres Dokument und fügen ihm einen klassischen „CSS-Reset“ hinzu.
* {
margin: 0;
padding: 0;
}
Dies entfernt den Rand (margin) und den Abstand (padding) von jedem einzelnen Element.
Wir werden auch Lato von Google Fonts als unsere font-family verwenden.
Wir brauchen etwas Inhalt, also erstellen wir einen <h1>-Tag mit etwas Text und setzen die line-height auf etwas Unverschämtes, wie 300px. Das Ergebnis ist eine einzelne Textzeile mit überraschend viel Platz sowohl oberhalb als auch unterhalb der einzelnen Textzeile.
Wenn ein Browser auf die Eigenschaft line-height stößt, nimmt er tatsächlich die Textzeile und platziert sie in die Mitte einer „Zeilenbox“, die eine Höhe hat, die der line-height des Elements entspricht. Anstatt den Durchschuss für eine Schriftart festzulegen, erhalten wir etwas Ähnliches wie ein Padding auf beiden Seiten der Zeilenbox.

Wie oben dargestellt, umgibt die Zeilenbox eine Textzeile, wobei der Durchschuss durch den Abstand unter einer Textzeile und über der nächsten erzeugt wird. Das bedeutet, dass für jedes Textelement auf einer Seite die Hälfte des Durchschusses oberhalb der ersten Textzeile und nach der letzten Textzeile in einem bestimmten Textblock vorhanden ist.
Was vielleicht überraschender ist, ist, dass die explizite Einstellung von line-height und font-size auf einem Element mit demselben Wert zusätzlichen Raum oberhalb und unterhalb des Textes lässt. Dies können wir sehen, indem wir unseren Elementen eine Hintergrundfarbe hinzufügen.
Das liegt daran, dass, obwohl die font-size auf 32px eingestellt ist, die tatsächliche Textgröße aufgrund des erzeugten Abstands etwas weniger als dieser Wert beträgt.
Damit CSS line-height wie Durchschuss behandelt
Wenn wir möchten, dass CSS einen traditionelleren typografischen Stil anstelle der Zeilenbox verwendet, möchten wir, dass eine einzelne Textzeile keinen Abstand oberhalb oder unterhalb hat – aber dass mehrzeilige Elemente ihren gesamten line-height-Wert beibehalten.
Es ist möglich, CSS mit etwas Aufwand zum Thema Durchschuss zu bringen. Michael Taranto hat ein Tool namens Basekick veröffentlicht, das genau dieses Problem löst. Es tut dies, indem es einen negativen oberen Rand für das ::before-Pseudoelement und ein translateY für das Element selbst anwendet. Das Endergebnis ist eine Textzeile ohne zusätzlichen Abstand um sie herum.
Die aktuellste Version von Basekicks Formel finden Sie im Quellcode für das Braid Design System von SEEK. Im folgenden Beispiel schreiben wir ein Sass-Mixin, das die Hauptarbeit für uns erledigt, aber dieselbe Formel kann mit JavaScript, Less, PostCSS-Mixins oder allem anderen verwendet werden, das diese Art von mathematischen Funktionen bietet.
@function calculateTypeOffset($lh, $fontSize, $descenderHeightScale) {
$lineHeightScale: $lh / $fontSize;
@return ($lineHeightScale - 1) / 2 + $descenderHeightScale;
}
@mixin basekick($typeSizeModifier, $baseFontSize, $descenderHeightScale, $typeRowSpan, $gridRowHeight, $capHeight) {
$fontSize: $typeSizeModifier * $baseFontSize;
$lineHeight: $typeRowSpan * $gridRowHeight;
$typeOffset: calculateTypeOffset($lineHeight, $fontSize, $descenderHeightScale);
$topSpace: $lineHeight - $capHeight * $fontSize;
$heightCorrection: 0;
@if $topSpace > $gridRowHeight {
$heightCorrection: $topSpace - ($topSpace % $gridRowHeight);
}
$preventCollapse: 1;
font-size: #{$fontSize}px;
line-height: #{$lineHeight}px;
transform: translateY(#{$typeOffset}em);
padding-top: $preventCollapse;
&::before {
content: "";
margin-top: #{-($heightCorrection + $preventCollapse)}px;
display: block;
height: 0;
}
}
Auf den ersten Blick sieht dieser Code definitiv nach vielen magischen Zahlen aus, die zusammengewürfelt wurden. Aber er kann erheblich aufgeschlüsselt werden, wenn man ihn im Kontext eines bestimmten Systems betrachtet. Sehen wir uns an, was wir wissen müssen.
$baseFontSize: Dies ist die normalefont-sizefür unser System, um das herum alles andere verwaltet wird. Wir verwenden 16px als Standardwert.$typeSizeModifier: Dies ist ein Multiplikator, der in Verbindung mit der Basis-Schriftgröße verwendet wird, um die Regel fürfont-sizezu bestimmen. Zum Beispiel ergibt ein Wert von 2, gekoppelt mit unserer Basis-Schriftgröße von 16px,font-size: 32px.$descenderHeightScale: Dies ist die Höhe des Durchschusses der Schriftart, ausgedrückt als Verhältnis. Für Lato liegt dieser Wert bei etwa 0,11.$capHeight: Dies ist die spezifische Versalhöhe der Schriftart, ausgedrückt als Verhältnis. Für Lato liegt dieser Wert bei etwa 0,75.$gridRowHeight: Layouts verlassen sich im Allgemeinen auf einen standardmäßigen vertikalen Rhythmus, um ein schönes und konsistent geordnetes Leseerlebnis zu erzielen. Zum Beispiel können alle Elemente auf einer Seite in Vielfachen von vier oder fünf Pixeln beabstandet sein. Wir verwenden 4 als Wert, da er sich leicht in unsere $baseFontSize von 16px teilen lässt.$typeRowSpan: Ähnlich wie$typeSizeModifierdient diese Variable als Multiplikator, der mit der Rasterzeilenhöhe verwendet wird, um den Wert derline-heightfür die Regel zu bestimmen. Wenn unsere Standard-Rasterzeilenhöhe 4 beträgt und unser Zeilenzeilenabstand 8, erhalten wirline-height: 32px.
Nun können wir diese Zahlen in die obige Basekick-Formel einsetzen (mit Hilfe von SCSS-Funktionen und Mixins), und das ergibt das folgende Ergebnis.
Das ist genau das, was wir wollen. Bei jeder Gruppe von Textblockelementen ohne Ränder sollten sich die beiden Elemente aneinander stoßen. Auf diese Weise sind alle zwischen den beiden Elementen gesetzten Ränder pixelgenau, da sie nicht mit dem Zeilenbox-Abstand kämpfen.
Verfeinern unseres Codes
Anstatt unseren gesamten Code in ein einziges SCSS-Mixin zu packen, organisieren wir ihn ein wenig besser. Wenn wir in Systemen denken, werden wir feststellen, dass es drei Arten von Variablen gibt, mit denen wir arbeiten.
| Variablentyp | Beschreibung | Mixin-Variablen |
|---|---|---|
| Systemebene | Diese Werte sind Eigenschaften des Designsystems, mit dem wir arbeiten. | $baseFontSize$gridRowHeight |
| Schriftartebene | Diese Werte sind intrinsisch für die verwendete Schriftart. Es kann zu Schätzungen und Anpassungen kommen, um die perfekten Zahlen zu ermitteln. | $descenderHeightScale$capHeight |
| Regel-Ebene | Diese Werte sind spezifisch für die CSS-Regel, die wir erstellen. | $typeSizeMultiplier$typeRowSpan |
Wenn wir so denken, wird es uns helfen, unser System einfacher zu skalieren. Sehen wir uns jede Gruppe der Reihe nach an.
Zunächst können die Variablen auf Systemebene global gesetzt werden, da diese im Laufe unseres Projekts wahrscheinlich nicht geändert werden. Das reduziert die Anzahl der Variablen in unserem Haupt-Mixin auf vier.
$baseFontSize: 16;
$gridRowHeight: 4;
@mixin basekick($typeSizeModifier, $typeRowSpan, $descenderHeightScale, $capHeight) {
/* Same as above */
}
Wir wissen auch, dass die Variablen auf Schriftartebene spezifisch für ihre jeweilige Schriftfamilie sind. Das bedeutet, es wäre einfach genug, ein übergeordnetes Mixin zu erstellen, das diese als Konstanten festlegt.
@mixin Lato($typeSizeModifier, $typeRowSpan) {
$latoDescenderHeightScale: 0.11;
$latoCapHeight: 0.75;
@include basekick($typeSizeModifier, $typeRowSpan, $latoDescenderHeightScale, $latoCapHeight);
font-family: Lato;
}
Nun können wir auf Regelbasis das Lato-Mixin mit wenig Aufwand aufrufen.
.heading--medium {
@include Lato(2, 10);
}
Diese Ausgabe liefert uns eine Regel, die die Lato-Schriftart mit einer font-size von 32px und einer line-height von 40px mit allen relevanten Transformationen und Rändern verwendet. Dies ermöglicht es uns, einfache Stilregeln zu schreiben und die Gitterkonsistenz zu nutzen, an die sich Designer bei der Verwendung von Tools wie Sketch und Figma gewöhnt sind.
Als Ergebnis können wir einfach pixelgenaue Designs ohne viel Aufwand erstellen. Sehen Sie, wie gut das Beispiel mit unserem Basis-4px-Raster unten übereinstimmt. (Sie müssen wahrscheinlich hineinzoomen, um das Raster zu sehen.)
Dies gibt uns eine einzigartige Superkraft, wenn es darum geht, Layouts auf unseren Websites zu erstellen: Wir können zum ersten Mal in der Geschichte tatsächlich pixelgenaue Seiten erstellen. Kombinieren Sie diese Technik mit einigen grundlegenden Layoutkomponenten, und wir können Seiten genauso erstellen, wie wir es in einem Designtool tun würden.
Hin zu einem Standard
Während die Vermittlung von CSS, sich mehr wie unsere Designtools zu verhalten, etwas Mühe kostet, gibt es möglicherweise gute Nachrichten am Horizont. Eine Ergänzung zur CSS-Spezifikation wurde vorgeschlagen, um dieses Verhalten nativ umzuschalten. Der Vorschlag würde, wie er derzeit aussieht, eine zusätzliche Eigenschaft für Textelemente hinzufügen, ähnlich wie line-height-trim oder leading-trim.
Eines der erstaunlichen Dinge an Web-Sprachen ist, dass wir alle die Möglichkeit haben, uns zu beteiligen. Wenn dies eine Funktion ist, die Sie als Teil von CSS sehen möchten, können Sie in diesem Thread einen Kommentar hinterlassen, um Ihre Stimme Gehör zu verschaffen.
Sehr interessante Technik! Enthaltene Elemente benötigen oben/unten-Abstand, um sicherzustellen, dass der Text nicht überläuft.
Sind Ihnen Nachteile bekannt? D.h. Leistungseinbußen durch die zusätzlichen Stile, die auf eine textlastige Seite angewendet werden?
Interessanter Ansatz, gibt es eine Vorstellung davon, wie sehr die Anpassungen die Leistung beeinflussen? Ich glaube, das würde zusätzliche Layoutzeit erfordern?
Ich habe versucht, die Zeilenhöhe einfacher zu gestalten, indem ich einfach für einen konsistenten vertikalen Rhythmus gesorgt habe.
https://medium.com/creative-technology-concepts-code/css-vertical-rhythm-with-typography-units-e2c3482a4ac0
Ich war mir nicht wirklich sicher, was dieser Artikel erreichen wollte, und wurde ziemlich gelangweilt.
Aber mein Interesse an Typografie im Web hat mich weitergebracht…
Oh Junge! Gott sei Dank habe ich das getan!
Das hat mich buchstäblich umgehauen! Bravo!
Die Zukunft ist ein Illustrator(tm) für die Gestaltung von Webseiten, mit einer JavaScript- und Sass-Engine!
Heiliger Bimbam!
Dies ist ein sehr detaillierter Artikel, und es ist klar, dass Sie viel Arbeit hineingesteckt haben.
Ich würde Sie ermutigen, das Mixin zu refaktorieren, um einheitenlose Werte für die Zeilenhöhe und relative Einheiten für die Schriftgröße zu verwenden. Sie können denselben Gitter-Lockup erreichen, indem Sie ein Verhältnis mit demselben Ansatz wie bei Ihrer Mixin-Logik festlegen.
Der Grund für diesen Vorschlag ist, dass absolute Werte, insbesondere für Zeilenhöhen, problematisch für Menschen sein können, die zoomen oder ihre Schriftgröße ändern, um sie lesen zu können. Durch die Beibehaltung von Proportionen in der ausgegebenen CSS gewährleisten Sie eine größere Bandbreite der Unterstützung für den Browser-Modus und den Kontext.
Wenn jemand interessiert ist, hier ist ein Vanilla-CSS-Ansatz zur Ausrichtung von Text auf einem Grundlinienraster.
Im Mixin:
padding-top: $preventCollapse;Im Ergebnis:
padding-top: 1;— ungültiger Eigenschaftswert)Ist diese Regel notwendig? Wenn diese Regel (und angewendet) ist, erscheint ein Pixel-Parasit zwischen den Kopfzeilen :(
Toller Artikel! Danke dafür, ich werde ihn auf jeden Fall in aktuellen und zukünftigen Projekten verwenden. Er ähnelt stark dem Line-Height-Crop von Codyhouse (https://codyhouse.co/ds/globals/typography) und dem Text-Crop-Tool von Eightshapes (http://text-crop.eightshapes.com/?typeface-selection=custom-font&typeface=Lato&custom-typeface-name=IBM%20Plex%20Serif&custom-typeface-url=http%3A%2F%2Fwww.paulrand.design%2Fhome%2Fdlewand691%2Fwww.paulrand.design%2Fnode_modules%2F%40ibm%2Fplex%2FIBM-Plex-Serif%2Ffonts%2Fsplit%2Fwoff2%2FIBMPlexSerif-Regular-Latin1.woff2&custom-typeface-weight=400&custom-typeface-style=normal&weight-and-style=100&size=51&line-height=1.2&top-crop=13&bottom-crop=12).
Danke Daniel! Wenn Sie mehr über meinen Ansatz erfahren möchten, schauen Sie hier: https://medium.com/eightshapes-llc/cropping-away-negative-impacts-of-line-height-84d744e016ce
Es versucht nicht, wie ein Designtool zu funktionieren, es entfernt einfach den oberen und unteren Abstand von einem Textblock. Etwas anderes Ziel als Basekick, denke ich.
Das ist ein großartiger Ansatz, und das erste Mal, dass ich das auf Marks Twitter-Account gesehen habe :D
Ich habe aber eine Frage: Ist es möglich, diesen Ansatz in Figma zu replizieren?
Soweit ich sehen kann, verhalten sich Figma-Zeilenhöhen wie das Web1; daher ist es, dies in Figma zu tun, eine Art großer Hack, was den ganzen Sinn davon zunichte macht.
Wenn jemand einen solchen Workflow verwendet, würde ich gerne ein paar Hinweise bekommen, wie das Gleiche gemacht werden kann :)
Hey Palash!
Ich habe mich auch mit diesem Thema beschäftigt und bin zu demselben Schluss gekommen.
Es gibt einen Workaround dafür, aber er ist nicht perfekt. Die Lösung ist hier recht gut dokumentiert: https://uxdesign.cc/the-4px-baseline-grid-89485012dea6 und Figma hat hier einiges dazu: https://www.figma.com/best-practices/everything-you-need-to-know-about-layout-grids/baseline-grids/
Was ich inzwischen zu erkennen beginne, ist, dass man am Ende des Tages, wenn man einen Workaround in CSS und Figma hinzufügen muss, um Grundlinienraster richtig funktionieren zu lassen, sich fragt, ob die Verfolgung des Grundlinienrasters es wert ist, lol.
Was ich getan habe, ist, das vertikale Raster so einzurichten, dass es mit den Zeilenboxen in Figma funktioniert, und die Abstände anderer Elemente entsprechend anzupassen. Bis es native CSS-Unterstützung dafür gibt, erwarte ich nicht, dass Figma seine
line-height-Eigenschaft ändert.Obwohl interessant, ist es völlig nutzlos. Die Schriftsätze haben einen Grund, eine Lücke oben und unten zu lassen. Obwohl es in einer Sprache mit einem begrenzten Repertoire an Zeichen keinen Nutzen hat, gibt es Sprachen, die Zeichen mit Akzenten und anderen Anhängseln verwenden, wie z.B. Französisch: ÊÉÈ oder Ç. In meiner Muttersprache gibt es Glyphen wie ĚŠČŘŽÝÁÍÉ… Oh.. Es scheint, dass einige nicht einmal von der Schriftart unterstützt werden. Sehen Sie das kleine Ding über dem S, um ein Š zu machen? Nun, dieser Akzent sollte auch über dem Ě, Č und Ř stehen.
Dass es den Anwendungsfall von Akzenten in Großbuchstaben nicht abdeckt, macht es kaum nutzlos :) Es könnte eine Korrektur gebrauchen, sicher. Vielleicht einfach $capHeight erhöhen?
Autor von Megatype hier. Wir haben vor ein paar Jahren ein ähnliches Problem gelöst: das Messen von typografischen Abständen von der Grundlinie bis zur Versalhöhe der folgenden Zeile. Ein wesentlicher Unterschied, den ich bemerkte, war, dass wir, wo Sie eine Unterlängen-Größe angeben, einfach einen y-Offset verwendet haben, um Schriftarten mit langen Unterlängen auf der y-Achse zu verschieben. Ich denke, Ihr Ansatz ist ausgefeilter.
Allerdings gab es – aus der Erinnerung – ein Problem, das uns zum Stillstand brachte, und das war die Entdeckung, dass es – häufiger als man erwarten würde – Schriftarten gibt, die schlecht eingestellte Metriken haben. Das kann bedeuten, dass sie unter Windows anders gerendert werden als unter Mac, da diese manchmal völlig unterschiedliche Metriken betrachten, und es ist schwer zu sehen, wie das nur mit CSS gelöst werden könnte. Für uns war es, dass eine Website auf Mac wunderschön aussah, aber auf Windows bewegte sich eine Schriftart um ein paar Pixel nach unten, während sich eine andere um ein paar Pixel nach oben bewegte. Verdammt…
Ich kam schließlich zu einer enttäuschenden Schlussfolgerung: Es kann nicht zuverlässig in CSS gemacht werden. Nicht ohne JS und nicht ohne bessere native CSS-typografische Eigenschaften (schauen Sie sich die ch- und ex-Einheiten an – es gibt auch Vorschläge für eine native Versalhöheneinheit, zu der ich beigetragen habe). Aber das war vor ein paar Jahren und ich bin bei weitem kein Experte. Es wird exponentiell komplexer, sobald Schriftmetrik-Tabellen Teil der Lösung sein müssen.
Mehr dazu, was wir taten:
https://medium.com/@tbredin/a-jolly-web-typesetting-adventure-42948ab0d1dd
Ich glaube, die Definition von Durchschuss ist falsch. Es ist der zusätzliche Abstand zwischen den Zeilen, also zwischen Unterlänge und Oberlänge. Beim manuellen Schriftsatz wurde dies durch Einfügen eines Bleistreifens zwischen jede Zeile erreicht.
Von Wikipedia: „Beim Handsatz ist der Durchschuss die dünnen Bleistreifen, die zwischen die Schriftzeilen im Setzkasten eingelegt wurden, um den vertikalen Abstand zwischen ihnen zu vergrößern. Die Dicke des Streifens wird als Durchschuss bezeichnet und entspricht der Differenz zwischen der Größe der Schrift und dem Abstand von einer Grundlinie zur nächsten. Wenn beispielsweise eine Schriftgröße von 10 Punkten und ein Abstand von 12 Punkten zwischen den Grundlinien gegeben ist, beträgt der Durchschuss 2 Punkte.“
Toller Artikel, meine Empfehlung wäre, eine Grafik zur Berechnung von Unterlängen und Versalhöhen einzufügen.
Ich besuche diese Technik noch einmal. Wie wäre es mit variablen Elementen wie Bildern? Wie können diese angepasst werden, um immer zur Grundlinie zu passen?
Eine
font-family-Definition enthält normalerweise mehrere Schriftarten und einen generischen Schriftartnamen als letzte Option, wie zum Beispiel:Wenn die verwendeten Schriftarten unterschiedliche Metriken haben, schlagen die Werte dieser Technik wahrscheinlich fehl, wenn der Client nicht die erste aus der Liste hat.
Ich denke, es wäre schön, CSS-Eigenschaften zu haben, die auf den intrinsischen Metriken von Schriftarten basieren, zum Beispiel, um eine Höhe basierend auf der Grundlinie festzulegen. Oder leere Pixel zu trimmen, wenn
text-justify:inter-characterverwendet wird.Gott segne dich.
Eine Folgefrage zu all dem: Wie geht man mit den horizontalen Ausrichtungsproblemen um, die man oft hat, wenn man versucht, 2 oder mehr Textzeilen auszurichten?
Eines Ihrer Beispiele oben illustriert dies perfekt. Screenshot hier: https://imgur.com/a/mt5jHEI
Wie stellen wir sicher, dass der erste Buchstabe der ersten Zeile immer mit dem ersten Buchstaben der zweiten Zeile ausgerichtet ist, unabhängig davon, mit welchem Buchstaben jeder beginnt?
Für mich scheint der einzige Weg, dies zu tun, mit Margin oder Translate-Offsets zu sein, die pro Zeichen bestimmt werden müssen. Mit anderen Worten, Sie können die erste Zeile für das 'H' um -3px oder was auch immer verschieben, aber dann funktioniert es nicht unbedingt für ein 'D'.
Mache ich Sinn? Jemand bitte helfen! Ich bin in typografischer Hölle hier. Vielen Dank!