Table des matières

সারসংক্ষেপ

এই গভীর টেকনিক্যাল নিবন্ধে, আমরা কীভাবে (light/dark) থিম সিলেক্টরকে পৃষ্ঠা লোড হওয়ার সময় হওয়া খারাপ জ্বিমকানোর ইফেক্ট ছাড়াই প্রয়োগ করা যায় সেটি পর্যালোচনা করি। আমরা ব্রাউজারের রেন্ডারিং চক্রকে বিস্তারিতভাবে বিশ্লেষণ করি, FOUC (Flash of Unstyled Content) এর গভীর কারণগুলোকে, এবং সিঙ্ক্রোনাস প্রি-লোডিং ভিত্তিক একটি শক্তিশালী সমাধান প্রস্তাব করি। এTrong`<head>`. এই কৌশলটি সুপট এবং পেশাদার ব্যবহারকারী অভিজ্ঞতা নিশ্চিত করে।

সমস্যা : এই শাপিত থিম ফ্লিকার

ব্যবহারকারী অভিজ্ঞতার জন্য একটি দুর্নিদ্রা

দৃশ্য কল্পনা করুন: আপনি আপনার ওয়েব অ্যাপ্লিকেশনের জন্য একটি সুন্দর ডার্ক থিম তৈরি করতে ঘন্টাবده সময় ব্যয় করেছেন। রঙগুলো exactamente সাম্য করা আছে, কনট্রাস্ট অপ্টিমাল, এবং আপনার ব্যবহারকারীরা এই বিকল্পটি খুব পছন্দ করে। কিন্তু একটি অপমানজনক সমস্যা আছে: প্রতিটি পৃষ্ঠা রিলোডের সময়, একটু সময়ের অংশের জন্য, ডিফল্ট লাইট থিম দৃশ্যমান হয় এবং তারপরে ডার্ক থিম দখল করে নেয়।

দৃশ্য "flash" অল্প সময়ের (কখনও ১০০ মিলিসেকেন্ডের কমও), মানুশ চোখে তৎক্ষণিকভাবে প্রাপ্য হয় এবং একটি অসুখদায়ক অভিজ্ঞতা সৃষ্টি করে। দৃশ্য��বিধা অথবা অ্যাক্সেসিবিলিটির কারণে ডার্ক টেমা বেছে নেন ব্যবহারকারীদের জন্য, এই জ্বালানী কখনও কখনও ব্যথাদায়ক হতে পারে, বিশেষত আলো কমা পরিবেশে।

FOUC : পুরানো ওয়েব সমস্যা

এই ফোমেনটি Flash of Unstyled Content (FOUC) নামের কিছুকে একটি ভেরিয়েন্ট যা ওয়েব ডেভেলপমেন্টের একটি ক্লাসিক সমস্যা, যা CSS‑র আওতায় প্রথম দিনগুলো থেকে বিদ্যমান। FOUC তখন ঘটে যখন ব্রাউজার לזمانی HTML কনটেন্টকে তার CSS‑কে প্রয়োগ না করে দেখায়, ফলে অনস্টাইল্ড কনটেন্টের একটি ফ্ল্যাশ তৈরি হয়।

আমাদের নির্দিষ্ট ক্ষেত্রে, আমরা সম্পূর্ণরূপে স্টাইল করা হয়নি এমন কোনো বিষয়বস্তু সম্পর্কে বলছি না, বরং একটিভুল থিমের ফ্ল্যাশ(FOWT) - কন্টেন্টটি স্টাইল করা হয়েছে, কিন্তু ভুল থিমে। এটি খুব বিরক্তিকর, কারণ এটি দেখায় যে আমাদের অ্যাপ্লিকেশনটি "বুলে যায়" ব্যবহারকারীর পছন্দকে প্রতিটি পৃষ্ঠা লোডে।

গুণমানের ধারণার উপর প্রভাব

এই সমস্যা, যদিও প্রযুক্তিগত, আপনার অ্যাপ্লিকেশনের গুণমানের ধারণায় গুরুত্বপূর্ণ প্রভাব ফেলে :

কচাপনদমকান একটি অসম্পূর্ণ অথবা খারাপভাবে অপ্টিমাইজ করা প্রোগ্রামের মতো মনে করে দাঁড়ায়। ব্যবহারকারীরা এই ছোট দৃষ্টিগত ত্রুটিগুলোকে সাধারণত সামগ্রিক পেশাদারিত্বের অভাবের সাথে যুক্ত করে Soxen.

