Progress Bar (Bounded)

Progress Bar (Bounded)

A bounded progress bar helps users understand how much time is remaining in a process. Both sighted and blind users should be informed of the intervals with the progress bar.



Turn on a screen reader to experience this example in action.

Attribute/Option Description
background_color This attribute/option sets the background color of the progressbar. By default, it is set to "aliceblue" color.
indicator_color This attribute/option sets the progress indicator color. By default, it is set to "#2e5f7a" (Deque teal).
indicator_color_gradient This attribute/option sets the progress indicator as a color gradient of your choice. By default, this attribute/option is not set.
height This attribute/option specifies the height of the progressbar widget. By default, it is set to 50px.
padding This attribute/option specifies the padding to be used for the progressbar widget. By default, it is set to 5px.

Task Completion

HTML Source Code

<div class="dqu-example">
<table class="data">
    <tr>
        <th width="150px" >Attribute/Option</th>
        <th>Description</th>
    </tr>
    <tr>
        <td><code>background_color</code></td>
        <td>This attribute/option sets the background color of the progressbar. 
            By default, it is set to <code>"aliceblue"</code> color.
        </td>
        </tr>
    <tr>
        <td><code>indicator_color</code></td>
        <td>This attribute/option sets the progress indicator color. 
            By default, it is set to <code>"#2e5f7a"</code> (Deque teal).
        </td>
    </tr>
    <tr>
        <td><code>indicator_color_gradient</code></td>
        <td>This attribute/option sets the progress indicator as a color gradient of your choice. 
            By default, this attribute/option is not set.
        </td>
    </tr>
    <tr>
        <td><code>height</code></td>
        <td>This attribute/option specifies the height of the progressbar widget.
            By default, it is set to <code>50px</code>. 
        </td>
    </tr>
    <tr>
        <td><code>padding</code></td>
        <td>This attribute/option specifies the padding to be used for the progressbar widget.
            By default, it is set to <code>5px</code>. 
        </td>
    </tr>
</table>
<br/>
<h2 id="task_label">
    Task Completion 
</h2>
<div id="progressbar-container"></div>
<p>
    <button class="deque-button" id="start-progressbar">
        Start
    </button>
    <button class="deque-button" id="reset-progressbar">
        Reset
    </button>
</p>
</div>

JavaScript Source Code

var progressbar;
window.addEventListener('load', function () {
    var options = [];
    var div = document.getElementById("progressbar-container");
    fetch(translationFile)
    .then(response => {
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      return response.json();
    })
    .then(data => {
        langText = data;
        progressbar = new Progressbar(div, options);
        var startButton = document.getElementById("start-progressbar");
        startButton.addEventListener("click", startStopProgressbar);
        var resetButton = document.getElementById("reset-progressbar");
        resetButton.addEventListener("click", resetProgressbar);
    })
    .catch(error => {
      console.error("Error fetching or parsing JSON:", error);
    });
      
});

var intervalId = null;
var progressValue = 10;
function startStopProgressbar(event)
{
    var button = document.getElementById("start-progressbar");
    if( button.textContent == langText.stop)
    {
        button.textContent = langText.start;
        if(intervalId) clearInterval(intervalId);  
    }
    else
    {
        var simulateProgress = function ()
        {
            progressbar.setProgressValue(progressValue);
            if( progressValue >= 100)
                if(intervalId) clearInterval(intervalId); 
            progressValue += 10;
        };
        if(progressValue < 100 )
            intervalId = setInterval(simulateProgress, 1500);
        button.textContent = langText.stop;
    }    
}

function resetProgressbar()
{
    if(intervalId) clearInterval(intervalId);  
    var button = document.getElementById("start-progressbar");
    progressbar.setProgressValue(5);
    progressValue = 10;
    if( button.textContent == langText.stop)    
        button.textContent = langText.start;
    
}

