Zum Artikel springen

Typografie Veröffentlicht Lesezeit 11 Min.

Arabisch hat keine x-Höhe: size-adjust aus den Konturen messen

CSS hat genau einen ehrlichen Hebel, um eine arabische Schrift optisch so groß dastehen zu lassen wie eine lateinische, und er braucht eine Zahl, die dir keine Plattform ausrechnet. Hier steht, woher diese Zahl auf dieser Seite kam, warum ein Wert am Ende nicht reichte und warum die Eigenschaft, die für genau diese Aufgabe gebaut wurde, sie nicht erledigen kann.

Kurz gesagt

Arabisch wirkt bei gleicher font-size kleiner als Latein, weil font-size das Geviert setzt und die beiden Schriftsysteme es unterschiedlich füllen: Quicksand zeichnet sein kleines x auf 0.516 seines Gevierts, Zain seinen Zahn — die Höhe, auf der ein arabischer Leser Fließtext liest — auf 0.4475. CSS hat dafür genau einen ehrlichen Hebel, den Deskriptor size-adjust in @font-face, und der braucht eine Zahl, die du selbst aus den Konturen messen musst. Auf dieser Seite ergab diese Messung zwei Zahlen statt einer: 112 Prozent für die Fließtextfamilie, angeglichen auf Zahnhöhe, und 103 Prozent für die Displayfamilie, angeglichen auf Alefhöhe. Das Verhältnis von Zahn zu Alef gehört dem jeweiligen Entwurf und nicht dem Schriftsystem, ein einziger gemeinsamer Wert hätte also eine der beiden Rollen um 7.8 bis 9.0 Prozent danebenliegen lassen.

Was du mitnimmst

  1. Arabisch hat keine x-Höhe, aber messbare Metriken: den Zahn für Fließtext und das Alef für Display, beide als Bruchteil des eigenen Gevierts der Schrift gelesen.
  2. In Cairo sind Zahn, Schlaufe, Auge und das lateinische kleine x auf exakt 0.5000em gezeichnet — wer die Schrift entworfen hat, hat den Zahn längst als x-Höhe behandelt, und die Konturen belegen es.
  3. Das Verhältnis von Zahn zu Alef gehört dem Entwurf und nicht dem Schriftsystem, und deshalb brauchte der Tausch einer arabischen Schrift gegen eine andere zwei size-adjust-Werte, 112 Prozent und 103.
  4. font-size-adjust kann diese Aufgabe nicht: jede Metrik, die es anbietet, ist an einer lateinischen oder einer CJK-Glyphe definiert, laut Spezifikation hebt es den Deskriptor size-adjust auf, und das OS/2-Feld, das es liest, liegt in der ausgelieferten Schrift um 30.4 Prozent daneben.
  5. size-adjust skaliert auch Ascent und Descent, verschiebt also deine Zeilenboxen — und ein Override, das daneben als rohe Prozentangabe der Zeichnung steht, schießt um genau diesen Anpassungsfaktor über.
  6. Den Zahn anzugleichen ist ein rein vertikaler Abgleich: mit kontextuellem Shaping gemessen, läuft die neue Schrift rund 25 Prozent breiter, die Absätze brechen also früher um und werden höher.

src/styles/tokens/type.css:43

Warum wirkt arabischer Text bei gleicher font-size kleiner als englischer?

font-size legt die Größe von nichts fest, was ein Leser sehen kann. Es legt das Geviert fest, und wie viel von dieser Box die Zeichnung füllt, entscheidet die Schriftgestaltung — deshalb können zwei Schriften bei derselben deklarierten Größe um ein Zehntel ihrer sichtbaren Höhe auseinanderliegen, ohne dass irgendwo etwas falsch wäre. Zwischen einer lateinischen und einer arabischen Groteske ist dieser Abstand der Normalfall und nicht die Ausnahme.

Diese Seite setzt Fließtext auf dem breiten Desktop auf 17px, und die arabischen Dokumente nehmen davon 0.9 über einen Multiplikator pro Sprache, also 15.3px. Bei dieser Größe zeichnete Cairo, die Schrift, die diese Seite früher ausgeliefert hat, seinen Zahn — die kurze Senkrechte, mit der beh beginnt, und die Höhe, auf der arabischer Fließtext gelesen wird — mit 7.650px Zeichnung. Zain, unangepasst eingesetzt, zeichnet dasselbe Merkmal mit 6.847px. Kein CSS hat sich geändert; der Absatz hat schlicht ein Zehntel der Zeichnung verloren, an der ein Leser seine Größe misst.