একতা ভঙ্গঅ্যাপ্লিকেশন মনে হচ্ছে ব্যবহারকারীর পছন্দগুলো ভুলে যাচ্ছে, ইন্টারফেস এবং ব্যবহারকারীর আশের মধ্যে অসমান্বয়ের অনুভূতি তৈর করছে।

দৃষ্টি ক্লান্তি: আলোকে সংবেদনশীল ব্যবহারকারী বা মাইগ্রেন-affected, এই উজ্জ্বল ফ্ল্যাশ শুধু একটি কসמטিক অসুবিধার চেয়ে বেশি হতে পারে।

অভijanিত কর্মক্ষমতাআরোদ্ভুতভাবে, আপনার সাইট যদিও দ্রুত লোড হয়, এই জ্বালান ধীর বা কম প্রতিক্রিয়াশীল একটি অ্যাপ্লিকেশনের মতো মনে হওয়ার অনুভূতি তৈরি করতে পারে।

সমস্যার প্রযুক্তিগত বিশ্লেষণ

এই সমস্যা কীভাবে সমাধান করতে হয় তা বুঝতে, প্রথমে বুঝতে হবে যে এটি কেন ঘটে। পৃষ্ঠার বিলিৎ (ব্লিংক) একটি ওয়েব পৃষ্ঠারজীবন চক্রের তিনটি গুরুত্বপূর্ণ ইভেন্ট মধ্যে সময়কালের পার্থক্যের কারণে ঘটে।

  1. HTML‑এর প্রারম্ভিক পার্সিং: ব্রাউজার আপনার পৃষ্ঠার গঠন পড়ে এবং বিশ্লেষণ করে

  2. CSS শৈলী প্রয়োগব্রাউজারটি CSS নিয়মগুলো প্রয়োগ করে এবং দৃশ্য রেন্ডারিংের গণনা করে

  3. জাভাস্ক্রিপ্টের কার্যকর্য: আপনার থিম পরিবর্তনকারী কোডটি চলছে

সমস্যা ঘটে যখন ইভেন্ট n°3 (জাভাস্ক্রিপ্টের এক্সিকিউশন) ব্রাউজার ইভেন্ট n°2 (স্টাইল প্রয়োগ) শুরু বা শেষ করার পরে আসে। সেই সময়ে, ব্রাউজার কোনো থিম দেখাবে সে সম্পর্কে একটি সিদ্ধান্ত ইতিমধ্যে নেয়া হয়েছে, এবং আপনার কোড প্রথম রেন্ডার থেকে আগে তা প্রভাবিত করতে cukup দেরি করে আসে।

কেন একটি ক্লাসিক স্ক্রিপ্ট পর্যাপ্ত নয়?

অনুভূতিগমক পদ্ধতি কিন্তু অকার্যকর

এক উন্নয়নকারীকে সবচেয়ে স্বাভাবিক পদ্ধতির অনুসারে একটি স্ক্রিপ্ট আমাদের শেষে রাখা উচিত`<body>`ব্যবহারকারীর পছন্দের থিমটি পরীক্ষা করে এবং প্রয়োগ করে। এই পদ্ধতিটি প্যারম্পারিক ওয়েবের সর্বোত্তম অনুশীলন অনুসরণ করে, যা পৃষ্ঠার শেষে স্ক্রিপ্ট লোড করার সুপারিশ করে যাতে রেন্ডারিং ব্লক না হয়।

// À 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 ডকুমেন্টটি সম্পূর্ণরূপে লোড ও ব্রাউজার द्वारा বিশ্লেষণ করা হলে তা সক্রিয় হয়,অপেক্ষা না করেইশৈলী শীটের, চিত্র ও সাবফ্রেমের লোডিং সমাপ্তি। এটি একটি গুরুত্বপূর্ণ বিন্দু যা বোঝা দরকার।

