একটি সুইচ উপাদান নির্মাণ

কিভাবে একটি প্রতিক্রিয়াশীল এবং অ্যাক্সেসযোগ্য সুইচ উপাদান তৈরি করতে হয় তার একটি মৌলিক ওভারভিউ।

এই পোস্টে আমি সুইচ উপাদানগুলি তৈরি করার উপায় সম্পর্কে চিন্তাভাবনা ভাগ করতে চাই। ডেমো চেষ্টা করুন .

ডেমো

আপনি যদি ভিডিও পছন্দ করেন তবে এখানে এই পোস্টটির একটি YouTube সংস্করণ রয়েছে:

ওভারভিউ

একটি সুইচ একটি চেকবক্সের মতো কাজ করে কিন্তু স্পষ্টভাবে বুলিয়ান চালু এবং বন্ধ অবস্থার প্রতিনিধিত্ব করে।

এই ডেমোটি তার বেশিরভাগ কার্যকারিতার জন্য <input type="checkbox" role="switch"> ব্যবহার করে, যার সুবিধা রয়েছে সম্পূর্ণরূপে কার্যকরী এবং অ্যাক্সেসযোগ্য হওয়ার জন্য CSS বা JavaScript এর প্রয়োজন নেই৷ CSS লোড করা ডান-থেকে-বাম ভাষা, উল্লম্বতা, অ্যানিমেশন এবং আরও অনেক কিছুর জন্য সমর্থন নিয়ে আসে। জাভাস্ক্রিপ্ট লোড করা সুইচটিকে টেনে আনার যোগ্য এবং মূর্ত করে তোলে।

কাস্টম বৈশিষ্ট্য

নিম্নলিখিত ভেরিয়েবলগুলি সুইচের বিভিন্ন অংশ এবং তাদের বিকল্পগুলিকে উপস্থাপন করে। শীর্ষ-স্তরের শ্রেণী হিসাবে, .gui-switch সমস্ত উপাদান শিশুদের জুড়ে ব্যবহৃত কাস্টম বৈশিষ্ট্য এবং কেন্দ্রীভূত কাস্টমাইজেশনের জন্য এন্ট্রি পয়েন্ট রয়েছে।

ট্র্যাক

দৈর্ঘ্য ( --track-size ), প্যাডিং এবং দুটি রঙ:

.gui-switch {
  --track-size: calc(var(--thumb-size) * 2);
  --track-padding: 2px;

  --track-inactive: hsl(80 0% 80%);
  --track-active: hsl(80 60% 45%);

  --track-color-inactive: var(--track-inactive);
  --track-color-active: var(--track-active);

  @media (prefers-color-scheme: dark) {
    --track-inactive: hsl(80 0% 35%);
    --track-active: hsl(80 60% 60%);
  }
}

থাম্ব

আকার, পটভূমির রঙ এবং মিথস্ক্রিয়া হাইলাইট রং:

.gui-switch {
  --thumb-size: 2rem;
  --thumb: hsl(0 0% 100%);
  --thumb-highlight: hsl(0 0% 0% / 25%);

  --thumb-color: var(--thumb);
  --thumb-color-highlight: var(--thumb-highlight);

  @media (prefers-color-scheme: dark) {
    --thumb: hsl(0 0% 5%);
    --thumb-highlight: hsl(0 0% 100% / 25%);
  }
}

গতি কমানো

একটি পরিষ্কার উপনাম যোগ করতে এবং পুনরাবৃত্তি কমাতে, মিডিয়া কোয়েরি 5-এ এই খসড়া বৈশিষ্ট্যের উপর ভিত্তি করে পোস্টসিএসএস প্লাগইন সহ একটি কম গতি পছন্দ ব্যবহারকারী মিডিয়া ক্যোয়ারী একটি কাস্টম সম্পত্তিতে রাখা যেতে পারে:

@custom-media --motionOK (prefers-reduced-motion: no-preference);

মার্কআপ

আমি আমার <input type="checkbox" role="switch"> উপাদানটিকে একটি <label> দিয়ে মোড়ানো বেছে নিয়েছি, চেকবক্স এবং লেবেল অ্যাসোসিয়েশনের অস্পষ্টতা এড়াতে তাদের সম্পর্ককে বান্ডিল করে, ব্যবহারকারীকে টগল করতে লেবেলের সাথে ইন্টারঅ্যাক্ট করার ক্ষমতা দেওয়ার সময় ইনপুট

ক প্রাকৃতিক, স্টাইলবিহীন লেবেল এবং চেকবক্স।

<label for="switch" class="gui-switch">
  Label text
  <input type="checkbox" role="switch" id="switch">
</label>

<input type="checkbox"> একটি API এবং রাজ্যের সাথে পূর্বনির্মাণ করা হয়। ব্রাউজার checked সম্পত্তি এবং ইনপুট ইভেন্টগুলি পরিচালনা করে যেমন oninput এবং onchanged

বিন্যাস

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

.gui-switch

সুইচের জন্য শীর্ষ-স্তরের বিন্যাস হল ফ্লেক্সবক্স। .gui-switch ক্লাসে এমন ব্যক্তিগত এবং সর্বজনীন কাস্টম বৈশিষ্ট্য রয়েছে যা শিশুরা তাদের লেআউট গণনা করতে ব্যবহার করে।

Flexbox DevTools একটি অনুভূমিক লেবেল ও সুইচ ওভারলে করছে, তাদের লেআউট দেখাচ্ছে স্থান বিতরণ।

.gui-switch {
  display: flex;
  align-items: center;
  gap: 2ch;
  justify-content: space-between;
}

ফ্লেক্সবক্স লেআউট প্রসারিত করা এবং পরিবর্তন করা যেকোনো ফ্লেক্সবক্স লেআউট পরিবর্তন করার মতো। উদাহরণস্বরূপ, একটি সুইচের উপরে বা নীচে লেবেল লাগাতে, বা flex-direction পরিবর্তন করতে:

Flexbox DevTools একটি উল্লম্ব লেবেল ও সুইচকে ওভারলে করছে।

<label for="light-switch" class="gui-switch" style="flex-direction: column">
  Default
  <input type="checkbox" role="switch" id="light-switch">
</label>

ট্র্যাক

চেকবক্স ইনপুটটিকে একটি সুইচ ট্র্যাক হিসাবে স্টাইল করা হয়েছে এর স্বাভাবিক appearance: checkbox এবং পরিবর্তে তার নিজস্ব আকার সরবরাহ করে:

গ্রিড DevTools সুইচ ট্র্যাককে ওভারলে করছে, নামযুক্ত গ্রিড ট্র্যাক দেখাচ্ছে৷ 'ট্র্যাক' নামের এলাকা।

.gui-switch > input {
  appearance: none;

  inline-size: var(--track-size);
  block-size: var(--thumb-size);
  padding: var(--track-padding);

  flex-shrink: 0;
  display: grid;
  align-items: center;
  grid: [track] 1fr / [track] 1fr;
}

একটি থাম্ব দাবি করার জন্য ট্র্যাকটি একটি একক সেল গ্রিড ট্র্যাক এলাকা তৈরি করে।

থাম্ব

শৈলীর appearance: none সরিয়ে দেয় না। এই উপাদানটি একটি ছদ্ম-উপাদান ব্যবহার করে এবং এই ভিজ্যুয়াল সূচকটি প্রতিস্থাপন করার জন্য ইনপুটে :checked সিউডো-ক্লাস ব্যবহার করে।

থাম্বটি একটি ছদ্ম-উপাদান শিশু যা input[type="checkbox"] এর সাথে সংযুক্ত থাকে এবং গ্রিড এলাকা track দাবি করে নীচের পরিবর্তে ট্র্যাকের উপরে স্ট্যাক করে:

DevTools একটি CSS গ্রিডের মধ্যে অবস্থান হিসাবে ছদ্ম-উপাদান থাম্ব দেখাচ্ছে।

.gui-switch > input::before {
  content: "";
  grid-area: track;
  inline-size: var(--thumb-size);
  block-size: var(--thumb-size);
}

শৈলী

কাস্টম বৈশিষ্ট্যগুলি একটি বহুমুখী সুইচ উপাদান সক্ষম করে যা রঙের স্কিম, ডান-থেকে-বাম ভাষা এবং গতি পছন্দগুলির সাথে খাপ খায়।

সুইচ এবং এর জন্য হালকা এবং অন্ধকার থিমের পাশাপাশি তুলনা রাজ্যগুলি

মিথস্ক্রিয়া শৈলী স্পর্শ করুন

