Layout Shift و Font Loading؛ چرا متن زیر پای کاربر می‌پره

تو درس اول این فصل، به‌طور خلاصه اشاره کردیم که فونت‌های وب می‌تونن باعث CLS بشن و font-display رو به‌عنوان راه‌حل معرفی کردیم. حالا وقتشه عمیق‌تر بریم سراغش، چون font loading یکی از رایج‌ترین و در عین حال کمتر‌فهمیده‌شده‌ترین منابع layout shift تو وب‌سایت‌های واقعیه.

چرا اصلاً لود شدن یه فونت باعث جابه‌جایی می‌شه؟

وقتی مرورگر به یه font-family سفارشی برخورد می‌کنه که هنوز دانلود نشده، دو راه داره: یا صبر کنه تا فونت برسه (و تا اون موقع هیچی نشون نده)، یا یه فونت جایگزین (fallback، مثل فونت سیستمی) رو موقتاً نشون بده و بعد از رسیدن فونت اصلی، جایگزینش کنه. مشکل اینجاست: هر فونتی، حتی با همون سایز، عرض متفاوتی برای حروف داره. یعنی وقتی فونت fallback با فونت اصلی جایگزین می‌شه، طول کلمات عوض می‌شه، خطوط می‌شکنن یا جمع می‌شن، و کل چیدمان زیر و رو می‌شه.

یادآوری و تکمیل: مقادیر font-display

تو درس اول این فصل با swap و optional آشنا شدیم. حالا بیایید همه‌ی مقادیر رو کنار هم ببینیم:

مقداررفتار
autoپیش‌فرض؛ رفتار به خود مرورگر بستگی داره
blockیه مدت کوتاه (معمولاً تا 3 ثانیه) متن رو کاملاً مخفی نگه می‌داره تا فونت برسه؛ اگه نرسید، fallback رو نشون می‌ده
swapفوراً fallback رو نشون می‌ده و هر وقت فونت اصلی رسید، جایگزینش می‌کنه (حتی دیر)
fallbackیه مدت خیلی کوتاه (حدود 100ms) متن مخفیه؛ بعد fallback میاد؛ اگه فونت اصلی تا یه مهلت کوتاه دیگه نرسه، دیگه هیچ‌وقت جایگزین نمی‌شه
optionalشبیه fallback ولی سخت‌گیرانه‌تر؛ اگه فونت زود نرسه (مثلاً به‌خاطر شبکه‌ی کند)، مرورگر کلاً از جایگزینی صرف‌نظر می‌کنه، حتی برای بازدیدهای بعدی همون سشن

برای صفحاتی که CLS براشون خیلی مهمه (مثلاً صفحات اصلی پربازدید)، optional امن‌ترین انتخابه چون عملاً تضمین می‌کنه هیچ پرش دیرهنگامی رخ نده؛ هزینه‌اش اینه که بعضی کاربرها ممکنه هیچ‌وقت فونت سفارشی رو نبینن و به‌جاش همیشه fallback رو ببینن.

راه‌حل دقیق‌تر: هماهنگ کردن فونت fallback با فونت اصلی

به‌جای فقط انتخاب یه font-display مناسب، یه تکنیک قدرتمندتر اینه که خود فونت fallback رو طوری تنظیم کنیم که ابعادش تا حد امکان به فونت اصلی نزدیک باشه؛ این‌جوری حتی وقتی جایگزینی اتفاق می‌افته، پرش خیلی کوچیک یا کاملاً نامحسوسه:

@font-face {
  font-family: "MyFont Fallback";
  src: local("Arial");
  size-adjust: 105%;
  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
}

body {
  font-family: "MyFont", "MyFont Fallback", sans-serif;
}

ویژگی‌های size-adjust، ascent-override، descent-override و line-gap-override اجازه می‌دن ابعاد یه فونت fallback موجود (مثل Arial) رو دستی طوری تنظیم کنید که عرض متوسط حروفش تا حد زیادی با فونت وب اصلی مطابقت داشته باشه. این کار دستی و دقیقه، ولی تو پروژه‌های حرفه‌ای که CLS اهمیت بالایی داره، خیلی مؤثره.