এটি ইভেন্টগুলোর স্বাভাবিক অনুক্রম:

  1. ব্রাউজার HTML ডাউনলোড শুরু করে

  2. এটি HTML-কে পাওয়ার সাথে সাথে বিশ্লেষণ করে।

  3. সে ট্যাগগুলি আবিষ্কার করে`<link>`CSS-_এর জন্য এবং их ডাউনলোড শুরু কর�ে

  4. সে ট্যাগগুলো খুঁজে পায়`<script>`এবং সেগুলোকে (তাদের ধরণ ও বৈশিষ্ট্যানুসারে) এক্সিকিউট করে

  5. এটি ডকুমেন্ট অবজেক্ট মডেল (DOM) তৈরি করে

  6. ইভেন্ট DOMContentLoaded ট্রিগার হয়

  7. সে স্টাইলগুলো প্রয়োগ করছে এবং লেআউট তৈরি করছে।

  8. প্রথম পেইন্ট (প্রদর্শন) হয়

  9. ঘটনা`load`এটি ট্রিগার হয় যখন সম্পূর্ণ সমস্ত সংস্থান লোড হয়

সমস্যা কি? ধাপ 6 (DOMContentLoaded) এবং ধাপ 8 (premier paint) এর মধ্যে, ব্রাউজারটি পৃষ্ঠাটি কীভাবে দেখাবে সে সম্পর্কে ইতিমধ্যে সিদ্ধান্ত নেয়েছে। যদি আপনার থিম পরিবর্তন স্ক্রিপ্ট ধাপ 6-এ চালু হয়, তাহলে ডিফল্ট শৈলির সাথে প্রথম প্রদর্শন এড়িয়ে যাওয়া ইতিমধ্যে অতিক্রম করেছে।

রেন্ডার ব্লকিং সমস্যা

বাস্তবে, টাইমিং আরও জটিল। আধুনিক ব্রাউজার সুসংলগ্ন অপ্টিমাইজেশন কৌশল ব্যবহার করে অনুভূত কর্মক্ষমতা উন্নত করে। তারা প্রথম পেইন্ট (First Contentful Paint) যত তাড়াতাড়ি সম্ভব করতে চেষ্টা করে, যাতে ব্যবহারকারী স্ক্রীনে কিছু দেখতে পায়।

CSS-এ "render-blocking" ডিফল্টরূপে থাকে, যা বোঝায় যে ব্রাউজার প্রথম পেইন্ট করার আগে স্টাইলশিট ডাউনলোড এবং পার্স শেষ করতে অপেক্ষা করে। এটি যুক্তিগত: আমরা অনস্টাইল করা কন্টেন্ট দেখাতে চাই না।

এখানে ফাঁসটি হলো: যখন ব্রাউজার প্রথমবার এই CSS স্টাইলগুলি প্রয়োগ করে, তখন তা বর্তমান DOMের অবস্থার ভিত্তিতে করে। যদি বৈশিষ্ট্য`data-bs-theme`ট্যাগে এখনো সেট করা হয়নি`<html>`, ব্রাউজারটি ডিফল্ট স্টাইলে প্রয়োগ করবে (সাধারণত নিষ্ক্রিয়া থিম).