Der Hebel, den CSS anbietet, ist der Deskriptor size-adjust in @font-face: ein Multiplikator auf die ganze Schrift, Konturen und Metriken zusammen, damit zwei Schriften bei einer deklarierten font-size zur Deckung kommen. MDN führt ihn seit September 2023 als Baseline, breit verfügbar, und caniuse setzt ihn bei 93.58 Prozent der Nutzer an. Was dir keiner von beiden gibt, ist die Zahl, die hineingehört.

  • 7.650 px Cairo, Zahn gezeichnet die Höhe, auf die angeglichen wird, bei 15.3px
  • 6.847 px Zain, Zahn unangepasst 10.5 Prozent weniger Zeichnung als Cairo
  • 7.668 px Zain, Zahn bei 112 Prozent 0.24 Prozent über dem Ziel
  • 8.263 px Quicksand, x-Höhe gezeichnet die lateinische Spalte, bei 17px
Höhe der Zeichnung bei den ausgelieferten Desktop-Größen, hergeleitet aus den gemessenen Konturen und den Token-Werten in src/styles/tokens/type.css. Arithmetik, keine Browser-Messung.

Jede dieser vier Zahlen ist Arithmetik über eine Glyphen-Bounding-Box und einen Token-Wert und kein Screenshot, und genau darum geht es bei dieser Methode: sie läuft, bevor die Schrift ausgeliefert wird, und sie läuft wieder an dem Tag, an dem die Schrift wechselt.

Dieselben Konturen bestimmen die line-height, auf der diese Absätze sitzen, und diese Zahl ist gemessen statt gewählt — den echten Boden berechnen.

fontTools 4.60.2 · five faces

Was ersetzt die x-Höhe, wenn ein Schriftsystem kein x hat?

Arabisch hat kein x und keine Versalien, und die Standardreferenz ist deutlich, was das Ausleihen dieser Wörter angeht: benutzt nicht x-Höhe und Versalhöhe, schreibt Azza Alameddine, schlicht weil es im Arabischen kein x und keine Versalien gibt. Was Arabisch stattdessen hat, ist der Zahn, die kurze Senkrechte, mit der beh und teh beginnen; die Schlaufe, der Bauch von heh und waw; das Auge, der Kopf von ain; und das Alef, eine schlichte Senkrechte, die die Oberlängenlinie setzt.

Diese Merkmale über die fünf arabischen Schnitte zu messen, die diese Seite ausgeliefert hat, brachte etwas zutage, das im Stylesheet nicht festgehalten ist. In Cairo sind Zahn, Schlaufe, Auge und das lateinische kleine x auf exakt dieselbe Linie gezeichnet — 0.5000em im Regular und 0.5010em im SemiBold, auf die Designeinheit genau. Wer eine Familie für zwei Schriftsysteme entwirft, hatte diese Gleichung längst in die Konturen geschrieben. CSS hat keine Eigenschaft, die sie wieder auslesen kann.

Eine Zahnhöhe aus einer Schrift auslesen. Die letzten beiden Zeilen sind die, die man auf jeder Schrift laufen lassen sollte, der man gleich vertrauen will.
from fontTools.ttLib import TTFont
from fontTools.pens.boundsPen import BoundsPen
 
f    = TTFont('zain/regular.woff2')
upm  = f['head'].unitsPerEm       # 800 bei Zain, 1000 bei Cairo
gs   = f.getGlyphSet()
name = f.getBestCmap()[0x0628]    # beh — der Zahn
 
pen = BoundsPen(gs)
gs[name].draw(pen)
print(pen.bounds[3] / upm)        # 0.4475
 
# und was man in dieser Schrift nicht glauben darf:
print(f['OS/2'].sxHeight   / upm) # 0.600 — gezeichnetes x endet bei 0.460
print(f['OS/2'].sCapHeight / upm) # 0.874 — gezeichnetes H endet bei 0.695
ymax der Glyphen-Bounding-Box als Bruchteil des Gevierts der jeweiligen Schrift, gelesen mit fontTools 4.60.2. Cairos Zeile ist der Fund: Zahn, Schlaufe und lateinisches x sind in beiden Schnitten eine Zahl.
Schrift tooth ب loop و Latein x
Cairo Regular 0.5000 0.5000 0.5000
Cairo SemiBold 0.5010 0.5010 0.5010
Noto Kufi Bold 0.4950 0.4950 0.5460
Zain Regular 0.4475 0.4713 0.4600
Zain Bold 0.4575 0.4800 0.4700

