एक लाइन में
Font fallback metrics असल में CSS के कुछ सुपरपावर्स हैं जो किसी सिस्टम फॉन्ट को आर्टिफिशियली ऐसे खींचते और सिकोड़ते हैं कि वह आपके कस्टम वेब फॉन्ट जितनी ही एग्ज़ैक्ट मैथमेटिकल स्पेस ले ले, ताकि जब आपका कस्टम फॉन्ट फाइनली लोड हो, तो पेज का लेआउट बुरी तरह से न हिले (layout shift न हो)।
यह कौन सी प्रॉब्लम सॉल्व करता है
इस प्रॉब्लम को समझने के लिए, आपको पहले वेब की सबसे पुरानी टाइपोग्राफिक जंग समझनी होगी: एक टूटे-फूटे 3G कनेक्शन पर 150KB का कस्टम वेब फॉन्ट डाउनलोड होने के इंतज़ार में हम यूज़र को क्या दिखाएं?
ऐतिहासिक रूप से, ब्राउज़र्स ने दो में से कोई एक तरीका अपनाया, और दोनों ही बकवास थे। पहला था FOIT (Flash of Invisible Text), जहाँ ब्राउज़र फॉन्ट डाउनलोड होने तक टेक्स्ट को बस हाइड कर देता था। यूज़र्स खाली स्क्रीन घूरते रहते थे, यह सोचते हुए कि क्या आपकी साइट टूट गई है। दूसरा था FOUT (Flash of Unstyled Text), जहाँ ब्राउज़र तुरंत एक लोकल सिस्टम फॉन्ट (जैसे Arial या Times New Roman) का उपयोग करके टेक्स्ट को रेंडर कर देता था, और फिर जैसे ही आपका खूबसूरत कस्टम फॉन्ट आता, वह उसे मिलीसेकंड में स्वैप कर देता था।
वेब डेवलपमेंट कम्युनिटी काफी हद तक इस बात पर सहमत हो गई कि FOUT एक्सेसिबिलिटी और पर्सीव्ड परफॉरमेंस (perceived performance) के लिए बेहतर है। आखिरकार, भद्दा टेक्स्ट पढ़ना कोई टेक्स्ट न पढ़ने से तो बेहतर ही है। हमने इसे CSS रूल font-display: swap के साथ फॉर्मलाइज़ कर दिया।
लेकिन font-display: swap ने एक नए मॉन्स्टर को जन्म दिया: Cumulative Layout Shift (CLS)।
हर फॉन्ट का अपना एक यूनीक स्ट्रक्चर (skeleton) होता है। Roboto में एक "x", Arial के "x" से फिजिकली ज़्यादा चौड़ा होता है। कैपिटल लेटर्स की हाइट अलग-अलग होती है। फॉन्ट फाइल्स में मौजूद स्पेसिंग बिलकुल अलग होती है। जब ब्राउज़र Arial को हटाकर Roboto लगाता है, तो टेक्स्ट अचानक से अलग हॉरिज़ॉन्टल और वर्टिकल स्पेस लेने लगता है। पैराग्राफ्स रिफ्लो होते हैं। टेक्स्ट नई लाइन में चला जाता है।
अगर ऐसा तब होता है जब यूज़र पहले से ही पढ़ रहा हो या किसी बटन पर क्लिक करने वाला हो, तो पूरा पेज ऐसे उछलता है जैसे DOM में कोई भूकंप आ गया हो। बटन्स यूज़र के कर्सर के नीचे से खिसक जाते हैं। इमेजेस पेज के नीचे धकेल दी जाती हैं। Google ने इसी फ्रस्ट्रेशन को नापने के लिए Core Web Vitals मेट्रिक्स पेश किए, और हाई CLS वाली साइट्स के SEO को बुरी तरह पेनलाइज़ (penalize) किया। हमें एक ऐसा तरीका चाहिए था जिससे वह भद्दा टेम्परेरी सिस्टम फॉन्ट, उस खूबसूरत कस्टम फॉन्ट के फिजिकल फुटप्रिंट की परफेक्ट कॉपी बन सके।
Under the hood यह कैसे काम करता है
लेआउट शिफ्ट को रोकने के लिए, हम कस्टम वेब फॉन्ट को नहीं बदलते। इसके बजाय, हम @font-face रूल का यूज़ करके एक लोकल सिस्टम फॉन्ट का "Frankenstein" वर्ज़न बनाते हैं। हम सिस्टम फॉन्ट की इंटरनल ज्योमेट्री (geometry) को ओवरराइड करने के लिए स्पेशलाइज़्ड CSS डिस्क्रिप्टर्स का इस्तेमाल करते हैं।
एक Font Box की एनाटॉमी
किसी फॉन्ट को मैनिपुलेट करने के लिए, आपको यह समझना होगा कि ब्राउज़र टेक्स्ट की एक लाइन के चारों ओर बॉक्स को कैसे कैलकुलेट करता है। यह सिर्फ font-size के बारे में नहीं है। यह खुद फॉन्ट फाइल में मौजूद तीन इंटरनल मेट्रिक्स पर निर्भर करता है:
- Ascent: इनविज़िबल बेसलाइन (जहाँ लेटर्स बैठते हैं) से लेकर सबसे ऊंचे कैरेक्टर्स (जैसे 'h' या 'T' का टॉप) तक की दूरी।
- Descent: बेसलाइन से लेकर सबसे नीचे लटकने वाले कैरेक्टर्स (जैसे 'y', 'g', या 'p' की पूंछ) के बॉटम तक की दूरी।
- Line Gap: एक लाइन के descent और अगली लाइन के ascent के बीच का रेकमेंडेड खाली स्पेस (जिसे अक्सर leading कहा जाता है)।
CSS में, टेक्स्ट की एक लाइन की टोटल हाइट लगभग ascent + descent + line gap होती है। अगर आपके कस्टम फॉन्ट और सिस्टम फॉन्ट में इन तीनों मेट्रिक्स की वैल्यूज़ अलग-अलग हैं, तो आपका लेआउट वर्टिकली उछलेगा। अगर लेटर्स की चौड़ाई अलग है, तो आपका लेआउट हॉरिज़ॉन्टली जंप करेगा।
एंट्री होती है Overrides की
CSS Fonts Module Level 4 ने चार मैजिकल डिस्क्रिप्टर्स पेश किए जिन्हें एक @font-face ब्लॉक के अंदर अप्लाई किया जा सकता है। ये डिस्क्रिप्टर्स ब्राउज़र द्वारा फॉन्ट फाइल की इंटरनल मेट्रिक्स की रीडिंग को इंटरसेप्ट करते हैं और उन्हें ऑन-द-फ्लाई (on the fly) ओवरराइट कर देते हैं।
size-adjust: यह पूरे फॉन्ट के लिए एक मल्टीप्लायर (multiplier) की तरह काम करता है। अगर आपका कस्टम फॉन्ट आम तौर पर Arial से ज़्यादा चौड़ा है, तो आप Arial परsize-adjust: 110%सेट कर सकते हैं ताकि इसे हॉरिज़ॉन्टली खींचा जा सके।ascent-override: फॉन्ट के इंटरनल ascent मेट्रिक को ओवरराइट करता है।descent-override: फॉन्ट के इंटरनल descent मेट्रिक को ओवरराइट करता है।line-gap-override: फॉन्ट के इंटरनल line gap मेट्रिक को ओवरराइट करता है।
मैथ्स को एक साथ जोड़ना
ट्रिक यह है कि size-adjust सब कुछ स्केल कर देता है—जिसमें हॉरिज़ॉन्टल विड्थ, ascent और descent शामिल हैं। इसलिए, अगर आप अपनी वेब फॉन्ट के साथ लाइन विड्थ मैच करने के लिए किसी सिस्टम फॉन्ट को 110% तक बढ़ाते हैं, तो आपने अनजाने में सिस्टम फॉन्ट की वर्टिकल हाइट को भी 110% लंबा कर दिया है, जिससे वर्टिकल लेआउट शिफ्ट हो जाता है!
इसे फिक्स करने के लिए, ओवरराइड्स (ascent-override, descent-override, line-gap-override) को ओरिजिनल फॉन्ट साइज़ के नहीं, बल्कि size-adjust वैल्यू के परसेंटेज के रूप में कैलकुलेट किया जाता है। आप हॉरिज़ॉन्टल विड्थ को एकदम परफेक्ट मैच करने के लिए size-adjust का यूज़ करते हैं, और फिर वर्टिकल हाइट को वापस सिकोड़ने (या खींचने) के लिए ओवरराइड मेट्रिक्स का इस्तेमाल करते हैं ताकि वह आपके कस्टम फॉन्ट के एग्ज़ैक्ट बाउंडिंग बॉक्स (bounding box) से मैच कर जाए।
नीचे देखें कि इसका कोड कैसा दिखता है। ध्यान दें कि हम Roboto-Fallback नाम की एक बिलकुल नई, इमेजिनरी फॉन्ट फैमिली कैसे बनाते हैं जो मैथमेटिकली प्रीसाइस (precise) ओवरराइड्स के साथ लोकल Arial फॉन्ट को रैप करती है:
/* 1. Load your actual web font */
@font-face {
font-family: 'Roboto';
src: url('/fonts/roboto.woff2') format('woff2');
font-display: swap;
}
/* 2. Create the Frankenstein fallback */
@font-face {
font-family: 'Roboto-Fallback';
src: local('Arial');
/* Stretch Arial horizontally to match Roboto */
size-adjust: 104.5%;
/* Squish Arial's vertical metrics to match Roboto */
ascent-override: 89.92%;
descent-override: 23.44%;
line-gap-override: 0%;
}
/* 3. Use them together in your CSS */
body {
font-family: 'Roboto', 'Roboto-Fallback', sans-serif;
}
जब पेज लोड होता है, तो ब्राउज़र तुरंत Roboto-Fallback को पेंट कर देता है। यह बस लोकल Arial है, लेकिन हॉरिज़ॉन्टली खींचा हुआ और वर्टिकली सिकुड़ा हुआ ताकि Roboto के एकदम सटिक मैथमेटिकल फुटप्रिंट से मैच कर सके। जब असली Roboto फाइल डाउनलोड हो जाती है, तो स्वैप होता है, लेटर्स की डिज़ाइन बदलती है, लेकिन लेआउट एक पिक्सेल भी नहीं खिसकता।
रियल-वर्ल्ड स्टोरीज़
द "Cancel" बटन ट्रैप
एक बड़ा ई-कॉमर्स प्लेटफ़ॉर्म कार्ट अबैंडनमेंट (cart abandonment) और भड़के हुए कस्टमर सपोर्ट टिकट्स से जूझ रहा था। यूज़र्स कंप्लेंट कर रहे थे कि वे गलती से अपने कार्ट्स खाली कर दे रहे हैं। इंजीनियरिंग टीम ने यूज़र सेशंस रिकॉर्ड किए और असली गुनहगार को ढूंढ निकाला। साइट पर एक भारी-भरकम ब्रांडेड फॉन्ट लोड होता था। यूज़र को "Empty Cart" बटन के ठीक बगल में "Checkout" बटन दिखाई देता। जैसे ही उनका माउस "Checkout" पर क्लिक करने के लिए नीचे जाता, ब्रांडेड फॉन्ट लोड हो जाता। ब्रांडेड फॉन्ट सिस्टम डिफ़ॉल्ट से काफी चौड़ा था। टेक्स्ट रिफ्लो हुआ, "Checkout" बटन दाईं ओर खिसक गया, और "Empty Cart" बटन ठीक यूज़र के माउस क्लिक के नीचे आ गया। size-adjust और मेट्रिक ओवरराइड्स को इम्प्लीमेंट करने से फॉन्ट स्वैप के दौरान बटन्स अपनी जगह पर लॉक हो गए, जिससे एक्सीडेंटल कैंसिलेशन रेट तुरंत गिर गया।
न्यूज़ आर्टिकल का भूकंप
एक पॉप्युलर टेक ब्लॉग ने एक मैसिव रिडिज़ाइन किया, जिसमें लम्बे आर्टिकल्स पढ़ने के लिए एक खूबसूरत, मॉडर्न सेरिफ (serif) फॉन्ट था। स्पीड को प्रायरिटी देने के लिए, उन्होंने font-display: swap का इस्तेमाल किया। हालाँकि, स्लो कनेक्शन वाले रीडर्स को एक बहुत ही इरिटेटिंग चीज़ का सामना करना पड़ा: वे कोई आर्टिकल लोड करते, तीन पैराग्राफ नीचे स्क्रॉल करते, और पढ़ना शुरू कर देते। दो सेकंड बाद, सेरिफ फॉन्ट लोड हो जाता। क्योंकि सेरिफ फॉन्ट का ascent और descent सिस्टम डिफ़ॉल्ट से बड़ा था, इसलिए टेक्स्ट की हर एक लाइन एक मिलीमीटर के कुछ हिस्से जितनी बड़ी हो जाती। तीन पैराग्राफ्स में, यह जुड़कर एक मैसिव वर्टिकल शिफ्ट बन गया, जिससे यूज़र जो सेंटेंस पढ़ रहा था वह पूरी तरह से स्क्रीन से बाहर हो गया। लोकल फॉलबैक फॉन्ट में एक ascent-override और descent-override ऐड करके, टेक्स्ट अपनी जगह पर परफेक्टली स्वैप हुआ, जिससे रीडर का फोकस ठीक वहीं रहा जहाँ उसे होना चाहिए था।
Lighthouse स्कोर का हार्टब्रेक
एक फ्रंटएंड डेवलपर ने Google के Core Web Vitals के लिए एक वेब एप्लिकेशन को ऑप्टिमाइज़ करने में तीन हफ़्ते बिताए। उन्होंने इमेजेस को WebP में कम्प्रेस किया, नॉन-क्रिटिकल JavaScript को डेफर (defer) किया, और अपनी CSS को मिनिफाई (minify) किया। उन्होंने Google Lighthouse रन किया और पूरे कॉन्फिडेंस के साथ 100 के परफेक्ट स्कोर की उम्मीद की। लेकिन, उन्हें मिला 75। रिपोर्ट ने एक बड़ा Cumulative Layout Shift पेनल्टी फ्लैग किया था। पूरा शिफ्ट सिर्फ एक <H1> हेडलाइन की वजह से हुआ था, जो कस्टम वेब फॉन्ट लोड होने पर एक लाइन से रैप होकर दो लाइन की हो गई थी। हेडर को रिडिज़ाइन करने या इनविज़िबल टेक्स्ट पर डिपेंड रहने के बजाय, डेवलपर ने हेडिंग फॉन्ट के लिए एकदम सटीक फॉलबैक मेट्रिक्स जनरेट किए। लेआउट शिफ्ट ज़ीरो हो गया, Lighthouse स्कोर 100 पर पहुँच गया, और डेवलपर फाइनली चैन की नींद सो सका।
कॉमन मिस्टेक्स और ट्रैप्स
- परसेंटेज का अंदाज़ा लगाना: आप इन नंबर्स को आँखों से देखकर गेस नहीं कर सकते। कैलकुलेशन के लिए वेब फॉन्ट और सिस्टम फॉन्ट दोनों फाइल्स की बाइनरी टेबल्स से सीधे यूनीक यूनिट्स पर एम (UPM), ascent, descent और line gap डेटा निकालकर उन्हें क्रॉस-मल्टीप्लाई करना होता है। मैथ्स सही करने के लिए हमेशा एक प्रोग्राममैटिक जनरेटर (programmatic generator) का यूज़ करें।
- वेब फॉन्ट पर ओवरराइड्स अप्लाई करना: एक क्लासिक ट्रैप है कस्टम वेब फॉन्ट के
@font-faceडिक्लेरेशन में सीधाsize-adjustऐड कर देना। इससे कुछ हासिल नहीं होता। ओवरराइड्स को एक बिलकुल अलग@font-faceडिक्लेरेशन में अप्लाई किया जाना चाहिए जो एकlocal()सिस्टम फॉन्ट को लोड करता हो, जिसे फिर आप अपनेfont-familyस्टैक में दूसरे नंबर पर रखते हैं। - स्ट्रक्चरली अपोज़िट फॉलबैक्स चुनना: फॉन्ट मेट्रिक्स सिर्फ एक हद तक ही काम आ सकते हैं। अगर आपका कस्टम फॉन्ट एक बहुत ही कन्डेन्स्ड (ultra-condensed) sans-serif है, और आप एक चौड़े, फैले हुए serif फॉन्ट को अपने लोकल फॉलबैक बेस के रूप में यूज़ करते हैं, तो
size-adjustका मैथ्स उस serif फॉन्ट को पूरी तरह से डिस्टॉर्टेड और पढ़ने में मुश्किल बना देगा जब तक कि वह स्वैप नहीं होता। हमेशा एक ऐसा लोकल सिस्टम फॉन्ट चुनें जिसका स्केलेटल स्ट्रक्चर (skeletal structure) आपके वेब फॉन्ट से लगभग मिलता-जुलता हो। line-gap-overrideको भूल जाना: डेवलपर्स अक्सर अपना पूरा फोकस विड्थ और हाइट पर रखते हैं और यह मान लेते हैं कि लाइन गैप कोnormalसेट करना या इसे छोड़ देना ठीक है। अगर ब्राउज़र फॉलबैक फॉन्ट में डिफ़ॉल्ट लीडिंग (leading) जोड़ने का फैसला करता है जो वेब फॉन्ट की इंटरनल लीडिंग से परफेक्टली मैच नहीं करता, तो आपकी वर्टिकल रिदम बिगड़ जाएगी। हमेशा इसे साफ़ तौर पर (explicitly) डिफाइन करें।
यह आपके राडार पर क्यों होना चाहिए
जिस पल आप किसी ऐसे प्रोजेक्ट पर कस्टम वेब फॉन्ट यूज़ करने का फैसला करते हैं जहाँ यूज़र एक्सपीरियंस और SEO मायने रखते हैं, आपको उसी वक्त फॉन्ट फॉलबैक मेट्रिक्स के बारे में सोचना चाहिए।
ऐतिहासिक रूप से, वेब पर टाइपोग्राफी एस्थेटिक्स (अच्छे फोंट्स का यूज़) और परफॉरमेंस (कूदते लेआउट्स से बचना) के बीच एक समझौता हुआ करती थी। CSS फॉन्ट मेट्रिक्स के आने के साथ, यह समझौता अब खत्म हो चुका है। जब भी आप किसी साइट के Core Web Vitals का ऑडिट कर रहे हों, हाई CLS स्कोर से जूझ रहे हों, या ऐसे रीयूज़ेबल UI कम्पोनेंट्स बना रहे हों जो स्क्रीन पर ज़रा भी हिलना (jitter) अफोर्ड नहीं कर सकते, फॉन्ट फॉलबैक ओवरराइड्स इसका पक्का इंजीनियरिंग सॉलूशन हैं। यह वेब फोंट्स को एक लोडिंग लायबिलिटी से बदलकर एक सीमलेस (seamless), नज़र न आने वाले ट्रांज़िशन में तब्दील कर देता है।
और गहराई में जाएं
- MDN Web Docs: size-adjust - size-adjust डिस्क्रिप्टर कैसे ग्लिफ आउटलाइन्स और मेट्रिक्स को स्केल करता है, इस पर Mozilla का ऑफिशियल डॉक्यूमेंटेशन।
- MDN Web Docs: ascent-override - फॉन्ट के ascent मेट्रिक को ओवरराइड करने पर एक डीप डाइव।
- web.dev: Improved font fallbacks - Cumulative Layout Shift को फिक्स करने के लिए इन्हीं CSS डिस्क्रिप्टर्स का उपयोग करने पर Google की बेहतरीन गाइड।
- W3C CSS Fonts Module Level 4 - फॉन्ट मेट्रिक ओवरराइड्स के लिए एग्ज़ैक्ट मैथमेटिक्स और ब्राउज़र रेंडरिंग रूल्स की डिटेल देने वाला ऑफिशियल स्पेसिफिकेशन।