From a2b876d7c54e95549a809fd551804dc5b6b18e06 Mon Sep 17 00:00:00 2001 From: Howard Edwards Date: Mon, 28 Jul 2025 10:19:17 -0400 Subject: [PATCH 1/5] Adds read-this-first.js --- content/shared/js/read-this-first.js | 170 +++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 content/shared/js/read-this-first.js diff --git a/content/shared/js/read-this-first.js b/content/shared/js/read-this-first.js new file mode 100644 index 0000000000..4f19b655b5 --- /dev/null +++ b/content/shared/js/read-this-first.js @@ -0,0 +1,170 @@ +'use strict'; + +/** + * Read This First Banner + * + * Inserts the "Read This First" banner from /content/shared/templates/read-this-first.html into + * pages after the h1 element when the DOM is loaded. The banner is configured using data + * attributes on the script element. + * + * USAGE: + * Add this script to your HTML page with the appropriate data attributes: + * + * + * + * CONFIGURATION OPTIONS: + * - showImage: boolean (default: true) - Controls whether the illustration image is displayed + * + * REQUIREMENTS: + * - Page must have an

element (banner is inserted after it) + * - Script automatically adjusts paths based on where it's called from + * - Works with both template file fetch and fallback banner + * + * BEHAVIOR: + * - Banner is inserted after the h1 element when DOM is loaded + * - If template file can't be fetched (e.g., CORS issues with file:// protocol), uses fallback + * - Paths are automatically adjusted based on script location + * - Image can be conditionally removed based on showImage setting + */ +(function () { + const defaultConfig = { + showImage: true, + }; + + // NOTE: If read-this-first.html is ever changed, update this fallback banner to match + // MUST HAVE `div class="read-this-first"` + const fallbackBanner = ` +
+
+ Illustration of a brown-skinned woman with a slight smile gesturing towards the right with her hand +

Read This First

+

+ No ARIA is better than Bad ARIA. Before using any ARIA, read this to understand why. +

+
+
+ `; + + function getScriptBasePath() { + const scriptElement = document.querySelector( + 'script[src*="read-this-first.js"]' + ); + if (!scriptElement) { + return '../../'; // Default fallback + } + + const scriptSrc = scriptElement.getAttribute('src'); + // Extract the directory path from the script src + // e.g., "../../shared/js/read-this-first.js" gives "../../" + const match = scriptSrc.match(/^(.*\/)shared\/js\/read-this-first\.js$/); + return match ? match[1] : '../../'; + } + + function adjustPaths(html, basePath) { + return html + .replace(/src="\.\.\/\.\.\//g, `src="${basePath}`) + .replace(/href="\.\.\/\.\.\//g, `href="${basePath}`); + } + + function parseConfigFromDataAttribute() { + const config = { ...defaultConfig }; + const configElem = document.querySelector('[data-read-this-first]'); + + if (configElem) { + const dataValue = configElem.getAttribute('data-read-this-first'); + if (dataValue) { + const values = dataValue.split(';'); + values.forEach((v) => { + let [prop, value] = v.split(':'); + if (prop) { + prop = prop.trim(); + } + if (value) { + value = value.trim(); + } + if (prop && value) { + // Convert string values to appropriate types + if (value === 'true' || value === 'false') { + config[prop] = value === 'true'; + } else { + config[prop] = value; + } + } + }); + } + } + + return config; + } + + function removeImageIfNeeded(bannerElement, config) { + if (!config.showImage) { + const img = bannerElement.querySelector('img'); + if (img) { + img.remove(); + } + } + } + + async function insertBanner(config) { + // Find the h1 element + const h1 = document.querySelector('h1'); + if (!h1) { + return; + } + + // Get the base path for relative URLs + const basePath = getScriptBasePath(); + + try { + // Fetch the banner HTML from the template file + const response = await fetch( + `${basePath}shared/templates/read-this-first.html` + ); + const html = await response.text(); + + // Parse the HTML and extract the read-this-first div + const parser = new DOMParser(); + const doc = parser.parseFromString(html, 'text/html'); + const bannerDiv = doc.querySelector('.read-this-first'); + + if (!bannerDiv) { + return; + } + + const bannerElement = bannerDiv.cloneNode(true); + removeImageIfNeeded(bannerElement, config); + + // Insert the banner after h1 + h1.parentNode.insertBefore(bannerElement, h1.nextSibling); + } catch (error) { + // Fallback to static banner if fetch fails (CORS will fail with file:// protocol) + const temp = document.createElement('div'); + // Adjust paths in the fallback banner based on script location + const adjustedFallbackBanner = adjustPaths(fallbackBanner, basePath); + temp.innerHTML = adjustedFallbackBanner; + const fallbackBannerElement = temp.firstElementChild; + removeImageIfNeeded(fallbackBannerElement, config); + + // Insert the banner after h1 + h1.parentNode.insertBefore(fallbackBannerElement, h1.nextSibling); + } + } + + async function init() { + const config = parseConfigFromDataAttribute(); + await insertBanner(config); + } + + // Initialize on DOMContentLoaded + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + // DOM is already loaded + init(); + } +})(); From e7b731b95db36da69d0dcfd4a976307191895e23 Mon Sep 17 00:00:00 2001 From: Howard Edwards Date: Mon, 28 Jul 2025 10:24:40 -0400 Subject: [PATCH 2/5] Update all content files with read-this-first.js script call --- content/patterns/accordion/accordion-pattern.html | 1 + content/patterns/alert/alert-pattern.html | 1 + content/patterns/alertdialog/alertdialog-pattern.html | 1 + content/patterns/breadcrumb/breadcrumb-pattern.html | 1 + content/patterns/button/button-pattern.html | 1 + content/patterns/carousel/carousel-pattern.html | 1 + content/patterns/checkbox/checkbox-pattern.html | 1 + content/patterns/combobox/combobox-pattern.html | 1 + content/patterns/dialog-modal/dialog-modal-pattern.html | 1 + content/patterns/disclosure/disclosure-pattern.html | 1 + content/patterns/feed/feed-pattern.html | 1 + content/patterns/grid/grid-pattern.html | 1 + content/patterns/landmarks/landmarks-pattern.html | 1 + content/patterns/link/link-pattern.html | 1 + content/patterns/listbox/listbox-pattern.html | 1 + content/patterns/menu-button/menu-button-pattern.html | 1 + content/patterns/menubar/menu-and-menubar-pattern.html | 1 + content/patterns/meter/meter-pattern.html | 1 + content/patterns/patterns.html | 1 + content/patterns/radio/radio-group-pattern.html | 1 + .../patterns/slider-multithumb/slider-multithumb-pattern.html | 1 + content/patterns/slider/slider-pattern.html | 1 + content/patterns/spinbutton/spinbutton-pattern.html | 1 + content/patterns/switch/switch-pattern.html | 1 + content/patterns/table/table-pattern.html | 1 + content/patterns/tabs/tabs-pattern.html | 1 + content/patterns/toolbar/toolbar-pattern.html | 1 + content/patterns/tooltip/tooltip-pattern.html | 1 + content/patterns/treegrid/treegrid-pattern.html | 2 ++ content/patterns/treeview/treeview-pattern.html | 1 + content/patterns/windowsplitter/windowsplitter-pattern.html | 1 + content/practices/practices.html | 1 + 32 files changed, 33 insertions(+) diff --git a/content/patterns/accordion/accordion-pattern.html b/content/patterns/accordion/accordion-pattern.html index 5e53b1158a..9704964d03 100644 --- a/content/patterns/accordion/accordion-pattern.html +++ b/content/patterns/accordion/accordion-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/alert/alert-pattern.html b/content/patterns/alert/alert-pattern.html index 125e2e017d..883b9d2b53 100644 --- a/content/patterns/alert/alert-pattern.html +++ b/content/patterns/alert/alert-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/alertdialog/alertdialog-pattern.html b/content/patterns/alertdialog/alertdialog-pattern.html index 0cfa66ee6e..f81bb1b2cd 100644 --- a/content/patterns/alertdialog/alertdialog-pattern.html +++ b/content/patterns/alertdialog/alertdialog-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/breadcrumb/breadcrumb-pattern.html b/content/patterns/breadcrumb/breadcrumb-pattern.html index 3cb30aa572..3558a3b9cb 100644 --- a/content/patterns/breadcrumb/breadcrumb-pattern.html +++ b/content/patterns/breadcrumb/breadcrumb-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/button/button-pattern.html b/content/patterns/button/button-pattern.html index 380085ce81..d91bb47130 100644 --- a/content/patterns/button/button-pattern.html +++ b/content/patterns/button/button-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/carousel/carousel-pattern.html b/content/patterns/carousel/carousel-pattern.html index a9c17cb2f4..c4e7078b37 100644 --- a/content/patterns/carousel/carousel-pattern.html +++ b/content/patterns/carousel/carousel-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/checkbox/checkbox-pattern.html b/content/patterns/checkbox/checkbox-pattern.html index f8477d0fb7..faef05602d 100644 --- a/content/patterns/checkbox/checkbox-pattern.html +++ b/content/patterns/checkbox/checkbox-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/combobox/combobox-pattern.html b/content/patterns/combobox/combobox-pattern.html index ec5e2d4bab..80bcb7dd57 100644 --- a/content/patterns/combobox/combobox-pattern.html +++ b/content/patterns/combobox/combobox-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/dialog-modal/dialog-modal-pattern.html b/content/patterns/dialog-modal/dialog-modal-pattern.html index ba37954138..09d8181eec 100644 --- a/content/patterns/dialog-modal/dialog-modal-pattern.html +++ b/content/patterns/dialog-modal/dialog-modal-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/disclosure/disclosure-pattern.html b/content/patterns/disclosure/disclosure-pattern.html index b30eb449f1..b553e9c58f 100644 --- a/content/patterns/disclosure/disclosure-pattern.html +++ b/content/patterns/disclosure/disclosure-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/feed/feed-pattern.html b/content/patterns/feed/feed-pattern.html index b4bcc0d1d1..9e9edd9b72 100644 --- a/content/patterns/feed/feed-pattern.html +++ b/content/patterns/feed/feed-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/grid/grid-pattern.html b/content/patterns/grid/grid-pattern.html index eec44961f9..ba34e00bba 100644 --- a/content/patterns/grid/grid-pattern.html +++ b/content/patterns/grid/grid-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/landmarks/landmarks-pattern.html b/content/patterns/landmarks/landmarks-pattern.html index c020b5ca08..c262af86c7 100644 --- a/content/patterns/landmarks/landmarks-pattern.html +++ b/content/patterns/landmarks/landmarks-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/link/link-pattern.html b/content/patterns/link/link-pattern.html index 21d92f9e89..dcbec34763 100644 --- a/content/patterns/link/link-pattern.html +++ b/content/patterns/link/link-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/listbox/listbox-pattern.html b/content/patterns/listbox/listbox-pattern.html index f17aab71bf..99a7798713 100644 --- a/content/patterns/listbox/listbox-pattern.html +++ b/content/patterns/listbox/listbox-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/menu-button/menu-button-pattern.html b/content/patterns/menu-button/menu-button-pattern.html index de0e4b42b0..84f47e1be5 100644 --- a/content/patterns/menu-button/menu-button-pattern.html +++ b/content/patterns/menu-button/menu-button-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/menubar/menu-and-menubar-pattern.html b/content/patterns/menubar/menu-and-menubar-pattern.html index c8af7ff04c..a5eebc5f5c 100644 --- a/content/patterns/menubar/menu-and-menubar-pattern.html +++ b/content/patterns/menubar/menu-and-menubar-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/meter/meter-pattern.html b/content/patterns/meter/meter-pattern.html index 6830ff3434..91781b3225 100644 --- a/content/patterns/meter/meter-pattern.html +++ b/content/patterns/meter/meter-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/patterns.html b/content/patterns/patterns.html index 88689af939..fae5214867 100644 --- a/content/patterns/patterns.html +++ b/content/patterns/patterns.html @@ -13,6 +13,7 @@ +

Patterns

diff --git a/content/patterns/radio/radio-group-pattern.html b/content/patterns/radio/radio-group-pattern.html index bb451c98e1..d1ac151271 100644 --- a/content/patterns/radio/radio-group-pattern.html +++ b/content/patterns/radio/radio-group-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/slider-multithumb/slider-multithumb-pattern.html b/content/patterns/slider-multithumb/slider-multithumb-pattern.html index 070f8b6f5b..72a653efa5 100644 --- a/content/patterns/slider-multithumb/slider-multithumb-pattern.html +++ b/content/patterns/slider-multithumb/slider-multithumb-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/slider/slider-pattern.html b/content/patterns/slider/slider-pattern.html index e57ccfea49..e59a8dbcc0 100644 --- a/content/patterns/slider/slider-pattern.html +++ b/content/patterns/slider/slider-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/spinbutton/spinbutton-pattern.html b/content/patterns/spinbutton/spinbutton-pattern.html index 58f1f3e052..047219cd4a 100644 --- a/content/patterns/spinbutton/spinbutton-pattern.html +++ b/content/patterns/spinbutton/spinbutton-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/switch/switch-pattern.html b/content/patterns/switch/switch-pattern.html index 96c291f78f..0f88576bcc 100644 --- a/content/patterns/switch/switch-pattern.html +++ b/content/patterns/switch/switch-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/table/table-pattern.html b/content/patterns/table/table-pattern.html index 9902055d57..35fe48c18a 100644 --- a/content/patterns/table/table-pattern.html +++ b/content/patterns/table/table-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/tabs/tabs-pattern.html b/content/patterns/tabs/tabs-pattern.html index c32132b334..56222ff2a0 100644 --- a/content/patterns/tabs/tabs-pattern.html +++ b/content/patterns/tabs/tabs-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/toolbar/toolbar-pattern.html b/content/patterns/toolbar/toolbar-pattern.html index 97b8bd832d..81c0d1d789 100644 --- a/content/patterns/toolbar/toolbar-pattern.html +++ b/content/patterns/toolbar/toolbar-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/tooltip/tooltip-pattern.html b/content/patterns/tooltip/tooltip-pattern.html index e89c741093..547184a0e3 100644 --- a/content/patterns/tooltip/tooltip-pattern.html +++ b/content/patterns/tooltip/tooltip-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/treegrid/treegrid-pattern.html b/content/patterns/treegrid/treegrid-pattern.html index 0b438b68e7..bcdc41b9eb 100644 --- a/content/patterns/treegrid/treegrid-pattern.html +++ b/content/patterns/treegrid/treegrid-pattern.html @@ -11,6 +11,8 @@ + +
diff --git a/content/patterns/treeview/treeview-pattern.html b/content/patterns/treeview/treeview-pattern.html index 8eaff4c7e1..223e56bc7e 100644 --- a/content/patterns/treeview/treeview-pattern.html +++ b/content/patterns/treeview/treeview-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/patterns/windowsplitter/windowsplitter-pattern.html b/content/patterns/windowsplitter/windowsplitter-pattern.html index 8d78e89e6d..5ada78bcd6 100644 --- a/content/patterns/windowsplitter/windowsplitter-pattern.html +++ b/content/patterns/windowsplitter/windowsplitter-pattern.html @@ -11,6 +11,7 @@ +
diff --git a/content/practices/practices.html b/content/practices/practices.html index 3f31e1ce80..560b56d89c 100644 --- a/content/practices/practices.html +++ b/content/practices/practices.html @@ -12,6 +12,7 @@ +

Practices

From d6654bad06eb9f84926464cdcb7018978797aaf9 Mon Sep 17 00:00:00 2001 From: Howard Edwards Date: Wed, 30 Jul 2025 13:06:59 -0400 Subject: [PATCH 3/5] Update comments and docs for read-this-first.js --- content/shared/js/read-this-first.js | 49 +++++++++------------------- 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/content/shared/js/read-this-first.js b/content/shared/js/read-this-first.js index 4f19b655b5..ac4dfa0887 100644 --- a/content/shared/js/read-this-first.js +++ b/content/shared/js/read-this-first.js @@ -1,8 +1,6 @@ 'use strict'; /** - * Read This First Banner - * * Inserts the "Read This First" banner from /content/shared/templates/read-this-first.html into * pages after the h1 element when the DOM is loaded. The banner is configured using data * attributes on the script element. @@ -15,11 +13,6 @@ * CONFIGURATION OPTIONS: * - showImage: boolean (default: true) - Controls whether the illustration image is displayed * - * REQUIREMENTS: - * - Page must have an

element (banner is inserted after it) - * - Script automatically adjusts paths based on where it's called from - * - Works with both template file fetch and fallback banner - * * BEHAVIOR: * - Banner is inserted after the h1 element when DOM is loaded * - If template file can't be fetched (e.g., CORS issues with file:// protocol), uses fallback @@ -31,7 +24,7 @@ showImage: true, }; - // NOTE: If read-this-first.html is ever changed, update this fallback banner to match + // NOTE: If /content/shared/templates/read-this-first.html is ever changed, update this fallback banner to match // MUST HAVE `div class="read-this-first"` const fallbackBanner = `
@@ -53,9 +46,7 @@ const scriptElement = document.querySelector( 'script[src*="read-this-first.js"]' ); - if (!scriptElement) { - return '../../'; // Default fallback - } + if (!scriptElement) return '../../'; const scriptSrc = scriptElement.getAttribute('src'); // Extract the directory path from the script src @@ -64,12 +55,6 @@ return match ? match[1] : '../../'; } - function adjustPaths(html, basePath) { - return html - .replace(/src="\.\.\/\.\.\//g, `src="${basePath}`) - .replace(/href="\.\.\/\.\.\//g, `href="${basePath}`); - } - function parseConfigFromDataAttribute() { const config = { ...defaultConfig }; const configElem = document.querySelector('[data-read-this-first]'); @@ -104,37 +89,31 @@ function removeImageIfNeeded(bannerElement, config) { if (!config.showImage) { const img = bannerElement.querySelector('img'); - if (img) { - img.remove(); - } + if (img) img.remove(); } } async function insertBanner(config) { - // Find the h1 element + // Get the first found h1 element on page const h1 = document.querySelector('h1'); - if (!h1) { - return; - } + if (!h1) return; // Get the base path for relative URLs const basePath = getScriptBasePath(); try { - // Fetch the banner HTML from the template file + // Fetch the banner HTML from the template file (will fail with file:// protocol) const response = await fetch( `${basePath}shared/templates/read-this-first.html` ); const html = await response.text(); - // Parse the HTML and extract the read-this-first div const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); + // Get the read-this-first div const bannerDiv = doc.querySelector('.read-this-first'); - if (!bannerDiv) { - return; - } + if (!bannerDiv) return; const bannerElement = bannerDiv.cloneNode(true); removeImageIfNeeded(bannerElement, config); @@ -143,11 +122,13 @@ h1.parentNode.insertBefore(bannerElement, h1.nextSibling); } catch (error) { // Fallback to static banner if fetch fails (CORS will fail with file:// protocol) - const temp = document.createElement('div'); + const tempBannerDiv = document.createElement('div'); // Adjust paths in the fallback banner based on script location - const adjustedFallbackBanner = adjustPaths(fallbackBanner, basePath); - temp.innerHTML = adjustedFallbackBanner; - const fallbackBannerElement = temp.firstElementChild; + tempBannerDiv.innerHTML = fallbackBanner + .replace(/src="\.\.\/\.\.\//g, `src="${basePath}`) + .replace(/href="\.\.\/\.\.\//g, `href="${basePath}`); + + const fallbackBannerElement = tempBannerDiv.firstElementChild; removeImageIfNeeded(fallbackBannerElement, config); // Insert the banner after h1 @@ -160,8 +141,8 @@ await insertBanner(config); } - // Initialize on DOMContentLoaded if (document.readyState === 'loading') { + // Initialize on DOMContentLoaded document.addEventListener('DOMContentLoaded', init); } else { // DOM is already loaded From 805371959f5ade52f1dcb6cd83c8aec31e722ac2 Mon Sep 17 00:00:00 2001 From: Howard Edwards Date: Wed, 30 Jul 2025 14:51:15 -0400 Subject: [PATCH 4/5] Additional comment --- content/shared/templates/read-this-first.html | 1 + 1 file changed, 1 insertion(+) diff --git a/content/shared/templates/read-this-first.html b/content/shared/templates/read-this-first.html index 348700ae25..7879da749e 100644 --- a/content/shared/templates/read-this-first.html +++ b/content/shared/templates/read-this-first.html @@ -3,6 +3,7 @@ Read This First (Template) +
Date: Tue, 19 Aug 2025 12:55:39 -0400 Subject: [PATCH 5/5] Remove duplicate read-this-first.js script from treegrid-pattern head --- content/patterns/treegrid/treegrid-pattern.html | 1 - 1 file changed, 1 deletion(-) diff --git a/content/patterns/treegrid/treegrid-pattern.html b/content/patterns/treegrid/treegrid-pattern.html index bcdc41b9eb..ed2a96f905 100644 --- a/content/patterns/treegrid/treegrid-pattern.html +++ b/content/patterns/treegrid/treegrid-pattern.html @@ -12,7 +12,6 @@ -