acessibilidade-widget
Version:
Um widget de acessibilidade para sites, com modos de fonte, grayscale, saturação, modo leitura, etc.
406 lines (379 loc) • 12.6 kB
JavaScript
// src/index.js
let currentFontSize = 100;
let isGrayscale = false;
let saturationValue = 100;
let isImagesHidden = false;
let isReadingMode = false;
let cssInjected = false; // Flag para evitar múltiplas injeções de CSS
/**
* Injeta os estilos CSS automaticamente no <head>.
*/
function injectCSS() {
const css = `
.grayscale-mode {
filter: grayscale(100%) !important;
-webkit-filter: grayscale(100%) !important;
}
.hidden-image {
display: none !important;
}
.accessibility-btn {
position: fixed !important;
bottom: 80px !important;
right: 20px !important;
background: #007bff !important;
color: white !important;
padding: 10px 15px !important;
border: none !important;
cursor: pointer !important;
font-size: 16px !important;
border-radius: 6px !important;
z-index: 999999 !important;
}
.accessibility-btn:hover {
background: #0056b3 !important;
}
.acessibilidade-widget {
position: fixed !important;
bottom: 130px !important;
right: 20px !important;
background: #fff !important;
border: 1px solid #ccc !important;
padding: 10px !important;
z-index: 999999 !important;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.2) !important;
border-radius: 6px !important;
font-family: Arial, sans-serif !important;
display: flex !important;
flex-direction: column !important;
}
.acessibilidade-widget button {
margin: 5px !important;
padding: 8px 12px !important;
cursor: pointer !important;
font-size: 14px !important;
border: none !important;
background-color: #e0e0e0 !important;
border-radius: 4px !important;
}
.hidden {
display: none !important;
}
body.reading-mode,
body.reading-mode *:not(.acessibilidade-widget):not(.acessibilidade-widget *) {
color: #333 !important;
font-size: 22px !important;
line-height: 1.6 !important;
background: rgb(236, 236, 236) !important;
}
body.reading-mode img:not(.acessibilidade-widget img) {
display: none !important;
}
body.reading-mode img:not(.acessibilidade-widget img),
body.reading-mode form {
display: none !important;
}
[vw-access-button].active {
background-color: transparent !important;
display: flex !important;
}
[vw-access-button].active img {
display: none !important;
}
`;
const style = document.createElement("style");
style.type = "text/css";
style.appendChild(document.createTextNode(css));
document.head.appendChild(style);
}
/**
* Cria automaticamente o HTML do widget e o botão de toggle,
* garantindo que sejam filhos diretos do <body>.
* Agora inclui também o botão para VLibras.
*/
function createWidgetHTML() {
// Cria o botão de toggle, se não existir.
let toggleBtn = document.getElementById("toggle-accessibility");
if (!toggleBtn) {
toggleBtn = document.createElement("button");
toggleBtn.id = "toggle-accessibility";
toggleBtn.className = "accessibility-btn";
toggleBtn.innerText = "Acessibilidade";
document.body.insertAdjacentElement("afterbegin", toggleBtn);
}
// Cria o container do widget, se não existir.
let widget = document.getElementById("acessibilidade-widget");
if (!widget) {
widget = document.createElement("div");
widget.id = "acessibilidade-widget";
widget.className = "acessibilidade-widget hidden";
widget.innerHTML = `
<button id="font-increase">A+</button>
<button id="font-decrease">A-</button>
<button id="toggle-grayscale">Monocromático</button>
<button id="increase-saturation">+ Saturação</button>
<button id="decrease-saturation">- Saturação</button>
<button id="toggle-images">Esconder Imagens</button>
<button id="toggle-reading-mode">Modo Leitura</button>
<button id="libras">Libras</button>
<button id="reset-accessibility" style="display: none;">Redefinir Ajustes</button>
`;
toggleBtn.insertAdjacentElement("afterend", widget);
}
}
/**
* Carrega o script do VLibras, se ainda não estiver carregado, e associa o evento.
*/
function loadVLibras() {
if (!document.getElementById("vlibras-script")) {
const script = document.createElement("script");
script.id = "vlibras-script";
script.src = "https://vlibras.gov.br/app/vlibras-plugin.js";
script.onload = () => {
new window.VLibras.Widget('https://vlibras.gov.br/app');
attachVLibrasEvent();
};
document.head.appendChild(script);
} else {
attachVLibrasEvent();
}
}
function attachVLibrasEvent() {
const librasBtn = document.getElementById("libras");
const pluginButton = document.querySelector("[vw-access-button]");
if (librasBtn && pluginButton) {
librasBtn.addEventListener("click", () => {
pluginButton.click();
});
}
}
/**
* Registra o listener para o botão de toggle.
*/
function registerToggleButton() {
const toggleBtn = document.getElementById("toggle-accessibility");
const widget = document.getElementById("acessibilidade-widget");
if (toggleBtn && widget) {
toggleBtn.addEventListener("click", () => {
widget.classList.toggle("hidden");
// Se o widget for aberto e o modo VLibras não estiver carregado, carrega-o
if (!widget.classList.contains("hidden")) {
loadVLibras();
}
});
}
}
/**
* Inicializa o widget de acessibilidade.
* Injeta o CSS, cria o HTML, registra os event listeners e carrega as preferências.
*/
export function init() {
if (!cssInjected) {
injectCSS();
cssInjected = true;
}
createWidgetHTML();
registerToggleButton();
const btnIncrease = document.getElementById("font-increase");
const btnDecrease = document.getElementById("font-decrease");
const btnResetFont = document.getElementById("font-reset");
const btnGray = document.getElementById("toggle-grayscale");
const btnSaturUp = document.getElementById("increase-saturation");
const btnSaturDown = document.getElementById("decrease-saturation");
const btnToggleImages = document.getElementById("toggle-images");
const btnResetAll = document.getElementById("reset-accessibility");
const btnReadingMode = document.getElementById("toggle-reading-mode");
if (btnIncrease) btnIncrease.addEventListener("click", increaseFont);
if (btnDecrease) btnDecrease.addEventListener("click", decreaseFont);
if (btnResetFont) btnResetFont.addEventListener("click", resetFontOnly);
if (btnGray) btnGray.addEventListener("click", toggleGrayscale);
if (btnSaturUp) btnSaturUp.addEventListener("click", increaseSaturation);
if (btnSaturDown) btnSaturDown.addEventListener("click", decreaseSaturation);
if (btnToggleImages) btnToggleImages.addEventListener("click", toggleImages);
if (btnResetAll) btnResetAll.addEventListener("click", resetAll);
if (btnReadingMode) btnReadingMode.addEventListener("click", toggleReadingMode);
loadPreferences();
}
// FONTES
function increaseFont() {
if (currentFontSize < 200) {
currentFontSize += 10;
applyFontSize();
showResetButton();
savePreferences();
}
}
function decreaseFont() {
if (currentFontSize > 60) {
currentFontSize -= 10;
applyFontSize();
showResetButton();
savePreferences();
}
}
function resetFontOnly() {
currentFontSize = 100;
applyFontSize();
showResetButton();
savePreferences();
}
function applyFontSize() {
const scale = currentFontSize / 100;
const textSelectors =
"p, h1, h2, h3, h4, h5, h6, span, li, a, blockquote, label, strong, em";
const textElements = document.querySelectorAll(textSelectors);
textElements.forEach((el) => {
if (!el.hasAttribute("data-orig-size")) {
const originalSize = parseFloat(window.getComputedStyle(el).fontSize) || 16;
el.setAttribute("data-orig-size", originalSize);
}
const baseSize = parseFloat(el.getAttribute("data-orig-size"));
const newSize = baseSize * scale;
el.style.fontSize = newSize + "px";
});
}
// GRAYSCALE
function toggleGrayscale() {
isGrayscale = !isGrayscale;
applyFilters();
showResetButton();
savePreferences();
}
// SATURAÇÃO
function increaseSaturation() {
saturationValue = Math.min(saturationValue + 200, 250);
applyFilters();
showResetButton();
savePreferences();
}
function decreaseSaturation() {
saturationValue = Math.max(saturationValue - 100, 50);
applyFilters();
showResetButton();
savePreferences();
}
function applyFilters() {
const grayValue = isGrayscale ? "100%" : "0%";
document.documentElement.style.filter =
`grayscale(${grayValue}) saturate(${saturationValue}%)`;
document.documentElement.style.webkitFilter =
`grayscale(${grayValue}) saturate(${saturationValue}%)`;
}
// IMAGENS
function toggleImages() {
isImagesHidden = !isImagesHidden;
const allImages = document.querySelectorAll("img");
if (isImagesHidden) {
allImages.forEach((img) => img.classList.add("hidden-image"));
} else {
allImages.forEach((img) => img.classList.remove("hidden-image"));
}
const btnToggleImages = document.getElementById("toggle-images");
if (btnToggleImages) {
btnToggleImages.textContent = isImagesHidden
? "Mostrar Imagens"
: "Esconder Imagens";
}
showResetButton();
savePreferences();
}
// LEITURA
function toggleReadingMode() {
isReadingMode = !isReadingMode;
document.body.classList.toggle("reading-mode", isReadingMode);
if (isReadingMode) {
document.querySelectorAll("img").forEach((img) => {
if (img.hasAttribute("alt")) {
img.dataset.originalAlt = img.getAttribute("alt");
img.removeAttribute("alt");
}
});
} else {
document.querySelectorAll("img").forEach((img) => {
if (img.dataset.originalAlt) {
img.setAttribute("alt", img.dataset.originalAlt);
delete img.dataset.originalAlt;
}
});
}
console.log("Modo leitura ativado?", isReadingMode);
showResetButton();
savePreferences();
}
// RESET
function resetAll() {
currentFontSize = 100;
isGrayscale = false;
saturationValue = 100;
isImagesHidden = false;
isReadingMode = false;
applyFontSize();
applyFilters();
const allImages = document.querySelectorAll("img");
allImages.forEach((img) => img.classList.remove("hidden-image"));
document.body.classList.remove("reading-mode");
const btnToggleImages = document.getElementById("toggle-images");
if (btnToggleImages) {
btnToggleImages.textContent = "Esconder Imagens";
}
savePreferences();
hideResetButton();
}
function showResetButton() {
const btn = document.getElementById("reset-accessibility");
if (btn) {
btn.style.display = "inline-block";
}
}
function hideResetButton() {
const btn = document.getElementById("reset-accessibility");
if (btn) {
btn.style.display = "none";
}
}
// LOCAL STORAGE
function savePreferences() {
localStorage.setItem("accessFontSize", currentFontSize);
localStorage.setItem("accessGray", isGrayscale);
localStorage.setItem("accessSaturation", saturationValue);
localStorage.setItem("accessImagesHidden", isImagesHidden);
localStorage.setItem("accessReadingMode", isReadingMode);
}
function loadPreferences() {
const savedSize = localStorage.getItem("accessFontSize");
const savedGray = localStorage.getItem("accessGray");
const savedSat = localStorage.getItem("accessSaturation");
const savedImgs = localStorage.getItem("accessImagesHidden");
const savedReading = localStorage.getItem("accessReadingMode");
if (savedSize) currentFontSize = parseInt(savedSize, 10);
if (savedGray === "true") isGrayscale = true;
if (savedSat) saturationValue = parseInt(savedSat, 10);
if (savedImgs === "true") isImagesHidden = true;
if (savedReading === "true") isReadingMode = true;
applyFontSize();
applyFilters();
if (isImagesHidden) {
const allImages = document.querySelectorAll("img");
allImages.forEach((img) => img.classList.add("hidden-image"));
const btnToggleImages = document.getElementById("toggle-images");
if (btnToggleImages) {
btnToggleImages.textContent = "Mostrar Imagens";
}
}
if (isReadingMode) {
document.body.classList.add("reading-mode");
} else {
document.body.classList.remove("reading-mode");
}
const changedFromDefault =
currentFontSize !== 100 ||
isGrayscale !== false ||
saturationValue !== 100 ||
isImagesHidden !== false ||
isReadingMode !== false;
if (changedFromDefault) {
showResetButton();
} else {
hideResetButton();
}
}