তারপরে, যখন আপনার স্ক্রিপ্ট চালু হয় এবং এই বৈশিষ্ট্য পরিবর্তন করে, браузе করবে :(No, that is not correct. The correct Bengali phrase for "browser must" is "ব্রাউজারকে করতে হবে". Let’s finalize.

Final answer: "তারপরে, যখন আপনার স্ক্রিপ্ট চালু হয় এবং এই বৈশিষ্ট্য পরিবর্তন করে, ব্রাউজারকে করতে হবে :" Actually original French ends with colon. Keep colon at end.

Thus output: "তারপরে, যখন আপনার স্ক্রিপ্ট চলছে এবং এই বৈশিষ্ট্য পরিবর্তন করে, ব্রাউজারকে করতে হবে :"

Check: "s’exécute" = "runs", "changing". "chale" maybe "chole"? "চালু হয়" fine. So final.

</think>

তারপরে, যখন আপনার স্ক্রিপ্ট চালু হয় এবং এই বৈশিষ্ট্য পরিবর্তন করে, ব্রাউজারকে করতে হবে :

  1. এই পরিবর্তন থেকে প্রভাবিত সব স্টাইলে পুনরগণনা করুন

  2. প্রয়োজনে লেআউট পুনরায় তৈরি করুন

  3. প্রভাবিত উপাদানগুলো পুন: রেন্জ করুন

এই পুন গণনা ও পুন রং রঙার প্রক্রিয়া হল যা দৃশ্যমান ঝillation সৃষ্টি করে।

সমস্যা ভিজ্যুয়ালাইজেশন

এই সমাসসংক্রান্ত ক্রমকে ভালোভাবে বুঝতে, একটি বিস্তারিত ক্রম নকশা দেখাইয়া বসাই:

Failed to generate image: PlantUML preprocessing failed: [From <input> (line 26) ]

@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=""note right: ডিফল্ট থিম (light)
Render -> নেভিগেটর: Premier Paint (আলো থিম)
^^^^^
 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=""note right: ডিফল্ট থিম (light)
Render -> নেভিগেটর: Premier Paint (আলো থিম)
note right: ব্যবহারকারীটি আলো থিম দেখে
deactivate Render

নেভিগেটর -> স্ক্রিপ্ট: স্ক্রিপ্ট চালায়
অ্যাক্টিভেট Script
স্ক্রিপ্ট -> স্ক্রিপ্ট: localStorage পড়ে
স্ক্রিপ্ট -> নেভিগেটর: setAttribute('data-bs-theme', 'dark')
deactivate Script

অ্যাক্টিভেট 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

এই ডায়াগ্রামটি সমস্যাকে স্পষ্টভাবে দেখায়: প্রথম পেইন্ট আমাদের স্ক্রিপ্টকে সঠিক থিম সেট করার সুযোগ পাওয়ার আগে ঘটে। পরবর্তী রেপেইন্ট দৃশ্যমান জ্বলে উঠার কারণ তৈরি করে।

অকার্যকর সমাধান প্রচেষ্টা

এই সমস্যার সমাধান করার জন্য বিভিন্ন পদ্ধতি চেষ্টা করা হয়েছে, কিন্তু অধিকাংশের নিজস্ব অসুবিধা আছে :

পদ্ধতি ১ : লোড হওয়া পর্যন্ত বিষয়বস্তুটি লুকান

body {
  opacity: 0;
  transition: opacity 0.3s;
}

body.loaded {
  opacity: 1;
}

এই পদ্ধতিটি JavaScript সঠিক থিম সেট না করা পর্যন্ত সমস্ত কন্টেন্ট লুকায় দেয়। সমস্যা? এটি কৃত্রিমভাবে কন্টেন্টের প্রদর্শনকে বন্ধ করে তোলে, যা সাইটকে ধীর মনে করায়। আর, যদি JavaScript অক্ষম করা হয়, ব্যবহারকারী কখনো কিছুই দেখে না!

প্রতিরণ 2: লোডার/স্পিনার ব্যবহার করুন

উপায় ১-এর মতো, কিন্তু লোডিং সপিনারের সাথে। এটি সমस्यাটিকে ঢাকা দেয়, বাস্তব পারফরম্যান্স উন্নত করে না এবং অপ্রয়োজনীয় অনুভূতবিহীন বিলম্ব যোগ করে।

উপায় 3: সন্ধ্যাবেলা থিমে ডিফল্ট

কিছু ডেভেলপার CSS-এ ডার্ক থিমটি ডিফল্ট হিসেবে সেট করে। এটি ডার্ক থিম ব্যবহারকারীদের জন্য জ্বালানোর সমস্যা থেকে রক্ষা করে, কিন্তু হালকা থিম ব্যবহারকারীদের জন্য বিপরীত সমস্যা তৈরি করে।

একটিও এই পদ্ধতিগুলি সন্তোষজনক নয়, কারণ তারা সমস্যার মূল কারণের পরিবর্তে লক্ষণকে মেলে নেয়।

আসল সমাধান: আমোদর কর!

সমস্যার সমাধানের কী হলো বুঝতে যে আমাদের এই বৈশিষ্ট্য সংজ্ঞায়িত করতে হবে`data-bs-theme` আগেব্রাউজার CSS স্টাইল প্রয়োগ শুরু না করলে। এই বোঝায় যে আমাদের স্ক্রিপ্টটিকে পৃষ্ঠার জীবনচক্রের আগে চালাতে হবে, এবং এটি ঠিক সেই বিষয়টিই যা আমরা পরবর্তী বিভাগে অনুসন্ধান করব।

সমাধান : প্রারম্ভিক লোডিং (Early Loading)

মৌলিক নীতি

আমাদের চকচকে সমস্যার শালী সমাধান একটি সহজ কিন্তু শক্তিশালী নিয়মের ভিত্তিতে আছে :অ্যাপ্লিকেশনের অবস্থা ব্রাউজারের রেন্ডারিং প্রক্রিয়ার সাথে সিঙ্ক্রোনাইজ করা. পেজ লোড হওয়ার অপেক্ষা না করে, থিম (বিষয়) সেট করার পরিবর্তে, আমরা তা সেট করতে হবে।সময়লোডিং, CSS শৈলী প্রয়োগ হওয়ার আগেও

এই পদ্ধতিটি উন্নয়ন জার্গনে 'Early Loading' বা 'Synchronous Preloading' নামে পরিচিত। ধারণাটি হল পৃষ্ঠার জীবনচক্রে যতটা সম্ভব দ্রুত থিম সনাক্তকরণ লজিকটি কার্যকর করা, আদর্শভাবে একটি ট্যাগের মধ্যে।<head>, নবিগেটর CSS ফাইলগুলি ডাউনলোড শুরু করার আগেও

কেন <head> হলো আদর্শ জায়গা

Le `<head>`একটি HTML ডকুমেন্ট ব্রাজারের द्वारा 크्रमanusarভাবে শীর্ষ থেকে নীচে প্রক্রিয়াজিত হয়। প্রতিটি উপাদান প্রদর্শিত হওয়ার 크्रमে প্রক্রিয়াজিত হয়। এই বৈশিষ্ট্য আমাদের সমাধানের জন্য গুরুত্বপূর্ণ।

ব্রাউজার একটি ট্যাগ পেলে`<script>`এর</tool_call>`<head>`গুণ বিনা[]async ou defer, il :

  1. HTML পার্সিং বন্ধ কর

  2. স্ক্রিপ্ট ডাউনলোড করুন(বাহ্যিক হলে) অথবা বেড (ইনলাইন হলে)

  3. অবিলম্বে স্ক্রিপ্টটি চালাও

  4. এইচটিএমএল পার্সিং পুনরায় চালু করুন

এই আচরণ, যা কখনো কখনো কর্মক্ষমতার সমস্যা হিসেবে বিবেচিত হয় (এতো কারণে scriptsকে পৃষ্ঠার শেষে রাখার পরামর্শ দেওয়া হয়), এই বিশেষ ক্ষেত্রে আমাদের বন্ধু হয়ে ওঠে। আমাদের থিম সনাক্তকরণ স্ক্রিপ্টকে পৃষ্ঠার শুরুতে`<head>`, আমরা নিশ্চিত করি যে এটি ব্রাউজার ট্যাগগুলো পোস্ট করার আগে চালু হয়`<link>`আমাদের স্টাইল শীটের।

তিন স্তরের সলিউশন আর্কিটেকচার

আমাদের সম্পূর্ণ সমাধান তিনটি পরস্পর আশ্রিত স্তরের দ্বারা গঠিত, প্রতিটি একটি নির্দিষ্ট ভূমিকা পালন করে:

স্তর ১: ধারাবাহিকতা (localStorage) এই স্তর ব্যবহারকারীর সেশনগুলোর মধ্যে বাছাই সংরক্ষণ ও পুনরুদ্ধারের জন্য দায়িত্বवान।

লেয়ার ২ : দ্রুত সিঙ্ক্রোনাইজেশন (ইনলাইন স্ক্রিপ্ট <head>) এই স্তরটি প্রারম্ভিক রেন্ডারিং এর আগে অ্যাপ্লিকেশনের অবস্থা ডোমের সাথে সমন্বয় করে।

লেয়ার ৩ : রিস্পন্সিভ স্টাইল (CSS বৈশিষ্ট্য সিলেক্টরসহ) এই স্তরটি স্তর ২ দ্বারা সংজ্ঞায়িত অবস্থার ভিত্তিতে দৃশ্য শৈলী সংজ্ঞায়িত করে।

এখন প্রতিটি স্তরকে বিস্তারিতভাবে খুঁজে নেই।

ধাপ ১: ব্যবহারকারীর চয়ন সংরক্ষণ করুন

localStorage: আপনার ধ্রুবক মেমরি

Le localStorage`একটি Web Storage API যা কী‑মান জোড়াসমূহকে ব্রাউজারে স্থায়ীভাবে সংরক্ষণ করার সুবিধা দেয়। কুকিজের পরিপ্রেক্ষিতে, ডেটা`localStorage</think>

(blank)

  • কখনও না স্বয়ংক্রিয়ভাবে সার্ভারে পাঠানো হয়

  • তাদের একটি বড় স্টোরেজ ক্যাপাসিটি আছে (সাধারণত 5-10MB)

  • কোনো মেয়াদোত্তীর্ণ তারিখ নেই (স্পষ্টভাবে মুছে ফেলা পর্যন্ত স্থায়ী)

  • প্রটোকল এবং ডোমেইনে সীমাবদ্ধ (Same-Origin Policy)

আমাদের ব্যবহারের ক্ষেত্রে, যে`localStorage`এটা পরিপূর্ণ причине :

  1. আমাদের এই তথ্য সার্ভারের সাথে ভাগ করার দরকার নেই

  2. আমরা চাই যে পছন্দটি অব্যাহতভাবে অনন্তকাল ধরে থাকুক।

  3. প্রয়োজনীয় স্টোরেজের আকার ন্যুনতম (একkaç বাইট)

থিম ব্যাকআপের বাস্তবায়ন

এখানে আমরা কীভাবে ব্যবহারকারীর থিম পরিবর্তন করার সময় তাদের পছন্দ সংরক্ষণ করি :

// 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);
});

