في جملة واحدة
@font-face هي قاعدة CSS اللي بتسمح لمتصفح الويب إنه ينزل ويعرض خطوط مخصصة (custom typography)، عشان أخيرًا تحرر الديزاينرز من استبداد النصف دستة خطوط "web-safe" العقيمة اللي بتيجي pre-installed على أي جهاز.
المشكلة التي تحلها
لو كنت بتبني مواقع في أواخر التسعينات أو أوائل الألفينات، التايبوجرافي على الويب كان عبارة عن أرض قاحلة وكئيبة. كنت تحت رحمة أي خطوط تصادف إنها موجودة على نظام تشغيل المستخدم (operating system). كنت بتكتب font-family: Arial, Helvetica, sans-serif; وتدعي ربنا. ولو ديزاينر سلمك mockup فوتوشوب فيه خط serif تفصيل وشكله حلو للعناوين، كان قدامك تلات اختيارات أسوأ من بعض:
- هاك الصور (The Image Hack): تقطع العنوان في الفوتوشوب وتعرضه كـ
<img>tag. الـ SEO كان بيكره الحركة دي. الـ Screen readers مكنتش بتشوفها. وشاشات الـ Retina بعد كده كانت بتحولها لغلوشة بصرية. - الـ sIFR (Scalable Inman Flash Replacement): حيلة شيطانية كانت بتستخدم JavaScript عشان تلاقي النص، تخفيه، وتعمل embed لملف Adobe Flash صغير بيعرض نفس النص بالظبط بخط مخصص. كانت تقيلة، مليانة bugs، وبتسيّح بطاريات اللابتوب.
- Cufón: مكتبة JavaScript كانت بتحول مسارات الخط (font paths) لـ vector graphics وتعرضهم على
<canvas>. حاجة جميلة، بس مكنتش تقدر تحدد (select) أو تنسخ النص بسهولة.
الـ W3C في الواقع اقترحت @font-face من زمان جدًا في CSS2 (سنة 1998)، لكن المتصفحات متفقتش على صيغ الخطوط (font formats)، ومصانع الخطوط (foundries) كانت مرعوبة من القرصنة (لو حطيت ملف .ttf على سيرفر، أي حد يقدر ينزله).
الموضوع فضل متعطل لحد فترة الـ 2010s، مع الانتشار الواسع لصيغة الـ Web Open Font Format (WOFF)—ودي صيغة مضغوطة ومصممة مخصوص للويب—وقتها بس @font-face بقت واقع عملي. النهارده، @font-face مش مجرد بتحمل خطوط؛ دي بتنظم رقصة معقدة من الـ network requests، الـ caching، الـ subsetting، واستراتيجيات الـ rendering عشان تحافظ على موقعك سريع جدًا في حين إنه بيظهر بالظبط زي ما الديزاينر كان قاصد.
إزاي بتشتغل تحت الكبوت (Under the hood)
في جوهرها، @font-face هي شوية تعليمات. بتربط اسم أنت بتخترعه بملف مرمي على سيرفر.
تشريح القاعدة
أي إعلان @font-face حديث وجاهز للـ production بيبقى شكله عامل كده:
@font-face {
font-family: 'MyAwesomeFont';
src: local('MyAwesomeFont Regular'),
url('/fonts/myawesomefont.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
unicode-range: U+0000-00FF;
}
تعالى نفصص السحر اللي جوه البلوك ده.
الصيغ واحتكار الـ WOFF2
لاحظ الـ format('woff2'). في العصور المظلمة، الديفيلوبرز كان لازم يوفروا stack ضخم و"مضاد للرصاص" من صيغ .eot (عشان الـ Internet Explorer القديم)، .ttf (عشان سفاري/أندرويد الأقدم)، .svg (عشان الـ iOS القديم)، والـ .woff.
النهارده، الويب شغال على WOFF2. الصيغة دي بتستخدم ضغط Brotli، وده بيخلي حجم الملفات أصغر بحوالي 30% من سلفها الـ WOFF. وعلشان WOFF2 بيتمتع بدعم شبه عالمي من المتصفحات (أكتر من 97% عالميًا)، فغالباً مش هتحتاج تقدم غير ملف .woff2 واحد، وممكن معاه WOFF عادي كـ fallback لو بتستهدف أجهزة شركات قديمة جدًا.
السيطرة على الـ Flash (خاصية font-display)
لما المتصفح بيلاقي نص واخد ستايل بخط مخصص، بس ملف الخط لسه مخلصش تحميل، بيبقى مضطر ياخد قرار. هل يعرض النص بخط بديل (fallback font)؟ ولا يخفي النص لحد ما الخط المخصص يوصل؟
القرار ده بيخلق مصطلحين سيئي السمعة: FOUT (Flash of Unstyled Text - ومضة النص الغير منسق) و FOIT (Flash of Invisible Text - ومضة النص المخفي). خاصية font-display بتسمحلك تفرض السلوك ده وتمشّي كلامك.
font-display |
السلوك (Behavior) | إمتى تستخدمها |
|---|---|---|
auto |
الافتراضي للمتصفح (عادةً بيعمل block للـ rendering لمدة بتوصل لـ 3 ثواني). | ولا مرة، ده لو يهمك الـ performance. |
swap |
بيعرض الخط البديل فوراً (FOUT)، وبعدين يبدل (swaps) للخط المخصص لما يبقى جاهز. | نصوص المحتوى (Body text)، المقالات، وعناصر الـ UI. المستخدمين يقدروا يبدأوا قراية فوراً. |
block |
بيخفي النص (FOIT) لفترة قصيرة، بعدين يعرض البديل، بعدين يبدل. | الأيقونات أو الشعارات اللي لو اتعرضت بخط بديل هتبان زي طلاسم أو رموز مكسورة. |
fallback |
فترة block قصيرة، وفترة swap قصيرة. لو أخد وقت أطول من اللازم، بيلغي الخط المخصص تماماً في التحميلة دي للصفحة. | النصوص الزخرفية (Decorative) على الاتصالات البطيئة. |
optional |
فترة block قصيرة جداً. لو الخط مش معموله cache أو مش سريع جداً، بيستسلم تماماً ومايحملهوش. | الخطوط اللي "حلو لو موجودة" (Nice to have) في الأماكن اللي استقرار الـ layout فيها أهم من أي حاجة. |
الـ Subsetting باستخدام unicode-range
ملف الخط الكامل اللي بيحتوي على حروف لاتينية، سيريلية، يونانية، وحروف CJK المعقدة (صيني، ياباني، كوري) ممكن وزنه يوصل لميجابايتات.
خاصية unicode-range دي أداة optimization عبقرية. بتقول للمتصفح: "ملف الخط ده بيغطي بس الحروف المعينة دي."
لو ضبطت unicode-range: U+0000-00FF (اللاتيني الأساسي)، المتصفح هيعمل parse للـ HTML بتاعك. لو ملقاش أي حروف لاتينية، مش هينزل ملف الخط أصلاً. ده بيسمحلك تقسم الخطوط الضخمة لحتت صغيرة على قد اللغة، وتحمل بس اللي المستخدم محتاجه فعلاً.
الخطوط المتغيرة (Variable Fonts)
زمان، لو كنت عايز Regular، و Italic، و Bold، و Bold Italic، كان لازم تحمل أربع ملفات .woff2 منفصلة.
الـ Variable fonts بتجمع العيلة كلها في ملف واحد عن طريق تعريف "محاور التغيير" (axes of variation). بدل الأوزان الثابتة زي 400 أو 700، الخط المتغير ممكن يتعرض بوزن 432 أو 781.
@font-face {
font-family: 'MyVariableFont';
src: url('/fonts/myfont-var.woff2') format('woff2');
font-weight: 100 900; /* Accepts any weight between 100 and 900 */
}
الـ Base64 Embedding مقابل الـ Preloading
أحياناً الـ network request نفسه بيكون هو عنق الزجاجة (bottleneck). هنا قدامك استراتيجيتين متقدمتين:
- الـ Preloading: إنك تضيف
<link rel="preload" href="/fonts/myfont.woff2" as="font" type="font/woff2" crossorigin>في الـ<head>بتاع الـ HTML وده بيقول للمتصفح يروح يجيب الخط فوراً، حتى قبل ما ينزل ملف الـ CSS. - الـ Base64 Embedding: ممكن تحول ملف الخط الـ binary لـ text string عملاق وتلزقه مباشرة في الـ
url()بتاع قاعدة الـ@font-faceبتاعتك. الحركة دي بتلغي الـ network request تماماً، لكنها بتزود حجم ملف الـ CSS بحوالي 33%. وبتوقف عملية الـ render بتاعت الصفحة كلها لحد ما الـ CSS ينزل، عشان كده هي محجوزة حصرياً للخطوط الصغيرة جداً واللي معمولها subsetting ومُحسّنة لأقصى درجة (زي لوجو مخصص).
حواديت من أرض الواقع
زرار الـ E-commerce الخفي
موقع e-commerce صغير قرر يستخدم خط أيقونات مخصص للـ UI بتاعه. استخدموا إعلان @font-face عادي من غير font-display. على شبكة Wi-Fi سريعة في المكتب، كل حاجة كان شكلها تحفة.
لكن، مستخدمين الموبايل اللي على شبكات 3G كانوا بيسيبوا الموقع (bouncing) بمعدلات قياسية. لما الديفيلوبرز عملوا محاكاة لشبكة بطيئة، لقوا الجاني: المتصفح كان مستني خط الأيقونات يحمل قبل ما يعمل render لزرار "Add to Cart". ده أدى لـ FOIT (النص المخفي) لمدة 4 ثواني. المستخدمين حرفياً مكنوش شايفين الزرار عشان يدفعوا فلوس للشركة. ببساطة إضافة font-display: swap; للنص وعمل preloading لخط الأيقونات أنقذ الـ conversion rate بتاعهم.
مأساة التايبوجرافي متعدد الميجابايتات
مدونة تقنية مشهورة قررت تدعم اللغات المختلفة (internationalization). عشان يضمنوا إن التايبوجرافي الأنيق بتاعهم يفضل متناسق، حطوا خط Noto Sans ضخم بيغطي كل حروف الـ CJK (الصيني، الياباني، الكوري). ملف الخط كان تقريباً 6MB.
فجأة، المستخدمين في أمريكا اللي بيقروا مقالات بالإنجليزية العادية بقوا يشتكوا من بطء أوقات التحميل. متصفحاتهم كانت بتنزل 6MB من الحروف اليابانية والكورية عشان يقروا مقال عن JavaScript. التيم طبق الـ unicode-range. قسموا الخط لـ Latin subset حجمه 30KB و CJK subset حجمه 5.9MB. المتصفح بقى يطلب أوتوماتيك الملف الـ 30KB بس للقراء الإنجليز، وده عالج مشكلة الـ performance في ثواني.
الري-ديزاين المهزوز
وكالة ديزاين عملت layout تحفة بيستخدم تسع أوزان مختلفة لخط sans-serif كلاسيكي. الديفيلوبر بكل اجتهاد عمل export لتسع ملفات .woff2 مختلفة وكتب تسع قواعد @font-face.
عشان المتصفحات بتنزل الملفات بشكل غير متزامن (asynchronously)، الصفحة كانت بتظهر بشكل متقطع ومهزوز. الوزن 300 يحمل، وبعدين 700، وبعدين 400، وده كان بيخلي الـ layout يتنفض ويحسب التفاف النص (text wrapping) أربع أو خمس مرات في الثانية. الديفيلوبر بدل التسع ملفات بملف Variable Font واحد (.woff2-variations). الملف الواحد كان أصغر بـ 40% من التسعة مع بعض، وحمل في request واحد، وعمل render للصفحة في خطوة render (paint) واحدة سلسة.
أخطاء شائعة وفخاخ
- تقديم صيغ خطوط أثرية للمتصفحات الحديثة: بطل تستخدم خطوط
.ttf،.eot، و.svgإلا لو عندك سبب صريح مبني على داتا إنك محتاج تدعم IE8 أو جهاز أندرويد من عشر سنين. خليك في WOFF2. - نسيان الـ
crossoriginفي الـ preloads: لو بتستخدم<link rel="preload">لخط، لازم تحط الـ attribute اللي اسمهcrossorigin، حتى لو الخط مرفوع على الدومين بتاعك. بسبب مواصفات الحماية بتاعة المتصفح، الخطوط بيتعملها fetch باستخدام anonymous mode CORS. لو نسيت الـ attribute ده، المتصفح هينزل الخط مرتين. - عمل Base64 encoding لخطوط الـ body الضخمة: إنك تعمل embed لخط حجمه 100KB مباشرة في الـ CSS بتاعك ده بينفخ الـ CSS payload وبيوقف الـ rendering. اعمل encode للخطوط الصغيرة جداً بس (زي subset حجمه 5KB بيستخدم للوجو).
- تجاهل الـ font-display: الاعتماد على الافتراضيات بتاعة المتصفح دايماً بيعاقب المستخدمين اللي على كونكشن بطيء. اكتب بوضوح
font-display: swapلخطوط المحتوى (body text) عشان تدي الأولوية للقراية على حساب الجماليات. - عمل Preloading لكل خط في الصفحة: الـ Preloading ده أمر ذو أولوية عالية (high-priority). لو عملت preload لخط bold، italic بتاع عناوين موجود في آخر الصفحة تحت خالص، أنت كده بتسرق الـ network bandwidth من ملفات الـ JavaScript والـ CSS الحرجة (critical) اللي محتاجينها عشان نحمل أول الصفحة. اعمل preload لـ "hero" font بس.
- الاعتماد بزيادة على
local(): استخدامlocal('Helvetica')بيقول للمتصفح يستخدم نسخة local لو موجودة. لكن، طرق تسمية الخطوط (naming conventions) بتختلف جداً بين الماك، والويندوز، واللينكس. اللي أنت فاكرهlocal('Helvetica Neue Bold')ممكن يفشل في صمت على نظام تشغيل تاني.
ليه لازم تكون على رادارك
تطوير الويب هو شد حبل أبدي بين الجماليات (aesthetics) والـ performance. أنت بتبني براند، والتايبوجرافي هو صوت البراند ده. لكن أنت كمان بتبني سوفتوير، وكل كيلوبايت بيفرق مع مستخدم فاتح من كونكشن بيقطع وهو في المترو.
كتابة قاعدة @font-face مثالية معروف إنها حاجة مملة جداً. بتتطلب فهم عميق لشذوذ المتصفحات (browser quirks)، الـ MIME types، الـ syntax بتاع الـ subsetting، وصيغ الضغط (compression formats). أي generator حديث مش مجرد بيكتبلك الـ CSS syntax؛ ده بيطبق الـ best practices جواه. بيعمل أوتوماتيك generate لملفات الـ WOFF2، بيهيكل الـ variable axes، بيطبق الـ font-display fallbacks الصح، وبيطلعلك الـ Base64 أو الـ preload tags المظبوطة اللي محتاجها عشان تتخطى عنق الزجاجة في الشبكة (network bottlenecks). بيضمن إن لما الأبلكيشن بتاعك يتكلم، المتصفح يكون عارف فعلاً إزاي ينطق الكلام.
اتعمق أكتر
- MDN Web Docs: @font-face - المرجع المطلق للـ syntax والـ compatibility بتاعت القاعدة دي.
- W3C CSS Fonts Module Level 4 - المواصفات الرسمية (official specification) اللي بتشرح بالتفصيل إزاي المتصفحات بتطبق
@font-face، والـ variable fonts، وآليات الـ fallback. - Optimize WebFont loading and rendering (web.dev) - دليل جوجل المعتمد عن تأثير خطوط الويب على الـ performance، والـ FOIT، والـ FOUT.
- MDN Variable Fonts Guide - غطس عميق في الـ syntax وفوايد استخدام الـ variable web fonts.
- W3C Specification for font-display - التفاصيل التقنية الدقيقة لإزاي المتصفحات بتدير التايم لاين بتاع الـ font rendering.