ট্যাব কম্পোনেন্ট তৈরি করা

iOS এবং Android অ্যাপে পাওয়া যায় এমন ট্যাব কম্পোনেন্ট কীভাবে তৈরি করতে হয় তার একটি প্রাথমিক ওভারভিউ।

এই পোস্টে আমি ওয়েবের জন্য একটি ট্যাব কম্পোনেন্ট তৈরি করার বিষয়ে চিন্তাভাবনা শেয়ার করতে চাই যা প্রতিক্রিয়াশীল, একাধিক ডিভাইস ইনপুট সমর্থন করে এবং ব্রাউজার জুড়ে কাজ করে। ডেমো ব্যবহার করে দেখুন।

ডেমো

আপনি ভিডিও দেখতে চাইলে, এই পোস্টের YouTube ভার্সন এখানে দেওয়া হল:

এক নজরে

ট্যাব হল ডিজাইন সিস্টেমের একটি সাধারণ উপাদান কিন্তু এটি বিভিন্ন আকার ও প্রকারের হতে পারে। প্রথমে <frame> এলিমেন্টের উপর ভিত্তি করে ডেস্কটপ ট্যাব তৈরি করা হয়েছিল এবং এখন আমাদের কাছে মোবাইল কম্পোনেন্ট আছে যা ফিজিক্স প্রপার্টির উপর ভিত্তি করে কন্টেন্ট অ্যানিমেট করে। তারা সবাই একই কাজ করার চেষ্টা করছে: জায়গা বাঁচানো।

আজ, ট্যাব ব্যবহারকারীর অভিজ্ঞতার অপরিহার্য অংশ হল একটি বোতাম নেভিগেশন এলাকা যা ডিসপ্লে ফ্রেমে কন্টেন্টের দৃশ্যমানতা টগল করে। অনেকগুলি আলাদা কন্টেন্ট এরিয়া একই স্পেস শেয়ার করে, তবে নেভিগেশনে বেছে নেওয়া বোতামের উপর ভিত্তি করে শর্তসাপেক্ষে উপস্থাপন করা হয়।

কোলাজটি বেশ বিশৃঙ্খল কারণ ওয়েব কম্পোনেন্ট ধারণার ক্ষেত্রে বিভিন্ন ধরনের স্টাইল প্রয়োগ করেছে
গত ১০ বছরেরও বেশি সময় ধরে ট্যাব কম্পোনেন্ট ওয়েব ডিজাইন স্টাইলের কোলাজ

ওয়েব কৌশল

সব মিলিয়ে, আমি এই কম্পোনেন্টটি তৈরি করা বেশ সহজ বলে মনে করেছি, যার জন্য আমি কয়েকটি গুরুত্বপূর্ণ ওয়েব প্ল্যাটফর্ম ফিচারকে ধন্যবাদ জানাই:

  • scroll-snap-points-এর জন্য উপযুক্ত স্ক্রল স্টপ পজিশন সহ স্মার্ট সোয়াইপ ও কীবোর্ড ইন্টার‍্যাকশন
  • ব্রাউজার দ্বারা পরিচালিত ইন-পেজ স্ক্রল অ্যাঙ্করিং এবং শেয়ারিং সংক্রান্ত সহায়তার জন্য URL হ্যাশের মাধ্যমে ডিপ লিঙ্ক
  • <a> এবং id="#hash" এলিমেন্ট মার্কআপ সহ স্ক্রিন রিডার সহায়তা
  • prefers-reduced-motion ক্রসফেড ট্রানজিশন ও ইনস্ট্যান্ট ইন-পেজ স্ক্রলিং চালু করার জন্য
  • ড্রাফ্টে থাকা @scroll-timeline ওয়েব ফিচার, বেছে নেওয়া ট্যাবে ডায়নামিকভাবে আন্ডারলাইন করা এবং রঙ পরিবর্তন করার জন্য

HTML

মূলত, এখানে UX হল: একটি লিঙ্কে ক্লিক করুন, URL-এ নেস্টেড পৃষ্ঠার অবস্থা দেখান এবং তারপরে ব্রাউজারটি মিলে যাওয়া এলিমেন্টে স্ক্রোল করার সাথে সাথে কন্টেন্ট এরিয়া আপডেট দেখুন।

সেখানে কিছু স্ট্রাকচারাল কন্টেন্ট মেম্বার আছে: লিঙ্ক এবং :target। আমাদের লিঙ্কের একটি তালিকা প্রয়োজন, যার জন্য একটি <nav> দুর্দান্ত, এবং <article> উপাদানের একটি তালিকা, যার জন্য একটি <section> দুর্দান্ত। প্রতিটি লিঙ্ক হ্যাশ একটি বিভাগের সাথে মিলবে, যা ব্রাউজারকে অ্যাঙ্করিংয়ের মাধ্যমে জিনিসগুলি স্ক্রোল করতে দেয়।

লিঙ্ক বোতামে ক্লিক করা হয়েছে, ফোকাস করা কন্টেন্ট স্লাইড করে দেখানো হচ্ছে