ত্রুটি কেসের ব্যবস্থাপনা

এটি গুরুত্বপূর্ণ যে কেসগুলোকে পরিচালনা করা যেখানে le`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
    }
  }
}

এই পদ্ধতিটি সংযুক্ত ব্যবহারকার govের জন্য ডিভাইসgovের মধ্যে পছন্দগুলো সিঙ্ক্‍রোনাইজ করে, তৎক্ষণিক স্থানীয় প্রতিক্রিয়া বজায় রাখে।

ধাপ ২ : প্রিলোড স্ক্রিপ্ট <head> এর</tool_call>

সমাধানের হৃদয়

এখানে আসলে জাদু কাজ করে। আমরা একটি ছোট স্ক্রিপ্ট রাখব।ইনলাইনআমাদের মধ্যে সরাসরি`<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>

স্ক্রিপ্টের অ্যানাটমি : প্রতিটি লাইন গণ্য

এই স্ক্রিপ্টকে লাইন-By-লাইন বিশ্লেষণ করি যাতে প্রতিটি ডিজাইন সিদ্ধান্ত বুঝতে পাই :

IIFE (ইমিডিয়েটলি ইনভোকড ফাংশন এক্সপ্রেশন)

(function() {
  // ...
})();

এই গঠনটি একটি ফাংশন তৈরি করে যা অবিলম্বে কার্যকর হয়। কেন? আমাদের ভেরিয়েবলকে স্থানীয় স্কোপে আলাদা করতে এবং গ্লোবাল স্কোপকে দূষিত না করতে। যদি আমরা শুধুমাত্র ব্যবহার করলেও`const`(যেটি একটি ব্লক স্কোপ আছে), IIFE একটি ভাল অভ্যাস যা আমাদের উদ্দেশ্য স্পষ্ট করে এবং সম্ভাব্য নামের সংঘর্ষ থেকে রক্ষা করে।