Noto Kufi ist das Gegenbeispiel. Sein Zahn liegt bei 0.4950 und sein lateinisches x bei 0.5460, 10.3 Prozent auseinander, ein arabisches Wort und ein lateinisches daneben standen also nie auf derselben Mittellinie. Zain landet zwischen beiden: sein lateinisches x steht 2.8 Prozent über seinem Zahn, was nahe an der Harmonie zwischen Latein und Arabisch liegt, die seine README behauptet, und nicht dasselbe ist wie Cairos exakte Deckung.

src/styles/locale/arabic.css:41-56

Warum braucht ein arabischer Entwurf zwei size-adjust-Werte?

Cairo und Noto Kufi waren zwei Entwürfe, eine Display-Überschrift und der Absatz darunter konnten sich also in der Schrift unterscheiden. Zain ist ein Entwurf, und zwei Familien zu einer zusammenzuziehen lässt nur Gewicht und Metriken als Hebel übrig — und genau da hört das Verhältnis von Zahn zu Alef auf, eine Randnotiz zu sein, und wird zur Einschränkung.

Dieses Verhältnis ist eine Eigenschaft des Entwurfs und nicht des Schriftsystems. Cairo bewegt sich zwischen seinen beiden Schnitten nur von 0.697 auf 0.702, sieben Tausendstel, innerhalb von Cairo hätte also ein einziger Multiplikator beide Rollen bedient. Zains Werte sind 0.644 und 0.658. Einen Entwurf mit abweichendem Verhältnis einzusetzen ist das, was zwei Werte erzwingt, und keine Sorgfalt im CSS kann sie wieder zu einem zusammenfalten.

Zahn und Alef als Bruchteile des Gevierts, festgehalten in src/styles/locale/arabic.css:41-46 und für diesen Text unabhängig aus den .woff2-Dateien reproduziert. Jede Stelle stimmt überein.
Schrift tooth ب alef ا Zahn zu Alef
Cairo Regular 50.000% 71.700% 0.697
Cairo SemiBold 50.100% 71.400% 0.702
Zain Regular 44.750% 69.500% 0.644
Zain Bold 45.750% 69.500% 0.658
Noto Kufi Bold 49.500% 76.000% 0.651

Fließtext wird auf Zahnhöhe gelesen, so wie lateinischer Fließtext auf x-Höhe, also gleicht die Fließtextfamilie Zains Zahn an Cairos an: 0.500 geteilt durch 0.4475 ist 1.117, und die Deklaration sagt 112 Prozent. Display wird auf Alefhöhe gelesen — jedes plakative Wort auf dieser Seite ist ein Wort aus Senkrechten —, also gleicht die Displayfamilie Zains Alef an das von Cairo SemiBold an: 0.714 geteilt durch 0.695 ist 1.027, und die Deklaration sagt 103 Prozent.

Diese beiden Antworten liegen 8.76 Prozent auseinander, und diese Spanne ist das ganze Argument. Nimm 112 Prozent auch für Display, und ein arabisches plakatives Wort rendert 9.02 Prozent höher als das Cairo-Alef, an das es angeglichen ist. Nimm 103 Prozent auch für Fließtext, und der Zahn fällt 7.82 Prozent zu kurz aus. Einen Wert zu wählen ist keine Vereinfachung; es ist die Wahl, bei welcher der beiden Rollen man falsch liegen will.

Es gibt außerdem eine konkurrierende Konvention darüber, welche Metrik überhaupt anzugleichen ist. Edo Smitshuijzen gleicht in seinem Text über Proportionen in Zweischrift-Fonts die lateinische x-Höhe an die Höhe der Schlaufe an statt an den Zahn und verlangt vom Entwurf, den Abstand zwischen beiden zu verkleinern. In Cairo ist das Argument gegenstandslos, weil Zahn, Schlaufe und lateinisches x dieselben 0.500em sind. In Zain gehen die beiden Regeln auseinander: der Zahn ergibt 112 Prozent, die waw-Schlaufe 106. Diese Seite ist dem Zahn gefolgt, weil die Schrift, die sie nachbildet, längst alle drei gleichgesetzt hatte.

