Build a Theme Switcher - Build a Theme Switcher

Tell us what’s happening:

  1. When a user clicks on the #theme-switcher-button and selects a theme, the corresponding theme- class should be added to the body element.
    Failed:27. When a user clicks the #theme-switcher-button and selects a theme, a message related to the selected theme from the themes array should be displayed in the aria-live=“polite” element.

Your code so far

<!-- file: index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Theme Switcher</title>
  <link rel="stylesheet" href="./styles.css">
</head>
<body>
  <button
    id="theme-switcher-button"
    aria-haspopup="true"
    aria-expanded="false"
    aria-controls="theme-dropdown"
  >
    Switch Theme
  </button>

  <ul
    id="theme-dropdown"
    role="menu"
    aria-labelledby="theme-switcher-button"
    hidden
  >
    <li id="theme-light" role="menuitem">Light</li>
    <li id="theme-dark"  role="menuitem">Dark</li>
  </ul>

  <p id="theme-status" aria-live="polite"></p>

  <script src="./script.js"></script>
</body>
</html>

/* file: styles.css */
body {
  margin: 0;
  font-family: sans-serif;
  transition: background 0.3s, color 0.3s;
}

ul {
  margin: 0;
  padding: 0;
}

li {
  list-style-type: none;
}

.theme-light {
  background: white;
  color: black;
}

.theme-dark {
  background: black;
  color: white;
}


#status {
  text-align: center;
  min-height: 20px;
}
/* file: script.js */
const themeButton = document.getElementById("theme-switcher-button");
const dropdown = document.getElementById("theme-dropdown");
const themeStatus = document.getElementById("theme-status");

const themes = [
  { name: "light", message: "Light theme applied." },
  { name: "dark", message: "Dark theme applied." }
];

// Toggle menu on button click
themeButton.addEventListener("click", () => {
  const isOpen = dropdown.hasAttribute("hidden") === false;

  if (isOpen) {
    dropdown.setAttribute("hidden", "");
    themeButton.setAttribute("aria-expanded", "false");
  } else {
    dropdown.removeAttribute("hidden");
    themeButton.setAttribute("aria-expanded", "true");
  }
});

// Handle selection of a theme
dropdown.addEventListener("click", (event) => {
  if (event.target.role !== "menuitem" && event.target.getAttribute("role") !== "menuitem") return;

  const chosenId = event.target.id; // e.g., "theme-light"
  const chosenTheme = chosenId.replace("theme-", ""); // e.g., "light"

  // Remove any existing theme-* classes
  document.body.classList.forEach((c) => {
    if (c.startsWith("theme-")) document.body.classList.remove(c);
  });

  // Add new theme class to body
  document.body.classList.add(`theme-${chosenTheme}`);

  // Find message and update aria-live region
  const selectedThemeObj = themes.find((t) => t.name === chosenTheme);
  if (selectedThemeObj) {
    themeStatus.textContent = selectedThemeObj.message;
  }

  // Close menu after selection
  dropdown.setAttribute("hidden", "");
  themeButton.setAttribute("aria-expanded", "false");
});

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36

Challenge Information:

Build a Theme Switcher - Build a Theme Switcher

Hey, these tests have slight bug, which causes event dispatched in test to not bubble. There’s PR that will fix it fix(curriculum): dispatch bubbling event in Theme Switcher tests by gikf · Pull Request #64102 · freeCodeCamp/freeCodeCamp · GitHub, but until it’s merged and then deployed, your approach unfortunately won’t work. Before that happens, you could rewrite code to add click event listeners on the list items themselves.

Okay thanks, I have done that now