অ্যাক্সেসযোগ্য ত্রুটি সংক্রান্ত ঘোষণা

ওয়েব ফিচার: :user-valid এবং :user-invalid

সমস্যা

স্ট্যান্ডার্ড HTML5 যাচাইকরণ ভিজ্যুয়াল ফিডব্যাক (:invalid বা :user-invalid-এর মাধ্যমে) প্রদান করে, কিন্তু এটি aria-invalid-এর মতো অ্যাক্সেসিবিলিটি অ্যাট্রিবিউটের সাথে অটোমেটিক সিঙ্ক করে না।

আপনি স্ট্যান্ডার্ড :invalid স্টাইলিং ব্যবহার করলে, কোনও ব্যবহারকারী বর্তমানে খালি থাকা কোনও প্রয়োজনীয় ফিল্ডে ট্যাব করার সাথে সাথেই স্ক্রিন রিডার "ভুল এন্ট্রি" ঘোষণা করতে পারে। এর ফলে, সহায়ক প্রযুক্তি ব্যবহারকারী ব্যক্তিদের অভিজ্ঞতা বিঘ্নিত হয়, কারণ ইন্টার‍্যাকশন হওয়ার আগেই সমস্যার কথা ঘোষণা করা হয়।

সমাধান

আমরা চাই প্রোগ্রাম্যাটিক স্টেট (aria-invalid="true") যেন ভিজ্যুয়াল স্টেট (:user-invalid) প্রয়োগ করার ঠিক একই মুহূর্তে প্রয়োগ করা হয়। যেহেতু :user-invalid ব্রাউজারের ইন্টার্নাল "ব্যবহারকারী-ইন্টার‍্যাক্ট করেছেন" ফ্ল্যাগের উপর নির্ভর করে, তাই স্ট্যান্ডার্ড ইন্টার‍্যাকশন ইভেন্টের সময় এই সিলেক্টর ম্যাচ করছে কিনা তা চেক করতে আমরা JavaScript ব্যবহার করতে পারি।

আরও বিবরণের জন্য MDN aria-invalid দেখুন।

প্রয়োগ সংক্রান্ত স্ট্র্যাটেজি

  1. ভিজ্যুয়াল লেয়ার: বর্ডার/আইকন দেখাতে CSS :user-invalid ব্যবহার করুন।
  2. অ্যাক্সেসিবিলিটি লেয়ার: অ্যাসিস্টিভ টেকনোলজির (AT) সাথে যোগাযোগ করতে aria-invalid ও aria-errormessage ব্যবহার করুন।
  3. ব্রিজ ভিজ্যুয়াল ও অ্যাক্সেসিবিলিটি লেয়ার: একটি লাইটওয়েট জাভাস্ক্রিপ্ট ইউটিলিটি তৈরি করুন যা blur ও input ইভেন্ট শোনে, এলিমেন্টটি :user-invalid-এর সাথে ম্যাচ করছে কিনা তা চেক করে এবং সেই অনুযায়ী ARIA অ্যাট্রিবিউট আপডেট করে।

প্রয়োগ করার নির্দেশিকা

১. HTML স্ট্রাকচার

aria-errormessage (অথবা আরও বেশি সমর্থনের জন্য aria-describedby) ব্যবহার করে আপনার ইনপুটকে এর ত্রুটি মেসেজের সাথে লিঙ্ক করুন।

<form>
  <div class="field">
    <label for="email">Email</label>
    <input 
      type="email" 
      id="email" 
      required 
      aria-errormessage="email-error"
    >
    <span id="email-error" class="error-msg">
      Please enter a valid email address.
    </span>
  </div>
</form>

২. CSS

নেটিভ সিউডো-ক্লাস :user-invalid ব্যবহার করে ত্রুটি মেসেজের দৃশ্যমানতা নিয়ন্ত্রণ করুন।

.error-msg {
  display: none;
  color: #d93025;
}

/* Show error message when input is user-invalid */
input:user-invalid ~ .error-msg {
  display: block;
}

/* Optional: Visual cues on the input itself */
input:user-invalid {
  border-color: #d93025;
}

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

যেহেতু কোনও "UserInvalidChanged" ইভেন্ট নেই, তাই স্ট্যাটাস চেক করতে স্ট্যান্ডার্ড ফর্ম ইভেন্টে হুক করুন।

const updateAriaState = (event) => {
  const input = event.target;
  if (!input.matches?.('input, textarea, select')) return;

  // Check if the browser currently considers this input "user-invalid"
  const isUserInvalid = input.matches(':user-invalid');
  
  if (isUserInvalid) {
    input.setAttribute('aria-invalid', 'true');
  } else {
    input.removeAttribute('aria-invalid');
  }
};

