CPCODELAB
HTML

data-* Attributes and ARIA Roles

12 min read

Extending HTML with Custom Metadata and ARIA

data-* Attributes

HTML allows you to attach custom data to any element using attributes that begin with data-. The browser ignores these completely — they exist purely for your CSS and JavaScript to read.

html
<!-- Store metadata on elements for JavaScript to read -->
<ul class="product-list">
  <li
    data-product-id="42"
    data-category="electronics"
    data-price="29.99"
    data-in-stock="true"
  >
    USB-C Cable
  </li>
  <li
    data-product-id="43"
    data-category="accessories"
    data-price="14.99"
    data-in-stock="false"
  >
    Phone Stand
  </li>
</ul>

<!-- CSS can target data attributes -->
<!-- [data-in-stock="false"] { opacity: 0.5; } -->

<script>
  const item = document.querySelector('[data-product-id="42"]');
  const price = item.dataset.price;  // "29.99"
  const id = item.dataset.productId; // camelCase in JS
</script>

In JavaScript, data-product-id becomes element.dataset.productId — hyphenated names are automatically converted to camelCase. data-* values are always strings; parse them with Number() or JSON.parse() when you need other types.

ARIA Roles and Landmarks

ARIA (Accessible Rich Internet Applications) adds semantic information that HTML alone cannot express. Landmark roles define regions of the page; widget roles describe interactive components.

html
<!-- Landmark roles (prefer native elements) -->
<div role="banner"><!-- same as <header> at page level --></div>
<div role="navigation" aria-label="Breadcrumb"><!-- same as <nav> --></div>
<div role="main"><!-- same as <main> --></div>
<div role="contentinfo"><!-- same as <footer> at page level --></div>

<!-- ARIA states and properties -->
<button
  aria-expanded="false"
  aria-controls="dropdown-menu"
  id="menu-btn"
>
  Options
</button>
<ul id="dropdown-menu" role="menu" aria-labelledby="menu-btn" hidden>
  <li role="menuitem"><a href="/profile">Profile</a></li>
  <li role="menuitem"><a href="/settings">Settings</a></li>
</ul>

<!-- Live regions announce dynamic content to screen readers -->
<div aria-live="polite" aria-atomic="true">
  <!-- Injected by JS: "3 results found" -->
</div>
  • aria-label — Overrides the accessible name with a custom string.
  • aria-labelledby — Points to another element whose text becomes the accessible name.
  • aria-describedby — Points to an element that provides additional description.
  • aria-hidden="true" — Removes the element from the accessibility tree entirely.
  • aria-expanded — Indicates whether a collapsible widget is open or closed.
  • aria-live — Tells assistive technology to announce changes to the region (off, polite, assertive).

The first rule of ARIA: do not use ARIA if a native HTML element exists. <button>, <nav>, <main>, and <header> already provide roles and keyboard behaviour for free. Reach for ARIA only to fill gaps that HTML cannot fill.

Ready to test yourself?
Practice HTML— quiz & coding exercises