ক্রস-ডকুমেন্ট ট্রানজিশন

সমস্যা

ক্রস-ডকুমেন্ট ভিউ ট্রানজিশন, একই-অরিজিন নেভিগেশনের সময় দুটি পৃষ্ঠার মধ্যে এলিমেন্ট অ্যানিমেট করে। ব্রাউজার পুরনো পৃষ্ঠার স্ন্যাপশট ক্যাপচার করে, নেভিগেট করে, তারপর স্ন্যাপশট থেকে নতুন পৃষ্ঠায় অ্যানিমেট করে। নতুন পৃষ্ঠা যদি গুরুত্বপূর্ণ রিসোর্স — স্টাইলশিট, লেআউট স্ক্রিপ্ট বা মূল DOM এলিমেন্ট — লোড করা শেষ না করে, তাহলে ট্রানজিশনটি অসম্পূর্ণ বা স্টাইলহীন অবস্থায় অ্যানিমেট করে। এর ফলে ভিজ্যুয়াল গ্লিচ হয়, যেমন এলিমেন্ট ভুল পজিশনে চলে যাওয়া, অ্যানিমেশনের মাঝখানে কন্টেন্ট রিফ্লো হওয়া অথবা ট্রানজিশন সম্পূর্ণ হওয়ার পরে ওয়েব ফন্টে ফলব্যাক ফন্ট ফ্ল্যাশ করা।

সমাধান

নতুন পৃষ্ঠার <head>-এ গুরুত্বপূর্ণ <link> এবং <script> এলিমেন্টে blocking="render" ব্যবহার করুন এবং নির্দিষ্ট DOM এলিমেন্ট পার্স না করা পর্যন্ত রেন্ডারিং ব্লক করতে <link rel="expect"> ব্যবহার করুন। এটি নিশ্চিত করে যে নতুন পৃষ্ঠার ভিজ্যুয়াল স্টেট স্থিতিশীল না হওয়া পর্যন্ত ব্রাউজার ভিউ ট্রানজিশন অ্যানিমেশন শুরু করবে না। ব্রাউজার ব্যাকগ্রাউন্ডে HTML পার্স করা চালিয়ে যায় — শুধু পেইন্টিং স্থগিত করা হয়।

প্রয়োগ সংক্রান্ত স্ট্র্যাটেজি

  1. বাধ্যতামূলক: দুটি পৃষ্ঠাতেই @view-transition CSS at-রুল সহ ক্রস-ডকুমেন্ট ভিউ ট্রানজিশন চালু করুন।
  2. বাধ্যতামূলক: গুরুত্বপূর্ণ স্টাইলশিট <head>-এ আছে কিনা তা নিশ্চিত করুন। <head>-এ স্টাইলশিট ডিফল্ট হিসেবে রেন্ডার-ব্লকিং হয়। ডায়নামিকভাবে ইনজেক্ট করা স্টাইলশিটের জন্য নির্দিষ্ট blocking="render" প্রয়োজন।
  3. ট্রানজিশন অ্যানিমেশনের আগে অবশ্যই এক্সিকিউট করতে হবে এমন <script> এলিমেন্টে blocking="render" ব্যবহার করুন (যেমন, স্ক্রিপ্ট যা থিম প্রয়োগ করে বা লেআউটকে প্রভাবিত করে)।
  4. উপরে-ভাঁজ করা কন্টেন্ট পার্স না করা পর্যন্ত রেন্ডারিং ব্লক করতে <link rel="expect" href="#element-id" blocking="render"> ব্যবহার করুন। এটি সব ধরনের ট্রানজিশনের ক্ষেত্রে প্রযোজ্য: ফুল-পেজ ক্রস-ফেড (ফাঁকা পৃষ্ঠায় অ্যানিমেশন এড়াতে), মর্ফ অ্যানিমেশন (DOM-এ নামযুক্ত এলিমেন্ট আছে কিনা তা নিশ্চিত করতে) এবং স্ক্রিপ্ট-নির্ভর লেআউট (স্টাইল করা কন্টেন্ট পার্স করা হয়েছে কিনা তা নিশ্চিত করতে)।
  5. অপ্রয়োজনীয় কন্টেন্টের ক্ষেত্রে রেন্ডারিং ব্লক করবেন না। শুধুমাত্র সেইসব রিসোর্স ও এলিমেন্ট ব্লক করুন যা প্রাথমিক ভিউপোর্টকে প্রভাবিত করে। অনেক বেশি কন্টেন্ট ব্লক করলে ট্রানজিশনে দেরি হয় এবং পারফর্ম্যান্স খারাপ হয়।

