@@ -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
|
||||
|
||||
@@ -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="<?php echo $params_a11y_links ? '1' : '0'; ?>"
|
||||
data-a11y-font="<?php echo $params_a11y_font ? '1' : '0'; ?>"
|
||||
data-a11y-animations="<?php echo $params_a11y_animations ? '1' : '0'; ?>"
|
||||
data-a11y-reading="<?php echo $params_a11y_reading ? '1' : '0'; ?>"
|
||||
data-a11y-pos="<?php echo htmlspecialchars($params_a11y_pos, ENT_QUOTES, 'UTF-8'); ?>"
|
||||
class="site <?php
|
||||
echo $option . ' ' . $wrapper
|
||||
@@ -344,6 +346,7 @@ $wa->useScript('user.js'); // js/user.js
|
||||
. $hasClass
|
||||
. ($this->direction == 'rtl' ? ' rtl' : '');
|
||||
?>">
|
||||
<a class="skip-link" href="#maincontent"><?php echo Text::_('JSKIP_TO_CONTENT') ?: 'Skip to content'; ?></a>
|
||||
<?php if (!empty($params_visitordetection) && (!empty($params_googletagmanager) || !empty($params_googleanalytics))) :
|
||||
$user = Factory::getUser();
|
||||
$visitorType = $user->guest ? 'guest' : 'logged_in';
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -303,6 +303,13 @@
|
||||
<option value="0">JNO</option>
|
||||
<option value="1">JYES</option>
|
||||
</field>
|
||||
<field name="a11y_reading_mode" type="radio" default="1"
|
||||
label="TPL_MOKO_A11Y_READING_MODE" description="TPL_MOKO_A11Y_READING_MODE_DESC"
|
||||
layout="joomla.form.field.radio.switcher" filter="boolean"
|
||||
showon="a11y_toolbar_enabled:1">
|
||||
<option value="0">JNO</option>
|
||||
<option value="1">JYES</option>
|
||||
</field>
|
||||
<!-- Position forced to bottom-right in index.php -->
|
||||
<field name="a11y_toolbar_pos" type="hidden" default="br"
|
||||
label="TPL_MOKO_A11Y_TOOLBAR_POS" description="TPL_MOKO_A11Y_TOOLBAR_POS_DESC"
|
||||
|
||||
Reference in New Issue
Block a user