इंटरैक्टिव होवरकार्ड बनाना

जब कोई उपयोगकर्ता किसी लिंक में दिलचस्पी दिखाता है (कर्सर घुमाकर या फ़ोकस करके), तो होवरकार्ड, कॉन्टेंट की इंटरैक्टिव झलक दिखाते हैं. यह जानकारी, उपयोगकर्ताओं को नेविगेट करने से पहले दिखती है. इससे उन्हें वह कॉन्टेंट मिलता है जिसकी उन्हें ज़रूरत है या यह पुष्टि करने में मदद मिलती है कि लिंक में वह कॉन्टेंट मौजूद है जिसे वे खोज रहे हैं.

अगर आपको स्ट्रक्चर्ड लिंक की झलक के बजाय, सादे टेक्स्ट वाले और नॉन-इंटरैक्टिव टूलटिप चाहिए, तो कर्सर घुमाने पर टूलटिप दिखाएं लेख पढ़ें.

1. होवरकार्ड को ट्रिगर करना

होवरकार्ड को ट्रिगर करने के लिए, Interest Invokers एपीआई का इस्तेमाल करें. इससे यह पक्का होता है कि कार्ड, कर्सर घुमाने और फ़ोकस करने, दोनों पर दिखे. साथ ही, सुलभता से जुड़ी वायरिंग अपने-आप मैनेज हो.

  • अपने कार्ड कंटेनर में popover="hint" एट्रिब्यूट जोड़ें. hint का इस्तेमाल करें, ताकि ऐसे पॉपओवर मेन्यू बंद न हों जो काम के नहीं हैं और खुले हो सकते हैं.
  • ट्रिगर लिंक में interestfor एट्रिब्यूट जोड़ें, जो कार्ड के id पर ले जाता हो.
<a href="/article" interestfor="article-preview">Article title</a>

<!-- The `id` must match the `interestfor` value on the trigger. -->
<div id="article-preview" popover="hint" class="hovercard">
  <h2>The buzz on hummingbirds</h2>
  <p class="byline">By Delphi Aguilar</p>
  <div>
    <img src="bird.jpg" alt="A vibrant hummingbird">
    <p>Brief summary of the article...</p>
  </div>
</div>

आपको होवरकार्ड में मौजूद कॉन्टेंट के लिए कोई अतिरिक्त लिंक शामिल करने की ज़रूरत नहीं है. ऐसा इसलिए, क्योंकि यह ट्रिगर करने वाले लिंक के लिए ग़ैर-ज़रूरी है.

interestfor में पहले से मौजूद सुलभता सुविधाएं

interestfor का इस्तेमाल करने पर, ब्राउज़र आपके लिए सहायक टेक्नोलॉजी की वायरिंग को मैनेज करता है:

  • इंप्लिसिट सिमैंटिक: popover="hint" वाले टारगेट को tooltip का इंप्लिसिट मिनिमम रोल मिलता है. role="tooltip" को खुद सेट न करें.
  • इंप्लिसिट असोसिएशन: ब्राउज़र, सोर्स एलिमेंट को टारगेट के साथ इंप्लिसिट तरीके से जोड़ता है. अगर टारगेट सादा टेक्स्ट है, तो aria-describedby के ज़रिए और अगर टारगेट में इंटरैक्टिव कॉन्टेंट है, तो aria-details के ज़रिए जोड़ा जाता है. ट्रिगर में aria-describedby या aria-details न जोड़ें.
  • इंटरैक्टिव कॉन्टेंट: ज़रूरत पड़ने पर असोसिएशन, aria-details पर स्विच हो जाता है.इसलिए, टारगेट में इंटरैक्टिव कॉन्टेंट शामिल करने की अनुमति है. उदाहरण के लिए, "दिलचस्पी वाले कार्ड" में मौजूद लिंक.

2. ऐंकर पोज़िशनिंग की मदद से पोज़िशनिंग करना

सीएसएस ऐंकर पोज़िशनिंग का इस्तेमाल करके, होवरकार्ड को उसके ट्रिगर के हिसाब से पोज़िशन करें. interestfor का इस्तेमाल करने पर, ट्रिगर पॉपओवर के लिए इंप्लिसिट ऐंकर बन जाता है.

[popover].hovercard {
  /* Position the card below the trigger, aligned with its inline-start edge
     and spanning toward the inline-end, flipping if it overflows the viewport. */
  position-area: block-end span-inline-end;
  position-try-fallbacks: flip-block, flip-inline;
}