Zwei arabische Familien durch eine zu ersetzen hat auch verändert, was mit den lateinischen Passagen innerhalb einer arabischen Seite passiert — das unicode-range, das eine Schrift vom Lateinischen fernhält.

OS/2 sxHeight 480 · upm 800

Warum nicht font-size-adjust nehmen, die Eigenschaft für genau das?

CSS liefert eine Eigenschaft für genau dieses Problem. font-size-adjust normalisiert eine Schrift gegen eine andere über das Verhältnis einer benannten Metrik, und der gängige Rat, wie man ihren Wert findet, lautet: setz ein kleines x in beiden Schriften und schieb, bis sie übereinstimmen. Dieser Rat ist durch und durch lateinisch, und die Eigenschaft kann diese Aufgabe aus drei voneinander unabhängigen Gründen nicht erledigen — von denen jeder für sich reichen würde.

Fang mit dem an, der am leichtesten zu sehen ist. Die Wahl der Metrik ist die Wahl der Antwort, und die Eigenschaft bietet keinen Weg, nach der Zeile zu fragen, die eine arabische Schrift braucht.

die heh-Schlaufe 87%
das lateinische Versal-H 99%
das Alef — ausgeliefert für Display 103%
die waw-Schlaufe und das Auge 106%
das lateinische x 109%
der Zahn — ausgeliefert für Fließtext 112%
Gleich Zain auf einer anderen Metrik an die Mittellinie von Cairo Regular bei 0.500em an, und du bekommst einen anderen Multiplikator. Ein Schriftpaar, sechs Metriken, 25 Punkte Spanne.Die Balken sind auf den größten Wert skaliert, 112 Prozent.
Es gibt keine arabische Metrik
Die Standardmetrik ist ex-height, und CSS Values definiert die Einheit ex als die verwendete x-Höhe der ersten verfügbaren Schrift, so genannt, weil sie oft der Höhe des kleinen x entspricht. Die Form mit zwei Werten ergänzt cap-height, ch-width, ic-width und ic-height. Alle fünf sind an einer lateinischen oder einer CJK-Referenzglyphe definiert, und eine arabische Option ist nicht darunter.
Das Feld ist in der ausgelieferten Schrift falsch
Zain trägt OS/2 sxHeight 480 und sCapHeight 699 in einem Geviert aus 800 Einheiten ein — 0.600em und 0.874em — während sein gezeichnetes x bei 0.460em endet und sein gezeichnetes H bei 0.695em, 30.4 und 25.7 Prozent daneben. Ein from-font auf dieser Schrift gäbe 0.600 zurück: nicht den Zahn, nicht die Schlaufe, nicht einmal die lateinische x-Höhe dieser Schrift selbst. Die Werte sehen aus wie Zahlen, die in einem Geviert aus 1000 Einheiten geschrieben und nie umgerechnet wurden, aber das ist eine Vermutung über die Ursache; gemessen ist nur die Abweichung.
Die beiden lassen sich nicht kombinieren
CSS Fonts 5 sagt, dass font-size-adjust nach dem Deskriptor size-adjust angewendet wird, mit der Folge, dass size-adjust ohne Wirkung erscheint. Sie sind Alternativen und kein Paar für progressive Verbesserung, und ein Stylesheet, das beides setzt, hat eines davon stillschweigend weggeworfen.
Drei Gründe, warum font-size-adjust die Zahlen oben nicht ausdrücken kann.

Die Verbreitung ist das kleinste der drei Probleme, und die beiden Stellen, an denen du nachschlägst, widersprechen sich. caniuse gibt font-size-adjust global 88.02 Prozent mit voller Unterstützung ab Chrome und Edge 127; MDN trennt die Syntaxen, setzt die Form mit Metrik-Schlüsselwort auf Chrome und Edge 129, Firefox 129 und Safari 18 und führt die Eigenschaft als Baseline 2024. Beides gelesen am 26. August 2026. Der Deskriptor ist dagegen seit drei Jahren breit verfügbar.