স্ট্রিক্ট মোড

'use strict';

এই নির্দেশিকা জাভাস্ক্রিপ্টের স্ট্রিক্ট মোড সক্রিয় করে, যা : ঘোষিত না করা ভেরিয়েবলের ব্যবহারটি নিষীধ করা হয়। - ঝুঁকিপূর্ণ ক্রিয়াকলাপ들의 জন্য ত্রুটি তৈরি করে কিছু জাভাস্ক্রিপ্ট ইঞ্জিনে কর্মক্ষমতা উন্নয়ন করে

একটি গুরুত্বপূর্ণ স্ক্রিপ্টের মতো এই ক্ষেত্রে, আমরা সর্বোচ্চ নিরাপত্তা চাই।

ব্লক 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-কে নিজের ডিফল্ট thèmes ব্যবহার করতে দিই। এই পদ্ধতিটি জাভাস্ক্রিপ্টে কোড করা ডিফল্ট মান থেকে আরও শক্তিশালী।

সিস্টেম পছন্দের সনাক্তকরণ (বোনাস)

একটি ঐচ্ছিক কিন্তু আকর্ষক উন্নয়ন হলো ব্যবহারকারীর অপারেটিং সিস্টেমের থিম পছন্দ সনাক্ত করা, যদি তারা আপনার অ্যাপ্লিকেশনে এখনও কোনো স্পষ্ট পছন্দ বেছে নিয়নি:

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-এ, এই ক্যুরি ব্যবহারকারীর সিস্টেম পছন্দ ফেরত দেয়।