3. एंट्री और एग्ज़िट के लिए ऐनिमेशन

डिफ़ॉल्ट रूप से, कीबोर्ड और माउस का इस्तेमाल करने वाले उपयोगकर्ताओं को आधे सेकंड की देरी से फ़ोकस दिखता है और हटता है. इससे, किसी पेज पर टैब करते समय या माउस को इधर-उधर घुमाते समय, टारगेट किया गया पॉपओवर नहीं खुलता. साथ ही, माउस को ट्रिगर से पॉपओवर पर ले जाने पर, पॉपओवर बंद नहीं होता. टाइमिंग अडजस्ट करने के लिए, interest-delay सीएसएस प्रॉपर्टी का इस्तेमाल करें.

[interestfor]{
  /* Shorthand for interest-delay-start and interest-delay-end */
  interest-delay: 0.2s 0.1s;
}

अगर उपयोगकर्ताओं के एक के बाद एक कई ट्रिगर पर जाने की संभावना है, तो शुरुआती देरी को 0s पर सेट करें. ऐसा तब करें, जब दिलचस्पी के आधार पर ट्रिगर होने वाला पॉपओवर पहले से खुला हो.

:root:has(:interest-source) [interestfor] {
  interest-delay-start: 0s;
}

एंट्री ऐनिमेशन तय करने के लिए, @starting-style का इस्तेमाल करें. साथ ही, टॉप लेयर से एग्ज़िट को ऐनिमेट करने के लिए, transition-behavior: allow-discrete (display और overlay के साथ) का इस्तेमाल करें. इससे ट्रांज़िशन @media (prefers-reduced-motion: no-preference) के पीछे गेट हो जाएंगे.

[popover].hovercard {
  opacity: 0;
  transform-origin: top;
}

[popover].hovercard:popover-open {
  opacity: 1;
  transform: translateY(0) scale(1);
}

@starting-style {
  [popover].hovercard:popover-open {
    opacity: 0;
    transform: translateY(-10px) scale(0.95);
  }
}

/* MANDATORY: Respect user preference for reduced motion by selectively applying transitions. */
@media (prefers-reduced-motion: no-preference) {
  [popover].hovercard {
    transition:
      opacity,
      transform,
      display,
      overlay;
    transition-duration: 0.4s;
    transition-behavior: allow-discrete;
  }
}

4. ज़रूरी नहीं: अगले पेज पर आसानी से मॉर्फ़ करना

होवरकार्ड और डेस्टिनेशन पेज के बीच एक बेहतर "मॉर्फ़" ऐनिमेशन बनाने के लिए, क्रॉस-डॉक्यूमेंट व्यू ट्रांज़िशन का इस्तेमाल किया जा सकता है. इसे लागू करने के बारे में जानने के लिए, एक जैसे क्रॉस-डॉक्यूमेंट ट्रांज़िशन देखें.

होवरकार्ड में मौजूद एलिमेंट और अगले पेज पर मौजूद उनके मिलते-जुलते एलिमेंट को एक जैसी view-transition-name वैल्यू असाइन करके, ब्राउज़र उन्हें नेविगेशन के दौरान ऐनिमेट करेगा. एक से ज़्यादा एलिमेंट (जैसे, टाइटल, इमेज, बायलाइन) को नाम दिया जा सकता है, ताकि एक से ज़्यादा एलिमेंट वाला कॉम्प्लेक्स मॉर्फ़िंग इफ़ेक्ट बनाया जा सके. :popover-open का इस्तेमाल करके, खुले हुए पॉपओवर पर मौजूद एलिमेंट को view-transition-name असाइन करें. इससे डुप्लीकेट view-transition-name बनाने या लिंक के हिसाब से view-transition-name बनाने की ज़रूरत नहीं पड़ती.

/* Opt-in to cross-document transitions on both pages */
@view-transition {
  navigation: auto;
}

/* Don't show transitions when loading this page */
::view-transition-group(*) {
  animation-duration: 0s;
}

/* Assign names ONLY when the popover is open to ensure only the correct elements are selected. */
[popover]:popover-open {
  view-transition-name: --hovercard;
  view-transition-class: morph;

  h2 {
    view-transition-name: --title;
    view-transition-class: morph;
  }

  img {
    view-transition-name: --image;
    view-transition-class: morph;
  }
}

