From ddf365550100672ea932ec885bd46e548a5faed4 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 14 Apr 2026 20:25:15 -0500 Subject: [PATCH] Fix a11y icon, FAB label colors, add high-contrast stylesheet, sync theme vars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix faIcon() to use elements (FA7 compatibility) - Fix #mokoThemeFab .label and button colors to white (visible on both light and dark themes) - Fix font-weight typo (600px → 600) - Add --footer-padding-* variables to all 6 theme files - Create a11y-high-contrast.css with WCAG AAA contrast ratios for both light and dark modes - Register high-contrast stylesheet in joomla.asset.json - Lazy-load high-contrast CSS when a11y contrast toggle is activated Co-Authored-By: Claude Opus 4.6 (1M context) --- src/joomla.asset.json | 6 + src/media/css/a11y-high-contrast.css | 227 +++++++++++++++++++++++++ src/media/css/template.css | 7 +- src/media/css/theme/dark.custom.css | 6 + src/media/css/theme/dark.standard.css | 6 + src/media/css/theme/light.custom.css | 6 + src/media/css/theme/light.standard.css | 6 + src/media/js/template.js | 20 ++- src/templates/dark.custom.css | 6 + src/templates/light.custom.css | 6 + 10 files changed, 289 insertions(+), 7 deletions(-) create mode 100644 src/media/css/a11y-high-contrast.css diff --git a/src/joomla.asset.json b/src/joomla.asset.json index 56ff7a1..737a35c 100644 --- a/src/joomla.asset.json +++ b/src/joomla.asset.json @@ -124,6 +124,12 @@ "uri": "media/templates/site/mokocassiopeia/css/theme/dark.custom.min.css", "attributes": {"media": "all"} }, + { + "name": "template.a11y-high-contrast", + "type": "style", + "uri": "media/templates/site/mokocassiopeia/css/a11y-high-contrast.css", + "attributes": {"media": "all"} + }, { "name": "template.js", "type": "script", diff --git a/src/media/css/a11y-high-contrast.css b/src/media/css/a11y-high-contrast.css new file mode 100644 index 0000000..10606fa --- /dev/null +++ b/src/media/css/a11y-high-contrast.css @@ -0,0 +1,227 @@ +@charset "UTF-8"; +/* Copyright (C) 2026 Moko Consulting + * + * This file is part of a Moko Consulting project. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * FILE INFORMATION + * DEFGROUP: Joomla.Template.Site + * INGROUP: MokoCassiopeia.Accessibility + * REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia + * PATH: ./media/css/a11y-high-contrast.css + * VERSION: 03.09.14 + * BRIEF: High-contrast stylesheet for accessibility toolbar + */ + +/* =================================================================== + * HIGH CONTRAST MODE + * Applied when .a11y-high-contrast is on . + * Overrides theme variables to maximise contrast ratios (WCAG AAA). + * =================================================================== */ + +/* ── Light mode high contrast ─────────────────────────────────────── */ +:root[data-bs-theme="light"].a11y-high-contrast { + --body-color: #000; + --body-color-rgb: 0, 0, 0; + --body-bg: #fff; + --body-bg-rgb: 255, 255, 255; + --heading-color: #000; + --emphasis-color: #000; + --secondary-color: #000000bf; + --tertiary-color: #00000080; + --muted-color: #333; + + /* Links — strong blue on white */ + --color-link: #0000ee; + --link-color: #0000ee; + --link-color-rgb: 0, 0, 238; + --color-hover: #cc0000; + --link-hover-color: #cc0000; + --link-hover-color-rgb: 204, 0, 0; + + /* Borders — visible on all backgrounds */ + --border-color: #000; + --border-color-soft: #333; + + /* Backgrounds */ + --secondary-bg: #e0e0e0; + --secondary-bg-rgb: 224, 224, 224; + --tertiary-bg: #f0f0f0; + --tertiary-bg-rgb: 240, 240, 240; + + /* Navigation */ + --nav-bg-color: #000; + --nav-text-color: #fff; + --mainmenu-nav-link-color: #fff; + + /* Buttons */ + --btn-color: #fff; + --btn-bg: #000; + --btn-border-color: #000; + --btn-hover-color: #000; + --btn-hover-bg: #ffff00; + --btn-hover-border-color: #000; + --btn-active-color: #000; + --btn-active-bg: #ffff00; + --btn-active-border-color: #000; + + /* Forms */ + --input-color: #000; + --input-bg: #fff; + --input-border-color: #000; + --input-focus-color: #000; + --input-focus-bg: #ffffcc; + --input-focus-border-color: #0000ee; + --input-placeholder-color: #555; + + /* Cards */ + --card-border-color: #000; + --card-bg: #fff; + --card-cap-bg: #e0e0e0; + + /* Tables */ + --table-color: #000; + --table-bg: #fff; + --table-border-color: #000; + --table-striped-bg: #f0f0f0; + --table-hover-bg: #ffff99; + + /* Alerts */ + --alert-border-width: 2px; + + /* Code */ + --code-color: #000; + --code-bg-color: #ffffcc; + + /* Selection */ + --selection-bg: #0000ee; + --selection-ink: #fff; + + /* Focus indicator — always visible */ + --focus-ring-color: #0000ee; + --focus-ring-width: 3px; +} + +/* ── Dark mode high contrast ──────────────────────────────────────── */ +:root[data-bs-theme="dark"].a11y-high-contrast { + --body-color: #fff; + --body-color-rgb: 255, 255, 255; + --body-bg: #000; + --body-bg-rgb: 0, 0, 0; + --heading-color: #fff; + --emphasis-color: #fff; + --secondary-color: #ffffffbf; + --tertiary-color: #ffffff80; + --muted-color: #ccc; + + /* Links — bright yellow on black */ + --color-link: #ffff00; + --link-color: #ffff00; + --link-color-rgb: 255, 255, 0; + --color-hover: #00ffff; + --link-hover-color: #00ffff; + --link-hover-color-rgb: 0, 255, 255; + + /* Borders */ + --border-color: #fff; + --border-color-soft: #ccc; + + /* Backgrounds */ + --secondary-bg: #1a1a1a; + --secondary-bg-rgb: 26, 26, 26; + --tertiary-bg: #111; + --tertiary-bg-rgb: 17, 17, 17; + + /* Navigation */ + --nav-bg-color: #000; + --nav-text-color: #fff; + --mainmenu-nav-link-color: #ffff00; + + /* Buttons */ + --btn-color: #000; + --btn-bg: #ffff00; + --btn-border-color: #ffff00; + --btn-hover-color: #000; + --btn-hover-bg: #00ffff; + --btn-hover-border-color: #00ffff; + --btn-active-color: #000; + --btn-active-bg: #00ffff; + --btn-active-border-color: #00ffff; + + /* Forms */ + --input-color: #fff; + --input-bg: #000; + --input-border-color: #fff; + --input-focus-color: #fff; + --input-focus-bg: #1a1a1a; + --input-focus-border-color: #ffff00; + --input-placeholder-color: #aaa; + + /* Cards */ + --card-border-color: #fff; + --card-bg: #000; + --card-cap-bg: #1a1a1a; + + /* Tables */ + --table-color: #fff; + --table-bg: #000; + --table-border-color: #fff; + --table-striped-bg: #111; + --table-hover-bg: #333; + + /* Alerts */ + --alert-border-width: 2px; + + /* Code */ + --code-color: #00ff00; + --code-bg-color: #1a1a1a; + + /* Selection */ + --selection-bg: #ffff00; + --selection-ink: #000; + + /* Focus indicator */ + --focus-ring-color: #ffff00; + --focus-ring-width: 3px; +} + +/* ── Shared high-contrast overrides (both modes) ──────────────────── */ +.a11y-high-contrast * { + border-color: var(--border-color) !important; +} + +.a11y-high-contrast *:focus-visible { + outline: var(--focus-ring-width, 3px) solid var(--focus-ring-color, #0000ee) !important; + outline-offset: 2px !important; +} + +.a11y-high-contrast img { + outline: 2px solid var(--border-color); +} + +.a11y-high-contrast a { + text-decoration: underline !important; + text-decoration-thickness: 2px !important; +} + +.a11y-high-contrast button, +.a11y-high-contrast .btn, +.a11y-high-contrast input, +.a11y-high-contrast select, +.a11y-high-contrast textarea { + border-width: 2px !important; + border-style: solid !important; +} + +.a11y-high-contrast .badge, +.a11y-high-contrast .alert { + border: 2px solid var(--border-color) !important; +} + +/* Ensure disabled states are still distinguishable */ +.a11y-high-contrast [disabled], +.a11y-high-contrast .disabled { + opacity: .5 !important; + text-decoration: line-through !important; +} diff --git a/src/media/css/template.css b/src/media/css/template.css index 0ff490b..5b82fc3 100644 --- a/src/media/css/template.css +++ b/src/media/css/template.css @@ -17070,8 +17070,8 @@ form .form-select { background: var(--muted-color, #6d757e); box-shadow: var(--box-shadow, 0 .5rem 1rem #00000066); font: inherit; - color: var(--body-bg, #0e1318); - font-weight: 600px; + color: #fff; + font-weight: 600; } #mokoThemeFab.pos-br { @@ -17133,10 +17133,11 @@ button#mokoThemeSwitch { #mokoThemeFab .label { user-select: none; font-size: .875rem; + color: #fff; } #mokoThemeFab button { - color: var(--body-bg, #0e1318); + color: #fff; } /* Auto toggle switch (on/off style) */ diff --git a/src/media/css/theme/dark.custom.css b/src/media/css/theme/dark.custom.css index 50f9782..b04fd7e 100644 --- a/src/media/css/theme/dark.custom.css +++ b/src/media/css/theme/dark.custom.css @@ -817,6 +817,12 @@ color-scheme: dark; --table-active-color: var(--body-color); --table-active-bg: rgba(var(--white-rgb), 0.1); +/* ===== FOOTER ===== */ +--footer-padding-top: 1rem; +--footer-padding-bottom: 80px; +--footer-grid-padding-y: 2.5rem; +--footer-grid-padding-x: 0.5em; + /* ===== BACKDROP ===== */ --backdrop-zindex: 1040; --backdrop-bg: hsl(0, 0%, 0%); diff --git a/src/media/css/theme/dark.standard.css b/src/media/css/theme/dark.standard.css index 97515a7..8f2a82d 100644 --- a/src/media/css/theme/dark.standard.css +++ b/src/media/css/theme/dark.standard.css @@ -817,6 +817,12 @@ color-scheme: dark; --table-active-color: var(--body-color); --table-active-bg: rgba(var(--white-rgb), 0.1); +/* ===== FOOTER ===== */ +--footer-padding-top: 1rem; +--footer-padding-bottom: 80px; +--footer-grid-padding-y: 2.5rem; +--footer-grid-padding-x: 0.5em; + /* ===== BACKDROP ===== */ --backdrop-zindex: 1040; --backdrop-bg: hsl(0, 0%, 0%); diff --git a/src/media/css/theme/light.custom.css b/src/media/css/theme/light.custom.css index 53dc8c4..a99b9db 100644 --- a/src/media/css/theme/light.custom.css +++ b/src/media/css/theme/light.custom.css @@ -816,6 +816,12 @@ color-scheme: light; --table-active-color: var(--body-color); --table-active-bg: rgba(var(--black-rgb), 0.075); +/* ===== FOOTER ===== */ +--footer-padding-top: 1rem; +--footer-padding-bottom: 80px; +--footer-grid-padding-y: 2.5rem; +--footer-grid-padding-x: 0.5em; + /* ===== BACKDROP ===== */ --backdrop-zindex: 1040; --backdrop-bg: hsl(0, 0%, 0%); diff --git a/src/media/css/theme/light.standard.css b/src/media/css/theme/light.standard.css index 3ca6e62..07a74c2 100644 --- a/src/media/css/theme/light.standard.css +++ b/src/media/css/theme/light.standard.css @@ -816,6 +816,12 @@ color-scheme: light; --table-active-color: var(--body-color); --table-active-bg: rgba(var(--black-rgb), 0.075); +/* ===== FOOTER ===== */ +--footer-padding-top: 1rem; +--footer-padding-bottom: 80px; +--footer-grid-padding-y: 2.5rem; +--footer-grid-padding-x: 0.5em; + /* ===== BACKDROP ===== */ --backdrop-zindex: 1040; --backdrop-bg: hsl(0, 0%, 0%); diff --git a/src/media/js/template.js b/src/media/js/template.js index 5345f42..f1fb442 100644 --- a/src/media/js/template.js +++ b/src/media/js/template.js @@ -229,6 +229,18 @@ function applyContrast(on) { root.classList.toggle("a11y-high-contrast", on); + // Lazy-load the high-contrast stylesheet + var hcId = "mokoA11yHcSheet"; + var existing = doc.getElementById(hcId); + if (on && !existing) { + var link = doc.createElement("link"); + link.id = hcId; + link.rel = "stylesheet"; + link.href = (doc.querySelector('link[href*="mokocassiopeia/css/template"]') || {}).href + ? (doc.querySelector('link[href*="mokocassiopeia/css/template"]').href.replace(/\/css\/template[^/]*$/, "/css/a11y-high-contrast.css")) + : "media/templates/site/mokocassiopeia/css/a11y-high-contrast.css"; + doc.head.appendChild(link); + } } function applyLinks(on) { @@ -245,10 +257,10 @@ /** Create a Font Awesome icon element (safe DOM, no innerHTML). */ function faIcon(classes) { - var span = doc.createElement("span"); - span.className = classes; - span.setAttribute("aria-hidden", "true"); - return span; + var i = doc.createElement("i"); + i.className = classes; + i.setAttribute("aria-hidden", "true"); + return i; } function buildA11yToolbar() { diff --git a/src/templates/dark.custom.css b/src/templates/dark.custom.css index f3fdbf3..689bfda 100644 --- a/src/templates/dark.custom.css +++ b/src/templates/dark.custom.css @@ -813,6 +813,12 @@ color-scheme: dark; --table-active-color: var(--body-color); --table-active-bg: rgba(var(--white-rgb), 0.1); +/* ===== FOOTER ===== */ +--footer-padding-top: 1rem; +--footer-padding-bottom: 80px; +--footer-grid-padding-y: 2.5rem; +--footer-grid-padding-x: 0.5em; + /* ===== BACKDROP ===== */ --backdrop-zindex: 1040; --backdrop-bg: hsl(0, 0%, 0%); diff --git a/src/templates/light.custom.css b/src/templates/light.custom.css index 9170f61..d4b6d60 100644 --- a/src/templates/light.custom.css +++ b/src/templates/light.custom.css @@ -812,6 +812,12 @@ color-scheme: light; --table-active-color: var(--body-color); --table-active-bg: rgba(var(--black-rgb), 0.075); +/* ===== FOOTER ===== */ +--footer-padding-top: 1rem; +--footer-padding-bottom: 80px; +--footer-grid-padding-y: 2.5rem; +--footer-grid-padding-x: 0.5em; + /* ===== BACKDROP ===== */ --backdrop-zindex: 1040; --backdrop-bg: hsl(0, 0%, 0%);