যেমন, কোনও লিঙ্কে ক্লিক করলে, Chrome 89-এ :target নিবন্ধে অটোমেটিক ফোকাস করে, কোনও JS-এর প্রয়োজন নেই। ব্যবহারকারী তারপরে তার ইনপুট ডিভাইসের মাধ্যমে নিবন্ধের কন্টেন্ট আগের মতোই স্ক্রল করতে পারবেন। এটি প্রশংসামূলক কন্টেন্ট, যেমনটি মার্কআপে উল্লেখ করা হয়েছে।

ট্যাবগুলি সাজাতে আমি নিম্নলিখিত মার্কআপ ব্যবহার করেছি:

<snap-tabs>
  <header>
    <nav>
      <a></a>
      <a></a>
      <a></a>
      <a></a>
    </nav>
  </header>
  <section>
    <article></article>
    <article></article>
    <article></article>
    <article></article>
  </section>
</snap-tabs>

আমি href এবং id প্রপার্টি সহ <a> এবং <article> এলিমেন্টের মধ্যে এইভাবে কানেকশন তৈরি করতে পারি:

<snap-tabs>
  <header>
    <nav>
      <a href="#responsive"></a>
      <a href="#accessible"></a>
      <a href="#overscroll"></a>
      <a href="#more"></a>
    </nav>
  </header>
  <section>
    <article id="responsive"></article>
    <article id="accessible"></article>
    <article id="overscroll"></article>
    <article id="more"></article>
  </section>
</snap-tabs>

এরপর আমি নিবন্ধগুলি বিভিন্ন পরিমাণ lorem এবং লিঙ্কগুলি বিভিন্ন দৈর্ঘ্য এবং ছবির সেট সহ শিরোনাম দিয়ে পূরণ করেছি। কন্টেন্ট নিয়ে কাজ করার জন্য, আমরা লেআউট শুরু করতে পারি।

স্ক্রলিং লেআউট

এই কম্পোনেন্টে ৩টি আলাদা ধরনের স্ক্রল করার জায়গা আছে:

  • নেভিগেশন (গোলাপি) অনুভূমিকভাবে স্ক্রল করা যায়
  • কন্টেন্ট এরিয়া (নীল) অনুভূমিকভাবে স্ক্রল করা যায়
  • প্রতিটি নিবন্ধ আইটেম (সবুজ) উল্লম্বভাবে স্ক্রল করা যায়।
রঙের সাথে ম্যাচ করা দিকনির্দেশক তীরচিহ্ন সহ ৩টি রঙিন বক্স যা স্ক্রল করার জায়গা আউটলাইন করে এবং কোন দিকে স্ক্রল করবে তা দেখায়।

স্ক্রল করার সাথে ২ ধরনের এলিমেন্ট যুক্ত থাকে:

  1. একটি উইন্ডো
    নির্দিষ্ট ডাইমেনশন সহ একটি বক্স যাতে overflow প্রপার্টি স্টাইল আছে।
  2. অতিরিক্ত বড় সারফেস
    এই লেআউটে, এটি হল তালিকা কন্টেনার: নেভিগেশন লিঙ্ক, বিভাগীয় নিবন্ধ এবং নিবন্ধের কন্টেন্ট।

<snap-tabs> লেআউট

আমি যে টপ লেভেল লেআউট বেছে নিয়েছিলাম সেটি ছিল ফ্লেক্স (Flexbox)। আমি দিকনির্দেশ সেট করেছি column, তাই হেডার এবং বিভাগ উল্লম্বভাবে সাজানো হয়েছে। এটি আমাদের প্রথম স্ক্রল উইন্ডো এবং এটি ওভারফ্লো হিডেন সহ সবকিছু লুকিয়ে রাখে। শীঘ্রই হেডার এবং বিভাগ পৃথক জোন হিসেবে ওভারস্ক্রল ব্যবহার করবে।

HTML
<snap-tabs>
  <header></header>
  <section></section>
</snap-tabs>
CSS
  snap-tabs {
  display: flex;
  flex-direction: column;

  /* establish primary containing box */
  overflow: hidden;
  position: relative;

  & > section {
    /* be pushy about consuming all space */
    block-size: 100%;
  }

  & > header {
    /* defend against 
needing 100% */ flex-shrink: 0; /* fixes cross browser quarks */ min-block-size: fit-content; } }

রঙিন ৩-স্ক্রল ডায়াগ্রামের দিকে আবার ইঙ্গিত করে:

  • <header> এখন (গোলাপী) স্ক্রল কন্টেনার হওয়ার জন্য প্রস্তুত।
  • <section>-কে (নীল) স্ক্রল কন্টেনার হিসেবে তৈরি করা হয়েছে।

আমি নিচে যে ফ্রেমগুলি হাইলাইট করেছি VisBug আমাদের উইন্ডো দেখতে সাহায্য করে স্ক্রল কন্টেনার তৈরি করেছে।

হেডার এবং বিভাগ এলিমেন্টের উপর হটপিঙ্ক ওভারলে আছে, যা কম্পোনেন্টে তাদের নেওয়া স্পেসের আউটলাইন করে

ট্যাব <header> লেআউট

