غالبًا ما تواجه المواقع الإلكترونية التي تحمّل الخطوط باستخدام font-display: swap مشكلة تغيُّر التصميم (CLS) عند تحميل خط الويب واستبداله بالخط الاحتياطي.
يمكنك منع حدوث CLS من خلال تعديل سمات الخط الاحتياطي لمطابقة سمات الخط الأساسي. يمكن أن تساعد السمات مثل size-adjust
وascent-override
وdescent-override
وline-gap-override
في قاعدة @font-face
على إلغاء مقاييس الخط الاحتياطي، ما يتيح للمطوّرين التحكّم بشكل أكبر في كيفية عرض الخطوط. يمكنك الاطّلاع على مزيد من المعلومات حول الخطوط الاحتياطية ومواقع البيانات التي تلغي الإعدادات التلقائية في هذه المشاركة. يمكنك أيضًا الاطّلاع على تطبيق عملي لهذه التقنية في هذا العرض التجريبي.
تتناول هذه المقالة كيفية تنفيذ تعديلات حجم الخط في إطارَي عمل Next.js وNuxt.js لإنشاء ملف CSS للخط الاحتياطي وتقليل مقياس CLS. ويوضّح أيضًا كيفية إنشاء خطوط احتياطية باستخدام أدوات شاملة مثل Fontaine وCapsize.
الخلفية
يتم استخدام font-display: swap بشكل عام لمنع ظهور وميض النص غير المرئي (FOIT) ولعرض المحتوى بشكل أسرع على الشاشة. تُعلم القيمة swap
المتصفّح بأنّه يجب عرض النص الذي يستخدم الخط على الفور باستخدام خط نظام، وأن يتم استبدال خط النظام فقط عندما يكون الخط المخصّص جاهزًا.
أكبر مشكلة في swap
هي التأثير الصاعق، حيث يؤدي الاختلاف في أحجام الأحرف بين الخطين إلى تحريك محتوى الشاشة. ويؤدي ذلك إلى انخفاض نتائج CLS، خاصةً للمواقع الإلكترونية التي تتضمّن الكثير من النصوص.
تعرض الصور التالية مثالاً على المشكلة. تستخدِم الصورة الأولى font-display: swap
بدون محاولة تعديل حجم الخط الاحتياطي. يوضّح المثال الثاني كيفية تحسين تجربة التحميل من خلال تعديل الحجم باستخدام قاعدة CSS @font-face
.
بدون تعديل حجم الخط
body {
font-family: Inter, serif;
}
بعد ضبط حجم الخط
body {
font-family: Inter, fallback-inter, serif;
}
@font-face {
font-family: "fallback-inter";
ascent-override: 90.20%;
descent-override: 22.48%;
line-gap-override: 0.00%;
size-adjust: 107.40%;
src: local("Arial");
}
يمكن أن يكون ضبط حجم الخط الاحتياطي استراتيجية فعّالة لمنع تغيير تنسيق تحميل الخط، ولكن قد يكون تنفيذ المنطق من البداية أمرًا صعبًا، كما هو موضّح في هذه المشاركة حول الخطوط الاحتياطية. لحسن الحظ، تتوفّر حاليًا عدة خيارات للأدوات لتسهيل ذلك أثناء تطوير التطبيقات.
كيفية تحسين الخطوط الاحتياطية باستخدام Next.js
يوفّر Next.js طريقة مضمّنة لتفعيل ميزة تحسين الخطوط الاحتياطية. تكون هذه الميزة مفعّلة تلقائيًا عند تحميل الخطوط باستخدام المكوّن @next/font.
تمّ تقديم المكوّن @next/font في الإصدار 13 من Next.js. يقدّم المكوّن واجهة برمجة تطبيقات لاستيراد خطوط Google أو الخطوط المخصّصة إلى صفحاتك، ويتضمن ميزة استضافة ذاتية تلقائية مضمّنة لملفات الخطوط.
عند استخدامها، يتم احتساب مقاييس الخطوط الاحتياطية تلقائيًا وإدراجها في ملف CSS.
على سبيل المثال، إذا كنت تستخدِم خط Roboto، يمكنك تحديده عادةً في CSS على النحو التالي:
@font-face {
font-family: 'Roboto';
font-display: swap;
src: url('/fonts/Roboto.woff2') format('woff2'), url('/fonts/Roboto.woff') format('woff');
font-weight: 700;
}
body {
font-family: Roboto;
}
لنقل البيانات إلى next/font:
انقل بيان خط Roboto إلى JavaScript من خلال استيراد دالة Roboto من next/font. ستكون قيمة عرض الدالة هي اسم فئة يمكنك الاستفادة منه في نموذج المكوّن. تذكَّر إضافة
display: swap
إلى عنصر الإعداد لتفعيل الميزة.import { Roboto } from '@next/font/google'; const roboto = Roboto({ weight: '400', subsets: ['latin'], display: 'swap' // Using display swap automatically enables the feature })
في المكوّن، استخدِم اسم الفئة الذي تم إنشاؤه:
javascript export default function RootLayout({ children }: { children: React.ReactNode; }) { return ( <html lang="en" className={roboto.className}> <body>{children}</body> </html> ); }
خيار الضبط adjustFontFallback:
بالنسبة إلى @next/font/google
: قيمة منطقية تحدّد ما إذا كان يجب استخدام خط احتياطي تلقائي لتقليل متغيّرات التصميم التراكمية. الإعداد التلقائي هو true. تضبط Next.js تلقائيًا الخط الاحتياطي على Arial
أو Times New Roman
استنادًا إلى نوع الخط (serif مقابل sans-serif على التوالي).
بالنسبة إلى @next/font/local
: سلسلة أو قيمة منطقية خاطئة تضبط ما إذا كان يجب استخدام خط احتياطي تلقائي لتقليل متغيّرات التصميم التراكمية. القيم المحتمَلة هي Arial
أو Times New Roman
أو false
. القيمة التلقائية هي Arial
. إذا كنت تريد استخدام خط مكتوب بحرف لاتيني عريض، ننصحك بضبط هذه القيمة على Times New Roman
.
خيار آخر لخطوط Google
إذا لم يكن استخدام المكوّن next/font
متاحًا، يمكنك استخدام علامة optimizeFonts
للاستفادة من هذه الميزة مع "Google Fonts". تم تفعيل ميزة optimizeFonts في Next.js تلقائيًا. تُدرج هذه الميزة ملف CSS الخاص بخدمة "خطوط Google" في استجابة HTML. بالإضافة إلى ذلك، يمكنك تفعيل ميزة تعديل الخطوط الاحتياطية من خلال ضبط العلامة experimental.adjustFontFallbacksWithSizeAdjust
في next.config.js، كما هو موضّح في المقتطف التالي:
// In next.config.js
module.exports = {
experimental: {
adjustFontFallbacksWithSizeAdjust: true,
},
}
ملاحظة: لا تتوفّر خطة لإتاحة هذه الميزة مع ملف التوجيه app
الذي تم طرحه مؤخرًا. على المدى الطويل، من الأفضل استخدام next/font
.
كيفية ضبط الخطوط الاحتياطية باستخدام Nuxt
@nuxtjs/fontaine هي وحدة لإطار عمل Nuxt.js تعمل تلقائيًا على احتساب قيم مقياس الخط الاحتياطي وإنشاء @font-face
CSS الاحتياطية.
فعِّل الوحدة من خلال إضافة @nuxtjs/fontaine
إلى إعدادات الوحدات:
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
modules: ['@nuxtjs/fontaine'],
})
إذا كنت تستخدم "خطوط Google" أو لم يكن لديك بيان @font-face
لخط، يمكنك الإفصاح عن هذه الخطوط كخيارات إضافية.
في معظم الحالات، يمكن للوحدة قراءة قواعد @font-face
من CSS واستنتاج التفاصيل تلقائيًا، مثل عائلة الخطوط وعائلة الخطوط الاحتياطية ونوع العرض.
إذا تمّ تحديد الخط في مكان لا يمكن للوحدة العثور عليه، يمكنك تمرير معلومات المقاييس كما هو موضّح في مقتطف الرمز البرمجي التالي.
export default defineNuxtConfig({
modules: ['@nuxtjs/fontaine'],
fontMetrics: {
fonts: ['Inter', { family: 'Some Custom Font', src: '/path/to/custom/font.woff2' }],
},
})
تفحص الوحدة تلقائيًا ملف CSS لقراءة بيانات @font-face وإنشاء قواعد @font-face الاحتياطية.
@font-face {
font-family: 'Roboto';
font-display: swap;
src: url('/fonts/Roboto.woff2') format('woff2'), url('/fonts/Roboto.woff') format('woff');
font-weight: 700;
}
/* This will be generated. */
@font-face {
font-family: 'Roboto override';
src: local('BlinkMacSystemFont'), local('Segoe UI'), local('Roboto'), local('Helvetica Neue'),
local('Arial'), local('Noto Sans');
ascent-override: 92.7734375%;
descent-override: 24.4140625%;
line-gap-override: 0%;
}
يمكنك الآن استخدام Roboto override
كخط احتياطي في CSS، كما هو موضّح في المثال التالي.
:root {
font-family: 'Roboto';
/* This becomes */
font-family: 'Roboto', 'Roboto override';
}
إنشاء ملف CSS بنفسك
يمكن أن تساعدك المكتبات المستقلة أيضًا في إنشاء ملف CSS لتعديلات حجم الخط الاحتياطية.
استخدام مكتبة Fontaine
إذا كنت لا تستخدم Nuxt أو Next.js، يمكنك استخدام Fontaine. Fontaine هي المكتبة الأساسية التي تشغّل @nuxtjs/fontaine. يمكنك استخدام هذه المكتبة في مشروعك لإدراج CSS للخط الاحتياطي تلقائيًا باستخدام مكوّنات إضافية من Vite أو Webpack.
لنفترض أنّ لديك خط Roboto محدّدًا في ملف CSS:
@font-face {
font-family: 'Roboto';
font-display: swap;
src: url('/fonts/Roboto.woff2') format('woff2'), url('/fonts/Roboto.woff') format('woff');
font-weight: 700;
}
توفّر Fontaine محوِّلَي Vite وWebpack للتوصيل بسلسلة الإنشاء بسهولة، ويمكنك تفعيل المكوّن الإضافي كما هو موضّح في JavaScript التالي.
import { FontaineTransform } from 'fontaine'
const options = {
fallbacks: ['BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Arial', 'Noto Sans'],
// You may need to resolve assets like `/fonts/Roboto.woff2` to a particular directory
resolvePath: (id) => 'file:///path/to/public/dir' + id,
// overrideName: (originalName) => `${name} override`
// sourcemap: false
}
إذا كنت تستخدم Vite، أضِف المكوّن الإضافي على النحو التالي:
javascript
// Vite
export default {
plugins: [FontaineTransform.vite(options)]
}
في حال استخدام Webpack، يمكنك تفعيله على النحو التالي:
// Webpack
export default {
plugins: [FontaineTransform.webpack(options)]
}
ستفحص الوحدة تلقائيًا ملفاتك لتعديل قواعد @font-face:
css
@font-face {
font-family: 'Roboto';
font-display: swap;
src: url('/fonts/Roboto.woff2') format('woff2'), url('/fonts/Roboto.woff') format('woff');
font-weight: 700;
}
/* This will be generated. */
@font-face {
font-family: 'Roboto override';
src: local('BlinkMacSystemFont'), local('Segoe UI'), local('Roboto'), local('Helvetica Neue'),
local('Arial'), local('Noto Sans');
ascent-override: 92.7734375%;
descent-override: 24.4140625%;
line-gap-override: 0%;
}
يمكنك الآن استخدام Roboto override
كخط احتياطي في CSS.
css
:root {
font-family: 'Roboto';
/* This becomes */
font-family: 'Roboto', 'Roboto override';
}
استخدام مكتبة Capsize
إذا كنت لا تستخدم Next.js أو Nuxt أو Webpack أو Vite، يمكنك استخدام مكتبة Capsize لإنشاء ملف CSS الاحتياطي.
واجهة برمجة التطبيقات الجديدة createFontStack
تشكّل واجهة برمجة التطبيقات جزءًا من حزمة @capsize/core المسماة createFontStack
، والتي تقبل صفيفًا من مقاييس الخطوط بالترتيب نفسه الذي تحدّد به حزمة الخطوط (السمة font-family
).
يمكنك الرجوع إلى المستندات حول استخدام Capsize هنا.
مثال
راجِع المثال التالي: خط الويب المطلوب هو Lobster، مع الرجوع إلى Helvetica Neue ثم Arial. في CSS، font-family: Lobster, 'Helvetica Neue', Arial
.
استورِد createFontStack من الحزمة الأساسية:
import { createFontStack } from '@capsizecss/core';
استورِد مقاييس الخطوط لكلّ من الخطوط المطلوبة (راجِع مقاييس الخطوط أعلاه):
javascript import lobster from '@capsizecss/metrics/lobster'; import helveticaNeue from '@capsizecss/metrics/helveticaNeue'; import arial from '@capsizecss/metrics/arial';`
أنشئ حزمة الخطوط، مع تمرير المقاييس كصفيف، باستخدام الترتيب نفسه الذي تستخدمه من خلال سمة font-family في CSS.
javascript const { fontFamily, fontFaces } = createFontStack([ lobster, helveticaNeue, arial, ]);
يعرض هذا الإجراء ما يلي:
{
fontFamily: Lobster, 'Lobster Fallback: Helvetica Neue', 'Lobster Fallback: Arial',
fontFaces: [
{
'@font-face' {
'font-family': '"Lobster Fallback: Helvetica Neue"';
src: local('Helvetica Neue');
'ascent-override': '115.1741%';
'descent-override': '28.7935%';
'size-adjust': '86.8251%';
}
'@font-face' {
'font-family': '"Lobster Fallback: Arial"';
src: local('Arial');
'ascent-override': 113.5679%;
'descent-override': 28.392%;
'size-adjust': 88.053%;
}
}
]
}
يجب إضافة رمزَي fontFamily وfontFaces إلى ملف CSS. توضّح التعليمة البرمجية التالية كيفية تنفيذها في جدول تنسيقات CSS أو ضمن كتلة <style>
.
<style type="text/css">
.heading {
font-family:
}
</style>
سيؤدي ذلك إلى إنشاء ملف CSS التالي:
.heading {
font-family: Lobster, 'Lobster Fallback: Helvetica Neue',
'Lobster Fallback: Arial';
}
@font-face {
font-family: 'Lobster Fallback: Helvetica Neue';
src: local('Helvetica Neue');
ascent-override: 115.1741%;
descent-override: 28.7935%;
size-adjust: 86.8251%;
}
@font-face {
font-family: 'Lobster Fallback: Arial';
src: local('Arial');
ascent-override: 113.5679%;
descent-override: 28.392%;
size-adjust: 88.053%;
}
يمكنك أيضًا استخدام الحزمة @capsize/metrics لاحتساب قيم الاستبدال وتطبيقها على CSS بنفسك.
const fontMetrics = require(`@capsizecss/metrics/inter`);
const fallbackFontMetrics = require(`@capsizecss/metrics/arial`);
const mainFontAvgWidth = fontMetrics.xAvgWidth / fontMetrics.unitsPerEm;
const fallbackFontAvgWidth = fallbackFontMetrics.xAvgWidth / fallbackFontMetrics.unitsPerEm;
let sizeAdjust = mainFontAvgWidth / fallbackFontAvgWidth;
let ascent = fontMetrics.ascent / (unitsPerEm * fontMetrics.sizeAdjust));
let descent = fontMetrics.descent / (unitsPerEm * fontMetrics.sizeAdjust));
let lineGap = fontMetrics.lineGap / (unitsPerEm * fontMetrics.sizeAdjust));