Warning: Accessibilty Features and .jsI on this site is currently deactivated, as JavaScript is disabled.

Joel A. A. Bredasbox Environment

The sbox Environment displays code in color via .jsI to make it more readable. Currently inspecting: .jsI

sbox: WCAG

Easily view code with jsI

(function () { "use strict"; const STORAGE_KEY = "a11y-widget-settings"; const FOCUSABLE = 'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])'; const defaultSettings = { fontScale: 1, lineSpacing: false, highContrast: false, invertColors: false, grayscale: false, underlineLinks: false, dyslexiaFont: false, reduceMotion: false, bigCursor: false, highlightFocus: false, }; let settings = loadSettings(); let lastFocusedBeforeOpen = null; function loadSettings() { try { const raw = localStorage.getItem(STORAGE_KEY); return raw ? { ...defaultSettings, ...JSON.parse(raw) } : { ...defaultSettings }; } catch (e) { return { ...defaultSettings }; } } function saveSettings() { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(settings)); } catch (e) {} } function announce(msg) { const live = document.getElementById("a11y-live-region"); if (live) { live.textContent = ""; requestAnimationFrame(() => { live.textContent = msg; }); } } function injectStyles() { const style = document.createElement("style"); style.id = "a11y-widget-styles"; style.textContent = ` #a11y-skip-link { position: absolute; left: -9999px; top: 0; z-index: 1000000; background: #000; color: #fff; padding: 0.75rem 1rem; font-family: system-ui, sans-serif; text-decoration: none; border-radius: 0 0 6px 0; } #a11y-skip-link:focus { left: 0; } #a11y-live-region { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; } #a11y-header { position: fixed !important; top: 0.5rem !important; right: 0.5rem !important; left: auto !important; width: auto !important; z-index: 999997 !important; display: block !important; margin: 0 !important; padding: 0 !important; background: transparent !important; } #a11y-toggle-btn { display: inline-flex !important; width: auto !important; max-width: max-content !important; align-items: center !important; gap: 0.3rem !important; background: #0b5fcc !important; color: #fff !important; border: 1px solid #66aaff !important; border-radius: 6px !important; padding: 0.25rem 0.55rem !important; margin: 0 !important; cursor: pointer !important; font-family: system-ui, sans-serif !important; font-size: 12px !important; line-height: 1.4 !important; box-shadow: 0 1px 4px rgba(0,0,0,0.4) !important; } #a11y-toggle-btn:hover, #a11y-toggle-btn:focus-visible { background: #0d70eb; outline: 3px solid #ffb400; outline-offset: 2px; } #a11y-overlay { position: fixed; inset: 0; z-index: 999998; background: rgba(0, 0, 0, 0.6); display: flex; align-items: center; justify-content: center; padding: 1rem; overflow-y: auto; } #a11y-overlay[hidden] { display: none; } #a11y-dialog { width: min(96vw, 900px); max-height: 90vh; overflow-y: auto; background: #1a1a1a; color: #fff; border: 1px solid #555; border-radius: 12px; padding: 1.5rem; font-family: system-ui, sans-serif; box-shadow: 0 8px 30px rgba(0,0,0,0.6); } #a11y-dialog-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1rem; } #a11y-dialog-header h2 { font-size: 18px; margin: 0; } #a11y-close-btn { background: #2e2e2e; color: #fff; border: 1px solid #555; border-radius: 6px; width: 32px; height: 32px; cursor: pointer; font-size: 16px; } #a11y-close-btn:hover, #a11y-close-btn:focus-visible { background: #4a4a4a; outline: 3px solid #ffb400; outline-offset: 2px; } #a11y-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.6rem; } #a11y-grid button { width: 100%; background: #2e2e2e; color: #fff; border: 1px solid #555; border-radius: 8px; padding: 0.6rem 0.5rem; cursor: pointer; font-size: 13px; text-align: center; } #a11y-grid button:hover, #a11y-grid button:focus-visible { background: #4a4a4a; outline: 3px solid #ffb400; outline-offset: 2px; } #a11y-grid button[aria-pressed="true"] { background: #0b5fcc; border-color: #66aaff; } #a11y-fontctrl { display: flex; align-items: center; justify-content: space-between; background: #2e2e2e; border: 1px solid #555; border-radius: 8px; padding: 0.4rem 0.6rem; margin-bottom: 0.6rem; } #a11y-fontctrl span { font-size: 13px; } #a11y-fontctrl .a11y-fontbtns { display: flex; gap: 0.4rem; } #a11y-fontctrl button { background: #1a1a1a; color: #fff; border: 1px solid #555; border-radius: 6px; padding: 0.3rem 0.6rem; cursor: pointer; } #a11y-fontctrl button:hover, #a11y-fontctrl button:focus-visible { background: #4a4a4a; outline: 3px solid #ffb400; outline-offset: 2px; } #a11y-reset-btn { width: 100%; margin-top: 1rem; background: #5a1a1a; color: #fff; border: 1px solid #ff5555; border-radius: 8px; padding: 0.6rem; cursor: pointer; font-size: 13px; } #a11y-reset-btn:hover, #a11y-reset-btn:focus-visible { background: #732020; outline: 3px solid #ffb400; outline-offset: 2px; } html.a11y-underline-links a { text-decoration: underline !important; } html.a11y-line-spacing body, html.a11y-line-spacing body * { line-height: 1.8 !important; letter-spacing: 0.02em !important; word-spacing: 0.05em !important; } html.a11y-high-contrast body { background: #000 !important; color: #fff !important; } html.a11y-high-contrast a { color: #ffe86b !important; } html.a11y-invert-colors { filter: invert(1) hue-rotate(180deg); } html.a11y-invert-colors img, html.a11y-invert-colors video { filter: invert(1) hue-rotate(180deg); } html.a11y-grayscale { filter: grayscale(1); } html.a11y-invert-colors.a11y-grayscale { filter: invert(1) hue-rotate(180deg) grayscale(1); } html.a11y-dyslexia-font body, html.a11y-dyslexia-font body * { font-family: Verdana, Tahoma, sans-serif !important; } html.a11y-reduce-motion *, html.a11y-reduce-motion *::before, html.a11y-reduce-motion *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; scroll-behavior: auto !important; } html.a11y-big-cursor, html.a11y-big-cursor * { cursor: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path d="M3 2l18 9-8 2-3 8-7-19z" fill="black" stroke="white" stroke-width="1"/></svg>') 0 0, auto !important; } html.a11y-highlight-focus *:focus-visible { outline: 4px solid #ffb400 !important; outline-offset: 3px !important; } `; document.head.appendChild(style); } function buildSkipLink() { let target = document.querySelector("main, [role='main'], #main, #content"); if (!target) return; if (!target.id) target.id = "a11y-main-content"; if (!target.hasAttribute("tabindex")) target.setAttribute("tabindex", "-1"); const link = document.createElement("a"); link.id = "a11y-skip-link"; link.href = "#" + target.id; link.textContent = "Skip to main content"; link.addEventListener("click", () => { target.focus(); }); document.body.insertBefore(link, document.body.firstChild); } function buildLiveRegion() { const live = document.createElement("div"); live.id = "a11y-live-region"; live.setAttribute("aria-live", "polite"); live.setAttribute("role", "status"); document.body.appendChild(live); } function buildHeader() { const header = document.createElement("header"); header.id = "a11y-header"; const btn = document.createElement("button"); btn.id = "a11y-toggle-btn"; btn.type = "button"; btn.setAttribute("aria-haspopup", "dialog"); btn.setAttribute("aria-expanded", "false"); btn.setAttribute("aria-controls", "a11y-overlay"); btn.innerHTML = "&#9855; Accessibility"; header.appendChild(btn); document.body.insertBefore(header, document.body.firstChild); return btn; } function buildOverlay() { const overlay = document.createElement("div"); overlay.id = "a11y-overlay"; overlay.hidden = true; overlay.innerHTML = ` <div id="a11y-dialog" role="dialog" aria-modal="true" aria-labelledby="a11y-dialog-title"> <div id="a11y-dialog-header"> <h2 id="a11y-dialog-title">Accessibility Settings</h2> <button type="button" id="a11y-close-btn" aria-label="Close accessibility settings">&times;</button> </div> <div id="a11y-fontctrl"> <span>Font Size</span> <div class="a11y-fontbtns"> <button type="button" data-action="font-dec" aria-label="Decrease font size">A−</button> <button type="button" data-action="font-inc" aria-label="Increase font size">A+</button> </div> </div> <div id="a11y-grid"> <button type="button" data-toggle="lineSpacing" aria-pressed="false">Line Spacing</button> <button type="button" data-toggle="highContrast" aria-pressed="false">High Contrast</button> <button type="button" data-toggle="invertColors" aria-pressed="false">Invert Colors</button> <button type="button" data-toggle="grayscale" aria-pressed="false">Grayscale</button> <button type="button" data-toggle="underlineLinks" aria-pressed="false">Underline Links</button> <button type="button" data-toggle="dyslexiaFont" aria-pressed="false">Readable Font</button> <button type="button" data-toggle="reduceMotion" aria-pressed="false">Reduce Motion</button> <button type="button" data-toggle="highlightFocus" aria-pressed="false">Highlight Focus</button> <button type="button" data-toggle="bigCursor" aria-pressed="false">Big Cursor</button> </div> <button type="button" id="a11y-reset-btn">Reset All Settings</button> <br><br> <p>Im always trying my best to pursue WCAG. If anything misses, contact <a href="https://docs.jbreda.dev/contact.php">me</p> </div> `; document.body.appendChild(overlay); return overlay; } function wireEvents(toggleBtn, overlay) { const dialog = overlay.querySelector("#a11y-dialog"); const closeBtn = overlay.querySelector("#a11y-close-btn"); toggleBtn.addEventListener("click", () => openOverlay(toggleBtn, overlay)); closeBtn.addEventListener("click", () => closeOverlay(toggleBtn, overlay)); overlay.addEventListener("click", (e) => { if (e.target === overlay) closeOverlay(toggleBtn, overlay); }); overlay.addEventListener("click", (e) => { const t = e.target.closest("button"); if (!t) return; const toggleKey = t.getAttribute("data-toggle"); const action = t.getAttribute("data-action"); const id = t.id; if (toggleKey) { settings[toggleKey] = !settings[toggleKey]; applySettings(); announce(t.textContent.trim() + (settings[toggleKey] ? " enabled" : " disabled")); return; } if (action === "font-inc") { settings.fontScale = Math.min(2, +(settings.fontScale + 0.1).toFixed(2)); applySettings(); announce("Font size " + Math.round(settings.fontScale * 100) + " percent"); } else if (action === "font-dec") { settings.fontScale = Math.max(0.7, +(settings.fontScale - 0.1).toFixed(2)); applySettings(); announce("Font size " + Math.round(settings.fontScale * 100) + " percent"); } else if (id === "a11y-reset-btn") { settings = { ...defaultSettings }; applySettings(); announce("All settings reset"); } }); dialog.addEventListener("keydown", (e) => { if (e.key === "Escape") { closeOverlay(toggleBtn, overlay); return; } if (e.key === "Tab") { const focusables = Array.from(dialog.querySelectorAll(FOCUSABLE)); if (focusables.length === 0) return; const first = focusables[0]; const last = focusables[focusables.length - 1]; if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } } }); } function openOverlay(toggleBtn, overlay) { lastFocusedBeforeOpen = document.activeElement; overlay.hidden = false; toggleBtn.setAttribute("aria-expanded", "true"); const dialog = overlay.querySelector("#a11y-dialog"); const firstFocusable = dialog.querySelector(FOCUSABLE); if (firstFocusable) firstFocusable.focus(); } function closeOverlay(toggleBtn, overlay) { overlay.hidden = true; toggleBtn.setAttribute("aria-expanded", "false"); if (lastFocusedBeforeOpen) lastFocusedBeforeOpen.focus(); } function applySettings() { const html = document.documentElement; html.style.fontSize = (settings.fontScale * 100) + "%"; html.classList.toggle("a11y-line-spacing", settings.lineSpacing); html.classList.toggle("a11y-high-contrast", settings.highContrast); html.classList.toggle("a11y-invert-colors", settings.invertColors); html.classList.toggle("a11y-grayscale", settings.grayscale); html.classList.toggle("a11y-underline-links", settings.underlineLinks); html.classList.toggle("a11y-dyslexia-font", settings.dyslexiaFont); html.classList.toggle("a11y-reduce-motion", settings.reduceMotion); html.classList.toggle("a11y-big-cursor", settings.bigCursor); html.classList.toggle("a11y-highlight-focus", settings.highlightFocus); saveSettings(); syncButtonStates(); } function syncButtonStates() { document.querySelectorAll("#a11y-overlay [data-toggle]").forEach((btn) => { const key = btn.getAttribute("data-toggle"); btn.setAttribute("aria-pressed", String(!!settings[key])); }); } function init() { injectStyles(); buildSkipLink(); buildLiveRegion(); const toggleBtn = buildHeader(); const overlay = buildOverlay(); wireEvents(toggleBtn, overlay); applySettings(); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", init); } else { init(); } })();