[JavaScript Series 13] Build Accessible UIs with Semantic HTML and JavaScript

한국어 버전

Every user should be able to navigate the web comfortably. In this post, we will explore accessibility and how to make interfaces more inclusive with semantic HTML, focus control, and assistive-technology hints. This chapter again follows the concept → code → reasoning cadence.

Key terms

  1. Semantic HTML: markup that uses tags such as header, nav, and section so roles are clear from the element names alone.
  2. ARIA attributes: properties like aria-label or aria-live that supplement assistive technologies when semantics are not enough.
  3. Focus trap: a control technique that keeps Tab navigation inside a modal so focus does not leak out.
  4. role attribute: explicitly defines an element’s role so screen readers can announce it precisely.

Core ideas

  • Semantic HTML: communicate meaning with tags like section, nav, button, and label so structure is predictable.
  • ARIA attributes: use helpers such as aria-live, aria-expanded, and aria-controls to add context when markup alone falls short.
  • Focus flow: design the order in which keyboard users traverse with Tab, applying tabindex and calling focus() purposefully.
  • Live regions: add attributes like aria-live="polite" near status text so assistive tech announces updates automatically.

Semantic structure

Start by combining semantic tags with navigation labels so the screen layout is self-explanatory.

<header>
  <h1>Student schedule dashboard</h1>
  <nav aria-label="Jump to page sections">
    <a href="#tasks">Tasks</a>
    <a href="#stats">Stats</a>
  </nav>
</header>

<section id="tasks" aria-labelledby="tasks-heading">
  <h2 id="tasks-heading">Task list</h2>
  <ul role="list" class="task-list"></ul>
</section>

Adding aria-label and aria-labelledby ties headings to regions so screen readers can guide users rapidly.

Focus control for modals

Next, control modal focus so users never lose track of where they are.

const addButton = document.querySelector(".task-add");
const modal = document.querySelector("#task-modal");
const modalClose = modal?.querySelector(".modal-close");

function openModal() {
  modal?.classList.add("is-open");
  modal?.setAttribute("aria-hidden", "false");
  modal?.setAttribute("aria-modal", "true");
  modal?.querySelector("input")?.focus();
}

function closeModal() {
  modal?.classList.remove("is-open");
  modal?.setAttribute("aria-hidden", "true");
  addButton?.focus();
}

addButton?.addEventListener("click", openModal);
modalClose?.addEventListener("click", closeModal);

Send focus into the modal when it opens and return it to the trigger button when it closes so keyboard users never feel stranded.

Live regions for feedback

Create a live region for state messages so feedback is read aloud automatically.

<div class="form-feedback" role="status" aria-live="polite"></div>
const feedback = document.querySelector(".form-feedback");

function showFeedback(message, type = "info") {
  if (!feedback) return;
  feedback.textContent = message;
  feedback.className = `form-feedback is-${type}`;
}

form?.addEventListener("submit", (event) => {
  event.preventDefault();
  showFeedback("Saving...", "info");
  saveTask().then(() => showFeedback("Saved!", "success"));
});

Using role="status" with aria-live="polite" ensures assistive technologies announce these state changes.

Focus styles

Finish by defining focus styles so the current position is obvious.

:focus-visible {
  outline: 3px solid #1ba784;
  outline-offset: 4px;
}

[tabindex="-1"]:focus {
  outline: none;
}

Clear focus styling helps keyboard users confirm their place instantly.

Why it matters

  • School projects and competitions often include accessibility criteria; mastering the basics improves evaluations.
  • Semantic structure and ARIA hints also raise SEO quality.
  • A predictable focus flow benefits everyone: when the keyboard alone is enough, testing speed goes up.

Practice

  • Follow along: apply semantic tags to headers, main sections, buttons, and forms, then add aria-label and aria-labelledby links.
  • Extend: build a modal that moves focus on open/close while toggling aria-hidden and aria-modal.
  • Debug: tour the UI with only the Tab key, spot places where focus disappears or escapes, and fix them with tabindex plus explicit focus() calls.
  • Done when: a screen reader (VoiceOver, NVDA, etc.) announces every key element and the full workflow operates via keyboard alone.

Wrap-up

Accessibility is not an add-on—it is a default you embed from the start. Next we will catalog reusable UI patterns and see how to keep these accessibility principles intact.

💬 댓글

이 글에 대한 의견을 남겨주세요