পরবর্তী লেআউটটি প্রায় একই রকম: আমি ভার্টিক্যাল অর্ডারিং তৈরি করতে ফ্লেক্স ব্যবহার করি।

HTML
<snap-tabs>
  <header>
    <nav></nav>
    <span class="snap-indicator"></span>
  </header>
  <section></section>
</snap-tabs>
CSS
header {
  display: flex;
  flex-direction: column;
}

.snap-indicator-কে লিঙ্কের গ্রুপের সাথে অনুভূমিকভাবে ট্রাভেল করতে হবে এবং এই হেডার লেআউট সেই স্টেজ সেট করতে সাহায্য করে। এখানে কোনও অ্যাবসোলিউট পজিশনড এলিমেন্ট নেই!

nav এবং span.indicator এলিমেন্টে হটপিঙ্ক ওভারলে আছে, যা কম্পোনেন্টে তাদের নেওয়া স্পেসের আউটলাইন দেখায়

এরপর, স্ক্রল স্টাইল। দেখা যাচ্ছে যে আমরা ২টি অনুভূমিক স্ক্রল এরিয়ার (হেডার ও বিভাগ) মধ্যে স্ক্রল স্টাইল শেয়ার করতে পারি, তাই আমি একটি ইউটিলিটি ক্লাস, .scroll-snap-x তৈরি করেছি।

.scroll-snap-x {
  /* browser decide if x is ok to scroll and show bars on, y hidden */
  overflow: auto hidden;
  /* prevent scroll chaining on x scroll */
  overscroll-behavior-x: contain;
  /* scrolling should snap children on x */
  scroll-snap-type: x mandatory;

  @media (hover: none) {
    scrollbar-width: none;

    &::-webkit-scrollbar {
      width: 0;
      height: 0;
    }
  }
}

প্রতিটির জন্য x অক্ষে ওভারফ্লো, ওভারস্ক্রল ট্র্যাপ করার জন্য স্ক্রল কন্টেনমেন্ট, টাচ ডিভাইসের জন্য লুকানো স্ক্রলবার এবং সবশেষে কন্টেন্ট প্রেজেন্টেশন এরিয়া লক করার জন্য স্ক্রল-স্ন্যাপ প্রয়োজন। আমাদের কীবোর্ড ট্যাব অর্ডার অ্যাক্সেসযোগ্য এবং যেকোনও ইন্টার‍্যাকশন গাইড স্বাভাবিকভাবে ফোকাস করে। স্ক্রল স্ন্যাপ কন্টেনারও তাদের কীবোর্ড থেকে একটি সুন্দর ক্যারোজেল স্টাইল ইন্টার‍্যাকশন পায়।

ট্যাব হেডার <nav> লেআউট

নেভিগেশন লিঙ্কগুলি একটি লাইনে সাজাতে হবে, কোনও লাইন ব্রেক ছাড়াই, উল্লম্বভাবে সেন্টার করা এবং প্রতিটি লিঙ্ক আইটেম স্ক্রল-স্ন্যাপ কন্টেনারে স্ন্যাপ করা উচিত। ২০২১ CSS-এর জন্য দ্রুত কাজ!

HTML
<nav>
  <a></a>
  <a></a>
  <a></a>
  <a></a>
</nav>
CSS
  nav {
  display: flex;

  & a {
    scroll-snap-align: start;

    display: inline-flex;
    align-items: center;
    white-space: nowrap;
  }
}

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

nav-এর a এলিমেন্টে হটপিঙ্ক ওভারলে আছে, যা কম্পোনেন্টে নেওয়া স্পেস এবং কোথায় ওভারফ্লো হচ্ছে তা আউটলাইন করে

ট্যাব <section> লেআউট

এই বিভাগটি একটি ফ্লেক্স আইটেম এবং এটি স্পেসের প্রধান গ্রাহক হতে হবে। এছাড়াও, নিবন্ধগুলি রাখার জন্য কলাম তৈরি করতে হবে। আবার, CSS 2021-এর জন্য দ্রুত কাজ! block-size: 100% এই এলিমেন্টকে যতটা সম্ভব পেরেন্ট এলিমেন্টকে পূরণ করার জন্য প্রসারিত করে, তারপর নিজস্ব লেআউটের জন্য, এটি পেরেন্ট এলিমেন্টের প্রস্থের 100% কলামের একটি সিরিজ তৈরি করে। এখানে শতাংশ খুব ভালো কাজ করে কারণ আমরা পেরেন্টের উপর শক্তিশালী সীমাবদ্ধতা লিখেছি।

HTML
<section>
  <article></article>
  <article></article>
  <article></article>
  <article></article>
</section>
CSS
  section {
  block-size: 100%;

  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 100%;
}

