ইন্টার‍্যাক্টিভ হোভারকার্ড তৈরি করুন

ব্যবহারকারী কোনও লিঙ্কে আগ্রহ প্রকাশ করলে (হোভার বা ফোকাস করার মাধ্যমে), হোভারকার্ড কন্টেন্টের একটি ইন্টার‍্যাক্টিভ প্রিভিউ প্রদান করে। ব্যবহারকারী নেভিগেট করার আগে এটি দেখানো হয় এবং ব্যবহারকারীকে তার প্রয়োজনীয় কন্টেন্ট প্রদান করে অথবা লিঙ্কে তার প্রয়োজনীয় কন্টেন্ট আছে কিনা তা যাচাই করতে সাহায্য করে।

স্ট্রাকচার্ড লিঙ্ক প্রিভিউয়ের পরিবর্তে আপনার যদি প্লেন-টেক্সট, নন-ইন্টার‍্যাক্টিভ টুলটিপ প্রয়োজন হয়, তাহলে হোভার করার সময় টুলটিপ দেখুন।

১. হোভারকার্ড ট্রিগার করা হচ্ছে

হোভারকার্ড ট্রিগার করতে Interest Invokers API ব্যবহার করুন। এটি নিশ্চিত করে যে কার্ডটি হোভার এবং ফোকাস উভয় ক্ষেত্রেই দেখা যাবে এবং অ্যাক্সেসিবিলিটি ওয়্যারিং অটোমেটিক ম্যানেজ করবে।

  • আপনার কার্ড কন্টেনারে 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-এ পাল্টে যায়, তাই টার্গেটে ইন্টার‍্যাক্টিভ কন্টেন্ট (যেমন, "ইন্টারেস্ট কার্ডের" মধ্যে লিঙ্ক) থাকতে পারে।

২. অ্যাঙ্কর পজিশনিংয়ের মাধ্যমে পজিশন করা

CSS অ্যাঙ্কর পজিশনিং ব্যবহার করে হোভারকার্ডের ট্রিগারের সাপেক্ষে সেটির পজিশন সেট করুন। 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;
}

৩. প্রবেশ করা ও বেরিয়ে আসার অ্যানিমেশন

ডিফল্ট হিসেবে, কীবোর্ড ও মাউস ব্যবহারকারীদের জন্য আগ্রহ দেখানো ও হারানোর বিষয়টি আধ সেকেন্ডের বিলম্বের সাথে দেখানো হয়। এটি কোনও পৃষ্ঠার মধ্যে ট্যাব করার সময় বা মাউস সরানোর সময় টার্গেট করা পপওভার খোলা এবং ট্রিগার থেকে পপওভারে মাউস সরানোর সময় বন্ধ হওয়া আটকায়। টাইমিং অ্যাডজাস্ট করতে, interest-delay CSS প্রপার্টি ব্যবহার করুন।

[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;
  }
}

৪. ঐচ্ছিক: পরের পৃষ্ঠায় নির্বিঘ্নে পরিবর্তন করা হচ্ছে

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

হোভারকার্ডে থাকা এলিমেন্ট এবং পরবর্তী পৃষ্ঠায় সেটির প্রতিরূপের জন্য ম্যাচিং view-transition-name ভ্যালু অ্যাসাইন করার মাধ্যমে, ব্রাউজার নেভিগেশন জুড়ে সেগুলিকে অ্যানিমেট করবে। আপনি একাধিক এলিমেন্টের (যেমন, শীর্ষক, ছবি, বাইলাইন) নাম দিতে পারেন যাতে জটিল, মাল্টি-এলিমেন্ট মর্ফিং এফেক্ট তৈরি করা যায়। ডুপ্লিকেট view-transition-name তৈরি করা বা লিঙ্ক-নির্দিষ্ট view-transition-name-এর প্রয়োজনীয়তা এড়াতে, :popover-open ব্যবহার করে খোলা পপওভারে এলিমেন্টে 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="..."> লিঙ্ক হওয়ার কারণে, আপনি যদি হোভারকার্ডকে প্রোগ্রেসিভ এনহ্যান্সমেন্ট হিসেবে বিবেচনা করার সিদ্ধান্ত নেন, তাহলে যেসব ব্রাউজারে এই সুবিধা নেই সেগুলিও ক্লিক করার মাধ্যমে নেভিগেট করতে পারবে। ক্রস-ব্রাউজার হোভারকার্ড সাপোর্ট প্রয়োজন হলে, প্রয়োজন অনুযায়ী শর্তসাপেক্ষে পলিফিল লোড করুন।