প্রয়োগ করার নির্দেশিকা

ধাপ ১: ক্রস-ডকুমেন্ট ভিউ ট্রানজিশন চালু করুন

বাধ্যতামূলক: সোর্স ও ডেস্টিনেশন, দুটি পৃষ্ঠাতেই @view-transition at-রুল অন্তর্ভুক্ত করতে হবে। এটি ছাড়া, কোনও ক্রস-ডকুমেন্ট ট্রানজিশন হয় না।

/*
  MANDATORY: Include this rule in every page that participates
  in cross-document view transitions.
  `navigation: auto` enables transitions for standard navigations
  (link clicks, form submissions, back/forward).
*/
@view-transition {
  navigation: auto;
}

/* MANDATORY Copy-Paste Safety: Disable cross-document view transitions for users requesting reduced motion */
@media (prefers-reduced-motion: reduce) {
  @view-transition {
    navigation: none;
  }
}

ধাপ ২: ক্রিটিক্যাল স্ক্রিপ্ট এক্সিকিউট না হওয়া পর্যন্ত রেন্ডারিং ব্লক করা

ট্রানজিশন অ্যানিমেশন শুরু হওয়ার আগে <head>-এ নন-ব্লকিং স্ক্রিপ্ট রান করতে হলে (যেমন, থিম ক্লাস প্রয়োগ করা বা লেআউট প্রভাবিত করা), সেটিকে blocking="render" দিয়ে চিহ্নিত করুন। এটি ছাড়া, ট্রানজিশন শুরু হওয়ার পরে async, defer বা type="module" স্ক্রিপ্ট এক্সিকিউট হতে পারে।

<head>
  <!--
    DO: Mark layout-critical scripts with blocking="render".
  -->
  <script type=module blocking="render">
    // Example: apply a stored theme before the page renders,
    // so the transition snapshot reflects the correct theme.
    document.documentElement.dataset.theme =
      localStorage.getItem('theme') || 'light';
  </script>
</head>

ধাপ ৩: মূল DOM এলিমেন্ট পার্স না করা পর্যন্ত রেন্ডারিং ব্লক করা

<head>-এ স্টাইলশিট ও blocking="render" স্ক্রিপ্ট শুধুমাত্র এই গ্যারেন্টি দেয় যে <head> সম্পূর্ণভাবে প্রসেস করা হয়েছে। তারা কোনও <body> কন্টেন্ট পার্স করার জন্য অপেক্ষা করেন না। অতিরিক্ত ব্লকিং ছাড়া, DOM-এ অ্যাবাভ-দ্য-ফোল্ড এলিমেন্ট থাকার আগে ব্রাউজার নতুন-পৃষ্ঠার স্ন্যাপশট নিতে পারে — যার ফলে এমন ট্রানজিশন হয় যা ফাঁকা বা আংশিকভাবে রেন্ডার করা পৃষ্ঠায় অ্যানিমেট করে।