Die Eigenschaft, die für den Größenabgleich zwischen Schriften entworfen wurde, hängt also an einem Feld, das gegen einen Buchstaben definiert ist, den Arabisch nicht hat — und in der Schrift, die diese Seite tatsächlich ausliefert, liegt dieses Feld um fast ein Drittel daneben. Der grobe Multiplikator pro Schrift ist das einzige Werkzeug der Plattform, das eine Messung tragen kann, für die CSS kein Vokabular hat.

hhea 1303/-571 against 869/-459

Warum die arabischen Schriften kein ascent-override tragen

Die lateinische Seite dieser Website deklariert auf jeder Schrift ein size-adjust, ein ascent-override und ein descent-override — drei synthetische Familien über vier physische Dateien, und zwei dieser Familien zeigen auf dieselbe .woff2 und deklarieren dabei verschiedene Metriken. Das arabische Stylesheet deklariert nur das size-adjust. Das ist die eine Stelle, an der die beiden Dateien absichtlich nicht übereinstimmen.

Die lateinischen Overrides gibt es, damit ein Quicksand-Tausch nicht gegen Arial neu umbricht. Arabisch kann sich dasselbe nicht kaufen, denn weder Quicksand noch Arial trägt überhaupt Arabisch: der echte Fallback ist irgendein Naskh, den die Plattform gerade mitbringt, und es gibt nichts Stabiles, woran man sich hängen könnte. Und dann sagt die Arithmetik, dass die Overrides ohnehin fast nichts gebracht hätten.

  • 1.87400 Cairo Content-Box, em Ascent 1303, Descent -571, upm 1000
  • 1.85920 Zain bei 112 Prozent, em 869 und -459 in einem Geviert aus 800 Einheiten
  • 0.79% der Abstand dazwischen was ein Override korrigiert hätte
Content-Boxen aus der hhea-Tabelle jeder Familie, aus den Font-Binärdateien gelesen. Beide Familien setzen USE_TYPO_METRICS mit sTypo-Werten, die mit hhea identisch sind, ein Browser liest also so oder so dasselbe Paar.

Was die Overrides sonst gebracht hätten — eine Zeilenbox, gegen die die bestehenden line-heights schon abgestimmt waren —, liefert das size-adjust von selbst, weil es Ascent und Descent der Schrift zusammen mit den Konturen skaliert.

Der Plan, mit dem diese Phase begann, schrieb diese Overrides als rohe Prozentangaben der Zeichnung neben das size-adjust, und er wäre um genau 12 Prozent falsch gewesen. Das Stylesheet hält diese Zahl fest und gibt den falschen Mechanismus dafür an: es sagt, das Override wäre nicht skaliert worden, weil die Anpassung zuerst greife und das Override das Ergebnis ersetze. CSS Fonts 5 sagt das Gegenteil — alle Metriken, die zu dieser Schrift gehören, einschließlich der über @font-face-Deskriptoren gesetzten Overrides, werden mit dem angegebenen Prozentsatz skaliert.

Die 12 Prozent überleben die Korrektur, aus dem spiegelbildlichen Grund. Ein ascent-override von 130.3 Prozent und ein descent-override von 57.1 Prozent, also Cairos Box als Zeichnung ausgedrückt, ergeben mit einem size-adjust von 112 Prozent multipliziert 2.0989em, wo 1.874em gemeint waren. Zwei unabhängige Wege kommen auf dieselbe Zahl, die Entscheidung war also so oder so richtig — die Begründung, die ein späterer Leser aus dieser Datei mitnimmt, aber nicht.

GSUB advances · four strings

Behalten die Absätze ihre Umbruchpunkte, wenn der Zahn passt?

Das Stylesheet sagt ja, und das Übergabedokument sagt es zweimal: jeder arabische Absatz behält die Satzbreite, die Umbruchpunkte und die Zeilenzahl, die er heute hat. Für diesen Text nachgemessen, zeigt alles in die andere Richtung, und die Behauptung wird von nichts gestützt, was im Repository festgehalten ist.

Einen Zahn an einen Zahn anzugleichen ist eine vertikale Operation. Die Dickte ist eine eigene Größe, die kein vertikaler Abgleich steuert, und Zain ist ein breiterer Entwurf als Cairo. Löst man die kontextuellen Formen über die Einzelsubstitutionen initial, medial, final und isoliert der jeweils eigenen GSUB-Tabelle auf — 91 Substitutionen im Testabsatz, in keiner der beiden Schriften eine fehlende Glyphe — und summiert die Dickten, ergeben sich die Zahlen unten.