এটি যেন আমরা বলছি "যতটা সম্ভব উল্লম্বভাবে প্রসারিত করুন, একটি পুশি উপায়ে" (মনে রাখবেন আমরা হেডারটি flex-shrink: 0-এ সেট করেছি: এটি এই সম্প্রসারণ পুশের বিরুদ্ধে একটি প্রতিরক্ষা), যা সম্পূর্ণ উচ্চতার কলামের সেটের জন্য সারির উচ্চতা সেট করে। auto-flow স্টাইলটি গ্রিডকে বলে যে চাইল্ড এলিমেন্টগুলিকে সবসময় একটি অনুভূমিক লাইনে সাজাতে হবে, কোনও র‍্যাপিং ছাড়াই, ঠিক যেমনটি আমরা চাই; যাতে সেগুলি পেরেন্ট উইন্ডোর বাইরে চলে যায়।

নিবন্ধের উপাদানগুলির উপর হটপিঙ্ক ওভারলে রয়েছে, যা কম্পোনেন্টে তারা যে স্থান দখল করে এবং যেখানে তারা উপচে পড়ে তার রূপরেখা দেয়

এগুলি কখনও কখনও আমার কাছে বেশ জটিল মনে হয়! এই বিভাগের এলিমেন্টটি একটি বক্সের মধ্যে ফিট করছে, তবে এটি বক্সের একটি সেটও তৈরি করেছে। আশা করি ভিজ্যুয়াল ও ব্যাখ্যা কাজে লাগছে।

ট্যাব <article> লেআউট

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

HTML
<article>
  <h2></h2>
  <p></p>
  <p></p>
  <h2></h2>
  <p></p>
  <p></p>
  ...
</article>
CSS
article {
  scroll-snap-align: start;

  overflow-y: auto;
  overscroll-behavior-y: contain;
}

আমি মূল স্ক্রলারের মধ্যে নিবন্ধগুলি স্ন্যাপ করার বিকল্প বেছে নিয়েছি। নেভিগেশন লিঙ্ক আইটেম এবং নিবন্ধের এলিমেন্টগুলি তাদের নিজ নিজ স্ক্রল কন্টেনারের ইনলাইন-স্টার্টে যেভাবে স্ন্যাপ করে তা আমার খুব ভালো লেগেছে। এটি দেখতে এবং অনুভব করতে একটি সুরেলা সম্পর্কের মতো।

আর্টিকেল এলিমেন্ট এবং তার চাইল্ড এলিমেন্টের উপর হটপিঙ্ক ওভারলে আছে, যা কম্পোনেন্টে তাদের নেওয়া জায়গা এবং যেদিকে তারা ওভারফ্লো করে তা আউটলাইন করে

নিবন্ধটি একটি গ্রিড চাইল্ড এবং এর আকারটি ভিউপোর্ট এলাকা হিসাবে পূর্বনির্ধারিত যা আমরা স্ক্রোল UX প্রদান করতে চাই। এর অর্থ হল, এখানে আমার কোনও উচ্চতা বা প্রস্থের স্টাইল প্রয়োজন নেই, আমাকে শুধু এটি কীভাবে ওভারফ্লো করে তা নির্ধারণ করতে হবে। আমি overflow-y অটোতে সেট করেছি, এবং তারপর হ্যান্ডি overscroll-behavior প্রপার্টির সাহায্যে স্ক্রল ইন্টার‍্যাকশন ট্র্যাপ করেছি।

৩টি স্ক্রল এরিয়া রিক্যাপ

নিচে আমি সিস্টেম সেটিংসে "সবসময় স্ক্রলবার দেখান" বিকল্প বেছে নিয়েছি। আমার মনে হয় এই সেটিং চালু রেখে লেআউট কাজ করাটা দ্বিগুণ গুরুত্বপূর্ণ, কারণ আমাকে লেআউট ও স্ক্রল অর্কেস্ট্রেশন পর্যালোচনা করতে হবে।

৩টি স্ক্রলবার দেখানোর জন্য সেট করা হয়েছে, এখন লেআউট স্পেস ব্যবহার করা হচ্ছে এবং আমাদের কম্পোনেন্ট এখনও দারুণ দেখাচ্ছে

আমার মনে হয়, এই কম্পোনেন্টে স্ক্রলবার গাটার দেখালে স্ক্রল করার জায়গা, স্ক্রল করার দিক এবং একে অপরের সাথে কীভাবে ইন্টার‍্যাক্ট করে তা স্পষ্টভাবে বোঝা যায়। এই স্ক্রল উইন্ডো ফ্রেমের প্রতিটি কীভাবে লেআউটের ফ্লেক্স বা গ্রিড পেরেন্ট হিসেবে কাজ করে তা বিবেচনা করুন।

DevTools আমাদের এটি ভিজ্যুয়ালাইজ করতে সাহায্য করতে পারে:

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

স্ক্রল লেআউট সম্পূর্ণ হয়েছে: স্ন্যাপিং, ডিপ লিঙ্কেবল এবং কীবোর্ড অ্যাক্সেসযোগ্য। UX-এর উন্নতি, স্টাইল ও আনন্দের জন্য শক্তিশালী ভিত্তি।

ফিচার হাইলাইট