সুবিধা : প্রথম ভিজিট থেকেই ব্যক্তিগতকৃত অভিজ্ঞতা - ব্যবহারকারীর সিস্টেম পরিবেশের সাথে সামঞ্জস্য - প্রথম ভিজিটের জন্য কোনো স্টোরেজ প্রয়োজন নেই

বিবেচনা : - সব ব্রাউজার এই বৈশিষ্ট্য সমর্থন করে না (কিন্তু ২০২০ থেকে সমর্থন অসাধারণ) যাচাই`window.matchMedia`সামঞ্জস্য নিশ্চিত 한다 - ব্যবহারকারী এই বিকল্পকে সর্বদা ওভাররাইড করতে পারে।

ক্ষমতা: এই স্ক্রিপ্ট দ্রুত কেন?

আমাদের প্রি-লোডিং স্ক্রিপ্টটি অত্যন্ত দ্রুত হওয়ার জন্য ডিজাইন করা হয়েছে :

স্বল্পতম আকার: প্রায় ৩০০ বایت নন-মিনিফাইড, ২০০ বাইট মিনিফাইড। এটি কোনো ছবি অথবা জাভাস্ক্রিপ্ট লাইব্রেরির তুলনায় নগণ্য।

সরাসরি: কোন অতিরিক্ত HTTP অনুরোধ নেই। স্ক্রিপ্ট HTML-এ আছে, সুতরাং এটি অবিলম্বে উপলব্ধ।

সহজ সিঙ্ক্রোনাস অপারেশন: localStorage-এ একটি কী পড়া (অতিশই ত্বরিত অপারেশন) এবং একটি DOM thuộcসুচক পরিবর্তন (ব্রাউজারের স্বদেশী অপারেশন).

কোনো নির্ভরতা নেইকোনো ফ্রেমওয়ার্ক নেই, কোনো লাইব্রেরি নেই, শুধু ব্যানিলা জাভাস্ক্রিপ্ট। কোনো স্টার্টআপ সময় নেই, কোনো ডিপেন্ডেন্সি পার্সিং নেই।

একমাত্র কার্যকরন: এই স্ক্রিপ্টটি লোডে একটিবার চালু হয়। ইভেন্ট লিসনার নেই, লুপ নেই, জটিল গণনা নেই।

বাস্তবে, আধুনিক হാർഡওয়েয়ারে, এই স্ক্রিপ্টটি ১ মিলিসেকেন্ডের কম সময়ে চলায়, যা অপেক্ষাযোগ্য সময় যা পৃষ্ঠার লোডিং কর্মক্ষমতায় কোনো প্রভাব ফেলে না।

অপ্টিমাল স্থাপন <head> এর মধ্যে

এলিমেন্টগুলোর ক্রমে`<head>`এটি গুরুত্বপূর্ণ। এখানে সুপারিশকৃত ครমিক: actually correct spelling: "ক্রম". Need correct Bengali characters. Let’s write properly: "এটি গুরুত্বপূর্ণ। এখানে সুপারিশকৃত ক্রম:"

Check for typo: super-shishokrito? Should be "সুপারিশকৃত". Yes.

Thus final: "এটি গুরুত্বপূর্ণ। এখানে সুপারিশকৃত ক্রম:" Done.

</think>

এটি গুরুত্বপূর্ণ। এখানে সুপারিশকৃত ক্রম:

<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>

এই আজ্ঞা নিশ্চিত করে যে :

  1. চার্সেট কোনো টেক্সট প্রক্রিয়াকরণের আগে নির্ধারণ করা হয়

  2. আমাদের স্ক্রিপ্ট CSS লোড হওয়ার আগে চালু হয়।

  3. CSS গুলি পরে লোড হয় এবং সরাসরি সঠিক থিম প্রয়োগ করে

  4. অন্যান্য অগুণবিধি সম্পদগুলো শেষে লোড করা হয়।

ধাপ ৩: CSS অট্রিবিউট সিলেক্টরের শক্তি

বুটস্ট্র্যাপ 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

Articles connexes