class Progressbar {
constructor(node, options) {
  // Check whether node is a DOM element
  if (typeof node !== 'object') {
      console.log(langText["errorNodeNotObject"]);
      return;
  }
  if( (typeof node.nodeName === "undefined") || ( node.nodeName.toLowerCase() !== "div" ))
  {
      console.log(langText["errorNodeNotDivObject"]);
      return;
  }
  
  this.parentNode = node;
  if ( (typeof options == "undefined") || (!Array.isArray(options)) ) options = [];
  var indicatorColor = "#2e5f7a";
  if(typeof options["indicator_color"] === "string")
      indicatorColor = options["indicator_color"];
   var indicatorColorGradient = "";
   if(typeof options["indicator_color_gradient"] === "string")
      indicatorColorGradient = options["indicator_color_gradient"];
  var backgroundColor = "";
  if(typeof options["background_color"] === "string")
      backgroundColor = options["background_color"];
    
  var height = "50";
  if(typeof options["height"] === "number")
      height = options["height"];
    var padding = "5";
  if(typeof options["padding"] === "number")
      padding = options["padding"];
      
  
   var progressbar = document.createElement("progressbar");
   progressbar.classList.add("progressbar");
   progressbar.style.width = "calc(100%-10px)";
   progressbar.style.height = height+"px";
   progressbar.style.display = "block";
   progressbar.style.padding = padding + "px";
   progressbar.style.backgroundColor = backgroundColor;
   
   progressbar.setAttribute("min",0);
   progressbar.setAttribute("max",100);
   progressbar.setAttribute("value",5);
      
   this.parentNode.appendChild(progressbar);
   var indicator = document.createElement("rect");
   indicator.style.height = "100%";
   indicator.style.display = "inline-block";
   indicator.style.backgroundColor = indicatorColor;
   if(indicatorColorGradient != "")
        indicator.style.backgroundImage = indicatorColorGradient;
   progressbar.appendChild(indicator);
   this.indicator = indicator;
   this.progressbar = progressbar;
   
   var notifyElement = document.createElement("p");
    notifyElement.setAttribute("aria-live", "assertive");
    notifyElement.classList.add("visually-hidden");
    this.parentNode.appendChild(notifyElement);
    this.notifyElement = notifyElement;
    this.updateProgress();
    
  
}

updateProgress()
{
    var value = this.progressbar.getAttribute("value");
    var max = this.progressbar.getAttribute("max");
    var min = this.progressbar.getAttribute("min");
    var currentProgress = value *100 /( max - min);
    this.indicator.style.width = currentProgress + "%";
    this.announce(currentProgress+"%", 60  , 2000, "updateProgress");
   
}
setProgressValue(value)
{
    var max = this.progressbar.getAttribute("max");
    var min = this.progressbar.getAttribute("min");
    if( (value > max) || ( value < min))
    {
        if( value > max )
            console.log(langText["errorValueGreaterThanMax"]);
        if( value < min )
            console.log(langText["errorValueLessThanMin"]);
        return;   
    }
    this.progressbar.setAttribute("value", value);
    this.updateProgress();
}

announce(message, initialDelay, msgTime, scope)
{
    var self=this;    
    this.message = message;
    if(typeof scope == "undefined") scope = "unknown";
    if(typeof this.scopeIds == "undefined" ) this.scopeIds = [];
    if( typeof this.scopeIds[scope] == "number")
    {
        clearTimeout(this.scopeIds[scope]);
        this.scopeIds[scope] = null;
    }
            
    if (initialDelay > 1)
    {
        this.scopeIds[scope] = setTimeout(function() {
            self.notifyElement.innerHTML = self.message;
        }, initialDelay);    
    }    
    else
        this.notifyElement.innerHTML = this.message;
    
    
    setTimeout(function() {
        self.notifyElement.innerHTML ="";
        this.message = "";
        self.scopeIds[scope] = null;
    }, msgTime);

}

}  

CSS Source Code

/*
  Progress Bar (Bounded) — Restyled
  Deque University ARIA Component
*/

:root {
  --dqu-interactive: #2e5f7a;
  --dqu-interactive-hover: #3a7a9a;
  --dqu-interactive-light: rgba(46, 95, 122, 0.08);
  --dqu-bg-primary: #fcfaf8;
  --dqu-bg-secondary: #f6f3ed;
  --dqu-border-secondary: #8c827d;
  --dqu-text-primary: #21201e;
  --dqu-font-family: "Noto Sans", sans-serif;
}

/* Buttons */
button.deque-button {
  padding: 8px 14px !important;
  width: auto !important;
  font-family: var(--dqu-font-family) !important;
  font-size: 0.875rem !important;
  font-weight: 600 !important;
  margin: 4px !important;
  border-radius: 9999px !important;
  cursor: pointer !important;
  border: 1px solid var(--dqu-interactive) !important;
  background: #ffffff !important;
  color: var(--dqu-interactive) !important;
  box-sizing: border-box !important;
  transition: background-color 0.15s ease !important;
}

button.deque-button:hover {
  padding: 8px 14px !important;
  background-color: var(--dqu-interactive-light) !important;
  outline: 2px solid var(--dqu-interactive) !important;
  outline-offset: 2px;
}

button.deque-button:focus {
  outline: 3px solid var(--dqu-interactive) !important;
  outline-offset: 2px;
}

.visually-hidden {
  border: 0;
  clip: rect(0 0 0 0);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

/* Progressbar container — just a margin wrapper; visible track styling
   lives on .progressbar itself. */
#progressbar-container {
  margin: 12px 0;
}

/* Single visible track — 20px total height (border-box). Cream fill
   shows through where the indicator hasn't reached yet. Flex layout
   stretches the indicator child to fill the inner area. */
.progressbar {
  width: calc(100% - 10px);
  height: 20px !important;
  padding: 0 !important;
  box-sizing: border-box !important;
  display: flex !important;
  border: 1px solid var(--dqu-border-secondary);
  border-radius: 9999px;
  background: var(--dqu-bg-secondary);
  overflow: hidden;
}

/* Indicator (the <rect> child created by JS). Flex stretch sizes it
   to the full inner height; the JS-set width drives the progress
   fill animation. The indicator itself has square corners; the
   parent's border-radius + overflow:hidden clips the LEFT edge to
   match the rounded track, while the growing RIGHT edge stays
   flat (matching the unbounded variant). */
.progressbar > * {
  border-radius: 0;
  transition: width 0.3s ease;
  height: auto !important;
  align-self: stretch;
}

/* Data table */
table.data {
  border-collapse: collapse;
  font-family: var(--dqu-font-family);
  font-size: 0.9375rem;
  margin: 12px 0;
}

table.data th,
table.data td {
  border: 1px solid var(--dqu-border-secondary);
  padding: 10px 12px;
  text-align: left;
  color: var(--dqu-text-primary);
}

table.data th {
  background: var(--dqu-bg-secondary);
  font-weight: 600;
}

.dqu-example h2 {
  font-family: var(--dqu-font-family);
  color: var(--dqu-text-primary);
}

.dqu-example p {
  font-family: var(--dqu-font-family);
}

Copy and Paste Full Page Example