স্ক্রল স্ন্যাপ করা চাইল্ড এলিমেন্ট ছোট বড় করার সময় তাদের লক করা পজিশন বজায় রাখে। এর অর্থ হল, ডিভাইস রোটেট বা ব্রাউজার রিসাইজ করার সময় JavaScript-কে কোনও কিছু ভিউতে নিয়ে আসতে হবে না। Chromium DevTools ডিভাইস মোড-এ রেসপন্সিভ ছাড়া অন্য কোনও মোড বেছে নিয়ে এবং তারপর ডিভাইস ফ্রেমের সাইজ পরিবর্তন করে এটি ব্যবহার করে দেখুন। লক্ষ্য করুন যে এলিমেন্টটি ভিউতে থাকে এবং এর কন্টেন্টের সাথে লক করা থাকে। স্পেক ম্যাচ করার জন্য Chromium তাদের ইমপ্লিমেন্টেশন আপডেট করার পর থেকে এটি উপলভ্য হয়েছে। এই বিষয়ে একটি ব্লগ পোস্ট এখানে দেওয়া হল।

অ্যানিমেশন

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

আমি নিবন্ধের স্ক্রল পজিশনের সাথে একটি ট্যাব আন্ডারলাইন লিঙ্ক করব। স্ন্যাপ করা মানে শুধু সুন্দরভাবে অ্যালাইন করা নয়, এটি অ্যানিমেশনের শুরু ও শেষ অ্যাঙ্করও করে। এটি <nav>-কে কন্টেন্টের সাথে কানেক্ট করে রাখে, যা মিনি-ম্যাপ-এর মতো কাজ করে। আমরা CSS এবং JS, দুটির থেকেই ব্যবহারকারীর মোশন সংক্রান্ত পছন্দ চেক করব। কিছু দারুণ জায়গা আছে যেগুলি বিবেচনা করা যেতে পারে!

স্ক্রল করার আচরণ

:target এবং element.scrollIntoView(), উভয়েরই মোশন বিহেভিয়ার উন্নত করার সুযোগ রয়েছে। ডিফল্ট হিসেবে, এটি ইনস্ট্যান্ট হয়। ব্রাউজার শুধু স্ক্রল পজিশন সেট করে। আচ্ছা, আমরা যদি সেখানে ব্লিংক করার পরিবর্তে, সেই স্ক্রল পজিশনে ট্রানজিশন করতে চাই?

@media (prefers-reduced-motion: no-preference) {
  .scroll-snap-x {
    scroll-behavior: smooth;
  }
}

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

ট্যাব ইন্ডিকেটর

এই অ্যানিমেশনের উদ্দেশ্য হল কন্টেন্টের স্ট্যাটাসের সাথে ইন্ডিকেটরকে যুক্ত করতে সাহায্য করা। আমি সিদ্ধান্ত নিয়েছি যে, যেসব ব্যবহারকারী কম মোশন পছন্দ করেন তাদের জন্য কালার ক্রসফেড border-bottom স্টাইল এবং যারা মোশন নিয়ে স্বচ্ছন্দ তাদের জন্য স্ক্রল লিঙ্কড স্লাইডিং + কালার ফেড অ্যানিমেশন ব্যবহার করব।

Chromium Devtools-এ, আমি পছন্দ টগল করতে এবং ২টি ভিন্ন ট্রানজিশন স্টাইল দেখাতে পারি। এটি তৈরি করতে আমার খুব মজা লেগেছে।

@media (prefers-reduced-motion: reduce) {
  snap-tabs > header a {
    border-block-end: var(--indicator-size) solid hsl(var(--accent) / 0%);
    transition: color .7s ease, border-color .5s ease;

    &:is(:target,:active,[active]) {
      color: var(--text-active-color);
      border-block-end-color: hsl(var(--accent));
    }
  }

  snap-tabs .snap-indicator {
    visibility: hidden;
  }
}

ব্যবহারকারী কম মোশন পছন্দ করলে আমি .snap-indicator লুকিয়ে রাখি, কারণ আমার আর এটির প্রয়োজন নেই। তারপর আমি এটি border-block-end স্টাইল এবং একটি transition দিয়ে পরিবর্তন করি। এছাড়াও, ট্যাব ইন্টার‍্যাকশনে লক্ষ্য করুন যে অ্যাক্টিভ নেভিগেশন আইটেমে শুধু ব্র্যান্ড আন্ডারলাইন হাইলাইটই নেই, এর টেক্সটের রঙও গাঢ়। অ্যাক্টিভ এলিমেন্টে টেক্সটের রঙের কনট্রাস্ট বেশি এবং উজ্জ্বল আন্ডারলাইট অ্যাকসেন্ট রয়েছে।

CSS-এর মাত্র কয়েকটি অতিরিক্ত লাইন যোগ করলেই কেউ বুঝতে পারবেন যে তার কথা ভাবা হয়েছে (অর্থাৎ, আমরা তার মোশন সংক্রান্ত পছন্দকে গুরুত্ব দিয়েছি)। আমার খুব পছন্দ হয়েছে।

@scroll-timeline

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

const { matches:motionOK } = window.matchMedia(
  '(prefers-reduced-motion: no-preference)'
);

আমি প্রথমে জাভাস্ক্রিপ্ট থেকে ব্যবহারকারীর মোশন সংক্রান্ত পছন্দ চেক করি। এর ফলাফল false হলে, অর্থাৎ ব্যবহারকারী কম মোশন পছন্দ করলে, আমরা কোনও স্ক্রল লিঙ্কিং মোশন এফেক্ট চালাব না।

