فن التحميل المسبق: التخلص من \"وميض\" السمة باستخدام JavaScript وCSS
Publié le 15 November 2025
ملخص
في هذا المقال التقني المتعمق، نستكشف كيفية تنفيذ محدد السمة (light/dark) دون التأثير المزعج للوميض الذي يحدث أثناء تحميل الصفحة. نحن نحلل بالتفصيل دورة عرض المتصفح، والأسباب العميقة لـ FOUC (Flash of Unstyled Content)، ونقترح حلاً قوياً يستند إلى التحميل المسبق المتزامن في`<head>`. هذه التقنية تضمن تجربة مستخدم سلسة واحترافية.
المشكلة: هذا وميض القالب الملعون
كابوس لتجربة المستخدم
تخيل المشهد: قضيت ساعات في تصميم سمة داكنة رائعة لتطبيقك على الويب. الألوان متوازنة تمامًا، والتعارض مثالي، ومستخدموك يعشقون هذا الخيار. لكن هناك مشكلة محرجة: في كل إعادة تحميل للصفحة، لجزء من الثانية، تظهر السمة الفاتحة الافتراضية قبل أن تتولى السمة الداكنة زمام الأمور.
هذا "فلاش" بصري، وعلى الرغم من قصره (أحيانًا أقل من 100 مللي ثانية)، يُدرك على الفور من قبل العين البشرية ويخلق تجربة غير مريحة. للمستخدمين الذين اختاروا النمط الداكن لأسباب تتعلق براحة البصر أو إمكانية الوصول، قد يكون هذا الوميض حتى مؤلمًا، خاصةً في بيئة ذات إضاءة ضعيفة.
FOUC : مشكلة ويب قديمة
هذه الظاهرة هي نسخة مما يُسمّى «Flash of Unstyled Content» (FOUC)، وهي مشكلة كلاسيكية في تطوير الويب تعود إلى الأيام الأولى لـ CSS. يحدث FOUC عندما يعرض المتصفح مؤقتًا محتوى HTML دون تطبيق أنماط CSS الخاصة به، مما يخلق وميضًا للمحتوى غير منسق.
في حالتنا الخاصة، نحن لا نتحدث عن محتوى غير منسق بالكامل، بل بدلاً منوميض من ثيم خاطئ(FOWT) - المحتوى مُصمم، لكن مع سمة خاطئة. هذا مزعج بشكل خاص لأنه يُظهر أن تطبيقنا "ينسى" تفضيل المستخدم في كل تحميل للصفحة.
التأثير على الإدراك للجودة
هذا المشكل، على الرغم من كونه تقنيًا، له تداعيات مهمة على تصور جودة تطبيقك :
نقص في اللمعانالوميض يعطي انطباعًا بأن التطبيق غير مكتمل أو غير محسن بشكل جيد. غالبًا ما يربط المستخدمون هذه العيوب البصرية الصغيرة بنقص الاحترافية العام.
انقطاع التماسكيبدو أن التطبيق "ينسى" تفضيلات المستخدم، مما يخلق شعورًا بعدم المزامنة بين الواجهة والتوقعات.
إجهاد بصري: للمستخدمين الحساسين للضوء أو المصابين بالصداع النصفي، يمكن أن يكون هذا الوميض الضوئي أكثر من مجرد إزعاج جمالي.
الأداء المدركبشكل ساخر، حتى إذا كان موقعك يُحمَّل بسرعة، يمكن أن يعطي هذا وميضًا الانطباع بأن التطبيق بطيء أو غير متجاوب.
التحليل الفني للمشكلة
لفهم كيفية حل هذه المشكلة، يجب أولاً فهم سبب حدوثها. يحدث الوميض بسبب تأخر زمني بين ثلاث أحداث حاسمة في دورة حياة صفحة ويب :
-
التحليل الأولي لـ HTML: المتصفح يقرأ ويحلل بنية صفحتك
-
تطبيق أنماط CSS: المتصفح يطبق قواعد CSS ويحسب العرض المرئي
-
تنفيذ جافاسكريبت: الكود الذي يغير السمة يتم تنفيذه
المشكلة تحدث عندما يصل الحدث رقم 3 (تنفيذ JavaScript) بعد أن يبدأ المتصفح أو يكمل الحدث رقم 2 (تطبيق الأنماط). في هذه اللحظة، يكون المتصفح قد اتخذ بالفعل قرارًا بشأن أي سمة سيُظهرها، ورمزك يأتي متأخرًا للغاية لتؤثر عليه قبل العرض الأول.
لماذا لا يكفي برنامج نصي كلاسيكي؟
النهج البديهي غير الفعّال
النهج الأكثر طبيعية لمطور أن يضع سكريبت في نهاية لدينا`<body>`الذي يتحقق منtheme المفضَّل للمستخدم ويطبّقه. هذا النهج يتبع أفضل الممارسات التقليدية للويب التي توصي بتحميل النصوص في أسفل الصفحة لتجنب تعطيل العرض.
// À la fin de <body> - L'APPROCHE INSUFFISANTE
document.addEventListener('DOMContentLoaded', () => {
const theme = localStorage.getItem('preferred-theme');
if (theme === 'dark') {
document.documentElement.setAttribute('data-bs-theme', 'dark');
}
});
هذا النهج يبدو منطقيًا للوهلة الأولى. ننتظر أن يصبح DOM جاهزًا ثم نطبق المظهر. بسيط، أليس كذلك؟ لسوء الحظ، هذه البساطة تخفي عيبًا أساسيًا مرتبطًا بتوقيت دورة عرض المتصفح.
فهم الحدث DOMContentLoaded
الحدث`DOMContentLoaded`يُطلق عندما يكون المستند HTML الأولي قد تم تحميله بالكامل وتحليله بواسطة المتصفحبدون انتظارنهاية تحميل أوراق الأنماط، والصور، والإطارات الفرعية. هذه نقطة مهمة لفهمها.
هذه هي السلسلة النموذجية للأحداث:
-
يبدأ المتصفح في تحميل الـHTML
-
يقوم بتحليل الـHTML أثناء استلامه
-
إنه يكتشف الوسوم`<link>`لـ CSS والبدء في تنزيلها
-
يكتشف العلامات`<script>`ويُنَفّذها (وفقًا لنوعها وسماتها)
-
يقوم ببناء DOM (نموذج كائن المستند)
-
حدث
DOMContentLoadedيتم تشغيله -
هو يستمر في تطبيق الأنماط وإجراء التنسيق
-
يحدث أول طلاء (عرض)
-
الحدث`load`يتم التشغيل عند تحميل جميع الموارد
المشكلة؟ بين الخطوة 6 (DOMContentLoaded) والخطوة 8 (الطلاء الأول)، قد اتخذ المتصفح بالفعل قرارات حول كيفية عرض الصفحة. إذا تم تنفيذ سكريبت تغيير السمة في الخطوة 6، فهذا بالفعل متأخر جدًا لتجنب عرض أولي باستخدام الأنماط الافتراضية.
مشكلة حظر العرض
في الواقع، التوقيت أكثر تعقيدًا. يستخدم المتصفحات الحديثة تقنيات تحسين متطورة لتحسين الأداء المدرك. يحاولون جعل أول طلاء (First Contentful Paint) بأسرع ما يمكن، ليتمكن المستخدم من رؤية شيء على الشاشة.
CSS هي "render-blocking" بشكل افتراضي، مما يعني أن المتصفح ينتظر حتى يتم تنزيل وتحليل أوراق الأنماط قبل تنفيذ أول رسم. هذا منطقي: لا نريد عرض محتوى غير منسق.
لكن هذا هو الفخ: عندما يطبق المتصفح هذه الأنماط CSS للمرة الأولى، فإنه يفعل ذلك بناءً على الحالة الحالية لنموذج المستند. إذا كان السمة`data-bs-theme`لم يتم تعريفه بعد على الوسم`<html>`سيطبق المتصفح الأنماط الافتراضية (عادةً سمة فاتحة).
ثم، عندما يقوم النص البرمجي الخاص بك بالتنفيذ وتغيير هذا السمة، يجب على المتصفح:
-
إعادة حساب جميع الأنماط المتأثرة بهذا التغيير
-
إعادة تنسيق التصميم إذا لزم الأمر
-
إعادة طلاء العناصر المتأثرة
هذه العملية لإعادة الحساب وإعادة الطلاء هي ما يسبب الوميض المرئي.
تصوّر المشكلة
لفهم أفضل لهذه التسلسلية المشكلة، دعونا نفحص مخططًا تفصيليًا للتسلسل :
Failed to generate image: PlantUML preprocessing failed: [From <input> (line 27) ]
@startuml
actor Utilisateur
participant Navigateur
participant "محلل HTML" as Parser
participant "CSSOM" as CSS
participant "Script.js (نهاية body)" as Script
participant "محرك التصيير" as Render
Utilisateur -> Navigateur: Demande la page
activate Navigateur
Navigateur -> Parser: Parse le HTML
activate Parser
Parser -> CSS: Découvre et charge les CSS
activate CSS
Parser -> Parser: Construit le DOM
Parser -> Navigateur: DOM Ready (DOMContentLoaded)
deactivate Parser
CSS -> CSS: Parse les feuilles de style
CSS -> Render: CSSOM Ready
deactivate CSS
activate Render
Render -> Render: Calcule les styles avec data-bs-theme=""ملاحظة اليمين: الموضوع الافتراضي (light)
Render -> المتصفح: أول رسم (الموضوع الفاتح)
ملاحظة اليمين: يرى المستخدم الموضوع الفاتح
^^^^^
Syntax Error? (Assumed diagram type: sequence)
@startuml
actor Utilisateur
participant Navigateur
participant "محلل HTML" as Parser
participant "CSSOM" as CSS
participant "Script.js (نهاية body)" as Script
participant "محرك التصيير" as Render
Utilisateur -> Navigateur: Demande la page
activate Navigateur
Navigateur -> Parser: Parse le HTML
activate Parser
Parser -> CSS: Découvre et charge les CSS
activate CSS
Parser -> Parser: Construit le DOM
Parser -> Navigateur: DOM Ready (DOMContentLoaded)
deactivate Parser
CSS -> CSS: Parse les feuilles de style
CSS -> Render: CSSOM Ready
deactivate CSS
activate Render
Render -> Render: Calcule les styles avec data-bs-theme=""ملاحظة اليمين: الموضوع الافتراضي (light)
Render -> المتصفح: أول رسم (الموضوع الفاتح)
ملاحظة اليمين: يرى المستخدم الموضوع الفاتح
إلغاء تنشيط Render
المتصفح -> البرنامج النصي: ينفذ البرنامج النصي
تنشيط البرنامج النصي
البرنامج النصي -> البرنامج النصي: يقرأ localStorage
البرنامج النصي -> المتصفح: setAttribute('data-bs-theme', 'dark')
إلغاء تنشيط البرنامج النصي
تنشيط Render
Render -> Render: إعادة حساب الأنماط مع data-bs-theme="dark"
note right: Thème sombre
Render -> Navigateur: Repaint (Thème Sombre)
note right: Clignotement visible
deactivate Render
deactivate Navigateur
@enduml
يوضح هذا المخطط بوضوح المشكلة: يحدث أول رسم قبل أن يحصل سكريبتنا على فرصة لتعيين السمة الصحيحة. إعادة الرسم اللاحقة تسبب وميضًا مرئيًا.
المحاولات غير الفعالة للحل
تم تجربة عدة مناهج لحل هذه المشكلة، لكن معظمها لها عيوبها الخاصة:
النهج 1 : إخفاء المحتوى حتى التحميل
body {
opacity: 0;
transition: opacity 0.3s;
}
body.loaded {
opacity: 1;
}
تقوم هذه الطريقة بإخفاء كل المحتوى حتى يحدد JavaScript السمة المناسبة. المشكلة؟ هذا يؤخر بشكل مصطنع عرض المحتوى، مما يعطي انطباعًا بأن الموقع أبطأ. علاوة على ذلك، إذا كان JavaScript معطلاً، فلن يرى المستخدم شيئًا على الإطلاق!
النهج 2: استخدام محمل/دوار
مشابه للنهج 1، ولكن مع عجلة تحميل. هذا يخفي المشكلة لكنه لا يحسن الأداء الفعلي ويضيف تأخيرًا مدركًا غير ضروري.
النهج 3 : الافتراضي إلى السمة الداكنة
بعض المطورين يحددون السمة المظلمة كافتراضي في CSS. هذا يتجنب الوميض لمستخدمي السمة المظلمة، لكنه يخلق المشكلة المعاكسة لمستخدمي السمة الفاتحة !
لا تفي أي من هذه الأساليب بالغرض لأنها تعالج الأعراض بدلاً من السبب الجذري للمشكلة.
الحل الحقيقي: التصرف في وقت أقرب
المفتاح لحل هذه المشكلة هو أن ندرك أنه يجب علينا تحديد السمة`data-bs-theme` قبلأن المتصفح لا يبدأ في تطبيق أنماط CSS. هذا يعني أن برنامجنا النصي يجب أن يُنفّذ في وقت سابق في دورة حياة الصفحة، وهذا exatamente ما سنستكشفه في القسم التالي.
الحل : التحميل المسبق (التحميل المبكر)
المبدأ الأساسي
الحل الأنيق لمشكلتنا المتعلقة بالوميض يعتمد على مبدأ بسيط لكن قوي :مزامنة حالة التطبيق مع عملية عرض المتصفح. بدلاً من الانتظار حتى تُحمّل الصفحة لتعيين السمة، يجب علينا تعيينهأثناءالتحميل، حتى قبل تطبيق أنماط CSS
هذه الطريقة تُسمى "Early Loading" أو "Synchronous Preloading" في مصطلحات تطوير الويب. الفكرة هي تنفيذ منطق كشف السمة في أقرب وقت ممكن في دورة حياة الصفحة، وبشكل مثالي داخل العلامة`<head>`, قبل أن يبدأ المتصفح في تنزيل ملفات CSS.
لماذا <head> هو المكان المثالي
Le `<head>`يتم معالجة مستند HTML بشكل تسلسلي بواسطة المتصفح، من الأعلى إلى الأسفل. يتم معالجة كل عنصر بالترتيب الذي يظهر فيه. هذه الخاصية حاسمة لحلنا.
عندما يواجه المتصفح وسم`<script>`في`<head>`بدون الخصائص`async` ou defer, il :
-
يقاطع تحليل HTML
-
حمّل البرنامج النصي(إذا كان خارجيًا) أو السرير (إذا كان داخليًا)
-
نفّذ النص البرمجي فورًا
-
استأنف تحليل HTML
هذا السلوك، الذي غالبًا ما يُعتبر مشكلة أداء (ومن هنا التوصية المعتادة بوضع النصوص في نهاية الصفحة)، يصبح حليفنا في هذه الحالة المحددة. من خلال وضع نص كشف السمات الخاص بنا في بداية`<head>`, نحن نضمن أنه يتم تنفيذه قبل أن يواجه المتصفح العلامات`<link>`من ملفات الأنماط الخاصة بنا.
هندسة الحل ذات الثلاث couches
الحل الكامل الخاص بنا يتكون من ثلاث طبقات مترابطة، كل منها يلعب دورًا محددًا :
الطبقة 1 : الاستمرارية (localStorage) هذه الطبقة مسؤولة عن حفظ واستعادة اختيار المستخدم بين الجلسات.
الطبقة 2 : المزامنة المبكرة (سكريبت مضمن داخل <head>) هذه الطبقة تتزامن حالة التطبيق مع الـ DOM قبل العرض الأولي.
الطبقة 3: الأنماط المتجاوبة (CSS مع محددات السمات) هذه الطبقة تحدد الأنماط البصرية بناءً على الحالة المحددة من قبل الطبقة 2.
لنستكشف الآن كل طبقة بالتفصيل.
الخطوة 1: حفظ اختيار المستخدم
الـ localStorage : ذاكرتك المستمرة
Le localStorage`هي واجهة برمجة تطبيقات Web Storage التي تسمح بتخزين أزواج المفاتيح والقيم في المتصفح بشكل دائم. على عكس ملفات تعريف الارتباط، البيانات`localStorage:
-
لا تُرسل أبداً إلى الخادم تلقائياً
-
لديهم قدرة تخزين أكبر (عادةً 5-10 ميجابايت)
-
ليس لديهم تاريخ انتهاء صلاحية (يستمر حتى الحذف الصريح)
-
إنها تقتصر على البروتوكول والنطاق (سياسة المصدر نفسه)
لحالة الاستخدام الخاصة بنا،`localStorage`هو مثالي لأن:
-
ليس لدينا حاجة لمشاركة هذه المعلومات مع الخادم
-
نريد أن يظل التفضيل مستمرًا إلى الأبد
-
حجم التخزين المطلوب قليل (عدة بايتات)
تنفيذ نسخة احتياطية للقالب
هذا هو كيف نحفظ اختيار المستخدم عندما يغير السمة :
// Fonction complète pour changer le thème
function setTheme(newTheme) {
// Validation de l'entrée
if (!['light', 'dark', 'auto'].includes(newTheme)) {
console.error('Thème invalide:', newTheme);
return;
}
try {
// Sauvegarde dans localStorage
localStorage.setItem('preferred-theme', newTheme);
// Application immédiate dans le DOM
document.documentElement.setAttribute('data-bs-theme', newTheme);
// Dispatch d'un événement personnalisé pour notifier d'autres composants
window.dispatchEvent(new CustomEvent('theme-changed', {
detail: { theme: newTheme }
}));
console.log('Thème changé:', newTheme);
} catch (error) {
console.error('Erreur lors de la sauvegarde du thème:', error);
// Fallback : on applique quand même le thème visuellement
document.documentElement.setAttribute('data-bs-theme', newTheme);
}
}
// Exemple d'utilisation avec un bouton
document.getElementById('theme-toggle').addEventListener('click', () => {
const currentTheme = document.documentElement.getAttribute('data-bs-theme') || 'light';
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
setTheme(newTheme);
});
إدارة حالات الخطأ
من الضروري إدارة الحالات التي`localStorage`غير متوفر أو غير قابل للوصول. عدة سيناريوهات قد تمنع الوصول إلى`localStorage` :
التصفح الخاص الصارم: Safari في وضع التصفح الخاص يثير استثناء`QuotaExceededError`أثناء محاولات الكتابة في`localStorage`.
إعدادات الخصوصية: بعض المتصفحات أو امتدادات الخصوصية يمكنها حظر الوصول إلى`localStorage`.
قيود المجال : Le `localStorage`غير قابل للوصول عبر البروتوكول`file://`في بعض المتصفحات
مساحة التخزين ممتلئةعلى الرغم من ندرته، يمكن أن يتم ملء مساحة التخزين بالكامل.
لهذا السبب يستخدم كودنا كتلة`try…catch`لإدارة هذه الحالات بسلاسة، مع الاستمرار في تقديم وظيفة تغيير السمة حتى إذا لم تكن الاستمرارية متاحة.
استراتيجيات متقدمة للاستمرارية
للتطبيقات الأكثر تطورًا، يمكنك التفكير في استراتيجيات إضافية :
مزامنة الخادم (اختياري)
async function setTheme(newTheme) {
// Sauvegarde locale immédiate
localStorage.setItem('preferred-theme', newTheme);
document.documentElement.setAttribute('data-bs-theme', newTheme);
// Synchronisation serveur en arrière-plan (si l'utilisateur est connecté)
if (userIsAuthenticated()) {
try {
await fetch('/api/user/preferences', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ theme: newTheme })
});
} catch (error) {
console.warn('Échec de la synchronisation serveur:', error);
// L'échec n'est pas critique car la préférence est déjà sauvegardée localement
}
}
}
تسمح هذه الطريقة بمزامنة التفضيلات بين الأجهزة للمستخدمين المتصلين، مع الحفاظ على الاستجابة الفورية المحلية.
الخطوة 2 : نص التحميل المسبق في `<head>
قلب الحل
هنا يحدث السحر حقًا. سنضع نصًا برمجيًا صغيرًا.مضمنبشكل مباشر فينا`<head>`, قبل جميع وسومنا`<link>`من أوراق الأنماط. هذا النص البرمجي متعمدًا أن يكون بسيطًا، مستقلًا، ومصممًا ليُنفّذ بأسرع ما يمكن.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mon Site Incroyable</title>
<!-- ==========================================
NOTRE SCRIPT MAGIQUE DE PRÉ-CHARGEMENT
Ce script DOIT être le premier élément
dans le <head> après les meta tags
========================================== -->
<script>
// IIFE pour ne pas polluer le scope global
(function() {
'use strict';
try {
// Lecture de la préférence sauvegardée
const savedTheme = localStorage.getItem('preferred-theme');
// Si une préférence existe, on l'applique immédiatement
if (savedTheme) {
document.documentElement.setAttribute('data-bs-theme', savedTheme);
}
// Optionnel : Détecter la préférence système si aucune sauvegarde
else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-bs-theme', 'dark');
}
// Sinon, le thème par défaut du CSS sera utilisé (généralement 'light')
} catch (error) {
// En cas d'erreur (localStorage bloqué, etc.), on log discrètement
// et on laisse le thème par défaut s'appliquer
console.warn('Impossible de charger la préférence de thème:', error);
}
})();
</script>
<!-- FIN DU SCRIPT MAGIQUE -->
<!-- Les feuilles de style sont chargées APRÈS le script -->
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/styles.css">
<!-- Autres ressources du head -->
<link rel="icon" href="favicon.ico">
</head>
<body>
<!-- Contenu de la page -->
</body>
</html>
تشريح السيناريو : كل سطر يهم
لنحلل هذا السطر سطرًا تلو الآخر لفهم كل قرار تصميمي:
الـ IIFE (التعبير الوظيفي الذي يتم استدعاؤه فورًا)
(function() {
// ...
})();
هذه البنية تنشئ دالةً تُنفَّذ فورًا. لماذا؟ لعزل متغيراتنا في نطاق محلي وتجنب تلوث النطاق العالمي. حتى إذا لم نستخدم سوى`const`(الذي يملك نطاق الكتلة)، IIFE هي ممارسة جيدة تجعل نوايانا واضحة وتحمي من تعارضات الأسماء المحتملة.
الوضع الصارم
'use strict';
هذه التوجيه تُفعِّل الوضع الصارم في JavaScript، والذي: يحظر استخدام المتغيرات غير المعلنة - يولد أخطاء للعمليات الخطرة يحسن الأداء في بعض محركات JavaScript
نريد أقصى درجات الأمان لنص برمجي حاسم مثل هذا.
كتلة try…catch
try {
// Code principal
} catch (error) {
console.warn('Impossible de charger la préférence de thème:', error);
}
هذا المقطع ضروري تمامًا. يضمن أنه إذا حدث شيء ما خطأً (localStorage محظور، خطأ بناء غير محتمل، إلخ)، فلن يعطل سكريبتنا تحميل الصفحة بالكامل. استخدام`console.warn`بدلاً من`console.error`يشير إلى أن المشكلة غير حرجة.
القراءة من localStorage
const savedTheme = localStorage.getItem('preferred-theme');
قد تثير هذه الخطوة استثناءً في بعض السياقات (التصفح الخاص الصارم في سفاري). لهذا السبب يتم وضعها في كتلة try…catch.
التطبيق الشرطي
if (savedTheme) {
document.documentElement.setAttribute('data-bs-theme', savedTheme);
}
نطبق السمة فقط إذا وجدنا واحدة محفوظة. وإلا، نسمح لـ CSS باستخدام السمة الافتراضية. هذا النهج أكثر قوة من قيمة افتراضية مُضمنة بشكل ثابت في JavaScript.
كشف تفضيل النظام (Bonus)
تحسين اختياري لكن أنيق هو اكتشاف تفضيل السمة لنظام التشغيل للمستخدم إذا لم يقم بعد باتخاذ خيار صريح في تطبيقك :
else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-bs-theme', 'dark');
}
هذه الميزة تستخدم Media Query`prefers-color-scheme`للاستعلام عن النظام. على macOS, Windows 10+, iOS، وAndroid الحديث، تُعيد هذه الاستعلام تفضيل النظام للمستخدم.
المميزات : - تجربة مخصصة من أول زيارة - اتساق مع بيئة نظام المستخدم - لا يلزم تخزين للزيارة الأولى
اعتبارات : - ليس جميع المتصفحات تدعم هذه الميزة (لكن الدعم ممتاز منذ عام 2020) - التحقق`window.matchMedia`يضمن التوافق - يمكن للمستخدم دائمًا تجاوز هذا الاختيار
الأداء: لماذا هذا النص البرمجي سريع
سكريبت التحميل المسبق مصمم ليكون سريعًا للغاية :
حجم بسيطحوالي 300 بايت غير مُصَغّر، 200 بايت مُصَغَّر. هذا ضئيل مقارنة بأي صورة أو مكتبة جافا سكريبت.
ضمن السطرلا توجد طلبية HTTP إضافية. النص البرمجي موجود في HTML، لذلك فهو متاح فورًا.
عمليات متزامنة بسيطة: قراءة مفتاح في localStorage (عملية فائقة السرعة) وتعديل سمة DOM (عملية أصلية في المتصفح).
لا يوجد اعتماد: لا إطار عمل، لا مكتبة، مجرد جافا سكريبت فانيليا. لا وقت تشغيل، لا تحليل للتبعية.
تنفيذ وحيدهذا النص البرمجي يُنفَّذ مرة واحدة فقط عند التحميل. لا توجد مستمعات أحداث، ولا حلقات، ولا حسابات معقدة.
من الناحية العملية، على الأجهزة الحديثة، ينفذ هذا البرنامج النصي في أقل من مللي ثانية، وهو زمن غير محسوس لا يؤثر على أداء تحميل الصفحة.
وضع مثالي في `<head>
ترتيب العناصر في`<head>`مهم. هذا الترتيب الموصى به :
<head>
<!-- 1. Métadonnées critiques -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 2. Notre script de pré-chargement (IMMÉDIATEMENT après les meta) -->
<script>
(function() { /* notre code */ })();
</script>
<!-- 3. Titre de la page -->
<title>Mon Site</title>
<!-- 4. Feuilles de style -->
<link rel="stylesheet" href="styles.css">
<!-- 5. Autres ressources (fonts, favicons, etc.) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="icon" href="favicon.ico">
<!-- 6. Autres scripts avec defer ou async -->
<script src="app.js" defer></script>
</head>
هذا الأمر يضمن أن :
-
تم تعريف مجموعة الأحرف قبل أي معالجة نصية
-
ينفذ سكريبتنا قبل تحميل ملفات CSS
-
ثم يتم تحميل ملفات CSS وتطبق مباشرة السمة الصحيحة
-
الموارد غير الحرجة المتبقية يتم تحميلها أخيرًا
الخطوة 3: القوة باستخدام محددات السمات CSS
نظام الثيم في Bootstrap 5
Bootstrap 5 قدم نظامًا أنيقًا لإدارة المظهر المستند إلى الخصائص المخصصة CSS (متغيرات CSS) ومحددات السمات. يستخدم هذا النظام السمة`data-bs-theme`على العنصر`<html>`لتحديد أي مجموعة من متغيرات اللون لتطبيقها
جمال هذا النظام يكمن في بساطته: بدلاً من تحميل أوراق أنماط مختلفة أو تبديل الفئات على آلاف العناصر، نغيّر ببساطة سمة على عنصر واحد، ويظلّ CSS يكمّل الباقي بفضل التدفق المتسلسل.
هيكل CSS لنظام ثيم
فيما يلي بنية CSS كاملة لتنفيذ نظام موضوع قوي :
/**
* SYSTÈME DE THÈME COMPLET
* Utilise les Custom Properties CSS pour une maintenance facile
*/
/* ============================================
THÈME PAR DÉFAUT (LIGHT)
Défini sur :root pour être le fallback
============================================ */
:root {
/* Couleurs de base */
--color-primary: #0d6efd;
--color-secondary: #6c757d;
--color-success: #198754;
--color-danger: #dc3545;
--color-warning: #ffc107;
--color-info: #0dcaf0;
/* Couleurs de fond */
--bg-primary: #ffffff;
--bg-secondary: #f8f9fa;
--bg-tertiary: #e9ecef;
/* Couleurs de texte */
--text-primary: #212529;
--text-secondary: #6c757d;
--text-tertiary: #adb5bd;
/* Couleurs de bordure */
--border-color: #dee2e6;
--border-color-subtle: #e9ecef;
/* Couleurs d'ombre */
--shadow-sm: rgba(0, 0, 0, 0.075);
--shadow-md: rgba(0, 0, 0, 0.15);
--shadow-lg: rgba(0, 0, 0, 0.25