Loading Developer Playground

Loading ...

Skip to main content
ARIA ATTRIBUTEWidget Attributes

aria-valuemax

Defines the maximum allowed value for a range widget. Used with sliders, progress bars, spinbuttons, and meters to establish the upper bound of the value range.

Value Type
number
Common Use
Progress Bars
Used With
valuemin, valuenow

Overview

The aria-valuemax attribute defines the maximum value allowed for a range widget. It works together with aria-valuemin and aria-valuenow to define the complete range.

For progress indicators, this defines the "complete" state. When aria-valuenow equals aria-valuemax, the task is 100% complete.

Indeterminate Progress

For loading states where the total is unknown, omit all three value attributes. This creates an indeterminate progress bar that screen readers announce as "busy" or "loading".

Live Demo: Progress Bar

File Upload Progress0%
0 (min)100 (max)

Current ARIA attributes:

aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"

Screen reader: "File Upload Progress, progress bar, 0%". At 100%: "File Upload Progress, progress bar, 100%, complete".

Code Examples

Basic Usage

<!-- Basic aria-valuemax usage -->

<!-- Slider with max value of 100 -->
<div
  role="slider"
  aria-valuemin="0"
  aria-valuemax="100"
  aria-valuenow="75"
  aria-label="Volume"
  tabindex="0"
>
  75%
</div>

<!-- Progress bar with custom max -->
<div
  role="progressbar"
  aria-valuemin="0"
  aria-valuemax="500"
  aria-valuenow="350"
  aria-label="File upload"
  aria-valuetext="350 of 500 MB uploaded"
>
  350 MB / 500 MB
</div>

<!-- Spinbutton with max limit -->
<div
  role="spinbutton"
  aria-valuemin="0"
  aria-valuemax="99"
  aria-valuenow="25"
  aria-label="Quantity"
  tabindex="0"
>
  25
</div>

Custom Max Values

<!-- Different max value scenarios -->

<!-- 5-star rating -->
<div
  role="slider"
  aria-valuemin="0"
  aria-valuemax="5"
  aria-valuenow="4"
  aria-label="Rating"
  aria-valuetext="4 out of 5 stars"
  tabindex="0"
>
  ★★★★☆
</div>

<!-- Percentage progress (common: max=100) -->
<div
  role="progressbar"
  aria-valuemin="0"
  aria-valuemax="100"
  aria-valuenow="65"
  aria-label="Course completion"
>
  65% complete
</div>

<!-- File count progress -->
<div
  role="progressbar"
  aria-valuemin="0"
  aria-valuemax="150"
  aria-valuenow="87"
  aria-label="Files processed"
  aria-valuetext="87 of 150 files"
>
  87 / 150 files
</div>

<!-- Step progress -->
<div
  role="progressbar"
  aria-valuemin="1"
  aria-valuemax="5"
  aria-valuenow="3"
  aria-label="Checkout progress"
  aria-valuetext="Step 3 of 5: Shipping"
>
  Step 3 of 5
</div>

Indeterminate Progress

<!-- Indeterminate progress (unknown max) -->

<!-- Loading spinner (no specific max) -->
<div
  role="progressbar"
  aria-label="Loading..."
>
  <!-- No aria-valuemin, aria-valuemax, or aria-valuenow -->
  <!-- This indicates indeterminate progress -->
  Loading...
</div>
<!-- Screen reader: "Loading..., progress bar, busy" -->

<!-- Alternative: Use aria-busy -->
<div
  role="region"
  aria-busy="true"
  aria-label="Content loading"
>
  <div role="progressbar" aria-label="Loading">
    <!-- Spinner animation -->
  </div>
</div>

<!-- Once complete, add values -->
<div
  role="progressbar"
  aria-valuemin="0"
  aria-valuemax="100"
  aria-valuenow="100"
  aria-label="Loading complete"
>
  Complete!
</div>

React Components