if (motionOK) {
  // motion based animation code
}

এটি লেখার সময়, @scroll-timeline-এর জন্য ব্রাউজার সাপোর্ট নেই। এটি একটি ড্রাফ্ট স্পেক যাতে শুধুমাত্র পরীক্ষামূলক প্রয়োগ রয়েছে। তবে এতে একটি পলিফিল আছে, যা আমি এই ডেমোতে ব্যবহার করি।

ScrollTimeline

CSS এবং JavaScript, দুটিই স্ক্রল টাইমলাইন তৈরি করতে পারলেও, আমি JavaScript বেছে নিয়েছি যাতে অ্যানিমেশনে লাইভ এলিমেন্ট পরিমাপ ব্যবহার করতে পারি।

const sectionScrollTimeline = new ScrollTimeline({
  scrollSource: tabsection,  // snap-tabs > section
  orientation: 'inline',     // scroll in the direction letters flow
  fill: 'both',              // bi-directional linking
});

আমি চাই যে ১টি জিনিস অন্যটির স্ক্রল পজিশন ফলো করুক এবং একটি ScrollTimeline তৈরি করে আমি স্ক্রল লিঙ্কের ড্রাইভার, scrollSource-কে সংজ্ঞায়িত করি। সাধারণত, ওয়েবে একটি অ্যানিমেশন একটি গ্লোবাল টাইম ফ্রেম টিকের বিপরীতে চলে, কিন্তু মেমরিতে একটি কাস্টম sectionScrollTimeline দিয়ে, আমি সেগুলি পরিবর্তন করতে পারি।

tabindicator.animate({
    transform: ...,
    width: ...,
  }, {
    duration: 1000,
    fill: 'both',
    timeline: sectionScrollTimeline,
  }
);

অ্যানিমেশনের কীফ্রেম নিয়ে আলোচনা করার আগে, আমার মনে হয়, স্ক্রলিংয়ের ফলোয়ার, tabindicator, কাস্টম টাইমলাইন, আমাদের বিভাগের স্ক্রল অনুযায়ী অ্যানিমেট করা হবে, এই বিষয়টি উল্লেখ করা গুরুত্বপূর্ণ। এটি লিঙ্কেজ সম্পূর্ণ করে, তবে চূড়ান্ত উপাদান, অ্যানিমেট করার জন্য স্টেটফুল পয়েন্ট, যা কীফ্রেম নামেও পরিচিত, তা মিস করে।

ডায়নামিক কীফ্রেম

@scroll-timeline-এর মাধ্যমে অ্যানিমেট করার জন্য একটি অত্যন্ত শক্তিশালী পিওর ডিক্লারেটিভ CSS পদ্ধতি আছে, কিন্তু আমি যে অ্যানিমেশনটি বেছে নিয়েছিলাম সেটি খুব বেশি ডায়নামিক ছিল। auto প্রস্থের মধ্যে ট্রানজিশন করার কোনও উপায় নেই এবং চিলড্রেন দৈর্ঘ্যের উপর ভিত্তি করে ডায়নামিকভাবে কয়েকটি কীফ্রেম তৈরি করার কোনও উপায় নেই।

তবে, জাভাস্ক্রিপ্ট জানে কীভাবে সেই তথ্য পাওয়া যায়, তাই আমরা চিলড্রেন এলিমেন্টের উপর ইটারেট করব এবং রানটাইমে কম্পিউটেড ভ্যালু সংগ্রহ করব:

tabindicator.animate({
    transform: [...tabnavitems].map(({offsetLeft}) =>
      `translateX(${offsetLeft}px)`),
    width: [...tabnavitems].map(({offsetWidth}) =>
      `${offsetWidth}px`)
  }, {
    duration: 1000,
    fill: 'both',
    timeline: sectionScrollTimeline,
  }
);

প্রতিটি tabnavitem-এর জন্য, offsetLeft পজিশন ডিস্ট্রাকচার করুন এবং একটি স্ট্রিং রিটার্ন করুন যা এটিকে translateX ভ্যালু হিসেবে ব্যবহার করে। এটি অ্যানিমেশনের জন্য ৪টি ট্রান্সফর্ম কীফ্রেম তৈরি করে। প্রস্থের ক্ষেত্রেও একই কাজ করা হয়, প্রত্যেককে জিজ্ঞাসা করা হয় যে তার ডায়নামিক প্রস্থ কত এবং তারপর এটি কীফ্রেম ভ্যালু হিসেবে ব্যবহার করা হয়।

আমার ফন্ট ও ব্রাউজার সংক্রান্ত পছন্দের উপর ভিত্তি করে এখানে আউটপুটের উদাহরণ দেওয়া হল:

TranslateX কীফ্রেম:

[...tabnavitems].map(({offsetLeft}) =>
  `translateX(${offsetLeft}px)`)

// results in 4 array items, which represent 4 keyframe states
// ["translateX(0px)", "translateX(121px)", "translateX(238px)", "translateX(464px)"]

