{"id":11119,"date":"2025-11-13T19:45:28","date_gmt":"2025-11-13T19:45:28","guid":{"rendered":"https:\/\/namastedev.com\/blog\/?p=11119"},"modified":"2025-11-13T19:45:28","modified_gmt":"2025-11-13T19:45:28","slug":"building-accessible-components-with-vanilla-javascript-2","status":"publish","type":"post","link":"https:\/\/namastedev.com\/blog\/building-accessible-components-with-vanilla-javascript-2\/","title":{"rendered":"Building Accessible Components with Vanilla JavaScript"},"content":{"rendered":"<h1>Building Accessible Components with Vanilla JavaScript<\/h1>\n<p>Accessibility is a cornerstone of modern web development. Every user deserves equitable access to online content, regardless of their abilities or disabilities. In this blog post, we&#8217;ll explore how to build accessible components using <strong>vanilla JavaScript<\/strong>. We&#8217;ll cover essential practices, techniques, and examples to ensure your web applications are inclusive for all users.<\/p>\n<h2>Why Accessibility Matters<\/h2>\n<p>According to the World Health Organization, over 1 billion people experience some form of disability. Web accessibility ensures that the digital world is navigable for everyone, including those with disabilities such as visual impairments, hearing loss, and motor challenges. Building accessible web components not only fulfills legal requirements in many regions but also enhances your project&#8217;s reach and usability.<\/p>\n<h2>Understanding ARIA Roles and Attributes<\/h2>\n<p><strong>Accessible Rich Internet Applications (ARIA)<\/strong> provides a set of attributes that can be added to HTML to improve accessibility. While semantic HTML elements like <code>&lt;button&gt;<\/code> and <code>&lt;form&gt;<\/code> already convey their purpose to assistive technologies, ARIA roles and attributes help clarify more complex components.<\/p>\n<p>For instance, if you&#8217;re creating a custom dropdown menu, you can use ARIA attributes to convey its structure and behavior:<\/p>\n<pre><code>&lt;div role=\"combobox\" aria-expanded=\"false\"&gt;\n  &lt;input type=\"text\" aria-autocomplete=\"list\" aria-haspopup=\"true\" \/&gt;\n  &lt;div role=\"listbox\"&gt;\n    &lt;div role=\"option\"&gt;Option 1&lt;\/div&gt;\n    &lt;div role=\"option\"&gt;Option 2&lt;\/div&gt;\n    &lt;div role=\"option\"&gt;Option 3&lt;\/div&gt;\n  &lt;\/div&gt;\n&lt;\/div&gt;<\/code><\/pre>\n<h2>Creating an Accessible Button Component<\/h2>\n<p>Let&#8217;s start with a simple yet essential UI component: a button. While we could use the <code>&lt;button&gt;<\/code> element directly, we can create a stylized button using a <code>&lt;div&gt;<\/code> or <code>&lt;a&gt;<\/code> element while maintaining accessibility via JavaScript.<\/p>\n<h3>HTML Structure<\/h3>\n<pre><code>&lt;div id=\"myButton\" tabindex=\"0\" role=\"button\" aria-pressed=\"false\"&gt;\n  Click Me!\n&lt;\/div&gt;<\/code><\/pre>\n<h3>JavaScript Implementation<\/h3>\n<pre><code>const button = document.getElementById('myButton');\n\nbutton.addEventListener('click', () =&gt; {\n  const pressed = button.getAttribute('aria-pressed') === 'true';\n  button.setAttribute('aria-pressed', !pressed);\n});\n\nbutton.addEventListener('keydown', (e) =&gt; {\n  if (e.key === 'Enter' || e.key === ' ') {\n    button.click();\n    e.preventDefault();\n  }\n});<\/code><\/pre>\n<p>In this example, we&#8217;ve created a non-button element that behaves like a button. We&#8217;ve set attributes to convey its role and state. The use of <code>tabindex=\"0\"<\/code> allows it to be focusable, which is essential for keyboard navigation.<\/p>\n<h2>Building an Accessible Modal<\/h2>\n<p>Modals are another common component in web applications. It&#8217;s crucial to ensure they are accessible to users who rely on keyboard navigation and screen readers.<\/p>\n<h3>HTML Structure<\/h3>\n<pre><code>&lt;div id=\"modal\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"modalTitle\" style=\"display:none\"&gt;\n  &lt;h2 id=\"modalTitle\"&gt;Modal Title&lt;\/h2&gt;\n  &lt;p&gt;This is an accessible modal window.&lt;\/p&gt;\n  &lt;button id=\"closeModal\"&gt;Close&lt;\/button&gt;\n&lt;\/div&gt;<\/code><\/pre>\n<h3>JavaScript to Control the Modal<\/h3>\n<pre><code>const modal = document.getElementById('modal');\nconst closeModal = document.getElementById('closeModal');\n\nfunction openModal() {\n  modal.style.display = 'block';\n  modal.setAttribute('aria-hidden', 'false');\n  closeModal.focus(); \/\/ Move focus to close button\n}\n\nfunction closeModalFunction() {\n  modal.style.display = 'none';\n  modal.setAttribute('aria-hidden', 'true');\n  document.getElementById('openModalButton').focus(); \/\/ Return focus to the trigger\n}\n\ncloseModal.addEventListener('click', closeModalFunction);\n\n\/\/ Close modal on Escape key\ndocument.addEventListener('keydown', (e) =&gt; {\n  if (e.key === 'Escape' &amp;&amp; modal.style.display === 'block') {\n    closeModalFunction();\n  }\n});<\/code><\/pre>\n<h2>Focus Management<\/h2>\n<p>One crucial aspect of accessible components is managing focus. When a modal opens, focus should be shifted to an element inside the modal. Conversely, when the modal closes, focus should return to the element that triggered it. This is essential for keyboard users, as it allows them to continue navigating the application intuitively.<\/p>\n<h2>Creating an Accessible Tab Component<\/h2>\n<p>Tab navigation is a common interface pattern, allowing users to navigate through sections of content without leaving the page. Here\u2019s how to implement an accessible tab component using vanilla JavaScript.<\/p>\n<h3>HTML Structure<\/h3>\n<pre><code>&lt;div role=\"tablist\"&gt;\n  &lt;div role=\"tab\" aria-selected=\"true\" tabindex=\"0\"&gt;Tab 1&lt;\/div&gt;\n  &lt;div role=\"tab\" tabindex=\"0\"&gt;Tab 2&lt;\/div&gt;\n  &lt;div role=\"tab\" tabindex=\"0\"&gt;Tab 3&lt;\/div&gt;\n&lt;\/div&gt;\n&lt;div role=\"tabpanel\"&gt;\n  &lt;p&gt;Content for Tab 1&lt;\/p&gt;\n&lt;\/div&gt;<\/code><\/pre>\n<h3>JavaScript Implementation<\/h3>\n<pre><code>const tabs = document.querySelectorAll('[role=\"tab\"]');\nconst panels = document.querySelectorAll('[role=\"tabpanel\"]');\n\ntabs.forEach(tab =&gt; {\n  tab.addEventListener('click', (e) =&gt; {\n    tabs.forEach(t =&gt; t.setAttribute('aria-selected', 'false'));\n    tab.setAttribute('aria-selected', 'true');\n    \n    panels.forEach(panel =&gt; panel.hidden = true);\n    const index = Array.from(tabs).indexOf(tab);\n    panels[index].hidden = false;\n  });\n\n  tab.addEventListener('keydown', (e) =&gt; {\n    if (e.key === 'ArrowRight') {\n      const nextTab = tab.nextElementSibling || tabs[0];\n      nextTab.focus();\n      nextTab.click();\n    } else if (e.key === 'ArrowLeft') {\n      const prevTab = tab.previousElementSibling || tabs[tabs.length - 1];\n      prevTab.focus();\n      prevTab.click();\n    }\n  });\n});<\/code><\/pre>\n<h2>Form Elements and Accessibility<\/h2>\n<p>Forms are critical in web applications, and properly labeling elements is vital for screen readers. Always include explicit labels and announcements for input states.<\/p>\n<h3>Example Form Component<\/h3>\n<pre><code>&lt;form aria-labelledby=\"formTitle\"&gt;\n  &lt;h2 id=\"formTitle\"&gt;Contact Us&lt;\/h2&gt;\n  &lt;label for=\"name\"&gt;Name:&lt;\/label&gt;\n  &lt;input type=\"text\" id=\"name\" required \/&gt;\n\n  &lt;label for=\"email\"&gt;Email:&lt;\/label&gt;\n  &lt;input type=\"email\" id=\"email\" required \/&gt;\n\n  &lt;button type=\"submit\"&gt;Send&lt;\/button&gt;\n&lt;\/form&gt;<\/code><\/pre>\n<h3>Live Feedback<\/h3>\n<p>Providing real-time feedback for form inputs is also vital. Here\u2019s how to do it:<\/p>\n<pre><code>const form = document.querySelector('form');\nconst emailInput = document.getElementById('email');\n\nemailInput.addEventListener('input', () =&gt; {\n  const message = emailInput.validity.valid ? 'Valid email!' : 'Invalid email, please correct it.';\n  emailInput.setAttribute('aria-describedby', 'emailFeedback');\n  document.getElementById('emailFeedback').textContent = message;\n});<\/code><\/pre>\n<h2>Best Practices for Building Accessible Components<\/h2>\n<ul>\n<li><strong>Use Semantic HTML:<\/strong> Always prefer semantic HTML over styling hacks for accessibility.<\/li>\n<li><strong>Check Focus Order:<\/strong> Ensure focus order is logical and intuitive.<\/li>\n<li><strong>Perform Regular Testing:<\/strong> Use accessibility testing tools like Lighthouse and screen readers to evaluate your components.<\/li>\n<li><strong>Stay Updated:<\/strong> Follow web accessibility guidelines such as WCAG (Web Content Accessibility Guidelines) for the latest practices.<\/li>\n<\/ul>\n<h2>Conclusion<\/h2>\n<p>Building accessible components with vanilla JavaScript is more than just a good practice; it\u2019s an ethical obligation. By leveraging semantic HTML, ARIA roles, and thoughtful event handling, you can create a more inclusive web experience. Remember, accessibility isn\u2019t a one-time consideration; it should be an ongoing part of your development process. Let\u2019s build a web that works for everyone!<\/p>\n<h2>Further Resources<\/h2>\n<ul>\n<li><a href=\"https:\/\/www.w3.org\/WAI\/WCAG21\/quickref\/\">WCAG Quick Reference<\/a><\/li>\n<li><a href=\"https:\/\/www.a11yproject.com\/\">The A11Y Project<\/a><\/li>\n<li><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Learn\/Accessibility\">MDN Web Docs on Accessibility<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Building Accessible Components with Vanilla JavaScript Accessibility is a cornerstone of modern web development. Every user deserves equitable access to online content, regardless of their abilities or disabilities. In this blog post, we&#8217;ll explore how to build accessible components using vanilla JavaScript. We&#8217;ll cover essential practices, techniques, and examples to ensure your web applications are<\/p>\n","protected":false},"author":107,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[202],"tags":[1283,335,1284],"class_list":["post-11119","post","type-post","status-publish","format-standard","category-ui-ux-design","tag-accessibility-tools","tag-best-practices","tag-keyboard-accessibility"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/posts\/11119","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/users\/107"}],"replies":[{"embeddable":true,"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/comments?post=11119"}],"version-history":[{"count":1,"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/posts\/11119\/revisions"}],"predecessor-version":[{"id":11120,"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/posts\/11119\/revisions\/11120"}],"wp:attachment":[{"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/media?parent=11119"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/categories?post=11119"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/tags?post=11119"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}