"टॉप लेयर" में रेंडर होने वाले एलिमेंट (जैसे, <dialog>, popover एट्रिब्यूट वाले एलिमेंट या टूलटिप) को ऐनिमेट करना मुश्किल होता है. ऐसा इसलिए, क्योंकि वे display: none और दिखने वाली स्थिति के बीच टॉगल करते हैं. मॉडर्न सीएसएस में @starting-style, transition-behavior: allow-discrete, और overlay प्रॉपर्टी मौजूद हैं. इनकी मदद से, इन एलिमेंट के लिए एंट्री और एग्ज़िट ट्रांज़िशन को आसानी से चालू किया जा सकता है. ध्यान दें कि नीचे दिए गए उदाहरणों में, नेटिव सीएसएस नेस्टिंग का इस्तेमाल किया गया है.
लागू करना
1. डिसक्रीट ट्रांज़िशन की सुविधा चालू करना
display प्रॉपर्टी को ऐनिमेट करने के लिए, आपको transition-behavior: allow-discrete सेट करना होगा. इससे एलिमेंट, एग्ज़िट ट्रांज़िशन के दौरान दिखता रहता है. अगर ट्रांज़िशन शॉर्टहैंड का इस्तेमाल किया जा रहा है, तो पक्का करें कि transition-behavior: allow-discrete को बाद में रखा जाए, ताकि शॉर्टहैंड इसे नकार न सके.
2. overlay प्रॉपर्टी
जब कोई एलिमेंट टॉप लेयर में जाता है या उससे बाहर निकलता है, तो उसे overlay प्रॉपर्टी को ट्रांज़िशन करना चाहिए. इससे यह पक्का होता है कि ऐनिमेशन के दौरान, एलिमेंट सबसे ऊपर वाली लेयर में बना रहे. इससे एलिमेंट को अन्य एलिमेंट या व्यूपोर्ट से समय से पहले क्लिप होने से रोका जा सकता है.
3. @starting-style के साथ एंट्री ऐनिमेशन
@starting-style at-rule का इस्तेमाल करके, उन स्टाइल को तय करें जिनसे किसी एलिमेंट को पहली बार रेंडर होने पर या उसकी display वैल्यू none से बदलने पर, ट्रांज़िशन करना चाहिए.
4. बैकड्रॉप को ऐनिमेट करना
::backdrop सूडो-एलिमेंट को भी इसी तरह ऐनिमेट किया जा सकता है. इसके लिए, इसकी प्रॉपर्टी पर ट्रांज़िशन लागू करें.
उदाहरण
/* 1. Define the visible (open) state */
dialog[open],
[popover]:popover-open {
opacity: 1;
transform: scale(1);
/* 2. Define the starting state for entry (must come after open state) */
@starting-style {
opacity: 0;
transform: scale(0.9);
}
}
/* 3. Define the base (closed/exit) state and transitions */
dialog,
[popover] {
opacity: 0;
transform: scale(0.9);
/* MANDATORY: transition display and overlay for top-layer elements */
transition-property: opacity, transform, display, overlay;
transition-duration: 0.3s;
transition-timing-function: ease-out;
/* Applies to discrete properties like display and overlay */
transition-behavior: allow-discrete; /* Note: be sure to write this after the shorthand */
}
/* 4. Animate the backdrop */
dialog::backdrop,
[popover]::backdrop {
background-color: rgba(0, 0, 0, 0);
transition-property: background-color, display, overlay;
transition-duration: 0.3s;
transition-timing-function: ease-out;
transition-behavior: allow-discrete;
}
dialog[open]::backdrop,
[popover]:popover-open::backdrop {
background-color: rgba(0, 0, 0, 0.5);
@starting-style {
background-color: rgba(0, 0, 0, 0);
}
}
/* 5. Respect user preference for reduced motion */
@media (prefers-reduced-motion: reduce) {
dialog,
[popover] {
/* Disable movement and shorten duration for a simple fade */
transform: none;
transition-duration: 0.1s;
}
@starting-style {
dialog[open],
[popover]:popover-open {
transform: none;
}
}
}
पाबंदियां और सुलभता
- ज़रूरी: टॉप लेयर में जाने वाले या उससे बाहर आने वाले किसी भी एलिमेंट के लिए, अपनी
transitionसूची मेंoverlayको शामिल करें. - ज़रूरी:
displayप्रॉपर्टी ट्रांज़िशन के लिए,allow-discreteका इस्तेमाल करें. - ज़रूरी:
prefers-reduced-motionका इस्तेमाल करके, कम मोशन के लिए उपयोगकर्ता की प्राथमिकताओं का सम्मान करें.इसके लिए, ट्रांज़िशन को आसान बनाएं. उदाहरण के लिए, ट्रांसफ़ॉर्म हटाएं और अवधि कम करें. - क्या करें: सही तरीके से कैस्केडिंग हो, इसके लिए
@starting-styleब्लॉक को "open" स्टेट सिलेक्टर के अंदर या उसके बाद रखें. - ऐसा न करें: एग्ज़िट ऐनिमेशन के लिए
@starting-styleका इस्तेमाल न करें. एग्ज़िट ऐनिमेशन, बेस (बंद) स्थिति में ट्रांज़िशन से तय होते हैं.
फ़ॉलबैक रणनीतियां
फ़ॉलबैक और transition-behavior के लिए ब्राउज़र की सुविधा
Firefox 129 और इसके बाद के वर्शन, display प्रॉपर्टी (Firefox की गड़बड़ी 1882408) को ट्रांज़िशन किए बिना ही transition-behavior: allow-discrete (CSS.supports('transition-behavior', 'allow-discrete'), true दिखाता है) को पार्स करते हैं. इस वजह से, एलिमेंट बाहर निकलने पर तुरंत गायब हो जाते हैं.
display ट्रांज़िशन के लिए अलग-अलग सपोर्ट का पता लगाने के लिए, यह जांच करें कि किसी अस्थायी एलिमेंट का कंप्यूट किया गया display, none पर ट्रांज़िशन करने पर दिखता है या नहीं. इसके लिए, सिर्फ़ CSS.supports('transition-behavior', 'allow-discrete') पर भरोसा न करें:
let supportsDisplayTransition;
function canTransitionDisplay() {
if (supportsDisplayTransition !== undefined) return supportsDisplayTransition;
if (!window.CSS?.supports?.('transition-behavior', 'allow-discrete') || !document.body) {
return false;
}
const probe = document.createElement('div');
// The shorthand is intentional here: browsers that don't parse allow-discrete
// drop the whole declaration, so display: none applies instantly and the probe
// correctly returns false. !important guards against global reduced-motion
// resets like `* { transition: none !important }`.
probe.style.cssText = 'transition: display 1s allow-discrete !important; display: block;';
document.body.appendChild(probe);
getComputedStyle(probe).display;
probe.style.display = 'none';
supportsDisplayTransition = getComputedStyle(probe).display === 'block';
probe.remove();
return supportsDisplayTransition;
}
टॉप-लेयर एग्ज़िट फ़ॉलबैक
एंट्री ऐनिमेशन, @starting-style के साथ काम करने वाले सभी ब्राउज़र पर प्योर सीएसएस में काम करते हैं. इसके लिए, किसी .is-opening क्लास की ज़रूरत नहीं होती, क्योंकि एंट्री ट्रांज़िशन, overlay या अलग-अलग display ट्रांज़िशन पर निर्भर नहीं करते.
एग्ज़िट ऐनिमेशन के लिए, overlay और डिसक्रीट display ट्रांज़िशन, दोनों की ज़रूरत होती है. अगर इनमें से कोई भी काम नहीं करता है (जैसे, Firefox और Safari में), तो ओपन-स्टेट सिलेक्टर को :is() में रैप करें और :where(:not([data-closing])) (नेस्टिंग &::backdrop और @starting-style) जोड़ें, ताकि data-closing सेट करने पर, एलिमेंट टॉप लेयर में रहते हुए एग्ज़िट ट्रांज़िशन ट्रिगर हो. इसके बाद, .close() या .hidePopover() को कॉल करने से पहले, getAnimations() के सेटल होने का इंतज़ार करें:
:is(dialog[open], [popover]:popover-open):where(:not([data-closing])) {
opacity: 1;
transform: scale(1);
@starting-style {
opacity: 0;
transform: scale(0.9);
}
&::backdrop {
background-color: rgb(0 0 0 / 0.5);
@starting-style {
background-color: transparent;
}
}
}
// Evaluate lazily: canTransitionDisplay() needs document.body, so a top-level
// const would be permanently false if this script runs in <head>.
function supportsTopLayerExit() {
return window.CSS?.supports?.('overlay', 'auto') && canTransitionDisplay();
}
async function closeTopLayer(element) {
if (!supportsTopLayerExit()) {
element.setAttribute('data-closing', '');
const animations = element.getAnimations({ subtree: true });
if (animations.length > 0) {
await Promise.race([
Promise.allSettled(animations.map((a) => a.finished)),
new Promise((r) => setTimeout(r, 2000)),
]);
}
if (!element.hasAttribute('data-closing')) return;
element.removeAttribute('data-closing');
}
element.close();
// Or for popover:
// element.hidePopover();
}
// Route native close requests (Esc, closedby light dismiss) through the same
// helper so they animate too, instead of closing instantly.
dialog.addEventListener('cancel', (event) => {
event.preventDefault();
closeTopLayer(dialog);
});
पॉपओवर लाइट डिसमिस और popovertarget टॉगल को इंटरसेप्ट नहीं किया जा सकता. beforetoggle को सिर्फ़ खोलते समय रद्द किया जा सकता है. इसलिए, जिन ब्राउज़र को फ़ॉलबैक की ज़रूरत होती है उनमें ये एग्ज़िट तुरंत होते हैं. बंद करने के लिए साफ़ तौर पर दिखने वाला कंट्रोल दें, जो एग्ज़िट ऐनिमेशन ज़रूरी होने पर closeTopLayer() को कॉल करे.
पॉपओवर के लिए फ़ॉलबैक और ब्राउज़र की सुविधा
Popover API ज़्यादातर प्रोग्रेसिव एन्हांसमेंट है. हालांकि, इसके मुख्य व्यवहार — टॉप-लेयर प्रमोशन, लाइट-डिसमिस, और popovertarget को शुरू करना — सिर्फ़ सीएसएस के बराबर नहीं हैं. पुराने ब्राउज़र के लिए, polyfill की ज़रूरत होती है. अगर आपको polyfill का इस्तेमाल नहीं करना है, तो मैन्युअल फ़ॉलबैक की ज़रूरत होती है.
पॉलीफ़िल: पुराने ब्राउज़र में popover एट्रिब्यूट का इस्तेमाल करने के लिए, शर्त के मुताबिक @oddbird/popover-polyfill लोड करें. ज़रूरी: HTMLElement.prototype पर popover प्रॉपर्टी की जांच करके, सुविधा का पता लगाएं. साथ ही, पॉलीफ़िल को सिर्फ़ तब लोड करें, जब नेटिव सपोर्ट मौजूद न हो. इसे बिना किसी शर्त के लोड न करें.
बंडलर या इंपोर्ट मैप के साथ:
// MANDATORY: Feature detect 'popover' on HTMLElement.prototype.
if (!("popover" in HTMLElement.prototype)) {
import("@oddbird/popover-polyfill");
}
बंडलर के बिना, <script type="module"> में सीडीएन से इंपोर्ट करें:
<script type="module">
if (!("popover" in HTMLElement.prototype)) {
import("https://unpkg.com/@oddbird/popover-polyfill@latest/dist/popover.min.js");
}
</script>
स्टाइलिंग से जुड़ी चेतावनी: polyfill, असल :popover-open स्यूडो-क्लास को तय नहीं कर सकता. इसलिए, यह इसके बजाय .\:popover-open क्लास लागू करता है. ज़रूरी: दोनों को :is() या :where() के साथ जोड़ें. ऐसा न करने पर, जिन ब्राउज़र में :popover-open नहीं है वे पूरे नियम को खारिज कर देते हैं:
[popover]:is(:popover-open, .\:popover-open) {
display: block;
}
इसके अलावा, बिना polyfill के लेगसी फ़ॉलबैक के लिए, position: fixed का इस्तेमाल करें और getBoundingClientRect() की मदद से मैन्युअल तरीके से निर्देशांकों का हिसाब लगाएं. इसके अलावा, अगर इस्तेमाल के उदाहरण के लिए यह स्वीकार्य है, तो inset: auto के साथ डिफ़ॉल्ट पोज़िशनिंग का इस्तेमाल करें.