غالبًا ما تتأثر المواقع الإلكترونية التي تحمِّل الخطوط باستخدام font-display: replace بمتغيّرات التصميم (CLS) عند تحميل خط الويب ويتم استبداله بالخط الاحتياطي.
ويمكنك منع متغيّرات التصميم التراكمية (CLS) من خلال ضبط أبعاد الخط الاحتياطي لتتطابق مع الخط الأساسي. يمكن أن تساعد السمات مثل size-adjust
وascent-override
وdescent-override
وline-gap-override
في القاعدة @font-face
في إلغاء مقاييس الخط الاحتياطي، ما يتيح للمطوّرين المزيد من التحكّم في كيفية عرض الخطوط. يمكنك الاطّلاع على المزيد من المعلومات حول الإجراءات الاحتياطية للخطوط وسمات الإلغاء في هذه المشاركة. يمكنك أيضًا الاطّلاع على طريقة تنفيذ صالحة لهذه التقنية في هذا العرض التوضيحي.
تستكشف هذه المقالة كيفية تنفيذ تعديلات حجم الخط في إطارَي عمل Next.js وNuxt.js لإنشاء CSS للخط الاحتياطي وتقليل متغيّرات التصميم التراكمية (CLS). كما يوضح أيضًا كيف يمكنك إنشاء خطوط احتياطية باستخدام أدوات القطع المتقاطع مثل Fontaine وCapsize.
الخلفية
font-display: reporting عمومًا لمنع FOIT (فلاش النص غير المرئي) وعرض المحتوى بشكل أسرع على الشاشة. تخبر قيمة swap
المتصفّح بأنّه يجب عرض النص الذي يستخدم الخط على الفور باستخدام خط النظام، واستبدال خط النظام فقط عندما يكون الخط المخصّص جاهزًا.
وأكبر مشكلة مع swap
هي التأثير المربك، إذ يؤدي الاختلاف في أحجام أحرف الخط إلى تغيير محتوى الشاشة. يؤدي ذلك إلى انخفاض نتائج متغيّرات التصميم التراكمية (CLS)، لا سيما للمواقع الإلكترونية التي تكتظ بالنصوص.
تعرض الصور التالية مثالاً على المشكلة. تستخدم الصورة الأولى font-display: swap
بدون محاولة تعديل حجم الخط الاحتياطي. ويعرض المستند الثاني كيفية تحسين تجربة التحميل من خلال ضبط الحجم باستخدام قاعدة @font-face
لصفحات الأنماط المتتالية (CSS).
بدون تعديل حجم الخط
body {
font-family: Inter, serif;
}
![نص يتغير فجأة في الخط والحجم، ما يؤدي إلى حدوث تأثير مزعج.](https://developer.chrome.com/static/blog/framework-tools-font-fallback/image/text-suddenly-changes-f-ad5f294238b66.gif?authuser=19&hl=ar)
بعد تعديل حجم الخط
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");
}
![نص ينتقل بسلاسة إلى خط مختلف.](https://developer.chrome.com/static/blog/framework-tools-font-fallback/image/text-transitions-smoothl-200e9965ef42b.gif?authuser=19&hl=ar)
يمكن أن يكون ضبط حجم الخط الاحتياطي استراتيجية فعّالة لمنع متغيّرات تصميم تحميل الخط، ولكن تنفيذ المنطق من البداية قد يكون صعبًا، كما هو موضّح في هذه المشاركة حول العناصر الاحتياطية للخطوط. لحسن الحظ، تتوفر العديد من خيارات الأدوات لتسهيل ذلك أثناء تطوير التطبيقات.
كيفية تحسين العناصر الاحتياطية للخطوط باستخدام 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;
}
للانتقال إلى التالي/الخط:
يمكنك نقل بيان خط 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
. إذا كنت تريد استخدام خط serif، ننصحك بضبط هذه القيمة على Times New Roman
.
من بين خيارات Google Fonts
إذا لم يكن استخدام المكوِّن next/font
متاحًا، يمكنك استخدام العلامة optimizeFonts
كطريقة أخرى لاستخدام هذه الميزة مع Google Fonts. سبق أن تم تفعيل ميزة opt.Fonts تلقائيًا بشكل تلقائي في Next.js. تعمل هذه الميزة على تضمين CSS لخط Google في استجابة HTML. علاوةً على ذلك، يمكنك تفعيل ميزة التعديل الاحتياطي للخط من خلال ضبط علامة experimental.adjustFontFallbacksWithSizeAdjust
في ملف next.config.js، كما هو موضّح في المقتطف التالي:
// In next.config.js
module.exports = {
experimental: {
adjustFontFallbacksWithSizeAdjust: true,
},
}
ملاحظة: لا تتوفّر خطة لإتاحة هذه الميزة مع تطبيق app
dir الذي تم طرحه حديثًا. وعلى المدى الطويل، من الأفضل استخدام next/font
.
كيفية ضبط العناصر الاحتياطية للخطوط باستخدام Nuxt
@nuxtjs/fontaine هي وحدة لإطار عمل Nuxt.js تحسب تلقائيًا قيم مقاييس الخطوط الاحتياطية وتنشئ لغة CSS @font-face
الاحتياطية.
فعِّل الوحدة عن طريق إضافة @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';`
أنشئ حزمة الخطوط، مع تمرير المقاييس كمصفوفة، باستخدام الترتيب نفسه الذي تستخدمه عبر خاصية 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));
شكر وتقدير
صورة رئيسية من تصميم ألكسندر أندرو على Unسباش.