Loading Developer Playground

Loading ...

Skip to main content
ARIA ATTRIBUTERelationship Attributes

aria-labelledby

Identifies the element (or elements) that labels the current element. Creates an accessible name by referencing visible text content on the page.

Value Type
ID Reference (List)
Creates
Accessible Name
Common Use
Dialogs, Regions, Groups

Overview

The aria-labelledby attribute identifies the element(s) that label the current element. It provides the accessible name that screen readers announce.

Unlike aria-label which provides text directly, aria-labelledby references existing visible content, keeping the accessible name in sync with what sighted users see.

Multiple IDs can be space-separated to combine text from multiple elements. The order of IDs determines the order of the concatenated text.

Live Demo: Labelled Elements

Form Group Labelled by Heading

Billing Information

aria-labelledby="billing-section" → Group announced as "Billing Information, group"

Multiple Labels Combined

Searchproducts

aria-labelledby="prefix-demo suffix-demo" → Announced as "Search products, search"

Dialog with Label

aria-labelledby="dialog-demo-title" → Dialog announced as "Delete Confirmation, dialog"

aria-labelledby uses existing visible text for labels, ensuring screen reader users hear the same information sighted users see.

Code Examples

Basic Usage

<!-- aria-labelledby creates accessible name from referenced elements -->

<!-- Form field labelled by heading -->
<h2 id="billing-heading">Billing Address</h2>
<div role="group" aria-labelledby="billing-heading">
  <label for="street">Street</label>
  <input type="text" id="street" />
  
  <label for="city">City</label>
  <input type="text" id="city" />
</div>

<!-- Screen reader: "Billing Address, group" -->

Multiple Labels

<!-- Combining multiple label sources -->

<!-- Label from multiple elements -->
<span id="name-prefix">Full</span>
<span id="name-label">Name</span>
<input 
  type="text" 
  aria-labelledby="name-prefix name-label"
/>
<!-- Announced as: "Full Name, edit text" -->

<!-- Label includes visible text + context -->
<table>
  <tr>
    <th id="date-header">Date</th>
    <th id="time-header">Time</th>
  </tr>
  <tr>
    <td>
      <input 
        type="date" 
        aria-labelledby="date-header row1-label"
      />
    </td>
    <td>
      <input 
        type="time" 
        aria-labelledby="time-header row1-label"
      />
    </td>
    <th id="row1-label">Meeting 1</th>
  </tr>
</table>
<!-- First input: "Date Meeting 1, edit text" -->

Dialog Modal

<!-- Dialog with aria-labelledby -->

<div
  role="dialog"
  aria-modal="true"
  aria-labelledby="dialog-title"
  aria-describedby="dialog-desc"
>
  <h2 id="dialog-title">Confirm Deletion</h2>
  <p id="dialog-desc">
    Are you sure you want to delete this item? 
    This action cannot be undone.
  </p>
  
  <button>Cancel</button>
  <button>Delete</button>
</div>

<!-- Screen reader: "Confirm Deletion, dialog" -->

Self-Referencing

<!-- Including the element's own text in label -->

<button 
  id="save-btn"
  aria-labelledby="save-btn save-status"
>
  Save
</button>
<span id="save-status" class="sr-only">
  (changes pending)
</span>
<!-- Announced as: "Save (changes pending), button" -->

<!-- Combining visible label with hidden context -->
<a 
  href="/products/widget"
  id="product-link"
  aria-labelledby="product-link product-category"
>
  Widget Pro
</a>
<span id="product-category" class="sr-only">
  in Electronics category
</span>
<!-- Announced as: "Widget Pro in Electronics category, link" -->

React Components

// React components with aria-labelledby
import { useId } from 'react';

// Form section with heading as label
function FormSection({ title, children }) {
  const headingId = useId();

  return (
    <div 
      role="group" 
      aria-labelledby={headingId}
      className="form-section"
    >
      <h3 id={headingId}>{title}</h3>
      {children}
    </div>
  );
}

// Modal dialog with aria-labelledby
function Modal({ title, description, children, onClose }) {
  const titleId = useId();
  const descId = useId();

  return (
    <div
      role="dialog"
      aria-modal="true"
      aria-labelledby={titleId}
      aria-describedby={description ? descId : undefined}
    >
      <h2 id={titleId}>{title}</h2>
      {description && <p id={descId}>{description}</p>}
      {children}
      <button onClick={onClose}>Close</button>
    </div>
  );
}

// Table cell input labelled by headers
function DataCell({ rowLabel, columnLabel, value, onChange }) {
  const rowId = useId();
  const colId = useId();

  return (
    <>
      <span id={rowId} className="sr-only">{rowLabel}</span>
      <span id={colId} className="sr-only">{columnLabel}</span>
      <input
        type="text"
        value={value}
        onChange={onChange}
        aria-labelledby={`${colId} ${rowId}`}
      />
    </>
  );
}

// Region with dynamic label
function ContentRegion({ labelPrefix, labelSuffix, children }) {
  const prefixId = useId();
  const suffixId = useId();

  return (
    <section 
      role="region"
      aria-labelledby={`${prefixId} ${suffixId}`}
    >
      <span id={prefixId} className="sr-only">{labelPrefix}</span>
      <span id={suffixId} className="sr-only">{labelSuffix}</span>
      {children}
    </section>
  );
}

// Usage
function App() {
  return (
    <>
      <FormSection title="Shipping Information">
        <label>Address</label>
        <input type="text" />
      </FormSection>
      
      <Modal 
        title="Delete Account"
        description="This will permanently delete your account."
        onClose={() => {}}
      >
        <button>Confirm Delete</button>
      </Modal>
    </>
  );
}

Related Attributes

Specifications & Resources