Geshapte Dickten in em für vier Strings, die diese Seite ausliefert. Nur Einzelsubstitutionen: keine Ligatur-Lookups, kein Kerning, kein Mark-Positioning und kein HarfBuzz, das sind also Summen kontextueller Dickten und keine fertig gerenderten Zeilenlängen.
String Cairo Regular Zain bei 112% gegen Cairo
Absatz im Stack-Abschnitt, 143 Zeichen 63.3040 79.0832 +24.93%
Titel der Journey-Karte 04, 38 Zeichen 16.6920 20.8656 +25.00%
Überschrift Projekte, 13 Zeichen 5.8590 7.3878 +26.09%
Überschrift Code und Design, 14 Zeichen 6.0130 7.4872 +24.52%

Die Zeilen werden länger, die Umbruchpunkte wandern und die Absätze werden höher. Die optische Entscheidung überlebt die Korrektur — den Zahn anzugleichen ist weiterhin der richtige Weg, arabischen Fließtext gegen eine lateinische Schrift zu bemessen —, die Folge aber, die dafür behauptet wurde, nicht, und ein Rebuild, der sein arabisches Layout auf unveränderten Umbruchpunkten kalkuliert hätte, wäre spät überrascht worden.

Die Messung hat ehrliche Grenzen. Der Testabsatz enthält keine Lam-Alef-Paare, was für diesen einen String den größten Ligatur-Störfaktor entfernt; nichts hier wurde von einem Browser gerendert. Es ist fontTools, das die Substitutionstabellen jeder Schrift selbst liest, was genügt, um Richtung und Größenordnung festzustellen, und nicht genügt, um vorherzusagen, wo eine bestimmte Zeile umbricht.

Eine Zeile ist eine Funktion aufgelöster Font-Metriken, und deshalb kann auch ein Splitter für Animationen nicht laufen, bevor die Schriften da sind — Text in einer verbundenen Schrift zerlegen.

src/styles/locale/arabic.css:286-326

Was ein size-adjust alles hinter sich herzieht

Ein Metrik-Multiplikator ist nie lokal. Weil size-adjust Ascent und Descent der Schrift zusammen mit den Konturen skaliert, ist Zains Content-Box bei 112 Prozent 1.859em hoch — jedes Element mit line-height 1 hat also negativen halben Durchschuss, und seine Zeichnung hängt in beide Richtungen aus der eigenen Zeilenbox heraus.

Der Button-Roll-up auf dieser Seite beschneidet sein Label mit einem Fenster, das bei 94 Prozent dieser Box geschnitten ist, was für Quicksand bequem ist und für Zain nicht. An den Konturen von Zain Bold gemessen, reicht das finale yeh bis -0.46625em und ain bis -0.41380em; bei 112 Prozent sind das -0.5222em und -0.4635em unter der Grundlinie einer 1em-Box. Das Fenster hat dem Wort die Punkte unten abgeschnitten, und der arabische Kontaktlink im Golden Master hat einen anderen Buchstaben gerendert — ein Schreibfehler in den eigenen Worten des Inhabers, in einem Linktext, in Produktion.

Drei Zahlen, die sich zusammen bewegen müssen, unter einer Invariante: Zwilling unter Fenster, Fenster unter Zeichnung.
/* src/styles/locale/arabic.css, gekürzt */
html[lang='ar'] :is(.btn-text, .text-eyebrow) {
  --ar-label-lh: 1.55;   /* war 1, und 1 hat beschnitten */
  --text-offset: 2em;    /* wo die Zeichnung des Zwillings beginnt */
}
 
html[lang='ar'] .text-clip-w {
  /* Latein schneidet bei 94%; Zains Unterlängen brauchen 114% */
  clip-path: polygon(0 -2%, 0 114%, 100% 114%, 100% -2%);
}
 
/* das Fenster bei 114% einer 1.55em-Box reicht bis 1.767em,
   und die Zeichnung des Zwillings beginnt bei 1.845em. */

Der Browser-Testlauf, der die arabischen Routen absichert, kann diese Klasse von Fehlern nicht sehen. Er vergleicht die Bounding Boxen aufeinanderfolgender Zeilenelemente bei vier Viewports, und zwei arabische Zeilen kollidieren Zeichnung gegen Zeichnung, nie Box gegen Box — die Herleitung mit fontTools ist also der stärkere Test und der Browserlauf der schwächere. Es andersherum zu sagen, verkehrt den ganzen Sinn des Messens an den Konturen.

