diff --git a/components/progress-bar-delay/progress-bar-delay.js b/components/progress-bar-delay/progress-bar-delay.js
index 7db6b29858e39aa57b6c60abbe3583aa12658951..3864337cc0f0e29bb940a072aa81a0ab4299dd34 100644
--- a/components/progress-bar-delay/progress-bar-delay.js
+++ b/components/progress-bar-delay/progress-bar-delay.js
@@ -12,14 +12,37 @@
 
   'use strict';
 
+  /**
+   * Represents a progress bar delay wrapper.
+   */
   class ProgressBarDelay {
 
+    /**
+     * Time in milliseconds before the progress bar is shown.
+     *
+     * @type {Number}
+     */
     #delay = 0;
 
+    /**
+     * The visit timeout ID, if any.
+     *
+     * @type {Number|null}
+     */
     #visitTimeout = null;
 
+    /**
+     * The form submit timeout ID, if any.
+     *
+     * @type {Number|null}
+     */
     #formTimeout = null;
 
+    /**
+     * The progress bar instance that we're wrapping.
+     *
+     * @type {ProgressBar}
+     */
     #progressBar;
 
     constructor(delay, progressBar) {
@@ -30,6 +53,15 @@
 
     }
 
+    /**
+     * Start the timeout to show the progress bar.
+     *
+     * @param {Number|null} timeoutId
+     *   An existing timeout ID, if any.
+     *
+     * @return {Number}
+     *   A new timeout ID.
+     */
     showAfterDelay(timeoutId) {
 
       if (timeoutId !== null) {
@@ -44,6 +76,13 @@
 
     }
 
+    /**
+     * Hide the progress bar and cancel an existing timeout, if any.
+     *
+     * @param {Number|null} timeoutId
+     *
+     * @return {null}
+     */
     hideAndCancelDelay(timeoutId) {
 
       if (timeoutId === null) {
@@ -62,18 +101,27 @@
 
     }
 
+    /**
+     * Show the progress bar after a delay for non-form submission visits.
+     */
     showVisitAfterDelay() {
 
       this.#visitTimeout = this.showAfterDelay(this.#visitTimeout);
 
     }
 
+    /**
+     * Hide the progress bar for non-form submission visits.
+     */
     hideVisit() {
 
       this.#visitTimeout = this.hideAndCancelDelay(this.#visitTimeout);
 
     }
 
+    /**
+     * Show the progress bar after a delay for form submissions.
+     */
     showFormAfterDelay() {
 
       // Unlike the visit progress bar, we prefer to not replace an existing
@@ -89,12 +137,20 @@
 
     }
 
+    /**
+     * Hide the progress bar for form submissions.
+     */
     hideForm() {
 
       this.#formTimeout = this.hideAndCancelDelay(this.#formTimeout);
 
     }
 
+    /**
+     * Get the progress bar instance we're wrapping.
+     *
+     * @return {ProgressBar}
+     */
     get progressBar() {
       return this.#progressBar;
     }
diff --git a/components/progress-bar/progress-bar.css b/components/progress-bar/progress-bar.css
index ebb39c299ccf2f0e36e6102cab5f9854fc86e613..785582b9a3f64f18e27910debb04dba3b0c3cdeb 100644
--- a/components/progress-bar/progress-bar.css
+++ b/components/progress-bar/progress-bar.css
@@ -1,24 +1,57 @@
 :root {
 
-  /* Drupal blue. */
+  /**
+   * The progress bar colour.
+   *
+   * Defaults to Drupal blue.
+   *
+   * @type {Color}
+   */
   --refreshless-progress-bar-colour: #0678be;
 
+  /**
+   * The progress bar thickness.
+   *
+   * @type {Number}
+   */
   --refreshless-progress-bar-thickness: 3px;
 
-  /* Themes should override this to a more sensible value. */
+  /**
+   * The progress bar z-index.
+   *
+   * Themes should override this to a more sensible value.
+   *
+   * @type {Number}
+   */
   --refreshless-progress-bar-z-index: 2147483647;
 
+  /**
+   * The progress bar minimum/start value.
+   *
+   * This should be a percentage.
+   *
+   * @type {Number}
+   */
   --refreshless-progress-bar-start: 10%;
 
+  /**
+   * Progress bar width transition.
+   */
   --refreshless-progress-bar-width-transition:
     width var(--refreshless-progress-bar-transition-duration) ease-out;
 
+  /**
+   * Progress bar opacity transition out.
+   */
   --refreshless-progress-bar-opacity-transition-out:
     opacity
     calc(var(--refreshless-progress-bar-transition-duration) / 2)
     calc(var(--refreshless-progress-bar-transition-duration) / 2)
     ease-in;
 
+  /**
+   * Progress bar opacity transition in.
+   */
   --refreshless-progress-bar-opacity-transition-in:
     opacity
     calc(var(--refreshless-progress-bar-transition-duration) / 2)
diff --git a/components/progress-bar/progress-bar.js b/components/progress-bar/progress-bar.js
index 2c7ed8a36b6159696b5fcba81e7e67761c124d9c..b00862f6a469c4a0d638c82380eec0bd0392d1d2 100644
--- a/components/progress-bar/progress-bar.js
+++ b/components/progress-bar/progress-bar.js
@@ -29,35 +29,92 @@
 
   }
 
+  /**
+   * CSS custom property base name.
+   *
+   * @type {String}
+   */
   const customPropertyBase = '--refreshless-progress-bar';
 
+  /**
+   * Name of the CSS custom property containing the transition duration.
+   *
+   * The value will be a time in ms.
+   *
+   * @type {String}
+   */
   const durationCustomProperty = `${customPropertyBase}-transition-duration`;
 
+  /**
+   * Name of the CSS custom property containing the progress bar delay.
+   *
+   * The value will be a time in ms.
+   *
+   * @type {String}
+   */
   const delayCustomProperty = `${customPropertyBase}-delay`;
 
+  /**
+   * Name of the CSS custom property containing the current progress bar value.
+   *
+   * The value will be float between 0 and 1, inclusive.
+   *
+   * @type {String}
+   */
   const valueCustomProperty = `${customPropertyBase}-value`;
 
-  const html = document.documentElement;
-
   /**
-   * The once() identifier for attaching the progress bar.
+   * The <html> element.
    *
-   * @type {String}
+   * @type {HTMLHtmlElement}
    */
-  const onceName = 'refreshless-turbo-progress-bar';
+  const html = document.documentElement;
 
+  /**
+   * Represents a progress bar.
+   */
   class ProgressBar {
 
+    /**
+     * The progress bar HTML element.
+     *
+     * @type {HTMLElement}
+     */
     #element;
 
+    /**
+     * Whether the progress bar is currently in the processing of hiding.
+     *
+     * @type {Boolean}
+     */
     #hiding = false;
 
+    /**
+     * The current value of the progress bar, from 0 to 1, inclusive.
+     *
+     * @type {Number}
+     */
     #value = 0;
 
+    /**
+     * Whether the progress bar is currently visible.
+     *
+     * @type {Boolean}
+     */
     #visible = false;
 
+    /**
+     * The trickle interval ID, or null if one is not active.
+     *
+     * @type {Number|null}
+     */
     #trickleInterval = null;
 
+    /**
+     * The progress bar transition duration, in milliseconds.
+     *
+     * @type {Number}
+     */
     #transitionDuration = 300;
 
     constructor() {
@@ -68,6 +125,9 @@
 
     }
 
+    /**
+     * Show the progress bar if not already visible.
+     */
     show() {
 
       if (this.#visible === true) {
@@ -80,6 +140,9 @@
 
     }
 
+    /**
+     * Hide the progress bar if visible and not already hiding.
+     */
     hide() {
 
       if (!(this.#visible === true && this.#hiding === false)) {
@@ -100,6 +163,9 @@
 
     }
 
+    /**
+     * Set the progress bar as active, causing CSS to transition it in.
+     */
     #setActive() {
 
       this.#element.classList.add(
@@ -108,6 +174,9 @@
 
     }
 
+    /**
+     * Set the progress bar as inactive, causing CSS to transition it out.
+     */
     #setInactive() {
 
       this.#element.classList.remove(
@@ -116,6 +185,12 @@
 
     }
 
+    /**
+     * Transition the progress bar out.
+     *
+     * @return {Promise}
+     *   A Promise that resolves when the transition is considered complete.
+     */
     #transitionOut() {
 
       this.#setInactive();
@@ -128,6 +203,15 @@
 
     }
 
+    /**
+     * Explicitly set the progress bar to a value.
+     *
+     * @param {Number} value
+     *   A number between 0 and 1, inclusive.
+     *
+     * @throws If value is NaN, or if the value is less than 0 or greater than
+     *   1.
+     */
     setValue(value) {
 
       if (Number.isNaN(value)) {
@@ -144,6 +228,9 @@
 
     }
 
+    /**
+     * Install the progress bar in the document and set various properties.
+     */
     install() {
 
       html.style.setProperty(
@@ -170,6 +257,9 @@
 
     }
 
+    /**
+     * Uninstall the progress bar from the document and remove properties.
+     */
     uninstall() {
 
       if (this.#element.parentNode) {
@@ -182,6 +272,9 @@
 
     }
 
+    /**
+     * Start the trickling animation.
+     */
     startTrickling() {
 
       if (this.#trickleInterval !== null) {
@@ -194,6 +287,9 @@
 
     }
 
+    /**
+     * Stop the trickling animation.
+     */
     stopTrickling() {
 
       window.clearInterval(this.#trickleInterval);
@@ -202,10 +298,18 @@
 
     }
 
+    /**
+     * Trickle animation interval callback.
+     *
+     * This generates a random value to give the trickle the irregular movement.
+     */
     trickle = () => {
       this.setValue(Math.min(1, this.#value + Math.random() / 100));
     }
 
+    /**
+     * Update the progress bar element's value with the current value.
+     */
     async refresh() {
 
       await new Promise(requestAnimationFrame);
@@ -214,6 +318,11 @@
 
     }
 
+    /**
+     * Finish/complete the progress bar to 100% and start hiding it.
+     *
+     * @return {[type]} [description]
+     */
     finish() {
 
       this.setValue(1);
@@ -222,14 +331,29 @@
 
     }
 
+    /**
+     * Get the current value of the progress bar.
+     *
+     * @return {Number}
+     */
     get value() {
       return this.#value;
     }
 
+    /**
+     * Get the progress bar HTML element.
+     *
+     * @return {HTMLElement}
+     */
     get element() {
       return this.#element;
     }
 
+    /**
+     * Get the progress bar transition value.
+     *
+     * @return {Number}
+     */
     get transitionDuration() {
       return this.#transitionDuration;
     }