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

A simple HTML loading screen needs two things: a status that says what is happening and a visual treatment that does not pretend to know progress it cannot measure. Use a text label such as Loading… with an optional decorative spinner for an indeterminate wait. When your code has real progress data, use the native <progress> element and update its value from that data.

The following complete example works as a standalone file. It keeps the message meaningful when animation is disabled, honors reduced-motion preferences, and exposes the busy state of the region being updated.

A minimal indeterminate loading screen

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Loading example</title>
  <style>
    :root { color-scheme: light dark; }
    body {
      font-family: system-ui, sans-serif;
      margin: 0;
      min-height: 100vh;
      display: grid;
      place-items: center;
      background: Canvas;
      color: CanvasText;
    }
    .panel {
      width: min(34rem, calc(100% - 2rem));
      padding: 2rem;
      text-align: center;
      border: 1px solid color-mix(in srgb, CanvasText 20%, transparent);
      border-radius: .75rem;
    }
    .loading-indicator {
      display: inline-flex;
      align-items: center;
      gap: .75rem;
      font-weight: 600;
    }
    .spinner {
      width: 1.25rem;
      height: 1.25rem;
      border: .2rem solid color-mix(in srgb, CanvasText 25%, transparent);
      border-top-color: currentColor;
      border-radius: 50%;
      animation: spin .8s linear infinite;
    }
    @keyframes spin { to { transform: rotate(360deg); } }
    @media (prefers-reduced-motion: reduce) {
      .spinner { animation: none; }
    }
  </style>
</head>
<body>
  <main id="results" aria-busy="true" aria-describedby="loading-status">
    <section class="panel">
      <div class="loading-indicator" role="status" id="loading-status">
        <span class="spinner" aria-hidden="true"></span>
        <span>Loading…</span>
      </div>
      <div id="content" hidden>Content is ready.</div>
    </section>
  </main>
  <script>
    // Replace this timeout with the real request or rendering work.
    setTimeout(() => {
      const region = document.querySelector('#results');
      const status = document.querySelector('#loading-status');
      const content = document.querySelector('#content');
      region.setAttribute('aria-busy', 'false');
      status.textContent = 'Loaded';
      content.hidden = false;
    }, 1500);
  </script>
</body>
</html>

The circle is deliberately decorative: it has aria-hidden="true" and no progressbar role. The text inside the status element communicates the state. A screen reader can present meaningful changes without keyboard focus moving to the loading message.

Why the status text matters

A rotating shape does not explain whether the page is loading, saving, connecting, or blocked. Keep a concise label visible, and replace it with a meaningful completion or error message when the operation ends. Do not continuously rewrite the label with cosmetic updates; announce only state changes that matter to the user.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Philips 24 Inch Computer Monitor FHD 100Hz VA VESA Flicker-Free, 241V8LB
  • CRISP CLARITY: This 23.8″ Philips V line monitor delivers crisp Full HD 1920x1080 visuals. Enjoy movies, shows and videos with remarkable detail
  • INCREDIBLE CONTRAST: The VA panel produces brighter whites and deeper blacks. You get true-to-life images and more gradients with 16.7 million colors
  • THE PERFECT VIEW: The 178/178 degree extra wide viewing angle prevents the shifting of colors when viewed from an offset angle, so you always get consistent colors
  • WORK SEAMLESSLY: This sleek monitor is virtually bezel-free on three sides, so the screen looks even bigger for the viewer. This minimalistic design also allows for seamless multi-monitor setups that enhance your workflow and boost productivity
  • A BETTER READING EXPERIENCE: For busy office workers, EasyRead mode provides a more paper-like experience for when viewing lengthy documents

How the busy state works

Put aria-busy="true" on the region whose contents are being updated, not automatically on the entire document. Associate that region with the status through aria-describedby when the relationship helps users understand what is waiting. Set the attribute to false (or remove it) as soon as the update is complete, including error and cancellation paths.

Indeterminate versus determinate loading

Use indeterminate loading when duration is unknown

Network requests, server rendering, authentication checks, and other operations often provide no trustworthy percentage. In that case, show the text status and optional decorative animation. Never rotate a spinner while labeling it “73%” unless your application actually measures 73 percent of the work.

Use native progress for measured work

For uploads, multi-file processing, installation steps, or any task that reports completed units, use the native <progress> element. Give it a visible label and set both value and max from real task data.

<section id="upload" aria-busy="true" aria-describedby="upload-status">
  <h2>Uploading photos</h2>
  <p id="upload-status">Uploading 0 of 12 files</p>
  <progress id="upload-progress" value="0" max="12">0 of 12</progress>
</section>
<script>
  const region = document.querySelector('#upload');
  const status = document.querySelector('#upload-status');
  const progress = document.querySelector('#upload-progress');

  function reportUpload(completed, total) {
    const safeTotal = Math.max(1, total);
    const safeCompleted = Math.min(Math.max(0, completed), safeTotal);
    progress.max = safeTotal;
    progress.value = safeCompleted;
    status.textContent = `Uploading ${safeCompleted} of ${safeTotal} files`;
    if (safeCompleted === safeTotal) {
      region.setAttribute('aria-busy', 'false');
      status.textContent = 'Upload complete';
    }
  }

  // Call reportUpload(completed, total) from the actual upload callbacks.