মোবাইলে, ব্রাউজারগুলি লেবেল এবং ইনপুটগুলিতে ট্যাপ হাইলাইট এবং পাঠ্য নির্বাচন বৈশিষ্ট্য যুক্ত করে৷ এগুলি নেতিবাচকভাবে শৈলী এবং ভিজ্যুয়াল মিথস্ক্রিয়া প্রতিক্রিয়াকে প্রভাবিত করে যা এই সুইচটির প্রয়োজন ছিল। CSS এর কয়েকটি লাইন দিয়ে আমি সেই প্রভাবগুলি সরিয়ে দিতে পারি এবং আমার নিজস্ব cursor: pointer শৈলী:

.gui-switch {
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

এই শৈলীগুলি সরানো সবসময় যুক্তিযুক্ত নয়, কারণ সেগুলি মূল্যবান ভিজ্যুয়াল মিথস্ক্রিয়া প্রতিক্রিয়া হতে পারে। কাস্টম বিকল্প প্রদান করতে ভুলবেন না যদি আপনি সেগুলি সরিয়ে দেন।

ট্র্যাক

এই উপাদানটির শৈলীগুলি বেশিরভাগই এর আকৃতি এবং রঙ সম্পর্কে, যা এটি প্যারেন্ট .gui-switch থেকে ক্যাসকেডের মাধ্যমে অ্যাক্সেস করে।

কাস্টম ট্র্যাক মাপ এবং রং সঙ্গে সুইচ বৈকল্পিক.

.gui-switch > input {
  appearance: none;
  border: none;
  outline-offset: 5px;
  box-sizing: content-box;

  padding: var(--track-padding);
  background: var(--track-color-inactive);
  inline-size: var(--track-size);
  block-size: var(--thumb-size);
  border-radius: var(--track-size);
}

সুইচ ট্র্যাকের জন্য বিভিন্ন ধরণের কাস্টমাইজেশন বিকল্পগুলি চারটি কাস্টম বৈশিষ্ট্য থেকে আসে। border: none appearance: none সব ব্রাউজারে চেকবক্স থেকে সীমানা সরিয়ে দেয় না।

থাম্ব

থাম্ব উপাদানটি ইতিমধ্যেই সঠিক track রয়েছে তবে বৃত্ত শৈলীর প্রয়োজন:

.gui-switch > input::before {
  background: var(--thumb-color);
  border-radius: 50%;
}

DevTools বৃত্ত থাম্ব ছদ্ম-উপাদান হাইলাইট দেখানো হয়েছে.

মিথস্ক্রিয়া

মিথস্ক্রিয়াগুলির জন্য প্রস্তুত করতে কাস্টম বৈশিষ্ট্যগুলি ব্যবহার করুন যা হোভার হাইলাইট এবং থাম্বের অবস্থানের পরিবর্তনগুলি দেখাবে৷ গতি বা হোভার হাইলাইট শৈলী পরিবর্তন করার আগে ব্যবহারকারীর পছন্দও পরীক্ষা করা হয়

.gui-switch > input::before {
  box-shadow: 0 0 0 var(--highlight-size) var(--thumb-color-highlight);

  @media (--motionOK) { & {
    transition:
      transform var(--thumb-transition-duration) ease,
      box-shadow .25s ease;
  }}
}

থাম্ব অবস্থান

কাস্টম বৈশিষ্ট্যগুলি ট্র্যাকে থাম্বের অবস্থানের জন্য একটি একক উত্স প্রক্রিয়া সরবরাহ করে। আমাদের হাতে রয়েছে ট্র্যাক এবং থাম্বের মাপ যা আমরা গণনায় ব্যবহার করব থাম্বটিকে সঠিকভাবে অফসেট এবং ট্র্যাকের মধ্যে রাখতে: 0% এবং 100%

input উপাদানটি পজিশন ভেরিয়েবল --thumb-position মালিক, এবং থাম্ব সিউডো উপাদান এটিকে translateX অবস্থান হিসাবে ব্যবহার করে:

.gui-switch > input {
  --thumb-position: 0%;
}

.gui-switch > input::before {
  transform: translateX(var(--thumb-position));
}

আমরা এখন CSS থেকে --thumb-position এবং চেকবক্স উপাদানে প্রদত্ত ছদ্ম-শ্রেণী পরিবর্তন করতে মুক্ত। যেহেতু আমরা শর্তসাপেক্ষে transition: transform var(--thumb-transition-duration) ease , এই পরিবর্তনগুলি পরিবর্তিত হলে অ্যানিমেট হতে পারে:

/* positioned at the end of the track: track length - 100% (thumb width) */
.gui-switch > input:checked {
  --thumb-position: calc(var(--track-size) - 100%);
}

/* positioned in the center of the track: half the track - half the thumb */
.gui-switch > input:indeterminate {
  --thumb-position: calc(
    (var(--track-size) / 2) - (var(--thumb-size) / 2)
  );
}

আমি ভেবেছিলাম এই দ্বৈত অর্কেস্ট্রেশন ভালভাবে কাজ করেছে। থাম্ব উপাদান শুধুমাত্র একটি শৈলী, একটি translateX অবস্থানের সাথে সম্পর্কিত। ইনপুট সমস্ত জটিলতা এবং গণনা পরিচালনা করতে পারে।

উল্লম্ব

সাপোর্টিং একটি মডিফায়ার ক্লাস -vertical দিয়ে করা হয়েছিল যা input উপাদানে CSS রূপান্তরের সাথে একটি ঘূর্ণন যোগ করে।

একটি 3D ঘোরানো উপাদান যদিও উপাদানটির সামগ্রিক উচ্চতা পরিবর্তন করে না, যা ব্লক লেআউটকে ফেলে দিতে পারে। --track-size এবং --track-padding ভেরিয়েবল ব্যবহার করে এর জন্য অ্যাকাউন্ট করুন। প্রত্যাশিতভাবে লেআউটে প্রবাহিত হওয়ার জন্য একটি উল্লম্ব বোতামের জন্য প্রয়োজনীয় ন্যূনতম পরিমাণ স্থান গণনা করুন:

.gui-switch.-vertical {
  min-block-size: calc(var(--track-size) + calc(var(--track-padding) * 2));

  & > input {
    transform: rotate(-90deg);
  }
}

(RTL) ডান থেকে বামে

একজন CSS বন্ধু, Elad Schecter এবং আমি একসাথে সিএসএস রূপান্তর ব্যবহার করে একটি স্লাইড আউট সাইড মেনু প্রোটোটাইপ করেছি যা একটি একক ভেরিয়েবল ফ্লিপ করে ডান থেকে বামে ভাষা পরিচালনা করে । আমরা এটি করেছি কারণ CSS-এ কোন লজিক্যাল প্রপার্টি ট্রান্সফর্ম নেই এবং কখনোই নাও হতে পারে। ইলাডের একটি কাস্টম সম্পত্তি মান ব্যবহার করে শতাংশগুলি উল্টে দেওয়ার জন্য দুর্দান্ত ধারণা ছিল, যৌক্তিক রূপান্তরের জন্য আমাদের নিজস্ব কাস্টম লজিকের একক অবস্থান পরিচালনার অনুমতি দিতে। আমি এই সুইচটিতে এই একই কৌশলটি ব্যবহার করেছি এবং আমি মনে করি এটি দুর্দান্ত কাজ করেছে:

.gui-switch {
  --isLTR: 1;

  &:dir(rtl) {
    --isLTR: -1;
  }
}

--isLTR নামক একটি কাস্টম প্রপার্টি প্রাথমিকভাবে 1 এর মান ধারণ করে, যার অর্থ এটি true কারণ আমাদের লেআউটটি ডিফল্টরূপে বাম থেকে ডানে থাকে। তারপর, CSS pseudo class :dir() ব্যবহার করে, যখন উপাদানটি ডান-থেকে-বাম লেআউটের মধ্যে থাকে তখন মানটি -1 এ সেট করা হয়।

ট্রান্সফর্মের ভিতরে একটি calc() মধ্যে ব্যবহার করে --isLTR কাজে লাগান:

.gui-switch.-vertical > input {
  transform: rotate(-90deg);
  transform: rotate(calc(90deg * var(--isLTR) * -1));
}

এখন উল্লম্ব সুইচের ঘূর্ণন ডান-থেকে-বাম লেআউটের জন্য প্রয়োজনীয় বিপরীত দিকের অবস্থানের জন্য অ্যাকাউন্ট করে।

থাম্ব সিউডো-এলিমেন্টের translateX রূপান্তরকে বিপরীত দিকের প্রয়োজনের জন্য অ্যাকাউন্ট আপডেট করতে হবে:

.gui-switch > input:checked {
  --thumb-position: calc(var(--track-size) - 100%);
  --thumb-position: calc((var(--track-size) - 100%) * var(--isLTR));
}

.gui-switch > input:indeterminate {
  --thumb-position: calc(
    (var(--track-size) / 2) - (var(--thumb-size) / 2)
  );
  --thumb-position: calc(
   ((var(--track-size) / 2) - (var(--thumb-size) / 2))
    * var(--isLTR)
  );
}

যদিও এই পদ্ধতিটি যৌক্তিক CSS রূপান্তরের মতো একটি ধারণা সম্পর্কিত সমস্ত চাহিদা সমাধানে কাজ করবে না, এটি অনেক ব্যবহারের ক্ষেত্রে কিছু DRY নীতি অফার করে।

রাজ্যগুলি

অন্তর্নির্মিত input[type="checkbox"] ব্যবহার করা বিভিন্ন স্টেটগুলি পরিচালনা না করে সম্পূর্ণ হবে না যেগুলি এটি হতে পারে: :checked , :disabled , :indeterminate এবং :hover:focus ইচ্ছাকৃতভাবে একা ছেড়ে দেওয়া হয়েছিল, শুধুমাত্র তার অফসেটে একটি সমন্বয় করা হয়েছিল; ফায়ারফক্স এবং সাফারিতে ফোকাস রিংটি দুর্দান্ত লাগছিল:

ফায়ারফক্স এবং সাফারির একটি সুইচে ফোকাস রিং এর একটি স্ক্রিনশট।

চেক করা হয়েছে

<label for="switch-checked" class="gui-switch">
  Default
  <input type="checkbox" role="switch" id="switch-checked" checked="true">
</label>

এই রাজ্যটি on প্রতিনিধিত্ব করে। এই অবস্থায়, ইনপুট "ট্র্যাক" ব্যাকগ্রাউন্ড সক্রিয় রঙে সেট করা হয় এবং থাম্বের অবস্থান "শেষ" তে সেট করা হয়।

.gui-switch > input:checked {
  background: var(--track-color-active);
  --thumb-position: calc((var(--track-size) - 100%) * var(--isLTR));
}

অক্ষম

<label for="switch-disabled" class="gui-switch">
  Default
  <input type="checkbox" role="switch" id="switch-disabled" disabled="true">
</label>

A :disabled বোতামটি কেবল দৃশ্যতই আলাদা দেখায় না, তবে উপাদানটিকে অপরিবর্তনীয় করে তুলতে হবে। ইন্টারঅ্যাকশন অপরিবর্তনীয়তা ব্রাউজার থেকে মুক্ত, তবে appearance: none

.gui-switch > input:disabled {
  cursor: not-allowed;
  --thumb-color: transparent;

  &::before {
    cursor: not-allowed;
    box-shadow: inset 0 0 0 2px hsl(0 0% 100% / 50%);

    @media (prefers-color-scheme: dark) { & {
      box-shadow: inset 0 0 0 2px hsl(0 0% 0% / 50%);
    }}
  }
}

অন্ধকার শৈলীযুক্ত সুইচ অক্ষম, চেক করা এবং টিক চিহ্নমুক্ত রাজ্যগুলি

এই অবস্থাটি জটিল কারণ এতে অক্ষম এবং চেক করা উভয় অবস্থাতেই গাঢ় এবং হালকা থিম প্রয়োজন। আমি শৈলীর সংমিশ্রণগুলির রক্ষণাবেক্ষণের বোঝা সহজ করার জন্য এই রাজ্যগুলির জন্য ন্যূনতম শৈলী বেছে নিয়েছি।

অনির্দিষ্ট

একটি প্রায়শই ভুলে যাওয়া অবস্থা হল :indeterminate , যেখানে একটি চেকবক্স চেক করা বা আনচেক করা হয় না। এটি একটি মজার রাষ্ট্র, এটি আমন্ত্রণমূলক এবং নিরপেক্ষ। একটি ভাল অনুস্মারক যে বুলিয়ান রাজ্যগুলির রাজ্যগুলির মধ্যে লুকোচুরি থাকতে পারে৷

একটি চেকবক্সকে অনিশ্চিত করার জন্য সেট করা কঠিন, শুধুমাত্র জাভাস্ক্রিপ্ট এটি সেট করতে পারে:

<label for="switch-indeterminate" class="gui-switch">
  Indeterminate
  <input type="checkbox" role="switch" id="switch-indeterminate">
  <script>document.getElementById('switch-indeterminate').indeterminate = true</script>
</label>

অনির্দিষ্ট অবস্থা যার মধ্যে ট্র্যাক থাম্ব আছে মধ্যম, সিদ্ধান্তহীনতা নির্দেশ করতে।

যেহেতু রাষ্ট্র, আমার কাছে, নিরপেক্ষ এবং আমন্ত্রণমূলক, তাই সুইচ থাম্বের অবস্থানটি মাঝখানে রাখা উপযুক্ত মনে হয়েছে:

.gui-switch > input:indeterminate {
  --thumb-position: calc(
    calc(calc(var(--track-size) / 2) - calc(var(--thumb-size) / 2))
    * var(--isLTR)
  );
}

হোভার

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

"হাইলাইট" প্রভাবটি box-shadow দিয়ে করা হয়। হোভারে, একটি অ-অক্ষম ইনপুটের, --highlight-size আকার বাড়ান। যদি ব্যবহারকারীর গতির সাথে ঠিক থাকে, আমরা box-shadow স্থানান্তরিত করি এবং এটিকে বাড়তে দেখি, যদি তারা গতির সাথে ঠিক না থাকে, হাইলাইটটি অবিলম্বে প্রদর্শিত হবে:

.gui-switch > input::before {
  box-shadow: 0 0 0 var(--highlight-size) var(--thumb-color-highlight);

  @media (--motionOK) { & {
    transition:
      transform var(--thumb-transition-duration) ease,
      box-shadow .25s ease;
  }}
}

.gui-switch > input:not(:disabled):hover::before {
  --highlight-size: .5rem;
}

জাভাস্ক্রিপ্ট

আমার কাছে, একটি সুইচ ইন্টারফেস একটি ভৌত ​​ইন্টারফেস অনুকরণ করার প্রয়াসে অস্বাভাবিক বোধ করতে পারে, বিশেষ করে এই ধরণের একটি ট্র্যাকের ভিতরে একটি বৃত্তের সাথে। আইওএস তাদের সুইচের মাধ্যমে এটি সঠিকভাবে পেয়েছে, আপনি তাদের পাশে টেনে আনতে পারেন এবং বিকল্পটি পাওয়া খুবই সন্তোষজনক। বিপরীতভাবে, একটি UI উপাদান নিষ্ক্রিয় বোধ করতে পারে যদি একটি টেনে আনার অঙ্গভঙ্গি চেষ্টা করা হয় এবং কিছুই ঘটে না।

টেনে আনা থাম্বস

থাম্ব ছদ্ম-উপাদানটি .gui-switch > input স্কোপড var(--thumb-position) থেকে তার অবস্থান গ্রহণ করে, জাভাস্ক্রিপ্ট ইনপুটে একটি ইনলাইন স্টাইল মান সরবরাহ করতে পারে থাম্বের অবস্থানকে গতিশীলভাবে আপডেট করতে যাতে এটি পয়েন্টার অঙ্গভঙ্গি অনুসরণ করে বলে মনে হয় . পয়েন্টার রিলিজ হলে, ইনলাইন শৈলীগুলি সরান এবং কাস্টম বৈশিষ্ট্য --thumb-position ব্যবহার করে ড্র্যাগটি বন্ধ বা অনের কাছাকাছি ছিল কিনা তা নির্ধারণ করুন। এটি সমাধানের মেরুদণ্ড; পয়েন্টার ইভেন্টগুলি শর্তসাপেক্ষে সিএসএস কাস্টম বৈশিষ্ট্যগুলি পরিবর্তন করতে পয়েন্টার অবস্থানগুলি ট্র্যাক করে।

যেহেতু এই স্ক্রিপ্টটি দেখানোর আগে কম্পোনেন্টটি ইতিমধ্যেই 100% কার্যকরী ছিল, তাই বিদ্যমান আচরণ বজায় রাখতে এটি বেশ কিছুটা কাজ করে, যেমন ইনপুট টগল করতে একটি লেবেলে ক্লিক করা। আমাদের জাভাস্ক্রিপ্ট বিদ্যমান বৈশিষ্ট্যের খরচে বৈশিষ্ট্য যোগ করা উচিত নয়.

touch-action

টেনে আনা একটি অঙ্গভঙ্গি, একটি কাস্টম, যা এটিকে touch-action সুবিধার জন্য একটি দুর্দান্ত প্রার্থী করে তোলে। এই সুইচের ক্ষেত্রে, একটি অনুভূমিক অঙ্গভঙ্গি আমাদের স্ক্রিপ্ট দ্বারা পরিচালনা করা উচিত, বা উল্লম্ব সুইচ বৈকল্পিকের জন্য একটি উল্লম্ব অঙ্গভঙ্গি ক্যাপচার করা উচিত। touch-action মাধ্যমে আমরা ব্রাউজারকে বলতে পারি এই উপাদানটিতে কোন অঙ্গভঙ্গি পরিচালনা করতে হবে, তাই একটি স্ক্রিপ্ট প্রতিযোগিতা ছাড়াই একটি অঙ্গভঙ্গি পরিচালনা করতে পারে।

নিম্নলিখিত CSS ব্রাউজারকে নির্দেশ দেয় যে যখন একটি পয়েন্টার অঙ্গভঙ্গি এই সুইচ ট্র্যাকের মধ্যে থেকে শুরু হয়, উল্লম্ব অঙ্গভঙ্গিগুলি পরিচালনা করুন, অনুভূমিকগুলির সাথে কিছুই করবেন না:

.gui-switch > input {
  touch-action: pan-y;
}

পছন্দসই ফলাফল হল একটি অনুভূমিক অঙ্গভঙ্গি যা পৃষ্ঠাটিকে প্যান বা স্ক্রোল করে না। একটি পয়েন্টার উল্লম্বভাবে ইনপুট থেকে শুরু করে পৃষ্ঠাটি স্ক্রোল করতে পারে, তবে অনুভূমিকগুলি কাস্টমভাবে পরিচালনা করা হয়।

পিক্সেল মান শৈলী ইউটিলিটি

সেটআপের সময় এবং টেনে আনার সময়, উপাদানগুলি থেকে বিভিন্ন গণনা করা সংখ্যার মানগুলিকে ধরতে হবে। নিম্নলিখিত জাভাস্ক্রিপ্ট ফাংশনগুলি একটি CSS বৈশিষ্ট্য প্রদত্ত গণনাকৃত পিক্সেল মান প্রদান করে। এটি সেটআপ স্ক্রিপ্টে এই ধরনের getStyle(checkbox, 'padding-left') ব্যবহার করা হয়।

​​const getStyle = (element, prop) => {
  return parseInt(window.getComputedStyle(element).getPropertyValue(prop));
}

const getPseudoStyle = (element, prop) => {
  return parseInt(window.getComputedStyle(element, ':before').getPropertyValue(prop));
}

export {
  getStyle,
  getPseudoStyle,
}

লক্ষ্য করুন কিভাবে window.getComputedStyle() একটি দ্বিতীয় আর্গুমেন্ট গ্রহণ করে, একটি টার্গেট সিউডো এলিমেন্ট। বেশ ঝরঝরে যে জাভাস্ক্রিপ্ট উপাদান থেকে অনেক মান পড়তে পারে, এমনকি ছদ্ম উপাদান থেকে.

dragging

এটি ড্র্যাগ লজিকের জন্য একটি মূল মুহূর্ত এবং ফাংশন ইভেন্ট হ্যান্ডলার থেকে লক্ষ্য করার সাথে কয়েকটি জিনিস রয়েছে:

const dragging = event => {
  if (!state.activethumb) return

  let {thumbsize, bounds, padding} = switches.get(state.activethumb.parentElement)
  let directionality = getStyle(state.activethumb, '--isLTR')

  let track = (directionality === -1)
    ? (state.activethumb.clientWidth * -1) + thumbsize + padding
    : 0

  let pos = Math.round(event.offsetX - thumbsize / 2)

  if (pos < bounds.lower) pos = 0
  if (pos > bounds.upper) pos = bounds.upper

  state.activethumb.style.setProperty('--thumb-position', `${track + pos}px`)
}

স্ক্রিপ্টের নায়ক হল state.activethumb , এই স্ক্রিপ্টটি একটি পয়েন্টার সহ অবস্থান করছে ছোট্ট বৃত্ত। switches অবজেক্ট হল একটি Map() যেখানে কীগুলি .gui-switch 's এবং মানগুলি ক্যাশে করা সীমানা এবং আকার যা স্ক্রিপ্টটিকে দক্ষ রাখে৷ ডান-থেকে-বামে একই কাস্টম বৈশিষ্ট্য ব্যবহার করে পরিচালনা করা হয় যা CSS --isLTR , এবং যুক্তিকে উল্টাতে এবং RTL সমর্থন করা চালিয়ে যেতে এটি ব্যবহার করতে সক্ষম। event.offsetX ও মূল্যবান, কারণ এতে একটি ডেল্টা মান রয়েছে যা থাম্বের অবস্থানের জন্য উপযোগী।

state.activethumb.style.setProperty('--thumb-position', `${track + pos}px`)

CSS-এর এই চূড়ান্ত লাইনটি থাম্ব এলিমেন্ট দ্বারা ব্যবহৃত কাস্টম প্রপার্টি সেট করে। এই মানের অ্যাসাইনমেন্ট অন্যথায় সময়ের সাথে রূপান্তরিত হবে, কিন্তু একটি পূর্ববর্তী পয়েন্টার ইভেন্ট অস্থায়ীভাবে --thumb-transition-duration0s সেট করেছে, যা একটি মন্থর মিথস্ক্রিয়া হতে পারে তা সরিয়ে দেয়।

dragEnd

ব্যবহারকারীকে সুইচের বাইরে অনেকদূর টেনে নিয়ে যাওয়ার অনুমতি দেওয়ার জন্য, একটি বিশ্বব্যাপী উইন্ডো ইভেন্ট নিবন্ধিত হওয়া প্রয়োজন:

window.addEventListener('pointerup', event => {
  if (!state.activethumb) return

  dragEnd(event)
})

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

const dragEnd = event => {
  if (!state.activethumb) return

  state.activethumb.checked = determineChecked()

  if (state.activethumb.indeterminate)
    state.activethumb.indeterminate = false

  state.activethumb.style.removeProperty('--thumb-transition-duration')
  state.activethumb.style.removeProperty('--thumb-position')
  state.activethumb.removeEventListener('pointermove', dragging)
  state.activethumb = null

  padRelease()
}

উপাদানের সাথে মিথস্ক্রিয়া সম্পন্ন হয়েছে, ইনপুট চেক করা সম্পত্তি সেট করার এবং সমস্ত অঙ্গভঙ্গি ইভেন্টগুলি সরানোর সময়। চেকবক্সটি state.activethumb.checked = determineChecked() দিয়ে পরিবর্তন করা হয়েছে।

determineChecked()

এই ফাংশনটি, যাকে dragEnd দ্বারা বলা হয়, তা নির্ধারণ করে যে থাম্ব কারেন্টটি তার ট্র্যাকের সীমানার মধ্যে কোথায় থাকে এবং ট্র্যাকের অর্ধেকের সমান বা তার বেশি হলে তা সত্য ফেরত দেয়:

const determineChecked = () => {
  let {bounds} = switches.get(state.activethumb.parentElement)

  let curpos =
    Math.abs(
      parseInt(
        state.activethumb.style.getPropertyValue('--thumb-position')))

  if (!curpos) {
    curpos = state.activethumb.checked
      ? bounds.lower
      : bounds.upper
  }

  return curpos >= bounds.middle
}

অতিরিক্ত চিন্তা

ড্র্যাগ অঙ্গভঙ্গিটি বেছে নেওয়া প্রাথমিক HTML কাঠামোর কারণে কিছুটা কোড ঋণ ব্যয় করেছে, বেশিরভাগ উল্লেখযোগ্যভাবে একটি লেবেলে ইনপুট মোড়ানো। লেবেল, একটি অভিভাবক উপাদান, ইনপুট পরে ক্লিক মিথস্ক্রিয়া গ্রহণ করবে. dragEnd ইভেন্টের শেষে, আপনি padRelease() একটি অদ্ভুত সাউন্ডিং ফাংশন হিসেবে লক্ষ্য করেছেন।

const padRelease = () => {
  state.recentlyDragged = true

  setTimeout(_ => {
    state.recentlyDragged = false
  }, 300)
}

এটি পরবর্তীতে ক্লিক করার লেবেলের জন্য দায়ী, কারণ এটি একজন ব্যবহারকারীর সম্পাদিত ইন্টারঅ্যাকশনটি আনচেক করবে বা চেক করবে।

যদি আমি আবার এটি করতে চাই, আমি UX আপগ্রেডের সময় জাভাস্ক্রিপ্টের সাথে DOM সামঞ্জস্য করার কথা বিবেচনা করতে পারি , একটি উপাদান তৈরি করতে যা লেবেল ক্লিকগুলি নিজেই পরিচালনা করে এবং বিল্ট-ইন আচরণের সাথে লড়াই করে না।

এই ধরনের জাভাস্ক্রিপ্ট লেখার জন্য আমার সবচেয়ে কম প্রিয়, আমি শর্তসাপেক্ষ ইভেন্ট বুদবুদ পরিচালনা করতে চাই না:

const preventBubbles = event => {
  if (state.recentlyDragged)
    event.preventDefault() && event.stopPropagation()
}

উপসংহার

এই টিনি সুইচ কম্পোনেন্টটি এখন পর্যন্ত সমস্ত GUI চ্যালেঞ্জের মধ্যে সবচেয়ে বেশি কাজ করে! এখন যেহেতু আপনি জানেন যে আমি কীভাবে এটি করেছি, আপনি কেমন হবে‽ 🙂৷

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

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

সম্পদ

GitHub-এ .gui-switch সোর্স কোড খুঁজুন।

,

কিভাবে একটি প্রতিক্রিয়াশীল এবং অ্যাক্সেসযোগ্য সুইচ উপাদান তৈরি করতে হয় তার একটি মৌলিক ওভারভিউ।

এই পোস্টে আমি সুইচ উপাদানগুলি তৈরি করার উপায় সম্পর্কে চিন্তাভাবনা ভাগ করতে চাই। ডেমো চেষ্টা করুন .

ডেমো

আপনি যদি ভিডিও পছন্দ করেন তবে এখানে এই পোস্টটির একটি YouTube সংস্করণ রয়েছে:

ওভারভিউ

একটি সুইচ একটি চেকবক্সের মতো কাজ করে কিন্তু স্পষ্টভাবে বুলিয়ান চালু এবং বন্ধ অবস্থার প্রতিনিধিত্ব করে।

এই ডেমোটি তার বেশিরভাগ কার্যকারিতার জন্য <input type="checkbox" role="switch"> ব্যবহার করে, যার সুবিধা রয়েছে সম্পূর্ণরূপে কার্যকরী এবং অ্যাক্সেসযোগ্য হওয়ার জন্য CSS বা JavaScript এর প্রয়োজন নেই৷ CSS লোড করা ডান-থেকে-বাম ভাষা, উল্লম্বতা, অ্যানিমেশন এবং আরও অনেক কিছুর জন্য সমর্থন নিয়ে আসে। জাভাস্ক্রিপ্ট লোড করা সুইচটিকে টেনে আনার যোগ্য এবং মূর্ত করে তোলে।

কাস্টম বৈশিষ্ট্য

নিম্নলিখিত ভেরিয়েবলগুলি সুইচের বিভিন্ন অংশ এবং তাদের বিকল্পগুলিকে উপস্থাপন করে। শীর্ষ-স্তরের শ্রেণী হিসাবে, .gui-switch সমস্ত উপাদান শিশুদের জুড়ে ব্যবহৃত কাস্টম বৈশিষ্ট্য এবং কেন্দ্রীভূত কাস্টমাইজেশনের জন্য এন্ট্রি পয়েন্ট রয়েছে।

ট্র্যাক

দৈর্ঘ্য ( --track-size ), প্যাডিং এবং দুটি রঙ:

.gui-switch {
  --track-size: calc(var(--thumb-size) * 2);
  --track-padding: 2px;

  --track-inactive: hsl(80 0% 80%);
  --track-active: hsl(80 60% 45%);

  --track-color-inactive: var(--track-inactive);
  --track-color-active: var(--track-active);

  @media (prefers-color-scheme: dark) {
    --track-inactive: hsl(80 0% 35%);
    --track-active: hsl(80 60% 60%);
  }
}

থাম্ব

আকার, পটভূমির রঙ এবং মিথস্ক্রিয়া হাইলাইট রং:

.gui-switch {
  --thumb-size: 2rem;
  --thumb: hsl(0 0% 100%);
  --thumb-highlight: hsl(0 0% 0% / 25%);

  --thumb-color: var(--thumb);
  --thumb-color-highlight: var(--thumb-highlight);

  @media (prefers-color-scheme: dark) {
    --thumb: hsl(0 0% 5%);
    --thumb-highlight: hsl(0 0% 100% / 25%);
  }
}

গতি কমানো

একটি পরিষ্কার উপনাম যোগ করতে এবং পুনরাবৃত্তি কমাতে, মিডিয়া কোয়েরি 5-এ এই খসড়া বৈশিষ্ট্যের উপর ভিত্তি করে পোস্টসিএসএস প্লাগইন সহ একটি কম গতি পছন্দ ব্যবহারকারী মিডিয়া ক্যোয়ারী একটি কাস্টম সম্পত্তিতে রাখা যেতে পারে:

@custom-media --motionOK (prefers-reduced-motion: no-preference);

মার্কআপ

আমি আমার <input type="checkbox" role="switch"> উপাদানটিকে একটি <label> দিয়ে মোড়ানো বেছে নিয়েছি, চেকবক্স এবং লেবেল অ্যাসোসিয়েশনের অস্পষ্টতা এড়াতে তাদের সম্পর্ককে বান্ডিল করে, ব্যবহারকারীকে টগল করতে লেবেলের সাথে ইন্টারঅ্যাক্ট করার ক্ষমতা দেওয়ার সময় ইনপুট

ক প্রাকৃতিক, স্টাইলবিহীন লেবেল এবং চেকবক্স।

<label for="switch" class="gui-switch">
  Label text
  <input type="checkbox" role="switch" id="switch">
</label>

<input type="checkbox"> একটি API এবং রাজ্যের সাথে পূর্বনির্মাণ করা হয়। ব্রাউজার checked সম্পত্তি এবং ইনপুট ইভেন্টগুলি পরিচালনা করে যেমন oninput এবং onchanged

বিন্যাস

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

.gui-switch

সুইচের জন্য শীর্ষ-স্তরের বিন্যাস হল ফ্লেক্সবক্স। .gui-switch ক্লাসে এমন ব্যক্তিগত এবং সর্বজনীন কাস্টম বৈশিষ্ট্য রয়েছে যা শিশুরা তাদের লেআউট গণনা করতে ব্যবহার করে।

Flexbox DevTools একটি অনুভূমিক লেবেল ও সুইচ ওভারলে করছে, তাদের লেআউট দেখাচ্ছে স্থান বিতরণ।

.gui-switch {
  display: flex;
  align-items: center;
  gap: 2ch;
  justify-content: space-between;
}

ফ্লেক্সবক্স লেআউট প্রসারিত করা এবং পরিবর্তন করা যেকোনো ফ্লেক্সবক্স লেআউট পরিবর্তন করার মতো। উদাহরণস্বরূপ, একটি সুইচের উপরে বা নীচে লেবেল লাগাতে, বা flex-direction পরিবর্তন করতে:

Flexbox DevTools একটি উল্লম্ব লেবেল ও সুইচকে ওভারলে করছে।

<label for="light-switch" class="gui-switch" style="flex-direction: column">
  Default
  <input type="checkbox" role="switch" id="light-switch">
</label>

ট্র্যাক

চেকবক্স ইনপুটটিকে একটি সুইচ ট্র্যাক হিসাবে স্টাইল করা হয়েছে এর স্বাভাবিক appearance: checkbox এবং পরিবর্তে তার নিজস্ব আকার সরবরাহ করে:

গ্রিড DevTools সুইচ ট্র্যাককে ওভারলে করছে, নামযুক্ত গ্রিড ট্র্যাক দেখাচ্ছে৷ 'ট্র্যাক' নামের এলাকা।

.gui-switch > input {
  appearance: none;

  inline-size: var(--track-size);
  block-size: var(--thumb-size);
  padding: var(--track-padding);

  flex-shrink: 0;
  display: grid;
  align-items: center;
  grid: [track] 1fr / [track] 1fr;
}

একটি থাম্ব দাবি করার জন্য ট্র্যাকটি একটি একক সেল গ্রিড ট্র্যাক এলাকা তৈরি করে।

থাম্ব

শৈলীর appearance: none সরিয়ে দেয় না। এই উপাদানটি একটি ছদ্ম-উপাদান ব্যবহার করে এবং এই ভিজ্যুয়াল সূচকটি প্রতিস্থাপন করার জন্য ইনপুটে :checked সিউডো-ক্লাস ব্যবহার করে।

থাম্বটি একটি ছদ্ম-উপাদান শিশু যা input[type="checkbox"] এর সাথে সংযুক্ত থাকে এবং গ্রিড এলাকা track দাবি করে নীচের পরিবর্তে ট্র্যাকের উপরে স্ট্যাক করে:

DevTools একটি CSS গ্রিডের মধ্যে অবস্থান হিসাবে ছদ্ম-উপাদান থাম্ব দেখাচ্ছে।

.gui-switch > input::before {
  content: "";
  grid-area: track;
  inline-size: var(--thumb-size);
  block-size: var(--thumb-size);
}

শৈলী

কাস্টম বৈশিষ্ট্যগুলি একটি বহুমুখী সুইচ উপাদান সক্ষম করে যা রঙের স্কিম, ডান-থেকে-বাম ভাষা এবং গতি পছন্দগুলির সাথে খাপ খায়।

সুইচ এবং এর জন্য হালকা এবং অন্ধকার থিমের পাশাপাশি তুলনা রাজ্যগুলি

মিথস্ক্রিয়া শৈলী স্পর্শ করুন

মোবাইলে, ব্রাউজারগুলি লেবেল এবং ইনপুটগুলিতে ট্যাপ হাইলাইট এবং পাঠ্য নির্বাচন বৈশিষ্ট্য যুক্ত করে৷ এগুলি নেতিবাচকভাবে শৈলী এবং ভিজ্যুয়াল মিথস্ক্রিয়া প্রতিক্রিয়াকে প্রভাবিত করে যা এই সুইচটির প্রয়োজন ছিল। CSS এর কয়েকটি লাইন দিয়ে আমি সেই প্রভাবগুলি সরিয়ে দিতে পারি এবং আমার নিজস্ব cursor: pointer শৈলী:

.gui-switch {
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

এই শৈলীগুলি সরানো সবসময় যুক্তিযুক্ত নয়, কারণ সেগুলি মূল্যবান ভিজ্যুয়াল মিথস্ক্রিয়া প্রতিক্রিয়া হতে পারে। কাস্টম বিকল্প প্রদান করতে ভুলবেন না যদি আপনি সেগুলি সরিয়ে দেন।

ট্র্যাক

এই উপাদানটির শৈলীগুলি বেশিরভাগই এর আকৃতি এবং রঙ সম্পর্কে, যা এটি প্যারেন্ট .gui-switch থেকে ক্যাসকেডের মাধ্যমে অ্যাক্সেস করে।

কাস্টম ট্র্যাক মাপ এবং রং সঙ্গে সুইচ বৈকল্পিক.

.gui-switch > input {
  appearance: none;
  border: none;
  outline-offset: 5px;
  box-sizing: content-box;

  padding: var(--track-padding);
  background: var(--track-color-inactive);
  inline-size: var(--track-size);
  block-size: var(--thumb-size);
  border-radius: var(--track-size);
}

সুইচ ট্র্যাকের জন্য বিভিন্ন ধরণের কাস্টমাইজেশন বিকল্পগুলি চারটি কাস্টম বৈশিষ্ট্য থেকে আসে। border: none appearance: none সব ব্রাউজারে চেকবক্স থেকে সীমানা সরিয়ে দেয় না।

থাম্ব

থাম্ব উপাদানটি ইতিমধ্যেই সঠিক track রয়েছে তবে বৃত্ত শৈলীর প্রয়োজন:

.gui-switch > input::before {
  background: var(--thumb-color);
  border-radius: 50%;
}

DevTools বৃত্ত থাম্ব ছদ্ম-উপাদান হাইলাইট দেখানো হয়েছে.

মিথস্ক্রিয়া

মিথস্ক্রিয়াগুলির জন্য প্রস্তুত করতে কাস্টম বৈশিষ্ট্যগুলি ব্যবহার করুন যা হোভার হাইলাইট এবং থাম্বের অবস্থানের পরিবর্তনগুলি দেখাবে৷ গতি বা হোভার হাইলাইট শৈলী পরিবর্তন করার আগে ব্যবহারকারীর পছন্দও পরীক্ষা করা হয়

.gui-switch > input::before {
  box-shadow: 0 0 0 var(--highlight-size) var(--thumb-color-highlight);

  @media (--motionOK) { & {
    transition:
      transform var(--thumb-transition-duration) ease,
      box-shadow .25s ease;
  }}
}

থাম্ব অবস্থান

কাস্টম বৈশিষ্ট্যগুলি ট্র্যাকে থাম্বের অবস্থানের জন্য একটি একক উত্স প্রক্রিয়া সরবরাহ করে। আমাদের হাতে রয়েছে ট্র্যাক এবং থাম্বের মাপ যা আমরা গণনায় ব্যবহার করব থাম্বটিকে সঠিকভাবে অফসেট এবং ট্র্যাকের মধ্যে রাখতে: 0% এবং 100%

input উপাদানটি পজিশন ভেরিয়েবল --thumb-position মালিক, এবং থাম্ব সিউডো উপাদান এটিকে translateX অবস্থান হিসাবে ব্যবহার করে:

.gui-switch > input {
  --thumb-position: 0%;
}

.gui-switch > input::before {
  transform: translateX(var(--thumb-position));
}

আমরা এখন CSS থেকে --thumb-position এবং চেকবক্স উপাদানে প্রদত্ত ছদ্ম-শ্রেণী পরিবর্তন করতে মুক্ত। যেহেতু আমরা শর্তসাপেক্ষে transition: transform var(--thumb-transition-duration) ease , এই পরিবর্তনগুলি পরিবর্তিত হলে অ্যানিমেট হতে পারে:

/* positioned at the end of the track: track length - 100% (thumb width) */
.gui-switch > input:checked {
  --thumb-position: calc(var(--track-size) - 100%);
}

/* positioned in the center of the track: half the track - half the thumb */
.gui-switch > input:indeterminate {
  --thumb-position: calc(
    (var(--track-size) / 2) - (var(--thumb-size) / 2)
  );
}

আমি ভেবেছিলাম এই দ্বৈত অর্কেস্ট্রেশন ভালভাবে কাজ করেছে। থাম্ব উপাদান শুধুমাত্র একটি শৈলী, একটি translateX অবস্থানের সাথে সম্পর্কিত। ইনপুট সমস্ত জটিলতা এবং গণনা পরিচালনা করতে পারে।

উল্লম্ব

সাপোর্টিং একটি মডিফায়ার ক্লাস -vertical দিয়ে করা হয়েছিল যা input উপাদানে CSS রূপান্তরের সাথে একটি ঘূর্ণন যোগ করে।

একটি 3D ঘোরানো উপাদান যদিও উপাদানটির সামগ্রিক উচ্চতা পরিবর্তন করে না, যা ব্লক লেআউটকে ফেলে দিতে পারে। --track-size এবং --track-padding ভেরিয়েবল ব্যবহার করে এর জন্য অ্যাকাউন্ট করুন। প্রত্যাশিতভাবে লেআউটে প্রবাহিত হওয়ার জন্য একটি উল্লম্ব বোতামের জন্য প্রয়োজনীয় ন্যূনতম পরিমাণ স্থান গণনা করুন:

.gui-switch.-vertical {
  min-block-size: calc(var(--track-size) + calc(var(--track-padding) * 2));

  & > input {
    transform: rotate(-90deg);
  }
}

(RTL) ডান থেকে বামে

একজন CSS বন্ধু, Elad Schecter এবং আমি একসাথে সিএসএস রূপান্তর ব্যবহার করে একটি স্লাইড আউট সাইড মেনু প্রোটোটাইপ করেছি যা একটি একক ভেরিয়েবল ফ্লিপ করে ডান থেকে বামে ভাষা পরিচালনা করে । আমরা এটি করেছি কারণ CSS-এ কোন লজিক্যাল প্রপার্টি ট্রান্সফর্ম নেই এবং কখনোই নাও হতে পারে। ইলাডের একটি কাস্টম সম্পত্তি মান ব্যবহার করে শতাংশগুলি উল্টে দেওয়ার জন্য দুর্দান্ত ধারণা ছিল, যৌক্তিক রূপান্তরের জন্য আমাদের নিজস্ব কাস্টম লজিকের একক অবস্থান পরিচালনার অনুমতি দিতে। আমি এই সুইচটিতে এই একই কৌশলটি ব্যবহার করেছি এবং আমি মনে করি এটি দুর্দান্ত কাজ করেছে:

.gui-switch {
  --isLTR: 1;

  &:dir(rtl) {
    --isLTR: -1;
  }
}

--isLTR নামক একটি কাস্টম প্রপার্টি প্রাথমিকভাবে 1 এর মান ধারণ করে, যার অর্থ এটি true কারণ আমাদের লেআউটটি ডিফল্টরূপে বাম থেকে ডানে থাকে। তারপর, CSS pseudo class :dir() ব্যবহার করে, যখন উপাদানটি ডান-থেকে-বাম লেআউটের মধ্যে থাকে তখন মানটি -1 এ সেট করা হয়।

ট্রান্সফর্মের ভিতরে একটি calc() মধ্যে ব্যবহার করে --isLTR কাজে লাগান:

.gui-switch.-vertical > input {
  transform: rotate(-90deg);
  transform: rotate(calc(90deg * var(--isLTR) * -1));
}

এখন উল্লম্ব সুইচের ঘূর্ণন ডান-থেকে-বাম লেআউটের জন্য প্রয়োজনীয় বিপরীত দিকের অবস্থানের জন্য অ্যাকাউন্ট করে।

থাম্ব সিউডো-এলিমেন্টের translateX রূপান্তরকে বিপরীত দিকের প্রয়োজনের জন্য অ্যাকাউন্ট আপডেট করতে হবে:

.gui-switch > input:checked {
  --thumb-position: calc(var(--track-size) - 100%);
  --thumb-position: calc((var(--track-size) - 100%) * var(--isLTR));
}

.gui-switch > input:indeterminate {
  --thumb-position: calc(
    (var(--track-size) / 2) - (var(--thumb-size) / 2)
  );
  --thumb-position: calc(
   ((var(--track-size) / 2) - (var(--thumb-size) / 2))
    * var(--isLTR)
  );
}

যদিও এই পদ্ধতিটি যৌক্তিক CSS রূপান্তরের মতো একটি ধারণা সম্পর্কিত সমস্ত চাহিদা সমাধানে কাজ করবে না, এটি অনেক ব্যবহারের ক্ষেত্রে কিছু DRY নীতি অফার করে।

রাজ্যগুলি

অন্তর্নির্মিত input[type="checkbox"] ব্যবহার করা বিভিন্ন স্টেটগুলি পরিচালনা না করে সম্পূর্ণ হবে না যেগুলি এটি হতে পারে: :checked , :disabled , :indeterminate এবং :hover:focus ইচ্ছাকৃতভাবে একা ছেড়ে দেওয়া হয়েছিল, শুধুমাত্র তার অফসেটে একটি সমন্বয় করা হয়েছিল; ফায়ারফক্স এবং সাফারিতে ফোকাস রিংটি দুর্দান্ত লাগছিল:

ফায়ারফক্স এবং সাফারির একটি সুইচে ফোকাস রিং এর একটি স্ক্রিনশট।

চেক করা হয়েছে

<label for="switch-checked" class="gui-switch">
  Default
  <input type="checkbox" role="switch" id="switch-checked" checked="true">
</label>

এই রাজ্যটি on প্রতিনিধিত্ব করে। এই অবস্থায়, ইনপুট "ট্র্যাক" ব্যাকগ্রাউন্ড সক্রিয় রঙে সেট করা হয় এবং থাম্বের অবস্থান "শেষ" তে সেট করা হয়।

.gui-switch > input:checked {
  background: var(--track-color-active);
  --thumb-position: calc((var(--track-size) - 100%) * var(--isLTR));
}

অক্ষম

<label for="switch-disabled" class="gui-switch">
  Default
  <input type="checkbox" role="switch" id="switch-disabled" disabled="true">
</label>

A :disabled বোতামটি কেবল দৃশ্যতই আলাদা দেখায় না, তবে উপাদানটিকে অপরিবর্তনীয় করে তুলতে হবে। ইন্টারঅ্যাকশন অপরিবর্তনীয়তা ব্রাউজার থেকে মুক্ত, তবে appearance: none

.gui-switch > input:disabled {
  cursor: not-allowed;
  --thumb-color: transparent;

  &::before {
    cursor: not-allowed;
    box-shadow: inset 0 0 0 2px hsl(0 0% 100% / 50%);

    @media (prefers-color-scheme: dark) { & {
      box-shadow: inset 0 0 0 2px hsl(0 0% 0% / 50%);
    }}
  }
}

অন্ধকার শৈলীযুক্ত সুইচ অক্ষম, চেক করা এবং টিক চিহ্নমুক্ত রাজ্যগুলি

এই অবস্থাটি জটিল কারণ এতে অক্ষম এবং চেক করা উভয় অবস্থাতেই গাঢ় এবং হালকা থিম প্রয়োজন। আমি শৈলীর সংমিশ্রণগুলির রক্ষণাবেক্ষণের বোঝা সহজ করার জন্য এই রাজ্যগুলির জন্য ন্যূনতম শৈলী বেছে নিয়েছি।

অনির্দিষ্ট

একটি প্রায়শই ভুলে যাওয়া অবস্থা হল :indeterminate , যেখানে একটি চেকবক্স চেক করা বা আনচেক করা হয় না। এটি একটি মজার রাষ্ট্র, এটি আমন্ত্রণমূলক এবং নিরপেক্ষ। একটি ভাল অনুস্মারক যে বুলিয়ান রাজ্যগুলির রাজ্যগুলির মধ্যে লুকোচুরি থাকতে পারে৷

একটি চেকবক্সকে অনিশ্চিত করার জন্য সেট করা কঠিন, শুধুমাত্র জাভাস্ক্রিপ্ট এটি সেট করতে পারে:

<label for="switch-indeterminate" class="gui-switch">
  Indeterminate
  <input type="checkbox" role="switch" id="switch-indeterminate">
  <script>document.getElementById('switch-indeterminate').indeterminate = true</script>
</label>

অনির্দিষ্ট অবস্থা যার মধ্যে ট্র্যাক থাম্ব আছে মধ্যম, সিদ্ধান্তহীনতা নির্দেশ করতে।

যেহেতু রাষ্ট্র, আমার কাছে, নিরপেক্ষ এবং আমন্ত্রণমূলক, তাই সুইচ থাম্বের অবস্থানটি মাঝখানে রাখা উপযুক্ত মনে হয়েছে:

.gui-switch > input:indeterminate {
  --thumb-position: calc(
    calc(calc(var(--track-size) / 2) - calc(var(--thumb-size) / 2))
    * var(--isLTR)
  );
}

হোভার

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

"হাইলাইট" প্রভাবটি box-shadow দিয়ে করা হয়। হোভারে, একটি অ-অক্ষম ইনপুটের, --highlight-size আকার বাড়ান। যদি ব্যবহারকারীর গতির সাথে ঠিক থাকে, আমরা box-shadow স্থানান্তরিত করি এবং এটিকে বাড়তে দেখি, যদি তারা গতির সাথে ঠিক না থাকে, হাইলাইটটি অবিলম্বে প্রদর্শিত হবে:

.gui-switch > input::before {
  box-shadow: 0 0 0 var(--highlight-size) var(--thumb-color-highlight);

  @media (--motionOK) { & {
    transition:
      transform var(--thumb-transition-duration) ease,
      box-shadow .25s ease;
  }}
}

.gui-switch > input:not(:disabled):hover::before {
  --highlight-size: .5rem;
}

জাভাস্ক্রিপ্ট

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

টেনে আনা থাম্বস

থাম্ব ছদ্ম-উপাদানটি .gui-switch > input স্কোপড var(--thumb-position) থেকে তার অবস্থান গ্রহণ করে, জাভাস্ক্রিপ্ট ইনপুটে একটি ইনলাইন স্টাইল মান সরবরাহ করতে পারে থাম্বের অবস্থানকে গতিশীলভাবে আপডেট করতে যাতে এটি পয়েন্টার অঙ্গভঙ্গি অনুসরণ করে বলে মনে হয় . পয়েন্টার রিলিজ হলে, ইনলাইন শৈলীগুলি সরান এবং কাস্টম বৈশিষ্ট্য --thumb-position ব্যবহার করে ড্র্যাগটি বন্ধ বা অনের কাছাকাছি ছিল কিনা তা নির্ধারণ করুন। এটি সমাধানের মেরুদণ্ড; পয়েন্টার ইভেন্টগুলি শর্তসাপেক্ষে সিএসএস কাস্টম বৈশিষ্ট্যগুলি পরিবর্তন করতে পয়েন্টার অবস্থানগুলি ট্র্যাক করে।

যেহেতু এই স্ক্রিপ্টটি দেখানোর আগে কম্পোনেন্টটি ইতিমধ্যেই 100% কার্যকরী ছিল, তাই বিদ্যমান আচরণ বজায় রাখতে এটি বেশ কিছুটা কাজ করে, যেমন ইনপুট টগল করতে একটি লেবেলে ক্লিক করা। আমাদের জাভাস্ক্রিপ্ট বিদ্যমান বৈশিষ্ট্যের খরচে বৈশিষ্ট্য যোগ করা উচিত নয়.

touch-action

টেনে আনা একটি অঙ্গভঙ্গি, একটি কাস্টম, যা এটিকে touch-action সুবিধার জন্য একটি দুর্দান্ত প্রার্থী করে তোলে। এই সুইচের ক্ষেত্রে, একটি অনুভূমিক অঙ্গভঙ্গি আমাদের স্ক্রিপ্ট দ্বারা পরিচালনা করা উচিত, বা উল্লম্ব সুইচ বৈকল্পিকের জন্য একটি উল্লম্ব অঙ্গভঙ্গি ক্যাপচার করা উচিত। touch-action মাধ্যমে আমরা ব্রাউজারকে বলতে পারি এই উপাদানটিতে কোন অঙ্গভঙ্গি পরিচালনা করতে হবে, তাই একটি স্ক্রিপ্ট প্রতিযোগিতা ছাড়াই একটি অঙ্গভঙ্গি পরিচালনা করতে পারে।

নিম্নলিখিত CSS ব্রাউজারকে নির্দেশ দেয় যে যখন একটি পয়েন্টার অঙ্গভঙ্গি এই সুইচ ট্র্যাকের মধ্যে থেকে শুরু হয়, উল্লম্ব অঙ্গভঙ্গিগুলি পরিচালনা করুন, অনুভূমিকগুলির সাথে কিছুই করবেন না:

.gui-switch > input {
  touch-action: pan-y;
}

পছন্দসই ফলাফল হল একটি অনুভূমিক অঙ্গভঙ্গি যা পৃষ্ঠাটিকে প্যান বা স্ক্রোল করে না। একটি পয়েন্টার উল্লম্বভাবে ইনপুট থেকে শুরু করে পৃষ্ঠাটি স্ক্রোল করতে পারে, তবে অনুভূমিকগুলি কাস্টমভাবে পরিচালনা করা হয়।

পিক্সেল মান শৈলী ইউটিলিটি

সেটআপের সময় এবং টেনে আনার সময়, উপাদানগুলি থেকে বিভিন্ন গণনা করা সংখ্যার মানগুলিকে ধরতে হবে। নিম্নলিখিত জাভাস্ক্রিপ্ট ফাংশনগুলি একটি CSS বৈশিষ্ট্য প্রদত্ত গণনাকৃত পিক্সেল মান প্রদান করে। এটি সেটআপ স্ক্রিপ্টে এই ধরনের getStyle(checkbox, 'padding-left') ব্যবহার করা হয়।

​​const getStyle = (element, prop) => {
  return parseInt(window.getComputedStyle(element).getPropertyValue(prop));
}

const getPseudoStyle = (element, prop) => {
  return parseInt(window.getComputedStyle(element, ':before').getPropertyValue(prop));
}

export {
  getStyle,
  getPseudoStyle,
}

লক্ষ্য করুন কিভাবে window.getComputedStyle() একটি দ্বিতীয় আর্গুমেন্ট গ্রহণ করে, একটি টার্গেট সিউডো এলিমেন্ট। বেশ ঝরঝরে যে জাভাস্ক্রিপ্ট উপাদান থেকে অনেক মান পড়তে পারে, এমনকি ছদ্ম উপাদান থেকে.

dragging

এটি ড্র্যাগ লজিকের জন্য একটি মূল মুহূর্ত এবং ফাংশন ইভেন্ট হ্যান্ডলার থেকে লক্ষ্য করার সাথে কয়েকটি জিনিস রয়েছে:

const dragging = event => {
  if (!state.activethumb) return

  let {thumbsize, bounds, padding} = switches.get(state.activethumb.parentElement)
  let directionality = getStyle(state.activethumb, '--isLTR')

  let track = (directionality === -1)
    ? (state.activethumb.clientWidth * -1) + thumbsize + padding
    : 0

  let pos = Math.round(event.offsetX - thumbsize / 2)

  if (pos < bounds.lower) pos = 0
  if (pos > bounds.upper) pos = bounds.upper

  state.activethumb.style.setProperty('--thumb-position', `${track + pos}px`)
}

স্ক্রিপ্টের নায়ক হল state.activethumb , এই স্ক্রিপ্টটি একটি পয়েন্টার সহ অবস্থান করছে ছোট্ট বৃত্ত। switches অবজেক্ট হল একটি Map() যেখানে কীগুলি .gui-switch 's এবং মানগুলি ক্যাশে করা সীমানা এবং আকার যা স্ক্রিপ্টটিকে দক্ষ রাখে৷ ডান-থেকে-বামে একই কাস্টম বৈশিষ্ট্য ব্যবহার করে পরিচালনা করা হয় যা CSS --isLTR , এবং যুক্তিকে উল্টাতে এবং RTL সমর্থন করা চালিয়ে যেতে এটি ব্যবহার করতে সক্ষম। event.offsetX ও মূল্যবান, কারণ এতে একটি ডেল্টা মান রয়েছে যা থাম্বের অবস্থানের জন্য উপযোগী।

state.activethumb.style.setProperty('--thumb-position', `${track + pos}px`)

CSS-এর এই চূড়ান্ত লাইনটি থাম্ব এলিমেন্ট দ্বারা ব্যবহৃত কাস্টম প্রপার্টি সেট করে। এই মানের অ্যাসাইনমেন্ট অন্যথায় সময়ের সাথে রূপান্তরিত হবে, কিন্তু একটি পূর্ববর্তী পয়েন্টার ইভেন্ট অস্থায়ীভাবে --thumb-transition-duration0s সেট করেছে, যা একটি মন্থর মিথস্ক্রিয়া হতে পারে তা সরিয়ে দেয়।

dragEnd

ব্যবহারকারীকে সুইচের বাইরে অনেকদূর টেনে নিয়ে যাওয়ার অনুমতি দেওয়ার জন্য, একটি বিশ্বব্যাপী উইন্ডো ইভেন্ট নিবন্ধিত হওয়া প্রয়োজন:

window.addEventListener('pointerup', event => {
  if (!state.activethumb) return

  dragEnd(event)
})

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

const dragEnd = event => {
  if (!state.activethumb) return

  state.activethumb.checked = determineChecked()

  if (state.activethumb.indeterminate)
    state.activethumb.indeterminate = false

  state.activethumb.style.removeProperty('--thumb-transition-duration')
  state.activethumb.style.removeProperty('--thumb-position')
  state.activethumb.removeEventListener('pointermove', dragging)
  state.activethumb = null

  padRelease()
}

উপাদানের সাথে মিথস্ক্রিয়া সম্পন্ন হয়েছে, ইনপুট চেক করা সম্পত্তি সেট করার এবং সমস্ত অঙ্গভঙ্গি ইভেন্টগুলি সরানোর সময়। চেকবক্সটি state.activethumb.checked = determineChecked() দিয়ে পরিবর্তন করা হয়েছে।

determineChecked()

এই ফাংশনটি, যাকে dragEnd দ্বারা বলা হয়, তা নির্ধারণ করে যে থাম্ব কারেন্টটি তার ট্র্যাকের সীমানার মধ্যে কোথায় থাকে এবং ট্র্যাকের অর্ধেকের সমান বা তার বেশি হলে তা সত্য ফেরত দেয়:

const determineChecked = () => {
  let {bounds} = switches.get(state.activethumb.parentElement)

  let curpos =
    Math.abs(
      parseInt(
        state.activethumb.style.getPropertyValue('--thumb-position')))

  if (!curpos) {
    curpos = state.activethumb.checked
      ? bounds.lower
      : bounds.upper
  }

  return curpos >= bounds.middle
}

অতিরিক্ত চিন্তা

ড্র্যাগ অঙ্গভঙ্গিটি বেছে নেওয়া প্রাথমিক HTML কাঠামোর কারণে কিছুটা কোড ঋণ ব্যয় করেছে, বেশিরভাগ উল্লেখযোগ্যভাবে একটি লেবেলে ইনপুট মোড়ানো। লেবেল, একটি অভিভাবক উপাদান, ইনপুট পরে ক্লিক মিথস্ক্রিয়া গ্রহণ করবে. dragEnd ইভেন্টের শেষে, আপনি padRelease() একটি অদ্ভুত সাউন্ডিং ফাংশন হিসেবে লক্ষ্য করেছেন।

const padRelease = () => {
  state.recentlyDragged = true

  setTimeout(_ => {
    state.recentlyDragged = false
  }, 300)
}

এটি পরবর্তীতে ক্লিক করার লেবেলের জন্য দায়ী, কারণ এটি একজন ব্যবহারকারীর সম্পাদিত ইন্টারঅ্যাকশনটি আনচেক করবে বা চেক করবে।

যদি আমি আবার এটি করতে চাই, আমি UX আপগ্রেডের সময় জাভাস্ক্রিপ্টের সাথে DOM সামঞ্জস্য করার কথা বিবেচনা করতে পারি , একটি উপাদান তৈরি করতে যা লেবেল ক্লিকগুলি নিজেই পরিচালনা করে এবং বিল্ট-ইন আচরণের সাথে লড়াই করে না।

এই ধরনের জাভাস্ক্রিপ্ট লেখার জন্য আমার সবচেয়ে কম প্রিয়, আমি শর্তসাপেক্ষ ইভেন্ট বুদবুদ পরিচালনা করতে চাই না:

const preventBubbles = event => {
  if (state.recentlyDragged)
    event.preventDefault() && event.stopPropagation()
}

উপসংহার

এই টিনি সুইচ কম্পোনেন্টটি এখন পর্যন্ত সমস্ত GUI চ্যালেঞ্জের মধ্যে সবচেয়ে বেশি কাজ করে! এখন যেহেতু আপনি জানেন যে আমি কীভাবে এটি করেছি, আপনি কেমন হবে‽ 🙂৷

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

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

সম্পদ

GitHub-এ .gui-switch সোর্স কোড খুঁজুন।