ज़रूरी: यह पक्का करने के लिए कि ट्रांज़िशन स्टेबल हो और फ़्लिकर न करे, डेस्टिनेशन पेज को blocking="render" और <link rel="expect"> का इस्तेमाल करना चाहिए. इससे पहले फ़्रेम को रेंडर करने से पहले, ज़रूरी कॉन्टेंट को पार्स होने में इंतज़ार करना पड़ता है.

<!-- On the destination page: -->
<head>
  <!-- Block rendering until the hero image and title are parsed,
       ensuring the view transition snapshot is taken at the right time. -->
  <link rel="expect" href="#main-content" blocking="render">
</head>
<body>
  <main id="main-content">
    <h1>The buzz on hummingbirds</h1>
    <img src="bird.jpg">
  </main>
</body>
/* On the destination page: */
main {
  view-transition-name: --hovercard;
  view-transition-class: morph;
}
h1 {
  view-transition-name: --title;
  view-transition-class: morph;
}
img {
  view-transition-name: --image;
  view-transition-class: morph;
}

/* By default no transition */
::view-transition-group(*) {
  animation-duration: 0s;
}
/* Short transition for morphing elements */
::view-transition-group(.morph) {
  animation-duration: 0.4s;
}
/* Disable transition if no `old` view */
::view-transition-new(.morph):only-child {
  animation-duration: 0s;
}

फ़ॉलबैक रणनीतियां

ट्रिगर एक स्टैंडर्ड <a href="..."> लिंक है. इसलिए, अगर आपने होवरकार्ड को प्रोग्रेसिव एन्हांसमेंट के तौर पर इस्तेमाल करने का विकल्प चुना है, तो जिन ब्राउज़र में यह सुविधा काम नहीं करती वे भी क्लिक करने पर नेविगेट कर सकते हैं. जब अलग-अलग ब्राउज़र पर होवर कार्ड की सुविधा की ज़रूरत हो, तब ज़रूरत के हिसाब से polyfill लोड करें.

पॉपओवर के लिए फ़ॉलबैक और ब्राउज़र की सुविधा

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 के साथ डिफ़ॉल्ट पोज़िशनिंग का इस्तेमाल करें.

इंटरेस्ट इन्वोकर के लिए फ़ॉलबैक और ब्राउज़र की सुविधा

दिलचस्पी के आधार पर ट्रिगर होने वाले इवेंट को, NPM के interestfor पॉलीफ़िल पैकेज का इस्तेमाल करके, शर्त के साथ पॉलीफ़िल किया जाना चाहिए. सीडीएन का इस्तेमाल करने के बजाय, पॉलीफ़िल को बंडल करें.

ज़रूरी: HTMLButtonElement.prototype पर interestForElement प्रॉपर्टी की जांच करके, सुविधा का पता लगाएं. साथ ही, नेटिव सपोर्ट मौजूद न होने पर ही polyfill लोड करें.

<script type="module">
  if (!HTMLButtonElement.prototype.hasOwnProperty("interestForElement")) {
    // CDN link only used for example, prefer bundling.
    await import("https://unpkg.com/interestfor@latest");
  }
</script>

सीएसएस पॉलीफ़िल से जुड़ी चेतावनियां

पॉलीफ़िल का इस्तेमाल करने पर, सीएसएस एपीआई में थोड़ा बदलाव होता है. ऐसा नेटिव स्यूडो-क्लास और प्रॉपर्टी के काम न करने की वजह से होता है:

  • Pseudo-classes: Polyfill, असल :interest-source और :interest-target pseudo-classes को तय नहीं कर सकता. इसलिए, यह इसके बजाय .interest-source और .interest-target क्लास लागू करता है. ज़रूरी: नेटिव और पॉलीफ़िल, दोनों स्थितियों के साथ काम करने के लिए, उन्हें :is() के साथ जोड़ें.
/* Styles to apply when the effect is being previewed */
:is(:interest-source, .interest-source) { /* ... */ }
:is(:interest-target, .interest-target) { /* ... */ }
  • सीएसएस प्रॉपर्टी: interest-delay, interest-delay-start, और interest-delay-end के लिए, पॉलीफ़िल को सीएसएस वैरिएबल का इस्तेमाल करना ज़रूरी है. ऐसा इसलिए, क्योंकि यह सभी ब्राउज़र में कस्टम प्रॉपर्टी को सीधे तौर पर नहीं पढ़ सकता.
/* Adjust the start and end delay for interest invokers */
[interestfor] {
  --interest-delay-start: 0.2s;
  interest-delay-start: var(--interest-delay-start);
  --interest-delay-end: 0.1s;
  interest-delay-end: var(--interest-delay-end);
}