<link rel="expect"> কোনও নির্দিষ্ট এলিমেন্ট (তার id দ্বারা শনাক্ত করা) পার্স না করা পর্যন্ত রেন্ডারিং ব্লক করে এই সমস্যার সমাধান করে। href ভ্যালু অবশ্যই ফ্র্যাগমেন্ট আইডেন্টিফায়ার (যেমন, #hero) হতে হবে যা টার্গেট এলিমেন্টের id অ্যাট্রিবিউটের সাথে ম্যাচ করে। সেই এলিমেন্টের ক্লোজিং ট্যাগ পার্স করা হয়ে গেলে, রেন্ডার ব্লক রিলিজ করা হয়। মনে রাখবেন যে <link rel="expect"> শুধুমাত্র গ্যারেন্টি দেয় যে টার্গেট DOM এলিমেন্ট ডকুমেন্টে আছে — এটি এক্সটার্নাল সাবরিসোর্স (যেমন <img> পিক্সেল) ডাউনলোড বা ডিকোড করার জন্য অপেক্ষা করে না।

নিম্নলিখিত সব পরিস্থিতিতে <link rel="expect"> ব্যবহার করুন:

ব্যবহারের ক্ষেত্র ১: ফুল-পেজ ক্রস-ফেড

এমনকি কোনও স্বতন্ত্র এলিমেন্টে view-transition-name না থাকলেও, ডিফল্ট root ট্রানজিশন সম্পূর্ণ পৃষ্ঠা ক্রস-ফেড করে। নতুন পৃষ্ঠার স্ন্যাপশট যদি অ্যাবাভ-দ্য-ফোল্ড কন্টেন্ট পার্স করার আগে নেওয়া হয়, তাহলে ক্রস-ফেড অ্যানিমেশন পুরনো পৃষ্ঠা থেকে ফাঁকা বা অসম্পূর্ণ পৃষ্ঠায় পরিবর্তন দেখায়। এমন এলিমেন্টে রেন্ডারিং ব্লক করুন যা ফোল্ডের উপরে দৃশ্যমান কন্টেন্টের শেষ অংশকে চিহ্নিত করে।

<head>
  <link rel="stylesheet" href="/css/styles.css">

  <!--
    DO: Block rendering until the main content area is parsed,
    even for a simple cross-fade. Without this, the browser may
    snapshot the page before visible content exists in the DOM,
    causing the cross-fade to reveal a blank or partial page.
  -->
  <link rel="expect" href="#main-content" blocking="render">
</head>
<body>
  <header>...</header>
  <main id="main-content">
    <h1>Page Title</h1>
    <p>Above-the-fold content the user should see immediately.</p>
  </main>
  <!-- Content below the fold does NOT need to be blocked on -->
  <section>...</section>
</body>

ব্যবহারের ক্ষেত্র ২: নির্দিষ্ট এলিমেন্টের মধ্যে মর্ফ অ্যানিমেশন

দুটি পৃষ্ঠার এলিমেন্ট যখন একটি view-transition-name শেয়ার করে, তখন ব্রাউজার নেভিগেশন জুড়ে সেগুলিকে মসৃণভাবে রূপান্তর করে। ট্রানজিশন শুরু হওয়ার সময় টার্গেট এলিমেন্ট পার্স করা না হলে, ব্রাউজার সেটি খুঁজে পায় না — মর্ফ আলাদা এক্সিট ও এন্ট্রি অ্যানিমেশনে ডিগ্রেড করে। view-transition-name অ্যাট্রিবিউট সহ এলিমেন্ট পার্স না করা পর্যন্ত রেন্ডারিং ব্লক করুন।

কারণ, টার্গেট DOM এলিমেন্ট পার্স না হওয়া পর্যন্ত <link rel="expect"> শুধু রেন্ডারিং পজ করে এবং ইমেজ ডেটা ডাউনলোড বা ডিকোড করার জন্য অপেক্ষা করে না, তাই নতুন পৃষ্ঠার স্ন্যাপশটে আনক্যাশড <img> খালি আয়তক্ষেত্র হিসেবে ক্যাপচার করা হতে পারে। ইমেজ মর্ফ করার সময় ফাঁকা স্ন্যাপশট বা লেআউট শিফ্ট হওয়া আটকাতে, গুরুত্বপূর্ণ ট্রানজিশন ইমেজ প্রিলোড বা প্রি-ডিকোড করুন (যেমন, <link rel="preload" as="image">-এর মাধ্যমে বা ডেস্টিনেশন পৃষ্ঠা প্রিরেন্ডার করে) এবং স্পষ্ট সাইজিং ফলব্যাক প্রদান করুন (width এবং height অ্যাট্রিবিউট বা CSS aspect-ratio, সাথে একটি প্লেসহোল্ডার ব্যাকগ্রাউন্ড কালার)।

<head>
  <link rel="stylesheet" href="/css/styles.css">

  <!--
    DO: Preload critical transition images so their pixels are
    available to decode before the new-page snapshot is captured.
    <link rel="expect"> only waits for the DOM element to be parsed,
    NOT for image pixels to download or decode.
  -->
  <link rel="preload" as="image" href="/img/product.webp" fetchpriority="high">

  <!--
    DO: Block rendering until the element participating in the
    morph animation has been parsed. Without this, the browser
    may start the transition before #hero exists, causing the
    morph to degrade to a fade-out/fade-in.
  -->
  <link rel="expect" href="#hero" blocking="render">

  <!--
    When multiple blocking="render" resources are present,
    rendering is blocked until ALL of them are satisfied.
    Here, the browser waits for both the script to execute
    AND the #hero element to be parsed — whichever comes last.
  -->
  <script async blocking="render" src="/js/transition-setup.js"></script>
</head>
<body>
  <header>...</header>
  <section id="hero">
    <h1 style="view-transition-name: page-title">Product Name</h1>
    <!--
      DO: Provide explicit dimensions (width/height or aspect-ratio)
      as a sizing fallback so the snapshot captures the correct layout
      box even if image decoding is still in progress.
    -->
    <img
      style="view-transition-name: hero-image"
      src="/img/product.webp"
      width="800"
      height="600"
      alt="Product"
    >
  </section>
</body>

ধাপ ৪: রেসপন্সিভ রেন্ডার ব্লকিংয়ের জন্য মিডিয়া কোয়েরি ব্যবহার করা

বিভিন্ন ভিউপোর্ট সাইজ অনুযায়ী, ফোল্ডের উপরে বিভিন্ন পরিমাণ কন্টেন্ট দেখানো হতে পারে। প্রদত্ত ভিউপোর্ট প্রস্থে দৃশ্যমান কন্টেন্টের জন্য শুধুমাত্র রেন্ডারিং ব্লক করতে <link rel="expect">-এ media অ্যাট্রিবিউট ব্যবহার করুন।

<head>
  <!--
    DO: Use media queries to conditionally block rendering.
    On wide screens, both the hero and the sidebar are visible,
    so block until both are parsed. On narrow screens, only the
    hero is visible initially.
  -->
  <link
    rel="expect"
    href="#hero"
    blocking="render"
    media="screen and (width <= 768px)"
  >
  <link
    rel="expect"
    href="#sidebar"
    blocking="render"
    media="screen and (width > 768px)"
  >
</head>

ধাপ ৫: প্রসঙ্গ-নির্ভর ট্রানজিশনের জন্য pagereveal ব্যবহার করা (ঐচ্ছিক)

কোর রেন্ডার-ব্লকিং স্ট্র্যাটেজির জন্য pagereveal ইভেন্ট প্রয়োজন নেই। এটি শুধুমাত্র তখনই প্রয়োজন হয় যখন ব্যবহারকারী কোথা থেকে নেভিগেট করেছেন তার উপর ভিত্তি করে view-transition-name মানগুলি অবশ্যই ডাইনামিকভাবে অ্যাসাইন করতে হবে — উদাহরণস্বরূপ, একটি নির্দিষ্ট তালিকার আইটেমকে একটি বিস্তারিত পৃষ্ঠার শিরোনামে রূপান্তর করা।

CSS-এ view-transition-name ভ্যালু স্ট্যাটিক্যালি অ্যাসাইন করা থাকলে অথবা আপনি শুধু ডিফল্ট ফুল-পেজ ক্রস-ফেড ব্যবহার করলে, এই ধাপটি সম্পূর্ণভাবে এড়িয়ে যান।

<head>
  <!--
    MANDATORY: The pagereveal listener must be registered before
    the page renders. Inline small setup scripts with blocking="render"
    to avoid an extra network round-trip on cold loads, or use an
    external async script with blocking="render" so the listener is
    registered early without blocking HTML parsing.
    If the listener is registered too late (e.g., in a deferred
    script), the event may have already fired.
  -->
  <script async blocking="render" src="/js/transition-setup.js"></script>
</head>
// transition-setup.js
window.addEventListener('pagereveal', async (event) => {
  if (!event.viewTransition) return;

  const from = navigation.activation?.from;
  if (!from) return;

  const fromUrl = new URL(from.url);

  // DO: Assign view-transition-name based on navigation context.
  // This enables a morph animation from the product card on the
  // list page to the heading on the detail page.
  if (fromUrl.pathname === '/products/') {
    const heading = document.querySelector('main h1');
    if (!heading) return;

    heading.style.viewTransitionName = 'product-title';

    try {
      await event.viewTransition.ready;
    } catch (error) {
      // Skipped view transitions reject `ready` with InvalidStateError.
      if (error.name !== 'InvalidStateError') {
        throw error;
      }
    } finally {
      // MANDATORY: Remove the temporary name after the transition
      // finishes. Stale names interfere with subsequent navigations
      // and prevent the page from entering the bfcache.
      await event.viewTransition.finished;
      heading.style.viewTransitionName = '';
    }
  }
});

পেশাদার পদ্ধতি

  • সম্ভব হলে, CSS-এর মাধ্যমে view-transition-name অ্যাসাইন করুন। যেসব ক্ষেত্রে নাম নেভিগেশন কনটেক্সটের উপর নির্ভর করে, সেখানে জাভাস্ক্রিপ্ট অ্যাসাইনমেন্ট (pagereveal-এর মাধ্যমে) রিজার্ভ করুন।
  • রেন্ডার-ব্লকিং স্ক্রিপ্ট ছোট ও দ্রুত রাখুন। ব্রাউজারে একটি বিল্ট-ইন টাইমআউট (প্রায় ৪ সেকেন্ড) থাকে, যার পরে ট্রানজিশনটি সম্পূর্ণভাবে এড়িয়ে যাওয়া হয় (ট্রানজিশন শুরু হলে InvalidStateError সহ viewTransition.ready প্রত্যাখ্যান করা হয়)।
  • পৃষ্ঠার গভীরে থাকা এলিমেন্ট ব্লক করতে <link rel="expect"> ব্যবহার করবেন না, কারণ এগুলি প্রাথমিক ভিউপোর্টে দেখা যায় না। এর ফলে ট্রানজিশনে দেরি হয় এবং কোনও ভিজ্যুয়াল সুবিধাও পাওয়া যায় না।
  • অ্যানালিটিক্স, বিজ্ঞাপন-নেটওয়ার্ক, ট্যাগ-ম্যানেজার বা অন্যান্য থার্ড-পার্টি লোডারকে blocking="render" দিয়ে চিহ্নিত করবেন না, এমনকি সেগুলি প্রাথমিক ভিউপোর্টে কন্টেন্ট ইনজেক্ট করলেও। তাদের নেটওয়ার্ক লেটেন্সি ট্রানজিশন টাইমআউটের দিকে গণনা করা হয় এবং তাদের আউটপুট পৃষ্ঠার স্থিতিশীল অবস্থার অংশ নয়।
  • একই পৃষ্ঠার একাধিক এলিমেন্টে একই view-transition-name অ্যাসাইন করবেন না। ডুপ্লিকেট নাম থাকলে পুরো ট্রানজিশন বাদ দেওয়া হয়।
  • সহায়ক প্রযুক্তি টাইমিংয়ের প্রভাব: blocking="render" ব্যবহার করলে ভিজ্যুয়াল আপডেট ও প্রাথমিক পেইন্ট হতে দেরি হয়। এটি দৃষ্টিশক্তি সম্পন্ন ব্যবহারকারীদের জন্য ভিজ্যুয়াল গ্লিচ প্রতিরোধ করলেও, স্ক্রিন রিডার এবং অন্যান্য সহায়ক প্রযুক্তির জন্য প্রসেসিং লেটেন্সি বা বিলম্বিত ইনিশিয়ালাইজেশনের কারণ হতে পারে যা রেন্ডার করা অ্যাক্সেসিবিলিটি ট্রির উপর নির্ভর করে। দৃষ্টিশক্তিহীন ব্যবহারকারীদের জন্য প্রাথমিক রিড লেটেন্সির সাথে ভিজ্যুয়াল কন্টিনিউটি সংক্রান্ত সুবিধার তুলনা করুন এবং নিশ্চিত করুন যে রেন্ডার-ব্লকিং স্ক্রিপ্টগুলি ন্যূনতম এবং অত্যন্ত অপ্টিমাইজ করা হয়েছে।

ফলব্যাক স্ট্র্যাটেজি

ক্রস-ডকুমেন্ট ভিউ ট্রানজিশন হল প্রোগ্রেসিভ এনহ্যান্সমেন্টের জন্য একটি চমৎকার ক্যান্ডিডেট। যেসব ব্রাউজারে এটি কাজ করে না, সেখানে @view-transition নিয়ম উপেক্ষা করা হয় এবং স্ট্যান্ডার্ড একই-উৎসের নেভিগেশন ঠিক সেভাবেই কাজ করে যেভাবে এই ফিচার ছাড়া কাজ করত। যেসব ব্রাউজার কাজ করে সেগুলিতে মসৃণ ট্রানজিশন হয়; বাকি সব ব্রাউজারে স্ট্যান্ডার্ড নেভিগেশন হয়। ব্রাউজারে সীমিত সাপোর্ট থাকার কারণে এটি গ্রহণ না করার কোনও কারণ নেই।

ক্রস-ডকুমেন্ট ভিউ ট্রানজিশন কাজ করে এমন সব ব্রাউজারে blocking="render" এবং <link rel="expect">-ও কাজ করে, তাই এই গাইডে বর্ণনা করা রেন্ডার-ব্লকিং ফিচারের জন্য আলাদা কোনও ফলব্যাক প্রয়োজন হয় না।

বিবেচনা করার মতো অন্যান্য বিষয়

  1. পারফর্ম্যান্সের উপর প্রভাব: প্রতিটি রেন্ডার-ব্লকিং রিসোর্স ভিউ ট্রানজিশন অ্যানিমেশন শুরু হতে দেরি করায়। রেন্ডার-ব্লকিং স্ক্রিপ্টের সংখ্যা কমান এবং শুধুমাত্র সেইসব এলিমেন্টের জন্য <link rel="expect"> ব্যবহার করুন যেগুলি স্ক্রিনের উপরের অংশে থাকে। লোডিংয়ের ক্ষেত্রে দেরি হওয়া সম্পূর্ণভাবে এড়াতে, Speculation Rules API ব্যবহার করে ডেস্টিনেশন পেজ প্রিরেন্ডার করুন।
  2. টাইম-আউট আচরণ: সম্মিলিত রেন্ডার-ব্লকিং সময় প্রায় ৪ সেকেন্ডের বেশি হলে, ব্রাউজার ট্রানজিশন এড়িয়ে যায় (এবং যেকোনও অ্যাক্টিভ viewTransition.ready প্রমিস InvalidStateError-এর সাথে বাতিল হয়ে যায়)। এই উইন্ডোর মধ্যে ক্রিটিক্যাল রিসোর্স যাতে ভালভাবে লোড হয় তা নিশ্চিত করুন।
  3. bfcache-এর সাথে মানানসই: ট্রানজিশনের পরে সাময়িক view-transition-name অ্যাসাইনমেন্ট মুছে না দিলে, পেজটি bfcache-এ প্রবেশ করতে পারবে না। finished কলব্যাকে ডাইনামিকভাবে অ্যাসাইন করা নাম সবসময় সরিয়ে দিন।