document.startViewTransition()-এর মাধ্যমে ভিউ ট্রানজিশন শুরু করলে, ::view-transition ওভারলে :root এলিমেন্টের সাথে অ্যাটাচ হয়ে যায়, যা পৃষ্ঠার বাকি অংশে পয়েন্টার ইভেন্ট ব্লক করে, একই সাথে হওয়া ট্রানজিশন আটকায় এবং position: fixed হেডার বা খোলা [popover] এলিমেন্ট অস্পষ্ট করে দেয় (এমনকি ওভারফ্লো ব্লিডিং আটকানোর জন্য নেস্টেড ভিউ ট্রানজিশন গ্রুপ ব্যবহার করা হলেও)।
কম্পোনেন্ট রুটে element.startViewTransition() কল করলে, সেই DOM সাবট্রিতে ট্রানজিশন স্কোপ করার মাধ্যমে এই সমস্যার সমাধান করা হয়: ::view-transition ছদ্ম-এলিমেন্ট ট্রি সরাসরি এলিমেন্টে ইনজেক্ট করা হয়, যার ফলে এক্সটার্নাল স্ট্যাকিং কনটেক্সট ও পেজ ইন্টার্যাক্টিভিটি বজায় থাকে এবং একই সাথে একাধিক কম্পোনেন্ট ট্রানজিশন করতে পারে।
প্রয়োগ
১. একটি স্কোপড ট্রানজিশন ট্রিগার করুন
document.startViewTransition()-এর পরিবর্তে কম্পোনেন্টের রুট কন্টেনার এলিমেন্টে element.startViewTransition() কল করুন। আপনি যে এলিমেন্টে startViewTransition() কল করেন সেটি ট্রানজিশনের স্কোপ (স্কোপ এলিমেন্ট) হয়ে যায়: শুধুমাত্র এর সাবট্রির মধ্যে থাকা এলিমেন্ট ক্যাপচার করা হয়, আপডেট কলব্যাকের সময় শুধুমাত্র স্কোপের রেন্ডারিং বন্ধ হয়ে যায় এবং এর ফলে ::view-transition ওভারলে স্কোপ এলিমেন্টের সাইজ, আকার এবং স্ট্যাকিং কনটেক্সটের সাথে মিলে যায়।
২. অংশগ্রহণকারী এলিমেন্টের নাম দিন
স্কোপের মধ্যে থাকা চাইল্ড এলিমেন্টে view-transition-name অ্যাসাইন করুন যা অ্যানিমেট করা উচিত। আপনি ডাইনামিক তালিকার আইটেমে view-transition-name: match-element ব্যবহার করতে পারেন যাতে প্রতিটি এলিমেন্টের ট্রানজিশন নাম তার DOM নোড আইডেন্টিটি থেকে অটোমেটিক পাওয়া যায় অথবা নির্দিষ্ট নাম অ্যাসাইন করতে পারেন (যা শুধুমাত্র সেই স্কোপের মধ্যে অনন্য হতে হবে)।
প্রতিটি স্কোপের মধ্যে নাম আলাদা করা থাকে, যা DOM-এর বিভিন্ন অংশে কম্পোনেন্ট জুড়ে সংঘর্ষ প্রতিরোধ করে। তবে, নেস্টেড স্কোপড ভিউ ট্রানজিশনে view-transition-name-এর সাথে নামের সংঘর্ষ হতে পারে। এটি আটকাতে, চাইল্ড স্কোপ এলিমেন্টে view-transition-scope: all যোগ করুন।
৩. সেল্ফ-পার্টিসিপেশন ও ওভারফ্লো ক্লিপিং
স্কোপ এলিমেন্ট অটোমেটিক নিজের ভিউ ট্রানজিশনে অংশগ্রহণ করে কারণ ইউজার-এজেন্ট স্টাইলশিট এতে view-transition-name: root ও view-transition-group: contain প্রয়োগ করে। এইভাবে, DOM-এ আপনার করা পরিবর্তনের ফলে স্কোপের জিওমেট্রি প্রভাবিত হলে, এটি ভিউ ট্রানজিশনের অংশ হিসেবে, তার চিলড্রেনের পাশাপাশি নিজের জিওমেট্রিকে মসৃণভাবে অ্যানিমেট করে।
স্কোপ যখন তার ওভারফ্লো (overflow: hidden, scroll, বা clip) ক্লিপ করে, তখন ::view-transition-group-children(root) অটোমেটিক overflow: clip প্রয়োগ করে, যা ট্রানজিশন করা চাইল্ডকে কম্পোনেন্ট বাউন্ডের বাইরে ব্লিড করা থেকে আটকায়।
৪. কনকারেন্ট ও নেস্টেড ট্রানজিশন
আলাদা কম্পোনেন্ট সাবট্রি একে অপরকে এড়িয়ে না গিয়ে একসাথে element.startViewTransition() রান করতে পারে। অ্যাক্টিভ স্কোপ অটোমেটিক UA স্টাইলশিটে view-transition-scope: all পায়, তাই ইন্টার্নাল view-transition-name ভ্যালু বাইরের ট্রানজিশনে লিক হয় না। কম্পোনেন্ট নিষ্ক্রিয় থাকলেও, বাহ্যিক ট্রানজিশন যাতে অভ্যন্তরীণ নাম ক্যাপচার করতে না পারে, তার জন্য বারবার ব্যবহৃত কম্পোনেন্ট কন্টেনারে CSS-এ view-transition-scope: all প্রয়োগ করুন।
ইনার ট্রানজিশন অ্যাক্টিভ থাকাকালীন আউটার ট্রানজিশনের জন্য কোনও কম্পোনেন্ট সরানোর প্রয়োজন হলে, ইনার স্কোপ র্যাপ করা এলিমেন্টে view-transition-name অ্যাসাইন করুন। তারপর, বাইরের ট্রানজিশনটি ভিতরের স্কোপ এলিমেন্টের পরিবর্তে শুধুমাত্র র্যাপারকে ক্যাপচার করে এবং অ্যানিমেট করে, যার ফলে ভিতরের ট্রানজিশনটি সম্পূর্ণ হতে পারে।
উদাহরণ
স্কোপ তৈরি করার জন্য স্কোপ এলিমেন্টে (.card বা .card ul) কোনও CSS-এর প্রয়োজন নেই—document-এর পরিবর্তে এলিমেন্টে startViewTransition() কল করে স্কোপ নির্ধারণ করা হয় (কম্পোনেন্টটি যদি কোনও আউটার ভিউ ট্রানজিশনের মধ্যে নেস্ট করা থাকে, তাহলে view-transition-scope: all-এর জন্য আউটার ট্রানজিশন চালানো দেখুন)।
/* 1. Name participating child items */
.card ul li {
view-transition-name: match-element;
}
/* 2. OPTIONAL: Customize transition timing (300ms is an example duration) */
::view-transition-group(*) {
animation-duration: 300ms;
animation-timing-function: ease-in-out;
}
/* 3. MANDATORY: Respect user preference for reduced motion */
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}
// 1. Trigger a transition scoped to a single component subtree
function runScopedTransition(scopeEl, updateDOM) {
// MANDATORY: Call startViewTransition on the component element, not document
const transition = scopeEl.startViewTransition(() => {
updateDOM();
});
// Re-triggering startViewTransition() on the same element skips the active transition
// and rejects `ready`. Catch the rejection to avoid unhandled promise rejection errors.
transition.ready.catch(() => {});
}
// 2. Reorder items by moving existing DOM nodes (required for `match-element`)
// Scope to `listEl` since only the list order changes, keeping controls outside `listEl` interactive
function reorderItems(listEl, orderedIds) {
const existing = new Map(
Array.from(listEl.children, (li) => [li.dataset.id, li]),
);
runScopedTransition(listEl, () => {
for (const id of orderedIds) {
const li = existing.get(id);
if (li) listEl.appendChild(li);
}
});
}
// 3. Remove an item while preserving exit animation and keyboard focus
// Scope to the outer `cardEl` so the card itself also transitions its height change
function removeItem(cardEl, itemEl) {
const hadFocus = itemEl.contains(document.activeElement);
const nextFocusTarget =
itemEl.nextElementSibling?.querySelector('button') ??
itemEl.previousElementSibling?.querySelector('button') ??
cardEl.querySelector('button');
runScopedTransition(cardEl, () => {
itemEl.remove();
if (hadFocus) {
nextFocusTarget?.focus();
}
});
}
আউটার ট্রানজিশন রান করা হচ্ছে
উদ্দিষ্ট স্কোপের বাইরে ভিউ ট্রানজিশন শুরু করার সময় (যেমন, পেরেন্ট কম্পোনেন্ট থেকে অথবা পৃষ্ঠা-লেভেল ট্রানজিশনের জন্য ডকুমেন্ট থেকে), বাইরের ট্রানজিশন স্কোপ এলিমেন্ট এবং তার মধ্যে থাকা সবকিছু ক্যাপচার ও অ্যানিমেট করবে।
এটি হওয়া আটকাতে, নিজস্ব view-transition-name সহ আলাদা র্যাপার এলিমেন্টে স্কোপ এলিমেন্ট র্যাপ করুন এবং ভিতরের স্কোপ এলিমেন্টে view-transition-scope: all প্রয়োগ করুন। এটি র্যাপারকে নিজেই ক্যাপচার করতে এবং বাইরের ট্রানজিশন দ্বারা অ্যানিমেট করতে দেয় এবং ভিতরের স্কোপের কন্টেন্ট ক্যাপচার করা থেকে আটকায়।
<div class="page" id="page">
<div class="card-wrapper">
<section class="card" id="left">
<h2>Card A</h2>
<ul id="left-list">
<li>…</li>
</ul>
</section>
</div>
<div class="card-wrapper">
<section class="card" id="right">
<h2>Card B</h2>
<ul id="right-list">
<li>…</li>
</ul>
</section>
</div>
</div>
document.querySelector('#page').startViewTransition(() => {
// do your DOM updates here...
});
/* Apply a view-transition-name on the wrapper */
.card-wrapper {
view-transition-name: match-element;
}
/* Prevent the contents of the card being captured by an outer transition */
.card {
view-transition-scope: all;
}
.card ul li {
view-transition-name: match-element;
}
অন্যথায়, আপনি চিলড্রেন ক্যাপচার করার জন্য বাইরের ট্রানজিশনকে অনুমতি দিতে পারেন। ক্লিপিং এফেক্ট বজায় রাখতে, নেস্টেড ভিউ ট্রানজিশন গ্রুপ ব্যবহার করে ক্যাপচার করা চাইল্ড এলিমেন্টকে তার র্যাপারের সীমানায় ক্লিপ করুন। তবে, সেক্ষেত্রে, আপনাকে ম্যানুয়ালি clip-কে ::view-transition-group-children() ছদ্ম-তে প্রয়োগ করতে হবে।
.card-wrapper {
overflow: clip;
view-transition-name: match-element; /* Capture me as part of the outer VT */
view-transition-group: contain; /* Nest children that also have a view-transition-name */
view-transition-class: card; /* For targeting purposes */
}
/* Manually copy back the clip onto the ::view-transition-group-children pseudo */
::view-transition-group-children(.card) {
overflow: clip;
}
.card ul li {
view-transition-name: match-element;
}
সীমাবদ্ধতা ও অ্যাক্সেসিবিলিটি
- বাধ্যতামূলক:
document-এর পরিবর্তে কম্পোনেন্ট রুট এলিমেন্টেstartViewTransition()কল করুন যাতে এক্সটার্নালposition: fixedএলিমেন্ট এবং[popover]ওভারলে উপরে লেয়ার হিসেবে থাকে এবং পৃষ্ঠার বাকি অংশ ইন্টার্যাক্টিভ থাকে। - বাধ্যতামূলক:
view-transition-name: match-elementব্যবহার করার সময় আগে থেকে থাকা DOM নোড (innerHTMLদিয়ে আবার তৈরি করার পরিবর্তে) সংরক্ষণ করুন ও সরান, কারণmatch-elementDOM নোডের পরিচয় ট্র্যাক করে। - বাধ্যতামূলক:
::view-transition-group(*),::view-transition-old(*)ও::view-transition-new(*)-এanimation: none !importantসেট করে@media (prefers-reduced-motion: reduce)ব্যবহার করে রিডিউসড মোশনের জন্য ব্যবহারকারীর পছন্দকে সম্মান করুন। - বাধ্যতামূলক: ট্রানজিশনের সময় ফোকাস করা এলিমেন্ট সরানোর সময় কীবোর্ড ফোকাস বজায় রাখুন। এর জন্য আপডেট কলব্যাকের মধ্যে থাকা কোনও সংলগ্ন আইটেম বা কন্ট্রোলে ফোকাস শিফ্ট করুন।
- করণীয়: স্কোপের মধ্যে ক্যাপচার করা এলিমেন্ট নিজের ওভারফ্লো ক্লিপ করলে এবং নেস্ট করা ক্যাপচার করা এলিমেন্ট থাকলে, নেস্টেড ভিউ ট্রানজিশন গ্রুপ (
::view-transition-group-children()-এview-transition-group: containএবংoverflow: clip) ব্যবহার করুন। - করণীয়: দ্রুত ইন্টার্যাকশন একই স্কোপ এলিমেন্টে
element.startViewTransition()আবার ট্রিগার করতে পারলেtransition.ready.catch(() => {})অ্যাটাচ করুন। - করণীয়: ইনার ট্রানজিশন চলাকালীন আউটার ট্রানজিশনের জন্য কম্পোনেন্টের পজিশন অ্যানিমেট করার প্রয়োজন হলে, ইনার স্কোপ এলিমেন্টকে আলাদা নামযুক্ত র্যাপার এলিমেন্টে র্যাপ করুন।
- করবেন না: এমন একটি স্কোপ এলিমেন্টে
view-transition-name: noneসেট করবেন না যা তার ওভারফ্লো (overflow: hidden,scrollবাclip) ক্লিপ করে বা নিজস্ব জ্যামিতি পরিবর্তন করে, কারণ সেল্ফ-পার্টিসিপেশন থেকে অপ্ট-আউট করলে অটোমেটিক ওভারফ্লো ক্লিপিং বন্ধ হয়ে যায়।
ফলব্যাক স্ট্র্যাটেজি
আপনার বেসলাইন টার্গেট যদি এলিমেন্ট-স্কোপড ভিউ ট্রানজিশন সাপোর্ট না করে, তাহলে অ্যানিমেশন ছাড়াই অবিলম্বে DOM আপডেট কলব্যাক রান করে সুন্দরভাবে ডিগ্রেড করুন। আপনি document.startViewTransition()-এ ফিরে যেতে পারলেও, বিচ্ছিন্ন কম্পোনেন্ট আপডেটের জন্য এটি সাজেস্ট করা হয় না, কারণ ডকুমেন্ট-স্কোপড ট্রানজিশন কনকারেন্ট কম্পোনেন্ট ট্রানজিশন ব্লক করে এবং উপরের লেয়ারে position: fixed ও [popover] এলিমেন্ট অস্পষ্ট করে দেয়।
// Feature detect specifically on Element.prototype (not document.startViewTransition)
const supportsScopedTransition = 'startViewTransition' in Element.prototype;
function runWithTransition(scopeEl, updateDOM) {
if (!supportsScopedTransition) {
// Fallback: apply the DOM update immediately without animation
updateDOM();
return;
}
const transition = scopeEl.startViewTransition(() => {
updateDOM();
});
transition.ready.catch(() => {});
}
document.startViewTransition() ব্যবহার করে আপনাকে যদি সত্যিই ফলব্যাক করতে হয়, তাহলে আপনাকে নিশ্চিত করতে হবে যে কন্টেনারের ক্লিপিং এফেক্ট যেন সংরক্ষিত থাকে, যেমনটি আউটার ট্রানজিশন চালানো বিভাগে বিস্তারিতভাবে বলা হয়েছে। মনে রাখবেন যে এই ফলব্যাক অ্যাপ্রোচ একই সাথে একাধিক ভিউ ট্রানজিশন রান করার ক্ষমতাকে নষ্ট করে এবং উপরের লেয়ারে position: fixed ও [popover] এলিমেন্টকে অস্পষ্ট করে দেয়।