move theme toggle to own Vue app for better tab order

Relocate the light/dark toggle button out of #app1 and into a new
#app3 mount just before the back-to-top element so keyboard focus
reaches it near the end of the page instead of right after the main
nav. Replace the library logo's reactive :src binding with a plain
src plus data-light-src/data-dark-src so app1 no longer needs any
theme state; app3 now owns toggleTheme, overlay updates, logo swap,
and the OS preference listener.
This commit is contained in:
Mark Eaton
2026-04-13 11:53:20 -04:00
parent ac12d21cac
commit dfbe8875f5
2 changed files with 65 additions and 59 deletions
+13 -9
View File
@@ -80,7 +80,9 @@
id="library-logo" id="library-logo"
class="image-fail" class="image-fail"
alt="Kibbee Library logo" alt="Kibbee Library logo"
:src="isDark ? 'https://d2jv02qf7xgjwx.cloudfront.net/accounts/16298/images/Kibbee_library_logo_inverted2.jpg' : 'https://libapps.s3.amazonaws.com/accounts/16298/images/xsmall-2.jpg'" src="https://libapps.s3.amazonaws.com/accounts/16298/images/xsmall-2.jpg"
data-light-src="https://libapps.s3.amazonaws.com/accounts/16298/images/xsmall-2.jpg"
data-dark-src="https://d2jv02qf7xgjwx.cloudfront.net/accounts/16298/images/Kibbee_library_logo_inverted2.jpg"
/> />
{{content_box_33471502}} {{content_box_33471502}}
</div> </div>
@@ -94,14 +96,6 @@
<!-- end of col-xs-12 --> <!-- end of col-xs-12 -->
</div> </div>
<!--end of row--> <!--end of row-->
<button
id="theme-toggle"
@click="toggleTheme"
:aria-label="isDark ? 'Switch to light mode' : 'Switch to dark mode'"
type="button"
>
<i :class="isDark ? 'fas fa-sun' : 'fas fa-moon'" aria-hidden="true"></i>
</button>
</div> </div>
<!--end of Vue app1--> <!--end of Vue app1-->
</div> </div>
@@ -369,6 +363,16 @@
<i class="fas fa-sync fa-spin" aria-hidden="true"></i> Loading... <i class="fas fa-sync fa-spin" aria-hidden="true"></i> Loading...
<button class="btn btn-secondary btn-sm popclose" type="button">Close</button> <button class="btn btn-secondary btn-sm popclose" type="button">Close</button>
</div> </div>
<div id="app3">
<button
id="theme-toggle"
@click="toggleTheme"
:aria-label="isDark ? 'Switch to light mode' : 'Switch to dark mode'"
type="button"
>
<i :class="isDark ? 'fas fa-sun' : 'fas fa-moon'" aria-hidden="true"></i>
</button>
</div>
<div id="s-lib-scroll-top" title="Back to Top"> <div id="s-lib-scroll-top" title="Back to Top">
<span class="fa-stack fa-lg" aria-hidden="true"> <span class="fa-stack fa-lg" aria-hidden="true">
<i class="far fa-square fa-stack-2x"></i> <i class="far fa-square fa-stack-2x"></i>
+52 -50
View File
@@ -238,7 +238,6 @@ const app1 = createApp({
}, },
data() { data() {
return { return {
isDark: false,
hamburger: [ hamburger: [
{ {
link: "https://cuny-kb.primo.exlibrisgroup.com/discovery/search?tab=Everything&vid=01CUNY_KB:CUNY_KB&lang=en", link: "https://cuny-kb.primo.exlibrisgroup.com/discovery/search?tab=Everything&vid=01CUNY_KB:CUNY_KB&lang=en",
@@ -471,56 +470,7 @@ const app1 = createApp({
}, },
}; };
}, },
methods: {
toggleTheme() {
this.isDark = !this.isDark;
this.applyTheme(this.isDark);
localStorage.setItem("kbcc-theme", this.isDark ? "dark" : "light");
},
applyTheme(isDark) {
if (isDark) {
document.body.setAttribute("data-bs-theme", "dark");
} else {
document.body.removeAttribute("data-bs-theme");
}
this.updateBackgroundOverlay(isDark);
},
updateBackgroundOverlay(isDark) {
var el = document.querySelector(".kbcc-background-image");
if (el && el.style.backgroundImage) {
// Extract the url() part from the existing background-image
var match = el.style.backgroundImage.match(/url\([^)]+\)/);
if (match) {
var overlay = isDark
? "rgba(0,0,0,0.4)"
: "rgba(255,255,255,0.6)";
el.style.backgroundImage =
"linear-gradient(to bottom, " + overlay + " 0%, " + overlay + " 100%), " + match[0];
}
}
}
},
mounted() { mounted() {
// Theme initialization
var stored = localStorage.getItem("kbcc-theme");
var prefersDark = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;
if (stored) {
this.isDark = stored === "dark";
} else {
this.isDark = prefersDark;
}
this.applyTheme(this.isDark);
// Listen for OS preference changes (only applies when no manual override)
var self = this;
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", function(e) {
if (!localStorage.getItem("kbcc-theme")) {
self.isDark = e.matches;
self.applyTheme(self.isDark);
}
});
// Dynamic aria-expanded for hamburger menu (WCAG 4.1.2) // Dynamic aria-expanded for hamburger menu (WCAG 4.1.2)
const hamburger = document.getElementById('hamburger'); const hamburger = document.getElementById('hamburger');
const hamburgerContainer = document.getElementById('hamburger-container'); const hamburgerContainer = document.getElementById('hamburger-container');
@@ -545,6 +495,58 @@ const app2 = createApp({
}); });
app2.mount("#app2"); app2.mount("#app2");
const app3 = createApp({
data() {
return {
isDark: false,
};
},
methods: {
toggleTheme() {
this.isDark = !this.isDark;
localStorage.setItem("kbcc-theme", this.isDark ? "dark" : "light");
if (this.isDark) {
document.body.setAttribute("data-bs-theme", "dark");
} else {
document.body.removeAttribute("data-bs-theme");
}
var bg = document.querySelector(".kbcc-background-image");
if (bg && bg.style.backgroundImage) {
var match = bg.style.backgroundImage.match(/url\([^)]+\)/);
if (match) {
var overlay = this.isDark ? "rgba(0,0,0,0.4)" : "rgba(255,255,255,0.6)";
bg.style.backgroundImage =
"linear-gradient(to bottom, " + overlay + " 0%, " + overlay + " 100%), " + match[0];
}
}
var logo = document.getElementById("library-logo");
if (logo) {
logo.src = this.isDark ? logo.dataset.darkSrc : logo.dataset.lightSrc;
}
},
},
mounted() {
this.isDark = document.body.getAttribute("data-bs-theme") === "dark";
var logo = document.getElementById("library-logo");
if (logo && this.isDark) {
logo.src = logo.dataset.darkSrc;
}
var self = this;
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", function(e) {
if (!localStorage.getItem("kbcc-theme")) {
self.isDark = e.matches;
if (self.isDark) {
document.body.setAttribute("data-bs-theme", "dark");
} else {
document.body.removeAttribute("data-bs-theme");
}
if (logo) logo.src = self.isDark ? logo.dataset.darkSrc : logo.dataset.lightSrc;
}
});
},
});
app3.mount("#app3");
// Randomly set the background image for .kbcc-background-image // Randomly set the background image for .kbcc-background-image
(function () { (function () {
const images = [ const images = [