প্রস্থ কীফ্রেম:

[...tabnavitems].map(({offsetWidth}) =>
  `${offsetWidth}px`)

// results in 4 array items, which represent 4 keyframe states
// ["121px", "117px", "226px", "67px"]

কৌশলটি সংক্ষেপে বলতে, ট্যাব ইন্ডিকেটর এখন ৪টি কীফ্রেম জুড়ে অ্যানিমেট করবে বিভাগের স্ক্রলারের স্ক্রল স্ন্যাপ পজিশনের উপর নির্ভর করে। স্ন্যাপ পয়েন্টগুলি আমাদের কীফ্রেমগুলির মধ্যে স্পষ্ট সীমানা তৈরি করে এবং অ্যানিমেশনের সিঙ্ক্রোনাইজড অনুভূতিতে সত্যিই যোগ করে।

VisBug ওভারলে সহ অ্যাক্টিভ ও ইনঅ্যাক্টিভ ট্যাব দেখানো হয়েছে, যা দুটির জন্যই পাসিং কনট্রাস্ট স্কোর দেখায়

ব্যবহারকারী ইন্টার‌্যাকশনের মাধ্যমে অ্যানিমেশনটি চালান, এক বিভাগ থেকে অন্য বিভাগে ইন্ডিকেটরের প্রস্থ এবং অবস্থান পরিবর্তন দেখেন, স্ক্রলের সাথে নিখুঁতভাবে ট্র্যাক করেন।

আপনি হয়ত লক্ষ্য করেননি, তবে হাইলাইট করা নেভিগেশন আইটেম বেছে নেওয়ার সময় রঙের যে পরিবর্তন হয়, তা নিয়ে আমি গর্বিত।

হাইলাইট করা আইটেমে আরও বেশি কনট্রাস্ট থাকলে, বেছে না নেওয়া হালকা ধূসর রঙ আরও বেশি পিছনে ঠেলে দেওয়া হয়েছে বলে মনে হয়। টেক্সটের রঙ পরিবর্তন করা সাধারণ ব্যাপার, যেমন হোভার করার সময় এবং বেছে নেওয়ার সময়, তবে স্ক্রল করার সময় সেই রঙ পরিবর্তন করা, আন্ডারলাইন ইন্ডিকেটরের সাথে সিঙ্ক করা, এটি হল নেক্সট-লেভেল।

আমি কীভাবে এটি করেছি তা এখানে দেওয়া হল:

tabnavitems.forEach(navitem => {
  navitem.animate({
      color: [...tabnavitems].map(item =>
        item === navitem
          ? `var(--text-active-color)`
          : `var(--text-color)`)
    }, {
      duration: 1000,
      fill: 'both',
      timeline: sectionScrollTimeline,
    }
  );
});

প্রতিটি ট্যাব নেভিগেশন লিঙ্কে এই নতুন কালার অ্যানিমেশন প্রয়োজন, যা আন্ডারলাইন ইন্ডিকেটরের মতো একই স্ক্রল টাইমলাইন ট্র্যাক করে। আমি আগের মতো একই টাইমলাইন ব্যবহার করি: যেহেতু এর ভূমিকা হল স্ক্রল করার সময় টিক নির্গত করা, তাই আমরা যেকোনও ধরনের অ্যানিমেশনে সেই টিক ব্যবহার করতে পারি। আগের মতোই, আমি লুপে ৪টি কীফ্রেম তৈরি করি এবং রঙ ফেরত দিই।

[...tabnavitems].map(item =>
  item === navitem
    ? `var(--text-active-color)`
    : `var(--text-color)`)

// results in 4 array items, which represent 4 keyframe states
// [
  "var(--text-active-color)",
  "var(--text-color)",
  "var(--text-color)",
  "var(--text-color)",
]

var(--text-active-color) রঙের কীফ্রেম লিঙ্কটি হাইলাইট করে এবং অন্যথায় এটি একটি স্ট্যান্ডার্ড টেক্সট রঙ। সেখানে নেস্টেড লুপ এটিকে তুলনামূলকভাবে সহজ করে তোলে, কারণ বাইরের লুপটি প্রতিটি নেভিগেশন আইটেম এবং ভিতরের লুপটি প্রতিটি নেভিগেশন আইটেমের ব্যক্তিগত কীফ্রেম। আমি চেক করি যে বাইরের লুপ এলিমেন্টটি ভিতরের লুপ এলিমেন্টের মতোই কিনা এবং এটি কখন বেছে নেওয়া হয়েছে তা জানতে এটি ব্যবহার করি।

এটি লিখতে আমার খুব ভালো লেগেছে। অনেক বেশি।

জাভাস্ক্রিপ্ট আরও উন্নত করা হয়েছে

It's worth a reminder that the core of what I'm showing you here works without JavaScript. JS উপলভ্য থাকলে, আমরা কীভাবে এটি উন্নত করতে পারি তা দেখা যাক।