// React Progress Bar with aria-valuemax
import { useState, useEffect } from 'react';

function ProgressBar({
  value,
  max = 100,
  min = 0,
  label,
  showPercentage = true,
  indeterminate = false,
}) {
  const percentage = indeterminate 
    ? null 
    : Math.round(((value - min) / (max - min)) * 100);
  
  return (
    <div className="progress-container">
      <label id="progress-label">{label}</label>
      <div
        role="progressbar"
        aria-labelledby="progress-label"
        {...(indeterminate ? {} : {
          'aria-valuemin': min,
          'aria-valuemax': max,
          'aria-valuenow': value,
        })}
        className={`progress-bar ${indeterminate ? 'indeterminate' : ''}`}
      >
        <div 
          className="progress-fill"
          style={{ 
            width: indeterminate ? '30%' : `${percentage}%`,
            animation: indeterminate ? 'indeterminate 1.5s infinite' : 'none'
          }}
        />
      </div>
      {!indeterminate && showPercentage && (
        <span className="progress-text">{percentage}%</span>
      )}
    </div>
  );
}

// File Upload Progress
function FileUploadProgress({ 
  uploadedBytes, 
  totalBytes, 
  fileName 
}) {
  const percentage = Math.round((uploadedBytes / totalBytes) * 100);
  const uploadedMB = (uploadedBytes / 1024 / 1024).toFixed(1);
  const totalMB = (totalBytes / 1024 / 1024).toFixed(1);
  
  return (
    <div className="upload-progress">
      <span className="file-name">{fileName}</span>
      <div
        role="progressbar"
        aria-valuemin={0}
        aria-valuemax={totalBytes}
        aria-valuenow={uploadedBytes}
        aria-label={`Uploading ${fileName}`}
        aria-valuetext={`${uploadedMB} of ${totalMB} MB uploaded`}
        className="progress-bar"
      >
        <div 
          className="progress-fill"
          style={{ width: `${percentage}%` }}
        />
      </div>
      <span className="progress-stats">
        {uploadedMB} MB / {totalMB} MB ({percentage}%)
      </span>
    </div>
  );
}

// Multi-step Progress
function StepProgress({ currentStep, totalSteps, stepLabels }) {
  return (
    <div className="step-progress">
      <div
        role="progressbar"
        aria-valuemin={1}
        aria-valuemax={totalSteps}
        aria-valuenow={currentStep}
        aria-label="Checkout progress"
        aria-valuetext={`Step ${currentStep} of ${totalSteps}: ${stepLabels[currentStep - 1]}`}
        className="steps-container"
      >
        {stepLabels.map((label, index) => (
          <div 
            key={label}
            className={`step ${index + 1 <= currentStep ? 'completed' : ''}`}
          >
            <div className="step-number">{index + 1}</div>
            <span className="step-label">{label}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// Usage
function App() {
  const [progress, setProgress] = useState(0);
  
  return (
    <>
      <ProgressBar
        value={progress}
        max={100}
        label="Download Progress"
      />
      
      <FileUploadProgress
        uploadedBytes={350 * 1024 * 1024}
        totalBytes={500 * 1024 * 1024}
        fileName="video.mp4"
      />
      
      <StepProgress
        currentStep={3}
        totalSteps={5}
        stepLabels={['Cart', 'Shipping', 'Payment', 'Review', 'Confirm']}
      />
    </>
  );
}

Best Practices

Set aria-valuemax higher than aria-valuemin to define a valid range

Use meaningful max values (e.g., file size in bytes, total steps)

Omit all value attributes for indeterminate/unknown progress

Consider using aria-valuetext for human-readable descriptions

Update valuenow dynamically as progress changes

×

Don't set aria-valuenow higher than aria-valuemax

×

Don't use on native HTML progress/meter elements—they have max attribute

×

Don't forget to pair with aria-valuemin and aria-valuenow

Related Attributes

Specifications & Resources