diff --git a/CHANGELOG.md b/CHANGELOG.md index 45b1c58..b2d7fac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,13 @@ # Changelog — MokoOnyx (VERSION: 02.33.04) ## [Unreleased] +### Added +- Accessibility toolbar: **Reading mode** toggle — hides sidebars/TOC, narrows the content to a comfortable measure, and relaxes line-height/letter/word spacing for easier reading (cognitive-accessibility / dyslexia aid). Persisted in `localStorage`; toggled via new `a11y_reading_mode` template param. (#83) +- Login view: the theme FAB (with its accessibility toggle) now relocates into the login card as a flush bottom bar with a theme-aware separator, matching the MokoAI widget behavior. Uses a `MutationObserver` so it works even if the login form renders late. (#178) + +### Fixed +- Skip-to-content link (WCAG 2.4.1 Bypass Blocks): the main template had no skip link (only the offline page did), and the `.skip-link:focus` rule had a typo (`.skip-l ink`) that broke the on-focus reveal. Added a "Skip to content" anchor as the first focusable element (targets `#maincontent`) and corrected the focus style. (#75) + ## [02.33.00] --- 2026-07-20 ### Fixed diff --git a/source/index.php b/source/index.php index fab89b4..665eaba 100644 --- a/source/index.php +++ b/source/index.php @@ -57,6 +57,7 @@ $params_a11y_contrast = $this->params->get('a11y_high_contrast', 1); $params_a11y_links = $this->params->get('a11y_highlight_links', 1); $params_a11y_font = $this->params->get('a11y_readable_font', 1); $params_a11y_animations = $this->params->get('a11y_pause_animations', 1); +$params_a11y_reading = $this->params->get('a11y_reading_mode', 1); $params_a11y_pos = 'br'; // Detecting Active Variables @@ -333,6 +334,7 @@ $wa->useScript('user.js'); // js/user.js data-a11y-links="" data-a11y-font="" data-a11y-animations="" + data-a11y-reading="" data-a11y-pos="" class="site useScript('user.js'); // js/user.js . $hasClass . ($this->direction == 'rtl' ? ' rtl' : ''); ?>"> + guest ? 'guest' : 'logged_in'; diff --git a/source/language/en-GB/tpl_mokoonyx.ini b/source/language/en-GB/tpl_mokoonyx.ini index b98e396..c806281 100644 --- a/source/language/en-GB/tpl_mokoonyx.ini +++ b/source/language/en-GB/tpl_mokoonyx.ini @@ -129,6 +129,8 @@ TPL_MOKO_A11Y_READABLE_FONT="Readable font" TPL_MOKO_A11Y_READABLE_FONT_DESC="Allow visitors to switch to a clean system font optimised for readability." TPL_MOKO_A11Y_PAUSE_ANIMATIONS="Pause animations" TPL_MOKO_A11Y_PAUSE_ANIMATIONS_DESC="Allow visitors to stop all CSS animations and transitions." +TPL_MOKO_A11Y_READING_MODE="Reading mode" +TPL_MOKO_A11Y_READING_MODE_DESC="Allow visitors to strip sidebars, narrow the content column, and relax typography for easier reading (cognitive-accessibility / dyslexia aid)." ; ===== CSS Variables tab ===== TPL_MOKOONYX_CSS_VARS_FIELDSET_LABEL="CSS Variables" diff --git a/source/language/en-US/tpl_mokoonyx.ini b/source/language/en-US/tpl_mokoonyx.ini index 416b95e..46b5716 100644 --- a/source/language/en-US/tpl_mokoonyx.ini +++ b/source/language/en-US/tpl_mokoonyx.ini @@ -129,6 +129,8 @@ TPL_MOKO_A11Y_READABLE_FONT="Readable font" TPL_MOKO_A11Y_READABLE_FONT_DESC="Allow visitors to switch to a clean system font optimized for readability." TPL_MOKO_A11Y_PAUSE_ANIMATIONS="Pause animations" TPL_MOKO_A11Y_PAUSE_ANIMATIONS_DESC="Allow visitors to stop all CSS animations and transitions." +TPL_MOKO_A11Y_READING_MODE="Reading mode" +TPL_MOKO_A11Y_READING_MODE_DESC="Allow visitors to strip sidebars, narrow the content column, and relax typography for easier reading (cognitive-accessibility / dyslexia aid)." ; ===== CSS Variables tab ===== TPL_MOKOONYX_CSS_VARS_FIELDSET_LABEL="CSS Variables" diff --git a/source/media/css/template.css b/source/media/css/template.css index 44648b2..722a7cc 100644 --- a/source/media/css/template.css +++ b/source/media/css/template.css @@ -17230,6 +17230,24 @@ body.has-gtranslate #mokoThemeFab.pos-bl { top: 1rem; } +/* Login view: FAB relocated into the login card as a flush bottom bar (parity with + MokoAI). Stretches to the card edges (assumes ~1rem card padding), drops its float + positioning, and uses a theme-aware separator instead of a shadow. */ +#mokoThemeFab.in-card { + position: static; + transform: none; + inset: auto; + width: auto; + margin: 1.25rem -1rem -1rem; + padding: .875rem; + border: 0; + border-top: 1px solid var(--border-color, #dfe3e7); + border-radius: 0; + box-shadow: none; + background: transparent; + justify-content: center; +} + /* Sun/Moon theme toggle button */ .theme-icon-btn { display: flex; @@ -17477,11 +17495,15 @@ body.site.error-page { overflow: hidden; } -.skip-l ink:focus { +.skip-link:focus { position: static; width: auto; height: auto; padding: .5rem 1rem; + z-index: 2000; + background: var(--color-primary, #112855); + color: #fff; + border-radius: 0 0 .25rem 0; } .btn { @@ -17554,6 +17576,28 @@ html.a11y-pause-animations *::after { scroll-behavior: auto !important; } +/* Reading mode: cognitive-accessibility / dyslexia aid — hide sidebars + TOC, + narrow the content to a comfortable measure, and relax typography. */ +html.a11y-reading-mode .container-sidebar-left, +html.a11y-reading-mode .container-sidebar-right, +html.a11y-reading-mode #toc, +html.a11y-reading-mode .container-toc { + display: none !important; +} +html.a11y-reading-mode .container-component { + max-width: 72ch; + margin-inline: auto; + float: none; + width: auto; +} +html.a11y-reading-mode #maincontent, +html.a11y-reading-mode #maincontent p, +html.a11y-reading-mode #maincontent li { + line-height: 1.9; + letter-spacing: 0.02em; + word-spacing: 0.08em; +} + /* Toolbar container */ #mokoA11yToolbar { position: fixed; diff --git a/source/media/js/template.js b/source/media/js/template.js index 205c5f4..b59b66e 100644 --- a/source/media/js/template.js +++ b/source/media/js/template.js @@ -209,7 +209,7 @@ var raw = localStorage.getItem(a11yStorageKey); if (raw) return JSON.parse(raw); } catch (e) {} - return { fontStep: defaultStep, inverted: false, contrast: false, links: false, font: false, paused: false }; + return { fontStep: defaultStep, inverted: false, contrast: false, links: false, font: false, paused: false, reading: false }; } function saveA11yPrefs(prefs) { @@ -256,6 +256,10 @@ root.classList.toggle("a11y-pause-animations", on); } + function applyReadingMode(on) { + root.classList.toggle("a11y-reading-mode", on); + } + /** Create a Font Awesome icon element (safe DOM, no innerHTML). */ function faIcon(classes) { var i = doc.createElement("i"); @@ -274,6 +278,7 @@ var showLinks = body.getAttribute("data-a11y-links") === "1"; var showFont = body.getAttribute("data-a11y-font") === "1"; var showAnimations = body.getAttribute("data-a11y-animations") === "1"; + var showReading = body.getAttribute("data-a11y-reading") === "1"; var pos = (body.getAttribute("data-a11y-pos") || "tl").toLowerCase(); if (!/^(br|bl|tr|tl)$/.test(pos)) pos = "tl"; @@ -417,6 +422,7 @@ addSwitchOption(showLinks, "links", "fa-solid fa-link", "Highlight links", applyLinks); addSwitchOption(showFont, "font", "fa-solid fa-font", "Readable font", applyFont); addSwitchOption(showAnimations, "paused", "fa-solid fa-pause", "Pause animations", applyPaused); + addSwitchOption(showReading, "reading", "fa-solid fa-book-open-reader", "Reading mode", applyReadingMode); // Apply saved preferences on load if (prefs.fontStep !== defaultStep) applyFontSize(prefs.fontStep); @@ -425,6 +431,7 @@ if (prefs.links) applyLinks(true); if (prefs.font) applyFont(true); if (prefs.paused) applyPaused(true); + if (prefs.reading) applyReadingMode(true); // If theme FAB is present, mount a11y toggle inside it; otherwise standalone var fabSlot = doc.getElementById("mokoA11ySlot"); @@ -477,6 +484,37 @@ } } + /** + * Relocate the theme FAB into the login card as a flush bottom bar (parity with + * MokoAI). Only on the com_users login view. The FAB (with its a11y toggle) becomes + * part of the card via the `in-card` class. Uses a MutationObserver in case the + * login form renders late; Joomla's login container is `.com-users-login`, with + * fallbacks in case a template override changes the wrapper. + */ + function relocateFabToLoginCard() { + if (!doc.body.classList.contains("view-login")) return; + + function move() { + var fab = doc.getElementById("mokoThemeFab"); + if (!fab) return false; + var card = doc.querySelector(".com-users-login, .login .card, main#maincontent .card, .login-max"); + if (!card) return false; + fab.classList.add("in-card"); + card.appendChild(fab); + return true; + } + + if (move()) return; + + if (typeof win.MutationObserver === "function") { + var obs = new win.MutationObserver(function () { + if (move()) obs.disconnect(); + }); + obs.observe(doc.body, { childList: true, subtree: true }); + win.setTimeout(function () { obs.disconnect(); move(); }, 3000); + } + } + // ======================================================================== // TEMPLATE UTILITIES // ======================================================================== @@ -940,6 +978,9 @@ buildA11yToolbar(); } + // On the login view, relocate the FAB into the login card as a flush bottom bar + relocateFabToLoginCard(); + // Sticky header behavior handleScroll(); win.addEventListener("scroll", handleScroll); diff --git a/source/templateDetails.xml b/source/templateDetails.xml index 8482b92..5483a1d 100644 --- a/source/templateDetails.xml +++ b/source/templateDetails.xml @@ -303,6 +303,13 @@ + + + +