غالبًا ما تتأثر المواقع الإلكترونية التي تحمِّل الخطوط باستخدام font-display: exchange بمتغيّرات التصميم (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
بدون محاولة تعديل حجم الخط الاحتياطي. يوضّح المثال الثاني كيفية تحسين تجربة التحميل من خلال تعديل الحجم باستخدام قاعدة @font-face
في CSS.
بدون تعديل حجم الخط
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 Fonts أو لم يتوفّر تعريف @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));