</script>

Do not invent a percentage or animate a value merely to make a wait feel shorter. A fabricated measurement can make users think a task is nearly finished when it is not. If the total changes, update max and the label together; if no reliable total exists, return to the indeterminate pattern.

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

Loading a particular region instead of the whole page

Single-page applications commonly keep navigation and controls usable while one panel refreshes. Wrap that panel in a region, set its busy state before starting the operation, and clear it in a finally block so failures cannot leave an endless loading state.

Rank #2
Philips 22 Inch Computer Monitor FHD 100Hz VA VESA Flicker-Free, 221V8LB
  • CRISP CLARITY: This 22 inch class (21.5″ viewable) Philips V line monitor delivers crisp Full HD 1920x1080 visuals. Enjoy movies, shows and videos with remarkable detail
  • 100HZ FAST REFRESH RATE: 100Hz brings your favorite movies and video games to life. Stream, binge, and play effortlessly
  • SMOOTH ACTION WITH ADAPTIVE-SYNC: Adaptive-Sync technology ensures fluid action sequences and rapid response time. Every frame will be rendered smoothly with crystal clarity and without stutter
  • INCREDIBLE CONTRAST: The VA panel produces brighter whites and deeper blacks. You get true-to-life images and more gradients with 16.7 million colors
  • THE PERFECT VIEW: The 178/178 degree extra wide viewing angle prevents the shifting of colors when viewed from an offset angle, so you always get consistent colors
<section id="account-panel" aria-busy="false" aria-describedby="account-status">
  <p id="account-status" role="status"></p>
  <div id="account-content"></div>
</section>
<script>
  async function refreshAccount() {
    const panel = document.querySelector('#account-panel');
    const status = document.querySelector('#account-status');
    const content = document.querySelector('#account-content');
    panel.setAttribute('aria-busy', 'true');
    status.textContent = 'Loading account details…';
    try {
      const response = await fetch('/api/account');
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      const data = await response.json();
      content.textContent = data.name;
      status.textContent = 'Account details loaded';
    } catch (error) {
      status.textContent = 'Account details could not be loaded. Try again.';
    } finally {
      panel.setAttribute('aria-busy', 'false');
    }
  }
</script>

Keep the status element in the DOM while the request runs. A role="status" region is normally announced without taking focus, which lets keyboard and screen-reader users remain where they are. For an urgent failure that requires immediate attention, choose an appropriate alert pattern rather than turning every progress update into an interruption.

Reduced motion without losing the message

prefers-reduced-motion: reduce is a user request to minimize nonessential movement. The media query in the first example stops the rotation but leaves the “Loading…” text and busy state intact. You can replace the spinner with a static symbol, a progress bar backed by real values, or no decoration at all. Do not hide the status when motion is reduced.

Reduced-motion handling is one documented accessibility technique, not a guarantee of WCAG conformance by itself. Evaluate the complete interaction: contrast, keyboard access, focus behavior, timeout handling, and the clarity of status messages still matter.

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

Native HTML semantics versus custom ARIA

Prefer built-in HTML elements when they describe the state you have. Native <progress> supplies progressbar semantics and exposes its value to user agents. A decorative spinner needs no ARIA role. Add ARIA only to express a relationship that native HTML does not provide, such as a region’s aria-busy state or a status message associated with that region.

A custom element with role="progressbar" carries more responsibility: you must maintain its value, maximum, accessible name, and state transitions correctly. Recreating native behavior with a generic <div> is rarely simpler or more reliable.

Rank #3
Sale
Dell 24 Monitor - SE2426H - 23.8-inch FHD (1920x1080) 144Hz 1ms Display, in-Plane Switching (IPS) Technology, AMD FreeSync™, TÜV 3-Star 2X HDMI, Tilt
  • Clear visuals. Fluid motion: A 144Hz refresh rate and 1ms MPRT deliver smooth, tear‑free motion across work, gaming, and streaming for clearer, more fluid viewing.
  • Eye comfort: TÜV Rheinland 3‑star* certification reduces harmful blue light while preserving stunning color quality without compromise. *TÜV Rheinland 3-star eye comfort certification.
  • Wide viewing angle: Get consistent views across a wide 178° /178° viewing angle.
  • In-Plane Switching (IPS): See excellent color accuracy and consistency across wide viewing angles with In-plane Switching (IPS) technology.
  • Ultra-thin bezels: Maximize your viewing experience with thin bezels.

Common implementation mistakes and fixes

The spinner is announced as meaningless noise

Cause: the decorative element has a role or accessible name that suggests it is informative. Fix: mark it aria-hidden="true" and put the explanation in visible text or a status element.

The page says “Loading” forever

Cause: the success path updates content but never clears aria-busy, or an exception skips the cleanup. Fix: set the state in a try/catch/finally flow and provide a retry or error message for failures.

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

A fake percentage reaches 100 percent early

Cause: a timer is being used as a substitute for task data. Fix: use an indeterminate label until the server or client can report completed work, then update a native progress element from those measurements.

