একটা পেজ দ্রুত হওয়া আর দ্রুত মনে হওয়া-র মধ্যে একটা ফাঁক আছে। ব্যবহারকারী শুধু দ্বিতীয়টা নিয়েই ভাবেন। আপনি load time থেকে মিলিসেকেন্ড কমাতে পারেন, তবুও এমন কিছু ship করতে পারেন যা সস্তা মনে হয় — image আসার সময় content স্ক্রিনে হেলেদুলে ওঠে, text এক font থেকে আরেকটায় ঝলকায়, যে main image মানুষকে দেখাতে চেয়েছিলেন সেটাই সবার শেষে পৌঁছায়। সরল timing-এ এসবের কোনোটাই "ধীর" হিসেবে দেখা যায় না, কিন্তু এসবই সেই জিনিস যা মানুষ বোঝায় যখন বলে একটা সাইট janky মনে হয়।
অনুভূত performance মূলত স্থিতিশীলতা ও ক্রম নিয়ে — জিনিস একবার দেখা দিলে আর না নড়া, আর গুরুত্বপূর্ণ জিনিস আগে দেখা দেওয়া। অনুভূত ধীরগতি সাধারণত কোথা থেকে আসে আর আসলে কী সেটা ঠিক করে তা এখানে।
যে layout shift সবকিছু সস্তা মনে করায়
এটা আপনি অনুভব করেছেন: একটা link-এ tap করতে যান, তার উপরে একটা image লোড শেষ হয়, সবকিছু নিচে লাফায় আর আপনি ভুল জিনিসে tap করেন। এটাই cumulative layout shift, আর টেকনিক্যালি দ্রুত একটা পেজকে অমসৃণ মনে করানোর একমাত্র বড় কারণ এটাই। এটা ঘটে কারণ কোনো কিছু কতটা জায়গা নেবে তা জানার আগেই browser পেজটা layout করে ফেলে, তারপর জানার পর সবকিছু ঠেলে সরাতে হয়।
প্রধান অপরাধী হলো dimension ছাড়া image। image কত বড় তা browser-কে না বললে, এটা শূন্য height সংরক্ষণ করে, তার নিচের সবকিছু layout করে, তারপর image পৌঁছালে পুরো পেজ reflow করে। সমাধানটা HTML-এর মতোই পুরনো — tag-এ dimension বসিয়ে দিন:
<!-- No reserved space — everything below jumps when this loads -->
<img src="/hero.jpg" alt="" />
<!-- Space reserved up front — nothing moves -->
<img src="/hero.jpg" alt="" width="1200" height="630" />width আর height দেওয়া মানে image-কে সেই pixel মাপে আটকে ফেলা নয় — CSS-এ height: auto দিয়ে browser অনুপাত ব্যবহার করে ঠিক পরিমাণ জায়গা সংরক্ষণ করে আর তবুও responsively scale করে। একই নীতি দেরিতে লোড হওয়া সবকিছুতে খাটে: ad slot, embed, banner, JavaScript দিয়ে inject করা যেকোনো কিছু। কিছু পরে দেখা দেবে জানলে, একটা fixed height বা aspect-ratio দিয়ে এখনই তার জায়গা সংরক্ষণ করুন, যাতে এর আগমন পেজটাকে ঠেলে না দেয়।
বেশিরভাগ "janky" পেজ ধীর না — এরা অস্থিতিশীল। জায়গা সংরক্ষণ ছাড়াই content লোড হয়, তাই সবকিছু লাফায়। আগেভাগে জায়গা সংরক্ষণ করুন আর একই load time হঠাৎ শান্ত মনে হবে।
Font: যে ঝলক আপনি নিজের মতো বেছে নিতে পারেন
Custom font নিজস্ব একটা ছোট্ট অস্থিতিশীল মুহূর্ত তৈরি করে। text আঁকার আগে browser-কে font file download করতে হয়, আর এর মধ্যে কী করবে তা ঠিক করতে হয়। কোনো বিনামূল্যের বিকল্প নেই — শুধু কোন বিরক্তিটা আপনি বেছে নিতে চান তার একটা পছন্দ, আর সেই পছন্দ করেন font-display দিয়ে।
@font-face {
font-family: "Inter";
src: url("/fonts/inter.woff2") format("woff2");
font-display: swap;
}swap সঙ্গে সঙ্গে একটা fallback font-এ text দেখায়, তারপর লোড হলে আপনার font-এ বদলে যায়। সুবিধা: content সঙ্গে সঙ্গে পড়া যায়। অসুবিধা: বদল হওয়ার সময় একটা দৃশ্যমান ঝলক, আর fallback আর আসল font-এর width আলাদা হলে text reflow হয় — এবার type থেকে আবার layout shift। এটা দুইভাবে কমাতে পারেন। এমন একটা fallback বেছে নিন যার metrics আপনার আসল font-এর কাছাকাছি (ঠিক এই কাজের জন্যই CSS-এর size-adjust আর ascent-override descriptor আছে), আর fold-এর উপরে যে font file দরকার হবে জানেন সেগুলো preload করুন যাতে বদলটা তাড়াতাড়ি হয়:
<link
rel="preload"
href="/fonts/inter.woff2"
as="font"
type="font/woff2"
crossorigin
/>এখানে কোনো নিখুঁত উত্তর নেই, কিন্তু ভুলটা হলো কিছু না করা আর font লোড হওয়ার সময় default আচরণকে আপনার text কয়েক সেকেন্ড পর্যন্ত লুকিয়ে রাখতে দেওয়া। ইচ্ছাকৃতভাবে আপনার trade-off বেছে নিন।
মানুষ যা দেখতে এসেছে সেটা lazy-load করবেন না
Image lazy-load করা ভালো পরামর্শ যা অতিরিক্ত প্রয়োগ হয়। ধারণাটা যুক্তিসঙ্গত: পেজের অনেক নিচের image ব্যবহারকারী কাছে scroll না করা পর্যন্ত download করবেন না। কিন্তু মানুষ পেজের প্রতিটা image-এ loading="lazy" যোগ করে দেয়, উপরের বড় hero-সহ — আর এখন সবচেয়ে গুরুত্বপূর্ণ image, যেটা ঠিক করে পেজ কখন "এসে গেছে" মনে হয়, সেটাই বাকি সবকিছুর পেছনে ফেলে দেওয়া হয়।
<!-- Wrong: the main image is the one thing you want ASAP -->
<img src="/hero.jpg" loading="lazy" width="1200" height="630" alt="" />
<!-- Right: load the hero eagerly, lazy-load what's below the fold -->
<img
src="/hero.jpg"
loading="eager"
fetchpriority="high"
width="1200"
height="630"
alt=""
/>নিয়ম: পেজ প্রথম paint হওয়ার সময় দৃশ্যমান যেকোনো কিছু eagerly লোড হওয়া উচিত, আর সবচেয়ে বড়টা queue-তে লাফ দিতে fetchpriority="high" পেতে পারে। loading="lazy" fold-এর নিচের জন্য রেখে দিন — সেখানেই এটা তার জায়গা অর্জন করে, কেউ এখনো scroll করে পৌঁছায়নি এমন image-এ bandwidth খরচ না করে।
Fold-এর উপরে Lazy-loading উল্টো ফল দেয়
আপনার hero বা logo-তে loading="lazy" browser-কে বলে "এটা জরুরি না" — পেজের
সবচেয়ে জরুরি জিনিসটা সম্পর্কে। এটা সেই কয়েকটা performance পরিবর্তনের একটা
যা নির্ভরযোগ্যভাবে অনুভূত অভিজ্ঞতাকে খারাপ করে দেয়। Lazy fold-এর নিচের
জন্য, উপরের জন্য নয়।
যা আসছে তার আকৃতির সঙ্গে মেলে এমন progress দেখান
সত্যিই অপেক্ষা করতে হলে — একটা ধীর request, একটা ভারী list — সেই ফাঁকটা কীভাবে ভরাট করেন তা বদলে দেয় সেটা কতটা দীর্ঘ মনে হয়। একটা কেন্দ্রীভূত spinner ব্যবহারকারীকে "অপেক্ষা করো" ছাড়া কিছু বলে না, আর তার চেয়ে খারাপ, এটা সাধারণত ভিন্ন আকারের content দিয়ে প্রতিস্থাপিত হয়, আসল জিনিস পৌঁছালে আরেকটা layout shift ঘটিয়ে।
একটা skeleton — যা আসছে তার মোটামুটি আকৃতিতে ধূসর ব্লক — দুটো কারণে দ্রুত মনে হয়। এটা সংকেত দেয় কী দেখা দিতে যাচ্ছে, আর skeleton-টা আসল content-এর মতো মাপে রাখলে বদলটায় কোনো লাফ হয় না, কারণ জায়গাটা আগে থেকেই সঠিক মাপে ছিল। আপনি এটা দ্রুত করছেন না; আপনি অপেক্ষাটাকে পাঠযোগ্য আর স্থিতিশীল করছেন, "দ্রুত মনে হওয়া" আসলে যার মানে তার বেশিরভাগটাই এটা।
যে মানসিক মডেল সবকিছু একসূত্রে বাঁধে
load time-কে একটা সংখ্যা হিসেবে ভাবা বন্ধ করুন আর ব্যবহারকারী যা দেখেন তার ক্রম ও স্থিতিশীলতা নিয়ে ভাবা শুরু করুন। দুটো প্রশ্নে বেশিরভাগটাই ধরা পড়ে:
- কোনো কিছু দেখা দেওয়ার পর নড়ে কি? হ্যাঁ হলে, আপনার একটা layout-shift সমস্যা আছে — image, embed, font আর পরে inject হওয়া যেকোনো কিছুর জন্য জায়গা সংরক্ষণ করুন। byte যত দ্রুতই পৌঁছাক, নড়াচড়া সস্তা হিসেবে পড়ে।
- গুরুত্বপূর্ণ জিনিসটা কি আগে? hero, headline, main content eagerly আর তাড়াতাড়ি লোড হওয়া উচিত; বাকি সবকিছু অপেক্ষা করতে পারে। fold-এর নিচে lazy-load করুন, উপরে কখনো না।
এই দুটো ঠিক করুন আর মাঝারি connection-এও আপনার পেজ দ্রুত মনে হবে — কারণ "দ্রুত" আসলে কখনোই মোট সময় নিয়ে ছিল না। এটা সঠিক ক্রমে জিনিস দেখা দেওয়া আর তারপর স্থির থাকা নিয়ে।