// Listen on the document to handle dynamically added fields.
// 'blur' and 'focus' do not bubble, so we must use the capture phase (true).
document.addEventListener('blur', updateAriaState, true);
document.addEventListener('focus', updateAriaState, true);

// Also update on input if we've already shown the error, 
// so the error clears immediately when fixed.
document.addEventListener('input', (event) => {
  const input = event.target;
  if (!input.matches?.('input, textarea, select')) return;

  const hasAriaInvalid = input.hasAttribute('aria-invalid');
  const ariaInvalid = input.getAttribute('aria-invalid');
  if (hasAriaInvalid && ariaInvalid === 'true') {
    updateAriaState(event);
  }
});

ফলব্যাকিং ও ব্রাউজার সাপোর্ট

ফিচার শনাক্তকরণ

আপনি CSS এবং জাভাস্ক্রিপ্টে সহায়তা চেক করতে পারেন।

জাভাস্ক্রিপ্ট চেক:

if (!CSS.supports('selector(:user-invalid)')) {
  // Fallback logic here
}

ফলব্যাকের জন্য CSS

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

/* Apply error styles to both native selector and fallback class */
input:user-invalid,
input.user-invalid-fallback {
  border-color: #d93025;
  background-color: #fce8e6;
}

/* Show error message for both cases */
input:user-invalid ~ .error-msg,
input.user-invalid-fallback ~ .error-msg {
  display: block;
}

ফলব্যাক লজিক

:user-invalid না থাকলে, WeakMap ব্যবহার করে ইন্টার‍্যাকশন স্টেট ম্যানুয়ালি ট্র্যাক করুন।

const UserInvalidFallback = (() => {
  const dirtyState = new WeakMap();

  const updateState = (input) => {
    const isValid = input.checkValidity();

    // Update both visual and ARIA state
    input.classList.toggle('user-invalid-fallback', !isValid);
    input.classList.toggle('user-valid-fallback', isValid);

    if (!isValid) {
      input.setAttribute('aria-invalid', 'true');
    } else {
      input.removeAttribute('aria-invalid');
    }
  };

  const handleEvent = (event) => {
    const input = event.target;

    if (event.type === 'reset' && input.matches?.('form')) {
      const controls = input.elements || [];
      for (const control of controls) {
        dirtyState.delete(control);
        control.classList.remove('user-invalid-fallback');
        control.classList.remove('user-valid-fallback');
        control.removeAttribute('aria-invalid');
      }
      return;
    }

    if (!input.matches?.('input, textarea, select')) return;

    if (event.type === 'input' || event.type === 'change') {
      const state = dirtyState.get(input) || { hasInteracted: false, hasBlurred: false };
      state.hasInteracted = true;
      dirtyState.set(input, state);
      if (state.hasBlurred) {
        updateState(input);
      }
    } else if (event.type === 'blur') {
      const state = dirtyState.get(input) || { hasInteracted: false, hasBlurred: false };
      state.hasBlurred = true;
      dirtyState.set(input, state);
      if (state.hasInteracted) {
        updateState(input);
      }
    }
  };

  const init = () => {
    if (CSS.supports('selector(:user-invalid)')) return;

    document.addEventListener('blur', handleEvent, true); // Capture phase required
    document.addEventListener('input', handleEvent, true);
    document.addEventListener('change', handleEvent, true);
    document.addEventListener('reset', handleEvent, true); // Capture resets
  };

  return { init };
})();

// Initialize globally
UserInvalidFallback.init();

বিবেচনা করার মতো অন্যান্য বিষয়

  1. aria-live বনাম aria-errormessage:

    • aria-errormessage ইনপুটকে টেক্সটের সাথে কানেক্ট করে, কিন্তু স্ক্রিন রিডার হয়ত এটি দেখানোর সাথে সাথে ঘোষণা করবে না (শুধুমাত্র ইনপুটে ফোকাস করার সময়)।
    • সমস্যা দেখা দিলে (যেমন, অস্পষ্টতার ক্ষেত্রে) আপনার যদি অবিলম্বে ঘোষণার প্রয়োজন হয়, তাহলে সমস্যা মেসেজ কন্টেনারে role="alert" বা aria-live="polite" যোগ করার কথা বিবেচনা করুন, তবে ব্যবহারকারী এটি ঠিক করার জন্য ফিল্ডে ফোকাস করলে "ডবল ঘোষণা" এড়াতে পুঙ্খানুপুঙ্খভাবে পরীক্ষা করুন।
  2. আন্তর্জাতিকীকরণ:

    • আপনার ত্রুটি মেসেজের (#email-error) টেক্সট কন্টেন্ট অনুবাদ করা হয়েছে কিনা তা নিশ্চিত করুন। লজিক একই থাকে।