Eine beschnittene Unterlänge ist einer der Fehler, an die logische Eigenschaften von allein nicht heranreichen — sieben Fehler in rechtsläufigem Layout.

@layer utilities · order.json:88

Wie man ein size-adjust herleitet, das man verteidigen kann

Keine der Zahlen oben lässt sich übertragen. Sie sind Zain gegen Cairo, bei den Größen einer einzigen Seite, für zwei Rollen, die es auf dieser Seite zufällig gibt. Übertragbar ist die Methode, und sie ist sieben Schritte lang — die ersten drei sind dieser Text, und die letzten vier haben dieses Repository je einen Fehler gekostet.

  1. Benenne die Rolle und ihre Glyphe Entscheide laut, an welchem Merkmal jede Rolle gelesen wird: Fließtext am Zahn, Display am Alef. Schreib den Grund ins Stylesheet, denn er ist der einzige Teil davon, den ein späterer Leser nicht aus den Deklarationen selbst zurückgewinnen kann.
  2. Miss ymax, nicht die OS/2-Tabelle Lies die Glyphen-Bounding-Box in der weichenden und in der kommenden Schrift und teile jede durch ihr eigenes unitsPerEm. Nimm nie 1000 an: Zains Geviert hat 800 Einheiten, und jedes Verhältnis hier liegt um ein Viertel daneben, wenn du durch die falsche Zahl teilst.
  3. Teile die weichende Höhe durch die kommende Dieser Quotient ist das size-adjust. 0.500 durch 0.4475 ist 1.117, ausgeliefert als 112 Prozent. Runde einen gemessenen Quotienten nicht auf einen runderen, bevor du ausgerechnet hast, was das Runden an Zeichnung kostet.
  4. Eine synthetische Familie pro Rolle Deklariere für jedes size-adjust eine eigene Familie, auch wenn zwei davon auf dieselbe Datei zeigen. Zwei Familien über einer .woff2 sind keine Redundanz — die Deskriptoren sind das, was eine Zeilenbox so hoch macht, wie sie ist, und sie zusammenzulegen ändert stillschweigend eine der Rollen.
  5. Lass die Overrides weg, oder gib sie in den Maßen der kommenden Schrift an Ein ascent-override, das als die Prozentzahl der gewünschten Zeichnung geschrieben ist, wird mit dem size-adjust multipliziert und schießt um genau diesen Faktor über. Wenn die kommende Schrift angepasst ohnehin nahe an der weichenden Box landet, ist der ehrliche Schritt, gar kein Override zu deklarieren.
  6. Leite danach jede line-height neu her Die Anpassung hat Ascent und Descent verschoben, also ist jeder Boden, gegen den die Typografie abgestimmt war, mitgewandert. Alles, was beschneidet, maskiert oder eine Höhe festschreibt, muss im selben Durchgang neu vermessen werden, sonst landet eine Unterlänge außerhalb ihres Fensters.
  7. Leg das Locale-Stylesheet in eine späte Cascade-Layer Eine Layer schlägt Spezifität, ein Stylesheet für einen Abschnitt kann eine Locale-Regel also nicht überstimmen, so eng es auch gescopt ist. Die Ausnahme stattdessen in der Abschnittsdatei auszuliefern ist der Weg, auf dem diese Seite einen Überschriftenblock mit 130.5 Pixeln gerendert hat, wo 112.5 gemeint waren.
Das Verfahren, in der Reihenfolge, in der die Fehler passieren.

Die Grenze der Methode gehört genauso klar ausgesprochen wie die Methode selbst. Sie regelt die vertikale Größe und sonst nichts: die Dickte wandert, die Umbruchpunkte wandern, und keine einzelne CSS-Eigenschaft hält beides. Und sie ist eine Konstante pro Schrift, die ganze Herleitung läuft also an dem Tag wieder, an dem die Schrift wechselt — was sie auf dieser Seite auch getan hat.

Schritt sechs ist eine Messung für sich, und die Böden dieser Seite sind mit ihrer Arithmetik veröffentlicht — der arabische line-height-Boden.

Wenn du einen Build in mehr als einem Schriftsystem planst und die Typografie lieber vor dem Layout klären willst, beginn ein Gespräch.