Reduced-motion users receive no feedback

Cause: the only indication is a CSS animation. Fix: keep the text status and disable only the nonessential animation in the reduced-motion media query.

The wrong area is marked busy

Cause: aria-busy is applied to <body> for a small panel update. Fix: scope it to the region whose content is incomplete, so unrelated controls remain understandable and usable.

Rank #4
Samsung 27" Essential S3 (S36GD) Series FHD 1800R Curved Computer Monitor
  • CURVED FOR ENHANCED ENGAGEMENT: An immersive viewing experience with a curved monitor that wraps more closely around your field of vision; It creates a wider view, enhancing depth perception and minimizing peripheral distraction
  • SMOOTH PERFORMANCE FOR SEAMLESS CONTENT: Stay in the action when playing games, watching videos, or working on creative projects; The 100Hz refresh rate reduces lag and motion blur so you don't miss a thing in fast-paced moments¹
  • MORE GAMING POWER: Gain the edge with optimizable game settings; Color and image contrast can be adjusted to see scenes more vividly and spot enemies hiding in the dark; Game Mode adjusts any game to fill the screen so you can view every detail²
  • KEEP IT EASY ON THE EYES: Care for your eyes and stay comfortable, even during long sessions; Advanced eye comfort technology certified by TÜV reduces eye strain by minimizing blue light and reducing irritating screen flicker²
  • INCREASED VERSATILITY: Connect to more; Plug devices straight into your monitor for increased flexibility, making your computing environment even more convenient

The request finishes but stale content remains visible

Cause: old content is left on screen with no indication that it is being replaced. Fix: decide explicitly whether to keep stale content visible while marking the region busy or clear it and show a placeholder; in either case, announce the state change and restore the final content atomically where possible.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Timing, performance, and reliability decisions

Avoid unnecessary full-screen blockers

Use a full-page overlay only when interaction would be unsafe during the operation, such as replacing the entire application shell. For ordinary panel refreshes, an in-place status is less disruptive. If you do block interaction, make the reason visible and ensure the operation has a timeout, cancellation path, or recoverable error.

Prevent flicker for very short tasks

Showing a spinner for a few milliseconds can be more distracting than helpful. You may delay its visual appearance slightly while still updating the programmatic status immediately. Do not delay a necessary error or completion message, and do not use a delay to imply progress that you cannot measure.

Handle retries and cancellation

On retry, set the region busy again and replace the previous error with “Loading…” or a more specific message. On cancellation, clear the busy state and report that the operation was canceled; do not leave a disabled control or an infinite animation behind.

Test the state transitions

  • Start, success, server error, network loss, timeout, and cancellation.
  • Keyboard navigation while the region is busy.
  • A screen reader announcement of start and completion without focus moving unexpectedly.
  • Reduced-motion preference enabled.
  • Long labels, zoom, narrow screens, dark mode, and high-contrast environments.

Standards guidance describes techniques as examples rather than mandatory recipes, so judge the complete user experience instead of treating one snippet as automatic compliance.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value
Sale
Sceptre New 22-Inch Gaming Monitor, FHD 1080p, Up to 144Hz, HDMI, DisplayPort, Built-in Speakers, Machine Black (E225W-FW144 Series, 2026)
  • 【INTEGRATED SPEAKERS】Whether you're at work or in the midst of an intense gaming session, our built-in speakers provide rich and seamless audio, all while keeping your desk clutter-free.
  • 【EASY ON THE EYES】 Protect your eyes and enhance your comfort with Blue-Light Shift technology. This feature reduces harmful blue light emissions from your screen, helping to alleviate eye strain during long hours of use and promoting healthier viewing habits.
  • 【WIDEN YOUR PERSPECTIVE】Our sleek minimal bezel design ensures undivided attention. The nearly bezel-free display seamlessly connects in a dual monitor arrangement, delivering an unobstructed view that lets you focus on more at once, completely distraction-free.

Or skip the browser setup

If you need a rendered screenshot of a loading page for documentation, visual checks, or an automated workflow, ScreenshotNeo captures a URL through one HTTP request. It can accept consent banners before capture and remove more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

Use your API key and the target URL. The full parameter reference is in the ScreenshotNeo documentation.

cURL

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

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo supports PNG, JPEG, WebP, and PDF output, plus full-page capture, selector capture, device presets, custom viewports, retina scale, waiting for a selector, delay or network idle, custom CSS and JavaScript, request blocking, cookies, headers, geolocation, caching, signed links, asynchronous webhooks, bulk capture, and usage reporting. Every feature is on every plan. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Yearly billing provides two months free.

Create a free ScreenshotNeo account to get 1,000 screenshots a month without a card.

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.

Frequently Asked Questions

Can a loading screen be visible only to assistive technology?

Usually no. A visible, concise status helps every user understand the wait. If visual design does not need a spinner, keep the text and state semantics while omitting decorative motion.

Should navigation controls be disabled while a panel loads?

Only disable controls whose action would conflict with the in-progress update. Leave unrelated navigation available, and explain any disabled control with a nearby status or label.

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.