ডিপ লিঙ্ক হল আরও বেশি মোবাইল টার্ম, তবে আমি মনে করি ডিপ লিঙ্কের উদ্দেশ্য এখানে ট্যাবের মাধ্যমে পূরণ করা হয়েছে যাতে আপনি সরাসরি ট্যাবের কন্টেন্টে একটি URL শেয়ার করতে পারেন। ব্রাউজার URL হ্যাশে ম্যাচ করা আইডিতে ইন-পেজ নেভিগেট করবে। আমি দেখেছি যে এই onload হ্যান্ডলার সব প্ল্যাটফর্ম জুড়ে প্রভাব ফেলেছে।

window.onload = () => {
  if (location.hash) {
    tabsection.scrollLeft = document
      .querySelector(location.hash)
      .offsetLeft;
  }
}

স্ক্রল এন্ড সিঙ্ক্রোনাইজেশন

আমাদের ব্যবহারকারীরা সবসময় ক্লিক করেন না বা কীবোর্ড ব্যবহার করেন না, কখনও কখনও তারা শুধু ফ্রিতে স্ক্রল করেন, যেমনটি তাদের করা উচিত। বিভাগ স্ক্রলার স্ক্রল করা বন্ধ করলে, এটি যেখানে থামে, সেটি উপরের নেভিগেশন বারের সাথে মিলতে হবে।

স্ক্রল শেষ হওয়ার জন্য আমি কীভাবে অপেক্ষা করি তা এখানে দেওয়া হল: js tabsection.addEventListener('scroll', () => { clearTimeout(tabsection.scrollEndTimer); tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100); });

Whenever the sections are being scrolled, clear the section timeout if there, and start a new one. বিভাগ স্ক্রল করা বন্ধ হয়ে গেলে, টাইমআউট মুছে দেবেন না, এবং থামার ১০০ মিলি সেকেন্ড পরে ফায়ার করুন। এটি ট্রিগার হলে, এমন ফাংশন কল করুন যা ব্যবহারকারী কোথায় থেমেছেন তা খুঁজে বের করার চেষ্টা করে।

const determineActiveTabSection = () => {
  const i = tabsection.scrollLeft / tabsection.clientWidth;
  const matchingNavItem = tabnavitems[i];

  matchingNavItem && setActiveTab(matchingNavItem);
};

স্ক্রল স্ন্যাপ করা হয়েছে ধরে নিয়ে, স্ক্রল এরিয়ার প্রস্থ দিয়ে বর্তমান স্ক্রল পজিশনকে ভাগ করলে একটি পূর্ণসংখ্যা পাওয়া যাবে, দশমিক নয়। তারপর আমি এই গণনা করা ইন্ডেক্সের মাধ্যমে আমাদের ক্যাশে থেকে একটি navitem নেওয়ার চেষ্টা করি এবং এটি কিছু খুঁজে পেলে, আমি ম্যাচটি সেট করার জন্য পাঠাই।

const setActiveTab = tabbtn => {
  tabnav
    .querySelector(':scope a[active]')
    .removeAttribute('active');

  tabbtn.setAttribute('active', '');
  tabbtn.scrollIntoView();
};

অ্যাক্টিভ ট্যাব সেট করার প্রসেস শুরু হয় বর্তমানে অ্যাক্টিভ ট্যাব মুছে দেওয়ার মাধ্যমে, তারপর ইনকামিং নেভিগেশন আইটেমকে অ্যাক্টিভ স্টেট অ্যাট্রিবিউট দেওয়া হয়। scrollIntoView()-এ কল করার সময় CSS-এর সাথে একটি মজার ইন্টার‍্যাকশন হয় যা লক্ষ্য করার মতো।

.scroll-snap-x {
  overflow: auto hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;

  @media (prefers-reduced-motion: no-preference) {
    scroll-behavior: smooth;
  }
}

হরাইজন্টাল স্ক্রল স্ন্যাপ ইউটিলিটি CSS-এ, আমরা একটি মিডিয়া কোয়েরি নেস্ট করেছি যা smooth স্ক্রলিং প্রয়োগ করে যদি ব্যবহারকারী মোশন টলারেন্ট হন। জাভাস্ক্রিপ্ট অবাধে ভিউতে স্ক্রল এলিমেন্ট কল করতে পারে এবং CSS ডিক্লেরেটিভভাবে UX ম্যানেজ করতে পারে। কখনও কখনও তারা বেশ আনন্দদায়ক ছোট ম্যাচ তৈরি করে।

উপসংহার

আমি কীভাবে করেছি তা তো জানলে, তুমি কীভাবে করবে?! এটি কিছু মজার কম্পোনেন্ট আর্কিটেকচার তৈরি করে! কে তার পছন্দের ফ্রেমওয়ার্কে স্লট সহ প্রথম ভার্সন তৈরি করতে চলেছেন? 🙂

আসুন, আমাদের দৃষ্টিভঙ্গিকে আরও বৈচিত্র্যময় করে তুলি এবং ওয়েবে বিল্ড করার সব উপায় সম্পর্কে জানি। আপনি কোনও ডেমো তৈরি করলে, আপনার ভার্সনটি আমাকে টুইট করুন এবং আমি এটি নিম্নলিখিত কমিউনিটি রিমিক্স বিভাগে যোগ করব।

কমিউনিটি রিমিক্স