DriversRecommendedOutdated drivers can make a good PC feel brokenScan driver issues before chasing fixes manually.Scan NowOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run Scan×
Skip to content
World desk6 min

How to Create a Cross-Browser Compatible HTML Progress Bar

Use the native element for task completion, label it accessibly, and test its appearance in the browsers your project supports.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use the native HTML <progress> element for a task’s progress. Give it a label, set max and value for determinate progress, and omit value when the amount of progress is unknown. Its basic semantics are broadly supported; test your CSS in the actual browsers and versions your project supports because native controls may not look identical.

Build a labeled progress bar with HTML

For a task that has a known amount of work, use a <progress> element with both a maximum and a current value:

<label for="upload-progress">Upload progress</label>
<progress id="upload-progress" max="100" value="45">45%</progress>

Here, max="100" makes the values percentage-like, so value="45" represents 45 out of 100. The default maximum is 1, so a fractional value also works: <progress value="0.45"></progress>. A specified max must be greater than zero, and value must be between zero and the maximum.

The text inside the element is fallback content; it does not provide the progress bar’s accessible name. Use an associated visible <label>, as above, or provide a programmatic name with aria-label or aria-labelledby. If you show a separate percentage or status message, keep it synchronized with the actual progress value.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Represent indeterminate progress

When work is underway but you cannot calculate how much is complete, omit the value attribute:

<label for="upload-progress">Uploading file</label>
<progress id="upload-progress" max="100"></progress>

Do not use value="0" to mean “unknown”: zero is a determinate value. If JavaScript changes an existing determinate progress element to indeterminate, remove the attribute:

const progress = document.querySelector('#upload-progress');
progress.removeAttribute('value');

The WHATWG HTML Standard specifies removing the value attribute to make a progress bar indeterminate.

Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option

Update progress safely with JavaScript

Update the element’s value as work advances, keeping it within the range from zero through its maximum. If visible status text is present, update that too:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const progress = document.querySelector('#upload-progress');
const status = document.querySelector('#upload-status');

function setProgress(completed, total) {
  if (total <= 0) throw new Error('Total work must be greater than zero');
  const value = Math.min(Math.max(completed, 0), total);
  progress.max = total;
  progress.value = value;
  status.textContent = `${Math.round((value / total) * 100)}% complete`;
}

For this example, include a status element in the page:

<label for="upload-progress">Upload progress</label>
<progress id="upload-progress" max="100" value="0">0%</progress>
<p id="upload-status">0% complete</p>

When the amount of work is not known, leave value absent rather than reporting a guessed percentage. If the task later becomes measurable, set a valid maximum and value.

Associate progress with a region being updated

If the bar describes a page region that is currently changing, connect the two with aria-describedby and mark the region busy while the update is in progress:

<section id="upload-panel" aria-busy="true" aria-describedby="upload-progress">
  <label for="upload-progress">Uploading file</label>
  <progress id="upload-progress" max="100" value="45">45%</progress>
  <p>Upload details appear here.</p>
</section>

Set aria-busy="false" when the region’s update finishes. The progress element already has an implicit progressbar role; a custom widget does not get those semantics automatically.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Style the bar without relying on identical browser rendering

Start with native <progress> semantics, then add visual styling only after checking how it renders in your supported browsers. Native control rendering differs, and browser-specific pseudo-elements do not have a single styling recipe established here that can be promised to work identically everywhere. Avoid claiming pixel-identical results unless you have verified the exact browsers and versions your project targets.

Use a project-specific compatibility check rather than assuming that broad support for the element guarantees matching appearance. Include the states your interface uses: empty, partial, complete, and indeterminate. Also confirm that the accessible name and current state are conveyed correctly with the assistive technologies in your support matrix.

Native progress or a custom progress bar?

Approach Semantics and accessibility Visual control Best fit
Native <progress> Provides task-progress semantics and an implicit progressbar role; give it an accessible name. Styling and appearance can vary by browser. Most task-progress indicators.
Custom markup You must supply the accessible name, progressbar role, and correctly maintained value and state. Allows custom markup, but does not remove the need to test rendering and accessibility. Only when the native control cannot meet a specific interface requirement and the team can maintain its semantics.

Use <meter> for a scalar measurement, such as a value within a known range, rather than for completion of a task. A progress indicator communicates work completed or underway; a meter communicates a measurement.

Test browser compatibility and troubleshoot common problems

The basic <progress> element is widely available. MDN describes it as available across browsers since July 2015, but that is not a guarantee for every older browser version or for identical styling. Your project’s minimum browser versions and assistive-technology combinations determine what you need to validate.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • The bar looks different between browsers: Native controls may use browser-specific rendering. Check the required browsers and versions, adjust the styling for those targets, and do not assume one vendor-specific rule works everywhere.
  • The bar appears empty even though work has not started: A value of zero means determinate progress at zero. For work with unknown progress, remove the value attribute.
  • The value is rejected or does not represent the intended amount: Check that max is greater than zero and that value is a valid number between zero and max. Remember that the default maximum is one.
  • A screen reader does not announce what the bar measures: Add a visible associated <label>, or provide an accessible name with aria-label or aria-labelledby. Text nested inside <progress> is fallback content, not its accessible name.
  • A custom bar is missing progress semantics: Prefer native <progress> where possible. A custom widget needs progressbar semantics and state values that are updated accurately; omit aria-valuenow when a custom progressbar is indeterminate.

Test the actual minimum browser versions your project supports, including determinate values at zero, between zero and the maximum, and at completion, plus indeterminate state if used. Check both visual presentation and the accessible name and announced state.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If you want screenshots to review how a page renders, ScreenshotNeo can capture the target URL; screenshots are useful for visual inspection, but they do not replace testing the relevant browsers or assistive technologies. For example, a cURL request is:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo documentation for API details. ScreenshotNeo accepts consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; those steps can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response indicates the page verdict and billing status in headers. Its MCP server offers take_screenshot, get_page_info, and capture_pdf for AI agents and MCP clients. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Sign up for ScreenshotNeo’s free plan to try 1,000 screenshots a month with no card.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Should I use <progress> or <meter> for a task that is 45% complete?

Use <progress> for completion of a task. Use <meter> for a scalar measurement within a known range.

Does <progress> need an explicit ARIA role?

No. The native element has an implicit progressbar role. Give it an accessible name; custom progress markup requires its own progressbar semantics.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. Shenzhen desk3 min
    HONOR Expands Beyond Smartphones With Humanoid Robot RevealHONOR said it unveiled its first humanoid robot at MWC 2026 and named shopping assistance, workplace inspections, and supportive companionship as intended uses. Later Robotics D1 claims and a reported…
  2. Cupertino desk5 min
    Apple Unveils AirPods Max 2: The Upgrade That Should Have Happened Years AgoAirPods Max 2 adds H2-powered audio features and Apple claims up to 1.5× more effective ANC, but its design, Smart Case, and 20-hour battery rating are unchanged. Wired lossless audio…
  3. Cupertino desk4 min
    Apple’s OLED Touch MacBooks Are Coming—but the Dynamic Island Is the Real GambleApple has not announced an OLED touchscreen MacBook, but reports point to high-end models arriving in late 2026 or early 2027. The reported Mac Dynamic Island could be useful, but…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.