font-synthesis: جلوگیری از یه مشکل مرتبط

یه مشکل مرتبط دیگه اینه: اگه یه وزن یا حالت خاص از یه فونت (مثلاً bold یا italic) رو دانلود نکرده باشید ولی تو CSS ازش استفاده کرده باشید، مرورگر گاهی خودش سعی می‌کنه اون حالت رو مصنوعی (synthetic) بسازه؛ که هم از نظر بصری معمولاً زشت‌تره، هم گاهی باعث تغییر عرض متن و در نتیجه layout shift می‌شه:

body {
  font-synthesis: none;
}

این کد به مرورگر می‌گه اگه یه وزن یا حالت خاص واقعاً لود نشده، اصلاً سعی نکنه مصنوعی بسازتش؛ به‌جاش نزدیک‌ترین وزن واقعی موجود رو نشون بده.

preload کردن فونت‌های حیاتی

همون‌طور که تو درس قبل هم اشاره شد، preload کردن فونت‌های اصلی (خصوصاً اونایی که تو بالای صفحه استفاده می‌شن) باعث می‌شه دانلودشون زودتر شروع بشه، در نتیجه فاصله‌ی زمانی بین نمایش fallback و فونت اصلی کوتاه‌تر می‌شه:

<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>

منابع دیگه‌ی layout shift که به فونت ربطی ندارن

برای کامل بودن تصویر، چند منبع رایج دیگه‌ی CLS که تو درس‌های قبل بهشون اشاره کردیم رو یه بار دیگه با هم مرور کنیم:

  • تصاویر و ویدیوهای بدون aspect-ratio یا سایز مشخص (فصل ۲۱)
  • المنت‌های تبلیغاتی یا داینامیک بدون min-height رزروشده (درس قبل)
  • انیمیشن‌هایی که از transform به‌جای ویژگی‌های Layout-محور استفاده نمی‌کنن (فصل ۲۰)

فونت‌ها معمولاً کمتر از این موارد به چشم میان، چون تأثیرشون تدریجی و پخش‌شده تو کل متن صفحه‌ست، نه یه پرش یهویی و بزرگ؛ ولی از نظر عددی CLS، همین جابه‌جایی‌های کوچیک و پخش‌شده هم جمع می‌شن و رو امتیاز نهایی اثر می‌ذارن.

نکته مهم: هیچ‌کدوم از این تکنیک‌ها (font-display، size-adjust، preload) جایگزین همدیگه نیستن؛ بهترین نتیجه وقتیه که با هم ترکیب بشن: preload برای سرعت دانلود، font-display برای رفتار لحظه‌ی انتقال، و فونت fallback هماهنگ‌شده برای کمینه کردن خود پرش وقتی که اتفاق میفته.

اشتباه رایج: تست نکردن روی شبکه‌ی کند

روی یه اتصال سریع (مثل وای‌فای دفتر کار)، فونت معمولاً آن‌قدر سریع لود می‌شه که اصلاً فرصت نمی‌کنید fallback رو ببینید، و در نتیجه فکر می‌کنید مشکلی نیست. ولی کاربر واقعی رو موبایل و شبکه‌ی 3G یا 4G ضعیف، ممکنه چند ثانیه fallback رو ببینه و بعد پرش رو کامل حس کنه. همیشه با throttling شبکه (تو DevTools) این سناریو رو تست کنید.

جمع‌بندی

font loading یکی از منابع اصلی و کمتر‌دیده‌شده‌ی CLS هست، چون فونت fallback و فونت اصلی معمولاً عرض متفاوتی دارن. font-display: optional امن‌ترین گزینه برای صفحاتی هست که CLS براشون حیاتیه، و تنظیم دستی ابعاد فونت fallback با size-adjust و ویژگی‌های override می‌تونه حتی وقتی جایگزینی اتفاق می‌افته، پرش رو تقریباً نامحسوس کنه. preload کردن فونت‌های حیاتی هم فاصله‌ی زمانی این انتقال رو کوتاه‌تر می‌کنه.