UNPKG

@fadhli_fajarsyah/accessibility-widget

Version:

A simple accessibility widget to adjust font size (A+, A-, Reset)

228 lines (196 loc) 7.64 kB
export function initAccessibilityWidget(): void { if (document.getElementById("accessibility-widget-container")) return; const container = document.createElement("div"); container.id = "accessibility-widget-container"; container.style.position = "fixed"; container.style.zIndex = "9999"; container.style.top = "0"; container.style.left = "0"; container.style.height = "100vh"; container.style.width = "auto"; document.body.appendChild(container); const shadow = container.attachShadow({ mode: "open" }); const savedTop = localStorage.getItem("accessibility-hamburger-top"); const style = document.createElement("style"); style.textContent = ` .toggle-button { width: 40px; height: 40px; border-radius: 0 8px 8px 0; background-color: #0000ff; color: white; font-size: 20px; border: none; cursor: grab; position: fixed; left: 0; z-index: 10000; transition: top 0.2s; } .popup-panel { position: fixed; top: 0; left: 0; height: 100vh; width: 50vw; max-width: 400px; background: white; box-shadow: 2px 0 10px rgba(0,0,0,0.3); padding: 20px; transform: translateX(-100%); transition: transform 0.3s ease-in-out; z-index: 9999; overflow-y: auto; } .popup-panel.open { transform: translateX(0); } .accessibility-controls button, .button { padding: 8px 12px; margin: 6px 4px; background-color: #0000ff; color: white; border: none; border-radius: 4px; font-size: 16px; cursor: pointer; } .accessibility-controls button:hover { background-color: #0000cc; } .accessibility-controls { margin-top: 10px; } `; const toggleBtn = document.createElement("button"); toggleBtn.className = "toggle-button"; toggleBtn.innerHTML = ` <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M12.4575 14.0572V9.02866M12.4575 14.0572H11.0861M12.4575 14.0572L15.2004 20.0001M12.4575 9.02866H11.0861M12.4575 9.02866L13.7381 8.81523C14.7101 8.65322 15.6618 8.38692 16.5768 8.02094L17.4861 7.65723M11.0861 14.0572V9.02866M11.0861 14.0572L7.88608 20.0001M11.0861 9.02866L9.97052 8.84273C8.89172 8.66293 7.84586 8.32283 6.86764 7.83372L6.51465 7.65723" stroke="white" stroke-width="2.28571" stroke-linecap="round" stroke-linejoin="round"/> <path d="M12.7309 4.91429C12.7309 5.41923 12.3216 5.82857 11.8166 5.82857C11.3117 5.82857 10.9023 5.41923 10.9023 4.91429C10.9023 4.40934 11.3117 4 11.8166 4C12.3216 4 12.7309 4.40934 12.7309 4.91429Z" fill="white" stroke="white" stroke-width="2.28571" stroke-linecap="round" stroke-linejoin="round"/> </svg> `; toggleBtn.style.top = savedTop ? `${savedTop}px` : "20px"; const panel = document.createElement("div"); panel.className = "popup-panel"; panel.innerHTML = ` <div class="accessibility-controls" id="controls"> <h2>Accessibility Options</h2> <div> <button id="btn-increase">A+</button> <button id="btn-decrease">A-</button> </div> <div> <button id="btn-underline">Underline Links</button> </div> <button id="btn-reset" class="button">Reset</button> </div> `; shadow.appendChild(style); shadow.appendChild(toggleBtn); shadow.appendChild(panel); // Panel Toggle let isOpen = false; toggleBtn.addEventListener("click", () => { isOpen = !isOpen; panel.classList.toggle("open", isOpen); if (isOpen) { toggleBtn.style.top = "20px"; toggleBtn.style.cursor = "default"; } else { toggleBtn.style.cursor = "grab"; const saved = localStorage.getItem("accessibility-hamburger-top"); toggleBtn.style.top = saved ? `${saved}px` : "20px"; } }); // Font Size Control const html = document.documentElement; const step = 2; const minSize = 12; const maxSize = 32; const defaultSize = 16; const savedFontSize = localStorage.getItem("accessibility-font-size"); if (savedFontSize) { const size = parseFloat(savedFontSize); document.querySelectorAll<HTMLElement>("body, body *").forEach((el) => { el.style.fontSize = `${size}px`; }); } function changeFontSize(direction: "increase" | "decrease") { document.querySelectorAll<HTMLElement>("body, body *").forEach((el) => { const currentSize = parseFloat(window.getComputedStyle(el).fontSize); if (isNaN(currentSize)) return; let newSize = currentSize; if (direction === "increase" && currentSize < maxSize) newSize += step; else if (direction === "decrease" && currentSize > minSize) newSize -= step; el.style.fontSize = `${newSize}px`; localStorage.setItem("accessibility-font-size", `${newSize}`); }); } function resetFontSize() { html.style.fontSize = `${defaultSize}px`; document.querySelectorAll<HTMLElement>("body, body *").forEach((el) => { el.style.fontSize = ""; }); } // Underline Feature let underlineActive = localStorage.getItem("accessibility-underline") === "true"; function applyUnderline() { document.querySelectorAll<HTMLElement>("a, .link, #link").forEach((el) => { el.style.textDecoration = underlineActive ? "underline" : "none"; }); } if (underlineActive) applyUnderline(); panel.querySelector("#btn-underline")?.addEventListener("click", () => { underlineActive = !underlineActive; localStorage.setItem("accessibility-underline", underlineActive.toString()); applyUnderline(); }); const observer = new MutationObserver(() => { if (underlineActive) applyUnderline(); }); observer.observe(document.body, { childList: true, subtree: true }); panel .querySelector("#btn-increase") ?.addEventListener("click", () => changeFontSize("increase")); panel .querySelector("#btn-decrease") ?.addEventListener("click", () => changeFontSize("decrease")); panel.querySelector("#btn-reset")?.addEventListener("click", () => { resetFontSize(); underlineActive = false; applyUnderline(); localStorage.removeItem("accessibility-underline"); localStorage.removeItem("accessibility-font-size"); }); // Drag Only When Closed let isDragging = false; let startY = 0; let startTop = 0; toggleBtn.addEventListener("mousedown", (e) => { if (isOpen) return; isDragging = true; startY = e.clientY; startTop = toggleBtn.getBoundingClientRect().top; document.body.style.userSelect = "none"; }); document.addEventListener("mousemove", (e) => { if (!isDragging || isOpen) return; const offsetY = e.clientY - startY; let newTop = startTop + offsetY; const maxTop = window.innerHeight - 50; newTop = Math.max(0, Math.min(maxTop, newTop)); toggleBtn.style.top = `${newTop}px`; }); document.addEventListener("mouseup", () => { if (isDragging) { isDragging = false; document.body.style.userSelect = ""; const rect = toggleBtn.getBoundingClientRect(); localStorage.setItem("accessibility-hamburger-top", `${rect.top}`); } }); }