পপওভারের জন্য ফলব্যাক ও ব্রাউজার সাপোর্ট

পপওভার API মূলত প্রোগ্রেসিভ এনহ্যান্সমেন্ট, তবে এর সংজ্ঞায়িত আচরণ — টপ-লেয়ার প্রোমোশন, লাইট-ডিসমিস এবং popovertarget ইনভোকেশন — শুধুমাত্র CSS-এর সমতুল্য নয়। পুরনো ব্রাউজারের ক্ষেত্রে পলিফিল বা ম্যানুয়াল ফলব্যাক প্রয়োজন হয়, যদি আপনি কোনওটিই শিপ করতে না চান।

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">-এর মধ্যে CDN থেকে ইমপোর্ট করুন:

<script type="module">
  if (!("popover" in HTMLElement.prototype)) {
    import("https://unpkg.com/@oddbird/popover-polyfill@latest/dist/popover.min.js");
  }
</script>

স্টাইলিং সংক্রান্ত সতর্কতা: পলিফিল আসল :popover-open ছদ্ম-ক্লাসকে সংজ্ঞায়িত করতে পারে না, তাই এটি পরিবর্তে .\:popover-open ক্লাস প্রয়োগ করে। বাধ্যতামূলক: :is() বা :where()-এর সাথে দুটিকে একত্রিত করুন, অন্যথায় :popover-open নেই এমন ব্রাউজার সম্পূর্ণ নিয়মটি বাতিল করে দেয়:

[popover]:is(:popover-open, .\:popover-open) {
  display: block;
}

বিকল্প হিসেবে, পলিফিল ছাড়া লিগ্যাসি ফলব্যাকের জন্য, position: fixed ব্যবহার করুন এবং getBoundingClientRect()-এর মাধ্যমে ম্যানুয়ালি কোঅর্ডিনেট হিসেব করুন অথবা ব্যবহারের ক্ষেত্রে গ্রহণযোগ্য হলে inset: auto-এর সাথে ডিফল্ট পজিশনিংয়ের উপর নির্ভর করুন।

ইন্টারেস্ট ইনভোকারের জন্য ফলব্যাক ও ব্রাউজার সহায়তা

NPM থেকে interestfor পলিফিল প্যাকেজ ব্যবহার করে ইন্টারেস্ট ইনভোকারকে শর্তসাপেক্ষে পলিফিল করতে হবে। CDN ব্যবহার করার পরিবর্তে পলিফিল বান্ডেল করাকে অগ্রাধিকার দিন।

বাধ্যতামূলক: HTMLButtonElement.prototype-এ interestForElement প্রপার্টি চেক করে ফিচার শনাক্ত করুন এবং নেটিভ সাপোর্ট না থাকলে শুধুমাত্র তখনই পলিফিল লোড করুন।

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

CSS পলিফিল সংক্রান্ত সতর্কতা

পলিফিল ব্যবহার করার সময়, নেটিভ সিউডো-ক্লাস ও প্রপার্টি সাপোর্ট না থাকার কারণে CSS API-তে সামান্য পরিবর্তন হয়:

  • সিউডো-ক্লাস: পলিফিল আসল :interest-source এবং :interest-target সিউডো-ক্লাস নির্ধারণ করতে পারে না, তাই এটি পরিবর্তে .interest-source এবং .interest-target ক্লাস প্রয়োগ করে। বাধ্যতামূলক: নেটিভ ও পলিফিল্ড, উভয় স্টেটেই কাজ করার জন্য :is()-এর সাথে এগুলি একত্রিত করুন।
/* Styles to apply when the effect is being previewed */
:is(:interest-source, .interest-source) { /* ... */ }
:is(:interest-target, .interest-target) { /* ... */ }
  • CSS প্রপার্টি: interest-delay, interest-delay-start ও interest-delay-end-এর জন্য, পলিফিলকে CSS ভেরিয়েবল ব্যবহার করতে হয় কারণ এটি সব ব্রাউজারে সরাসরি কাস্টম প্রপার্টি পড়তে পারে না।
/* 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);
}