From 8cf61993fb2986aab25756d0f7cf67cab97ffddc Mon Sep 17 00:00:00 2001
From: abdishakoor-dev <294276791+abdishakoor-dev@users.noreply.github.com>
Date: Wed, 1 Jul 2026 08:16:00 +0100
Subject: [PATCH] Add Auditing changes to the DOM prep block and Snapshot mode
workshop
Refs #1926, #1929
---
.../en/module/js2/auditing-the-dom/index.md | 35 ++++
.../workshops/accessibility-snapshot/index.md | 62 ++++++
.../itp/data-groups/sprints/3/prep/index.md | 3 +
.../accessibility-snapshot/assets/app.js | 84 ++++++++
.../accessibility-snapshot/assets/styles.css | 190 ++++++++++++++++++
.../accessibility-snapshot/index.html | 144 +++++++++++++
6 files changed, 518 insertions(+)
create mode 100644 common-content/en/module/js2/auditing-the-dom/index.md
create mode 100644 common-content/en/workshops/accessibility-snapshot/index.md
create mode 100644 org-cyf/static/workshops/accessibility-snapshot/assets/app.js
create mode 100644 org-cyf/static/workshops/accessibility-snapshot/assets/styles.css
create mode 100644 org-cyf/static/workshops/accessibility-snapshot/index.html
diff --git a/common-content/en/module/js2/auditing-the-dom/index.md b/common-content/en/module/js2/auditing-the-dom/index.md
new file mode 100644
index 000000000..1e01faade
--- /dev/null
+++ b/common-content/en/module/js2/auditing-the-dom/index.md
@@ -0,0 +1,35 @@
++++
+title = 'Auditing changes to the DOM'
+
+time = 15
+[objectives]
+ 1='Explain how changing the DOM can introduce problems that were not there when the page loaded'
+ 2='Use Lighthouse Snapshot mode to audit a page in its changed state'
+[build]
+ render = 'never'
+ list = 'local'
+ publishResources = false
+
++++
+
+You have just been changing pages by updating the DOM: adding elements, changing text, and reacting to events. Whenever we change a page after it has loaded, we can introduce problems that were not there when it first loaded. Some are accessibility problems, like text with not enough colour contrast. Others might be a broken layout or a slower page.
+
+So far we have only audited a page as it loads. But once we update the interface using JavaScript, the page changes, so we need to **check it in its changed state too**.
+
+### Lighthouse audit
+
+You have already used **Lighthouse** to audit pages. Lighthouse is an automated tool built into Chrome that checks a page for problems with accessibility, performance, best practices, and more. It is a natural tool to reach for here too. But there is a catch.
+
+When you run Lighthouse the usual way, it **reloads the page** and audits it as it first appears. This is called **Navigation mode**. It is great for checking the page you start with, but it cannot see any of the changes our JavaScript makes _after_ the user starts interacting with the page, such as clicking a button.
+
+In other words, a normal audit checks the page _before_ the user has interacted with it. Such problems may only appear _after_ the user interacts with the page, so a normal audit can miss them.
+
+### Snapshot mode
+
+Lighthouse has a **Snapshot mode** that audits the page **in its current state**, without reloading the page, so it sees everything your JavaScript added and can catch problems that appear after the page changes. You can read more in the [Lighthouse documentation](https://developer.chrome.com/docs/lighthouse/overview#modes).
+
+### Try it yourself
+
+Open the [Accessibility and the DOM demo page](/workshops/accessibility-snapshot) and follow the steps on the page. You will run a Lighthouse audit in Navigation mode, then add some elements and run a Snapshot audit, and compare what each one finds.
+
+The key takeaway: whenever your JavaScript changes the page, remember to test the page **after** those changes with Lighthouse's Snapshot mode, not just when it loads.
diff --git a/common-content/en/workshops/accessibility-snapshot/index.md b/common-content/en/workshops/accessibility-snapshot/index.md
new file mode 100644
index 000000000..185f4e4c2
--- /dev/null
+++ b/common-content/en/workshops/accessibility-snapshot/index.md
@@ -0,0 +1,62 @@
++++
+title = 'Accessibility and the DOM'
+[build]
+ render = 'link'
+ list = 'local'
+ publishResources = false
++++
+
+# Accessibility & the DOM
+
+A short demo of how changing the DOM can change the accessibility of a page, and
+how to find those problems with Lighthouse **Snapshot mode**.
+
+## Learning Objectives
+
+```objectives
+- [ ] Explain how changing the DOM can change the accessibility of a page
+- [ ] Find accessibility problems that only appear after user interaction
+- [ ] Use Lighthouse Snapshot mode to audit a page after it has changed
+```
+
+## Requirements
+
+For this demo you will need:
+
+- [ ] [Chrome](https://www.google.com/intl/en_uk/chrome/) with Dev Tools
+- [ ] This [demo page](/workshops/accessibility-snapshot)
+
+The page is fully accessible when it first loads. Each time you press **Add an
+element**, JavaScript adds a new element to the page. Every new element renders
+fine, but each one has a different accessibility problem:
+
+- text with not enough colour contrast
+- an image with no alt text
+- a link with no accessible name
+- a button with no accessible name
+
+A normal Lighthouse audit runs in **Navigation mode**: it reloads the page and
+checks it as it first appears. At that point the page is empty, so it finds
+nothing. The problems only appear _after_ you interact with the page, so a
+normal audit misses them. **Snapshot mode** audits the page in its current
+state, without reloading it, so it can catch the problems your clicks
+introduced.
+
+### Try it
+
+1. Open the [demo page](/workshops/accessibility-snapshot) in Chrome.
+2. Open Dev Tools and go to the **Lighthouse** panel.
+3. With **Mode** set to **Navigation** (the default), click **Analyze page load** and wait for the report. The **Accessibility** section should report no problems, because the page is fine when it first loads.
+4. Press the **Add an element** button until it is disabled. The page has now changed, but your Navigation report still shows the page as it was when it loaded.
+5. To run another audit, first start a new report by clicking the **+** button at the top of the Lighthouse panel. Then change **Mode** to **Snapshot**, click **Analyze page load**, and wait for the report.
+6. Compare the two reports. Write down every accessibility issue Snapshot mode finds.
+
+## Acceptance Criteria
+
+- A **Navigation** mode audit of the page on load reports an Accessibility score of 100. The page is accessible to begin with.
+- After adding the elements, a **Snapshot** mode audit reports the accessibility problems, and you can list each one and say which element caused it.
+
+> [!NOTE]
+> The accessibility problems on this page are deliberate. They are added by
+> JavaScript after you interact with the page, so they only show up in a
+> Snapshot audit. That is the whole point of the demo.
diff --git a/org-cyf/content/itp/data-groups/sprints/3/prep/index.md b/org-cyf/content/itp/data-groups/sprints/3/prep/index.md
index c55b0765a..34ec5fe79 100644
--- a/org-cyf/content/itp/data-groups/sprints/3/prep/index.md
+++ b/org-cyf/content/itp/data-groups/sprints/3/prep/index.md
@@ -37,6 +37,9 @@ name="DOM events"
src="module/js2/reacting"
name="Reacting to events"
[[blocks]]
+src="module/js2/auditing-the-dom"
+name="Auditing changes to the DOM"
+[[blocks]]
src="module/js2/check-progress"
name="check progress"
[[blocks]]
diff --git a/org-cyf/static/workshops/accessibility-snapshot/assets/app.js b/org-cyf/static/workshops/accessibility-snapshot/assets/app.js
new file mode 100644
index 000000000..9bed16681
--- /dev/null
+++ b/org-cyf/static/workshops/accessibility-snapshot/assets/app.js
@@ -0,0 +1,84 @@
+const menuToggle = document.getElementById("toggle");
+const menu = document.getElementById("menu");
+
+function toggleMenu() {
+ menu.classList.toggle("is-active");
+ menu.toggleAttribute("hidden");
+ if (menu.getAttribute("hidden") == null) {
+ menu.focus();
+ } else {
+ menuToggle.focus();
+ }
+}
+menuToggle.addEventListener("click", toggleMenu);
+
+window.addEventListener("keydown", (e) => {
+ if (e.key === "Escape" && menu.classList.contains("is-active")) {
+ toggleMenu();
+ }
+ if (e.key === "/" && e.metaKey) {
+ toggleMenu();
+ }
+});
+
+const addButton = document.querySelector("#add");
+const output = document.querySelector("#output");
+const status = document.querySelector("#status");
+
+const items = [
+ () => {
+ const p = document.createElement("p");
+ p.textContent =
+ "Thanks for clicking! Here is some extra information for you.";
+ p.style.color = "#cccccc";
+ return p;
+ },
+ () => {
+ const img = document.createElement("img");
+ const svg =
+ "";
+ img.src = "data:image/svg+xml," + encodeURIComponent(svg);
+ img.width = 120;
+ img.height = 80;
+ return img;
+ },
+ () => {
+ const link = document.createElement("a");
+ link.href = "https://codeyourfuture.io";
+ link.innerHTML =
+ "";
+ return link;
+ },
+ () => {
+ const button = document.createElement("button");
+ button.type = "button";
+ button.innerHTML =
+ "";
+ return button;
+ },
+];
+
+let next = 0;
+
+addButton.addEventListener("click", () => {
+ if (next >= items.length) {
+ return;
+ }
+
+ const wrapper = document.createElement("div");
+ wrapper.className = "added-item";
+ wrapper.append(items[next]());
+ output.append(wrapper);
+
+ next = next + 1;
+
+ if (next >= items.length) {
+ addButton.disabled = true;
+ status.textContent = `All ${items.length} elements added. Now run a Lighthouse audit in Snapshot mode.`;
+ } else {
+ status.textContent = `Added element ${next} of ${items.length}.`;
+ }
+});
diff --git a/org-cyf/static/workshops/accessibility-snapshot/assets/styles.css b/org-cyf/static/workshops/accessibility-snapshot/assets/styles.css
new file mode 100644
index 000000000..e063ad104
--- /dev/null
+++ b/org-cyf/static/workshops/accessibility-snapshot/assets/styles.css
@@ -0,0 +1,190 @@
+:root {
+ --copy: "Lato";
+ --paper: hsla(260, 35%, 90%, 0.95);
+ --ink: hsla(270, 8%, 20%, 0.98);
+ --brand: hsla(0, 100%, 67%, 0.98);
+ --shade: hsla(270, 8%, 20%, 0.1);
+ --space: 2rem;
+ --box: 18rem;
+ --container: clamp(var(--box), calc(100vw - calc(var(--space) * 2)), 74rem);
+ --finger: 48px;
+ --line-length: 60ch;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body,
+html {
+ padding: 0;
+ margin: 0;
+}
+
+a,
+a:any-link,
+button {
+ color: currentColor;
+ text-decoration: none;
+ font-weight: bold;
+ transition: all 0.3s;
+}
+
+:focus {
+ outline: none;
+}
+
+a:hover,
+a:focus,
+button:hover,
+button:focus {
+ color: var(--brand);
+}
+
+a:focus,
+button:focus {
+ outline: 2px dashed var(--brand);
+}
+
+button {
+ appearance: none;
+ border: none;
+ background: transparent;
+}
+
+ul,
+ul li {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ font: 100%/1.5 var(--copy), system-ui, sans-serif;
+ background-color: var(--paper);
+ color: var(--ink);
+ display: grid;
+ gap: var(--space);
+ grid-template:
+ ". ...... ." var(--space)
+ ". header ." min-content
+ ". main ." minmax(75vh, 1fr)
+ ". footer ." min-content
+ ". ...... ." var(--space) /
+ minmax(var(--space), 1fr) var(--container) minmax(var(--space), 1fr);
+ place-content: center;
+}
+
+header {
+ grid-area: header;
+ display: grid;
+ grid-template:
+ "logo space button" var(--finger) /
+ var(--finger) 1fr var(--finger);
+}
+
+header h1 {
+ grid-area: logo;
+ margin: 0;
+}
+
+main {
+ grid-area: main;
+ display: flex;
+ flex-flow: row wrap;
+ gap: var(--space);
+}
+
+main h2 {
+ width: 100%;
+ text-align: center;
+ font-size: 2em;
+}
+
+footer {
+ grid-area: footer;
+ display: grid;
+ grid-template: "github space" var(--finger) / var(--finger) 1fr;
+}
+
+footer a {
+ grid-area: github;
+}
+
+.menu {
+ background-color: var(--ink);
+ color: var(--paper);
+ height: 100vh;
+ width: max-content;
+ padding: var(--space);
+ z-index: 1;
+ position: absolute;
+ left: -100vw;
+}
+
+.menu.is-active {
+ left: 0;
+}
+
+.copy {
+ max-width: var(--line-length);
+}
+
+.is-invisible {
+ position: absolute;
+ left: -100vw;
+}
+
+.is-invisible:focus {
+ left: 0;
+}
+
+#toggle {
+ grid-area: button;
+}
+
+.logo,
+footer svg {
+ width: var(--finger);
+ height: var(--finger);
+}
+
+.copy ol {
+ list-style: decimal;
+ padding-left: 1.25rem;
+}
+
+.copy ol li {
+ margin-bottom: 0.5rem;
+}
+
+#add {
+ background-color: var(--ink);
+ color: var(--paper);
+ padding: 0.6rem 1.2rem;
+ border-radius: 0.3rem;
+ cursor: pointer;
+}
+
+#add:disabled {
+ opacity: 0.55;
+ cursor: not-allowed;
+}
+
+#status {
+ font-weight: bold;
+ min-height: 1.5rem;
+}
+
+#output {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space);
+ margin-top: var(--space);
+}
+
+.added-item {
+ background-color: #ffffff;
+ padding: var(--space);
+ box-shadow: 2px 4px 16px var(--shade);
+}
diff --git a/org-cyf/static/workshops/accessibility-snapshot/index.html b/org-cyf/static/workshops/accessibility-snapshot/index.html
new file mode 100644
index 000000000..56ba3a568
--- /dev/null
+++ b/org-cyf/static/workshops/accessibility-snapshot/index.html
@@ -0,0 +1,144 @@
+
+
+
+
+
+ Accessibility & the DOM Workshop
+
+
+
+
+
+
+ Skip to main
+
+
+ This page is fully accessible when it first loads. Each time you press
+ the button below, JavaScript adds a new element to the page. Every new
+ element looks fine, but each one hides a different accessibility
+ problem.
+
+
+
Try it
+
You will need Chrome with Dev Tools.
+
+
+ Open Dev Tools and go to the Lighthouse panel.
+
+
+ With Mode set to Navigation (the
+ default), click Analyze page load and wait for the
+ report. The Accessibility section should report no
+ problems, because the page is fine when it first loads.
+
+
+ Press the Add an element button below until it is
+ disabled. The page has now changed, but your Navigation report still
+ shows the page as it was when it loaded.
+
+
+ To run another audit, first start a new report by clicking the
+ + button at the top of the Lighthouse panel. Then
+ change Mode to Snapshot, click
+ Analyze page load, and wait for the report.
+
+
+ Compare the two reports. This time the Accessibility
+ section lists the problems your clicks introduced. Write down each
+ one.
+