Fragen

Ist arabischer Text bei gleicher font-size wirklich kleiner als lateinischer, oder sieht es nur so aus?

Er ist messbar kleiner in dem Teil des Buchstabens, an dem das Auge die Größe abliest. font-size setzt das Geviert, und die beiden Schriftsysteme füllen es unterschiedlich. Bei der Fließtextgröße dieser Seite zeichnet Quicksand sein kleines x auf 0.516 seines Gevierts, Zain seinen Zahn auf 0.4475. Das sind rund acht Prozent weniger Zeichnung bei derselben deklarierten Größe, noch vor jeder anderen Variablen. Es ist keine Täuschung und kein Rendering-Fehler; es sind zwei Schriftgestalter, die innerhalb derselben Box unterschiedlich entschieden haben.

Warum nicht einfach die arabische font-size um 10 Prozent anheben und fertig?

Weil font-size das Layout mitnimmt und size-adjust nicht. Jedes der elf Typo-Token hier ist ein clamp mal einem Multiplikator pro Sprache, diesen Multiplikator zu verschieben verschiebt also alle elf auf einmal und entwertet jede festgehaltene arabische Messung im Repository. size-adjust gilt für ein einzelnes @font-face, und genau das lässt die Fließtextfamilie 112 Prozent nehmen und die Displayfamilie 103, bei derselben deklarierten Größe. Ein Anheben der font-size kann diesen Unterschied gar nicht ausdrücken.

Kann ich stattdessen font-size-adjust nehmen?

Für Arabisch nicht. Seine Standardmetrik ist ex-height, und sowohl CSS als auch OpenType definieren die x-Höhe gegen das lateinische kleine x, einen Buchstaben, den Arabisch nicht hat; die Form mit zwei Werten ergänzt cap-height, ch-width, ic-width und ic-height, allesamt lateinische oder CJK-Referenzglyphen. In der Schrift, die diese Seite ausliefert, ist das zugrunde liegende Feld ohnehin falsch: Zain trägt OS/2 sxHeight 480 in einem Geviert aus 800 Einheiten ein, also 0.600em, während sein gezeichnetes x bei 0.460em endet. Und die Spezifikation schließt beide gegenseitig aus, da font-size-adjust nach dem Deskriptor size-adjust angewendet wird.

Brauche ich ascent-override und descent-override auch?

Nur wenn du einen stabilen Fallback hast, der den Abgleich wert ist. Die lateinischen Schriften hier tragen sie, damit ein Webfont-Tausch nicht gegen Arial neu umbricht. Arabisch hat keinen solchen Fallback, denn weder Quicksand noch Arial trägt Arabisch, der echte Fallback ist also, was die Plattform gerade mitbringt. Wenn du sie doch ergänzt, gib sie neben einem size-adjust nicht als rohe Prozentangaben der Zeichnung an: die Spezifikation skaliert @font-face-Overrides zusammen mit allem anderen, und hier wäre dieser Fehler 12 Prozent zu hoch herausgekommen.

Quellen

In diesem Repository gemessen

  • src/styles/locale/arabic.css Die Tabelle mit Zahn und Alef für alle fünf Schnitte, die Herleitung von 112 und 103 Prozent, die zwei synthetischen Familien und die Reparatur des Clip-Fensters.
  • src/assets/fonts/zain/ Die drei ausgelieferten Schnitte, aus denen jede Konturmessung in diesem Text stammt, mit ihrer SIL Open Font Licence.
  • src/styles/base/fonts.css Die lateinische Seite desselben Verfahrens: drei synthetische Familien über vier physische Dateien, jede mit eigenem size-adjust und eigenen Metrik-Overrides.
  • src/styles/tokens/type.css Die Typo-Skala aus elf Token und die zwei optischen Multiplikatoren pro Sprache, durch die jede arabische Größe läuft.

Abgeglichen mit

Alaa Abbod

Geschrieben von

Alaa Abbod

Creative Developer — Herne, Deutschland

Designer und Entwickler, der barrierefreie Websites, Mobile Apps, Onlineshops und visuelle Identitäten als eine einzige Arbeit baut, von Hand. Diese Website erscheint auf Englisch, Deutsch und Arabisch aus einer Quelle, und daher kommen die meisten dieser Fragen.

Bitte dreh dein Gerät,
Diese Seite ist hochkant gebaut.