aria-valuetext
Defines the human-readable text alternative of aria-valuenow for a range widget. Provides meaningful context when the numeric value alone isn't sufficient.
Overview
The aria-valuetext attribute provides a human-readable text alternative to the numeric aria-valuenow value. When present, screen readers announce the valuetext instead of the raw number.
This is essential when the numeric value isn't meaningful on its own—like star ratings ("4 out of 5 stars"), temperatures with descriptions, time values, or progress states.
When to Use
Use aria-valuetext when the numeric value needs context: star ratings, temperature feelings, time formats, progress phases, or any value that benefits from description.
Live Demo
22°C - Comfortable
4 stars - Very Good
Screen reader: Temperature: "22°C - Comfortable". Rating: "4 stars - Very Good". The valuetext provides meaningful context instead of just numbers.
Code Examples
Basic Usage
<!-- aria-valuetext provides human-readable value -->
<!-- Temperature slider with descriptive text -->
<div
role="slider"
aria-valuemin="0"
aria-valuemax="40"
aria-valuenow="22"
aria-valuetext="22 degrees Celsius, Comfortable"
aria-label="Room temperature"
tabindex="0"
>
22°C
</div>
<!-- Screen reader: "Room temperature, slider, 22 degrees Celsius, Comfortable" -->
<!-- Without valuetext: "Room temperature, slider, 22" (less meaningful) -->
<!-- Star rating with descriptive text -->
<div
role="slider"
aria-valuemin="0"
aria-valuemax="5"
aria-valuenow="4"
aria-valuetext="4 out of 5 stars, Very Good"
aria-label="Product rating"
tabindex="0"
>
★★★★☆
</div>Contextual Descriptions
<!-- Contextual value descriptions -->
<!-- Time slider -->
<div
role="slider"
aria-valuemin="0"
aria-valuemax="1440"
aria-valuenow="810"
aria-valuetext="1:30 PM"
aria-label="Meeting time"
tabindex="0"
>
1:30 PM
</div>
<!-- Progress with descriptive phases -->
<div
role="progressbar"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="65"
aria-valuetext="65%, Processing images"
aria-label="Upload progress"
>
65% - Processing images...
</div>
<!-- Price range -->
<div
role="slider"
aria-valuemin="0"
aria-valuemax="1000"
aria-valuenow="299"
aria-valuetext="$299, Mid-range"
aria-label="Price filter"
tabindex="0"
>
$299
</div>
<!-- Date selection -->
<div
role="slider"
aria-valuemin="1"
aria-valuemax="365"
aria-valuenow="45"
aria-valuetext="February 14th, Valentine's Day"
aria-label="Date selector"
tabindex="0"
>
Feb 14
</div>React Components
// React components with aria-valuetext
import { useState, useMemo } from 'react';
// Temperature Slider with descriptive text
function TemperatureSlider({ value, onChange, min = 0, max = 40 }) {
const valueText = useMemo(() => {
if (value < 10) return `${value}°C - Cold`;
if (value < 18) return `${value}°C - Cool`;
if (value < 24) return `${value}°C - Comfortable`;
if (value < 30) return `${value}°C - Warm`;
return `${value}°C - Hot`;
}, [value]);
return (
<div className="temperature-slider">
<label id="temp-label">Room Temperature</label>
<div
role="slider"
aria-labelledby="temp-label"
aria-valuemin={min}
aria-valuemax={max}
aria-valuenow={value}
aria-valuetext={valueText}
tabIndex={0}
onKeyDown={(e) => {
if (e.key === 'ArrowRight') onChange(Math.min(value + 1, max));
if (e.key === 'ArrowLeft') onChange(Math.max(value - 1, min));
}}
>
<div className="slider-track">
<div
className="slider-fill"
style={{ width: `${((value - min) / (max - min)) * 100}%` }}
/>
</div>
<span className="value-display">{valueText}</span>
</div>
</div>
);
}
// Star Rating with descriptive text
function StarRating({ value, onChange, max = 5 }) {
const ratingTexts = [
'No rating',
'1 star - Poor',
'2 stars - Fair',
'3 stars - Good',
'4 stars - Very Good',
'5 stars - Excellent'
];
return (
<div className="star-rating">
<label id="rating-label">Your Rating</label>
<div
role="slider"
aria-labelledby="rating-label"
aria-valuemin={0}
aria-valuemax={max}
aria-valuenow={value}
aria-valuetext={ratingTexts[value]}
tabIndex={0}
onKeyDown={(e) => {
if (e.key === 'ArrowRight') onChange(Math.min(value + 1, max));
if (e.key === 'ArrowLeft') onChange(Math.max(value - 1, 0));
}}
>
{[1, 2, 3, 4, 5].map(star => (
<button
key={star}
onClick={() => onChange(star)}
className={star <= value ? 'filled' : 'empty'}
aria-label={`Rate ${star} stars`}
>
{star <= value ? '★' : '☆'}
</button>
))}
</div>
<span className="rating-text">{ratingTexts[value]}</span>
</div>
);
}
// Time Picker with formatted time
function TimePicker({ minutes, onChange }) {
const formatTime = (mins) => {
const hours = Math.floor(mins / 60);
const m = mins % 60;
const period = hours >= 12 ? 'PM' : 'AM';
const displayHours = hours % 12 || 12;
return `${displayHours}:${m.toString().padStart(2, '0')} ${period}`;
};
return (
<div
role="slider"
aria-label="Select time"
aria-valuemin={0}
aria-valuemax={1440}
aria-valuenow={minutes}
aria-valuetext={formatTime(minutes)}
tabIndex={0}
>
{formatTime(minutes)}
</div>
);
}
