From 66366682608ef9757657824f0b62f0a882a94ae9 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 14 Apr 2026 20:50:10 -0500 Subject: [PATCH 001/120] Add Unicode icon fallback for a11y, fix duplicate label color - Add wheelchair symbol fallback when FA7 glyph fails to render - Fix duplicate #mokoThemeFab .label rule missing color: #fff Co-Authored-By: Claude Opus 4.6 (1M context) --- src/media/css/template.css | 1 + src/media/js/template.js | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/media/css/template.css b/src/media/css/template.css index 5b82fc3..1365d55 100644 --- a/src/media/css/template.css +++ b/src/media/css/template.css @@ -17389,6 +17389,7 @@ button#mokoThemeSwitch { #mokoThemeFab .label { user-select: none; font-size: .875rem; + color: #fff; } #mokoThemeFab.debug-outline { diff --git a/src/media/js/template.js b/src/media/js/template.js index f1fb442..49ae3ae 100644 --- a/src/media/js/template.js +++ b/src/media/js/template.js @@ -292,7 +292,17 @@ toggle.className = "a11y-toggle"; toggle.setAttribute("aria-label", "Accessibility options"); toggle.setAttribute("aria-expanded", "false"); - toggle.appendChild(faIcon("fa-solid fa-universal-access")); + var a11yIcon = faIcon("fa-solid fa-universal-access"); + // Unicode fallback if FA7 glyph doesn't render (e.g. FA6/FA7 conflict) + setTimeout(function () { + var cs = win.getComputedStyle(a11yIcon, "::before"); + if (!cs.content || cs.content === "none" || cs.content === '""' || cs.content === '"" / ""') { + a11yIcon.className = ""; + a11yIcon.textContent = "\u267F"; + a11yIcon.style.fontSize = "1.1rem"; + } + }, 500); + toggle.appendChild(a11yIcon); // Panel var panel = doc.createElement("div"); From 37943e28dc41a203a001df68259d8c87e796adce Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 14 Apr 2026 20:59:10 -0500 Subject: [PATCH 002/120] Fix updates.xml: GitHub fallback URLs, update dev SHA-256 Co-Authored-By: Claude Opus 4.6 (1M context) --- updates.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/updates.xml b/updates.xml index 7c41b38..1742731 100644 --- a/updates.xml +++ b/updates.xml @@ -18,9 +18,9 @@ https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/tag/development https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/development/mokocassiopeia-03.09.14-dev.zip - https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/development/mokocassiopeia-03.09.14-dev.zip + https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/development/mokocassiopeia-03.09.14-dev.zip - 4cbe4fc379182ef17580396e7d12ce4ce95a90017ef364b922bdc2d04b0b3d97 + 20c49e0940d99e7d682127cd8cb4c1787047e41ab974da4c6535f581b000b615 development Moko Consulting https://mokoconsulting.tech @@ -40,7 +40,7 @@ https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/tag/alpha https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/alpha/mokocassiopeia-03.09.14-alpha.zip - https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/alpha/mokocassiopeia-03.09.14-alpha.zip + https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/alpha/mokocassiopeia-03.09.14-alpha.zip c2660acdf7389244462485f7ab4c286e9f851366a148acc16739a184576f7932 alpha @@ -62,7 +62,7 @@ https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/tag/beta https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/beta/mokocassiopeia-03.09.14-beta.zip - https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/beta/mokocassiopeia-03.09.14-beta.zip + https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/beta/mokocassiopeia-03.09.14-beta.zip 4cbe4fc379182ef17580396e7d12ce4ce95a90017ef364b922bdc2d04b0b3d97 beta @@ -84,7 +84,7 @@ https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/tag/release-candidate https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/release-candidate/mokocassiopeia-03.09.14-rc.zip - https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/release-candidate/mokocassiopeia-03.09.14-rc.zip + https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/release-candidate/mokocassiopeia-03.09.14-rc.zip c2660acdf7389244462485f7ab4c286e9f851366a148acc16739a184576f7932 rc @@ -106,7 +106,7 @@ https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/tag/v03 https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/v03/mokocassiopeia-03.09.14.zip - https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/v03/mokocassiopeia-03.09.14.zip + https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/v03/mokocassiopeia-03.09.14.zip c2660acdf7389244462485f7ab4c286e9f851366a148acc16739a184576f7932 stable From a570b5d4680de9a1517b6b0d000d49bbbc81c1dc Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 14 Apr 2026 21:04:02 -0500 Subject: [PATCH 003/120] =?UTF-8?q?chore(version):=20bump=2003.09.14=20?= =?UTF-8?q?=E2=86=92=2003.09.15?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 2 +- src/joomla.asset.json | 2 +- src/templateDetails.xml | 4 ++-- updates.xml | 32 ++++++++++++++++---------------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 00684c6..5eeac79 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ INGROUP: MokoCassiopeia.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia FILE: ./README.md - VERSION: 03.09.14 + VERSION: 03.09.15 BRIEF: Documentation for MokoCassiopeia template --> diff --git a/src/joomla.asset.json b/src/joomla.asset.json index 16a8f1a..37d692f 100644 --- a/src/joomla.asset.json +++ b/src/joomla.asset.json @@ -17,7 +17,7 @@ "defgroup": "Joomla.Template.Site", "ingroup": "MokoCassiopeia.Template.Assets", "path": "./media/templates/site/mokocassiopeia/joomla.asset.json", - "version": "03.09.14", + "version": "03.09.15", "brief": "Joomla asset registry for MokoCassiopeia" } }, diff --git a/src/templateDetails.xml b/src/templateDetails.xml index f928b11..1706f11 100644 --- a/src/templateDetails.xml +++ b/src/templateDetails.xml @@ -39,13 +39,13 @@ MokoCassiopeia - 03.09.14 + 03.09.15 script.php 2026-04-14 Jonathan Miller || Moko Consulting hello@mokoconsulting.tech (C)GNU General Public License Version 3 - 2026 Moko Consulting - Version 03.09.14 License Joomla PHP

MokoCassiopeia Template Description

MokoCassiopeia continues Joomla's tradition of space-themed default templates— building on the legacy of Solarflare (Joomla 1.0), Milkyway (Joomla 1.5), and Protostar (Joomla 3.0).

This template is a customized fork of the Cassiopeia template introduced in Joomla 4, preserving its modern, accessible, and mobile-first foundation while introducing new stylistic enhancements and structural refinements specifically tailored for use by Moko Consulting.

Custom Colour Themes

Starter palette files are included with the template. To create a custom colour scheme, copy templates/mokocassiopeia/templates/light.custom.css to media/templates/site/mokocassiopeia/css/theme/light.custom.css, or templates/mokocassiopeia/templates/dark.custom.css to media/templates/site/mokocassiopeia/css/theme/dark.custom.css. Customise the CSS variables to match your brand, then activate your palette in System → Site Templates → MokoCassiopeia → Theme tab by selecting "Custom" for the Light or Dark Mode Palette. A full variable reference is available in the CSS Variables tab in template options.

Custom CSS & JavaScript

For site-specific styles and scripts that should survive template updates, create the following files:

  • media/templates/site/mokocassiopeia/css/user.css — loaded on every page for custom CSS overrides.
  • media/templates/site/mokocassiopeia/js/user.js — loaded on every page for custom JavaScript.

These files are gitignored and will not be overwritten by template updates.

Code Attribution

This template is based on the original Cassiopeia template developed by the Joomla! Project and released under the GNU General Public License.

Modifications and enhancements have been made by Moko Consulting in accordance with open-source licensing standards.

It includes integration with Bootstrap TOC, an open-source table of contents generator by A. Feld, licensed under the MIT License.

All third-party libraries and assets remain the property of their respective authors and are credited within their source files where applicable.

]]>
+ Version 03.09.15 License Joomla PHP

MokoCassiopeia Template Description

MokoCassiopeia continues Joomla's tradition of space-themed default templates— building on the legacy of Solarflare (Joomla 1.0), Milkyway (Joomla 1.5), and Protostar (Joomla 3.0).

This template is a customized fork of the Cassiopeia template introduced in Joomla 4, preserving its modern, accessible, and mobile-first foundation while introducing new stylistic enhancements and structural refinements specifically tailored for use by Moko Consulting.

Custom Colour Themes

Starter palette files are included with the template. To create a custom colour scheme, copy templates/mokocassiopeia/templates/light.custom.css to media/templates/site/mokocassiopeia/css/theme/light.custom.css, or templates/mokocassiopeia/templates/dark.custom.css to media/templates/site/mokocassiopeia/css/theme/dark.custom.css. Customise the CSS variables to match your brand, then activate your palette in System → Site Templates → MokoCassiopeia → Theme tab by selecting "Custom" for the Light or Dark Mode Palette. A full variable reference is available in the CSS Variables tab in template options.

Custom CSS & JavaScript

For site-specific styles and scripts that should survive template updates, create the following files:

  • media/templates/site/mokocassiopeia/css/user.css — loaded on every page for custom CSS overrides.
  • media/templates/site/mokocassiopeia/js/user.js — loaded on every page for custom JavaScript.

These files are gitignored and will not be overwritten by template updates.

Code Attribution

This template is based on the original Cassiopeia template developed by the Joomla! Project and released under the GNU General Public License.

Modifications and enhancements have been made by Moko Consulting in accordance with open-source licensing standards.

It includes integration with Bootstrap TOC, an open-source table of contents generator by A. Feld, licensed under the MIT License.

All third-party libraries and assets remain the property of their respective authors and are credited within their source files where applicable.

]]>
1 component.php diff --git a/updates.xml b/updates.xml index 1742731..cf3529c 100644 --- a/updates.xml +++ b/updates.xml @@ -1,7 +1,7 @@ @@ -13,12 +13,12 @@ mokocassiopeia template site - 03.09.14 + 03.09.15 2026-04-14 https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/tag/development - https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/development/mokocassiopeia-03.09.14-dev.zip - https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/development/mokocassiopeia-03.09.14-dev.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/development/mokocassiopeia-03.09.15-dev.zip + https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/development/mokocassiopeia-03.09.15-dev.zip 20c49e0940d99e7d682127cd8cb4c1787047e41ab974da4c6535f581b000b615 development @@ -35,12 +35,12 @@ mokocassiopeia template site - 03.09.14 + 03.09.15 2026-04-14 https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/tag/alpha - https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/alpha/mokocassiopeia-03.09.14-alpha.zip - https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/alpha/mokocassiopeia-03.09.14-alpha.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/alpha/mokocassiopeia-03.09.15-alpha.zip + https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/alpha/mokocassiopeia-03.09.15-alpha.zip c2660acdf7389244462485f7ab4c286e9f851366a148acc16739a184576f7932 alpha @@ -57,12 +57,12 @@ mokocassiopeia template site - 03.09.14 + 03.09.15 2026-04-14 https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/tag/beta - https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/beta/mokocassiopeia-03.09.14-beta.zip - https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/beta/mokocassiopeia-03.09.14-beta.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/beta/mokocassiopeia-03.09.15-beta.zip + https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/beta/mokocassiopeia-03.09.15-beta.zip 4cbe4fc379182ef17580396e7d12ce4ce95a90017ef364b922bdc2d04b0b3d97 beta @@ -79,12 +79,12 @@ mokocassiopeia template site - 03.09.14 + 03.09.15 2026-04-14 https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/tag/release-candidate - https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/release-candidate/mokocassiopeia-03.09.14-rc.zip - https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/release-candidate/mokocassiopeia-03.09.14-rc.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/release-candidate/mokocassiopeia-03.09.15-rc.zip + https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/release-candidate/mokocassiopeia-03.09.15-rc.zip c2660acdf7389244462485f7ab4c286e9f851366a148acc16739a184576f7932 rc @@ -101,12 +101,12 @@ mokocassiopeia template site - 03.09.14 + 03.09.15 2026-04-14 https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/tag/v03 - https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/v03/mokocassiopeia-03.09.14.zip - https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/v03/mokocassiopeia-03.09.14.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/v03/mokocassiopeia-03.09.15.zip + https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/v03/mokocassiopeia-03.09.15.zip c2660acdf7389244462485f7ab4c286e9f851366a148acc16739a184576f7932 stable From d97ed3bded5b38fcbe514b04ad1e7f5f5362ee68 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 14 Apr 2026 21:07:54 -0500 Subject: [PATCH 004/120] chore: update dev SHA-256 for 03.09.15 [skip ci] Co-Authored-By: Claude Opus 4.6 (1M context) --- updates.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/updates.xml b/updates.xml index cf3529c..3fb3a81 100644 --- a/updates.xml +++ b/updates.xml @@ -20,7 +20,7 @@ https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/development/mokocassiopeia-03.09.15-dev.zip https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/development/mokocassiopeia-03.09.15-dev.zip - 20c49e0940d99e7d682127cd8cb4c1787047e41ab974da4c6535f581b000b615 + 310fc79b042a042c0b518a806018011d5243893f239d000224fb197943fee5e3 development Moko Consulting https://mokoconsulting.tech From 336208826893d1e8bd5733113705d4acb4146fcd Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 14 Apr 2026 21:18:43 -0500 Subject: [PATCH 005/120] Style a11y button: blue bg, white icon, theme-aware shade MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Light mode uses darker blue (#1565c0), dark mode uses lighter blue (#42a5f5). No hover effect — only active ring when panel is open. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/media/css/template.css | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/media/css/template.css b/src/media/css/template.css index 1365d55..5c05495 100644 --- a/src/media/css/template.css +++ b/src/media/css/template.css @@ -17207,6 +17207,15 @@ button#mokoThemeSwitch { } /* Inline a11y toggle inside theme FAB */ +/* Light mode: darker blue */ +:root[data-bs-theme="light"] .a11y-toggle-inline { + --a11y-btn-bg: #1565c0; +} +/* Dark mode: lighter blue */ +:root[data-bs-theme="dark"] .a11y-toggle-inline { + --a11y-btn-bg: #42a5f5; +} + .a11y-toggle-inline { display: flex; align-items: center; @@ -17214,25 +17223,16 @@ button#mokoThemeSwitch { width: 28px; height: 28px; border-radius: 50%; - border: 1.5px solid currentColor; - background: transparent; - color: inherit; + border: none; + background: var(--a11y-btn-bg, #1976d2); + color: #fff; font-size: 1rem; cursor: pointer; padding: 0; - transition: background .2s, color .2s; - opacity: .8; -} - -.a11y-toggle-inline:hover, -.a11y-toggle-inline:focus-visible { - opacity: 1; - background: rgba(255,255,255,.15); } .a11y-toggle-inline.active { - opacity: 1; - background: rgba(255,255,255,.25); + box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--a11y-btn-bg, #1976d2); } /* Floating a11y panel when inline */ From 4e66562958faa5532667e5d2c99194490bc4c868 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 14 Apr 2026 21:34:42 -0500 Subject: [PATCH 006/120] Remove redundant #mokoThemeFab button color rule Co-Authored-By: Claude Opus 4.6 (1M context) --- src/media/css/template.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/media/css/template.css b/src/media/css/template.css index 5c05495..5a5daa9 100644 --- a/src/media/css/template.css +++ b/src/media/css/template.css @@ -17136,10 +17136,6 @@ button#mokoThemeSwitch { color: #fff; } -#mokoThemeFab button { - color: #fff; -} - /* Auto toggle switch (on/off style) */ .auto-toggle-wrap { display: flex; From 2a7c173f7bc30c768cc7520816621b4c7fcf4e0b Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 14 Apr 2026 21:43:28 -0500 Subject: [PATCH 007/120] Replace light/dark switch with sun/moon icon button - Remove toggle switch, Light/Dark labels, knob/track CSS - Single button with sun (light) and moon (dark) FA icons - Icons cross-fade with rotation transition on theme change - Compact circular button matches FAB aesthetic - Removed duplicate old switch CSS rules Co-Authored-By: Claude Opus 4.6 (1M context) --- src/media/css/template.css | 110 ++++++++++++++----------------------- src/media/js/template.js | 49 +++++++++-------- 2 files changed, 65 insertions(+), 94 deletions(-) diff --git a/src/media/css/template.css b/src/media/css/template.css index 5a5daa9..b144a33 100644 --- a/src/media/css/template.css +++ b/src/media/css/template.css @@ -17064,7 +17064,7 @@ form .form-select { display: flex; align-items: center; gap: .5rem; - padding: calc(var(--padding-x, 0.25rem) * 2) calc(var(--padding-y, 0.25rem) * 3) calc(var(--padding-x, 0.25rem) * 2) calc(var(--padding-y, 0.25rem) * 8); + padding: .4rem .6rem; border-radius: 999px; border: none; background: var(--muted-color, #6d757e); @@ -17094,46 +17094,47 @@ form .form-select { top: 1rem; } -#mokoThemeFab .switch { - display: inline-flex; +/* Sun/Moon theme toggle button */ +.theme-icon-btn { + display: flex; align-items: center; - position: relative; - width: 44px; - height: 24px; - background: var(--secondary-color, #e6ebf1bf); - transition: background .2s, border-color .2s; - border-radius: var(--border-radius-xxl, 2rem); -} - -#mokoThemeFab .knob { - position: absolute; - top: 2px; - left: 2px; - width: 20px; - height: 20px; - border-radius: var(--border-radius-xxl, 2rem); - background: var(--bs-body-bg, #fff); - box-shadow: var(--box-shadow, 0 .5rem 1rem #00000066); - transition: transform .2s ease; -} - -#mokoThemeFab [role="switch"][aria-checked="true"] .knob { - transform: translateX(20px); -} - -#mokoThemeFab [role="switch"][aria-checked="true"] .switch { - background: rgba(var(--secondary-color, #e6ebf1bf), .15); -} - -button#mokoThemeSwitch { - border: unset; - background-color: unset; -} - -#mokoThemeFab .label { - user-select: none; - font-size: .875rem; + justify-content: center; + width: 32px; + height: 32px; + border: none; + border-radius: 50%; + background: rgba(255,255,255,.15); color: #fff; + font-size: 1.1rem; + cursor: pointer; + padding: 0; + position: relative; +} + +.theme-icon-btn .fa-sun, +.theme-icon-btn .fa-moon { + position: absolute; + transition: opacity .2s, transform .2s; +} + +/* Light mode: show sun, hide moon */ +.theme-icon-btn.is-light .fa-sun { + opacity: 1; + transform: rotate(0deg); +} +.theme-icon-btn.is-light .fa-moon { + opacity: 0; + transform: rotate(-90deg); +} + +/* Dark mode: show moon, hide sun */ +.theme-icon-btn.is-dark .fa-moon { + opacity: 1; + transform: rotate(0deg); +} +.theme-icon-btn.is-dark .fa-sun { + opacity: 0; + transform: rotate(90deg); } /* Auto toggle switch (on/off style) */ @@ -17357,37 +17358,6 @@ body.site.error-page { text-decoration: none; } -#mokoThemeFab .knob { - position: absolute; - top: 2px; - left: 2px; - width: 20px; - height: 20px; - border-radius: var(--border-radius-xxl, 2rem); - background: var(--bs-body-bg, #fff); - box-shadow: var(--box-shadow, 0 .5rem 1rem #00000066); - transition: transform .2s ease; -} - -#mokoThemeFab [role="switch"][aria-checked="true"] .knob { - transform: translateX(20px); -} - -#mokoThemeFab [role="switch"][aria-checked="true"] .switch { - background: rgba(var(--secondary-color, #e6ebf1bf), .15); -} - -button#mokoThemeSwitch { - border: unset; - background-color: unset; -} - -#mokoThemeFab .label { - user-select: none; - font-size: .875rem; - color: #fff; -} - #mokoThemeFab.debug-outline { outline: 2px dashed var(--pink, #ff8fc0); outline-offset: 2px; diff --git a/src/media/js/template.js b/src/media/js/template.js index 49ae3ae..94a6909 100644 --- a/src/media/js/template.js +++ b/src/media/js/template.js @@ -62,30 +62,33 @@ wrap.id = 'mokoThemeFab'; wrap.className = posClassFromBody(); - // Light label - var lblL = doc.createElement('span'); - lblL.className = 'label'; - lblL.textContent = 'Light'; - - // Switch + // Sun/Moon toggle button var switchWrap = doc.createElement('button'); switchWrap.id = 'mokoThemeSwitch'; switchWrap.type = 'button'; - switchWrap.setAttribute('role', 'switch'); + switchWrap.className = 'theme-icon-btn'; switchWrap.setAttribute('aria-label', 'Toggle dark mode'); - switchWrap.setAttribute('aria-checked', 'false'); - var track = doc.createElement('span'); - track.className = 'switch'; - var knob = doc.createElement('span'); - knob.className = 'knob'; - track.appendChild(knob); - switchWrap.appendChild(track); + var sunIcon = doc.createElement('i'); + sunIcon.className = 'fa-solid fa-sun'; + sunIcon.setAttribute('aria-hidden', 'true'); - // Dark label - var lblD = doc.createElement('span'); - lblD.className = 'label'; - lblD.textContent = 'Dark'; + var moonIcon = doc.createElement('i'); + moonIcon.className = 'fa-solid fa-moon'; + moonIcon.setAttribute('aria-hidden', 'true'); + + switchWrap.appendChild(sunIcon); + switchWrap.appendChild(moonIcon); + + function updateThemeIcon(theme) { + if (theme === 'dark') { + switchWrap.classList.add('is-dark'); + switchWrap.classList.remove('is-light'); + } else { + switchWrap.classList.add('is-light'); + switchWrap.classList.remove('is-dark'); + } + } // Auto toggle (on/off switch style) var autoWrap = doc.createElement('div'); @@ -127,7 +130,7 @@ var current = (root.getAttribute('data-bs-theme') || 'light').toLowerCase(); var next = current === 'dark' ? 'light' : 'dark'; applyTheme(next); - switchWrap.setAttribute('aria-checked', next === 'dark' ? 'true' : 'false'); + updateThemeIcon(next); // Turn off auto when manually switching auto.classList.remove('on'); auto.setAttribute('aria-checked', 'false'); @@ -145,7 +148,7 @@ clearStored(); var sys = systemTheme(); applyTheme(sys); - switchWrap.setAttribute('aria-checked', sys === 'dark' ? 'true' : 'false'); + updateThemeIcon(sys); } }); @@ -154,7 +157,7 @@ if (!getStored()) { var sys = systemTheme(); applyTheme(sys); - switchWrap.setAttribute('aria-checked', sys === 'dark' ? 'true' : 'false'); + updateThemeIcon(sys); } }; if (typeof mql.addEventListener === 'function') mql.addEventListener('change', onMql); @@ -162,12 +165,10 @@ // Initial state var initial = getStored() || systemTheme(); - switchWrap.setAttribute('aria-checked', initial === 'dark' ? 'true' : 'false'); + updateThemeIcon(initial); // Mount - wrap.appendChild(lblL); wrap.appendChild(switchWrap); - wrap.appendChild(lblD); wrap.appendChild(autoWrap); wrap.appendChild(divider); wrap.appendChild(a11ySlot); From 3c9cd8383c43646f0c285ab9a50b45ff88ea0ee8 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 14 Apr 2026 23:31:18 -0500 Subject: [PATCH 008/120] Upated Module: footer --- src/media/css/template.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/media/css/template.css b/src/media/css/template.css index b144a33..889b8a3 100644 --- a/src/media/css/template.css +++ b/src/media/css/template.css @@ -15771,7 +15771,13 @@ body.wrapper-fluid header>.grid-child { } footer .grid-child>div { - padding: var(--navbar-padding-y, 1rem) var(--navbar-padding-x, 1rem) 0; + padding: calc(var(--navbar-padding-y, 1rem) * 3) + calc(var(--navbar-padding-x, 1rem) * 1) + 0; +} + +.mod-footer { + border-top: 1px solid var(--border-gray, #b2bfcds); } header .grid-child .navbar-brand { From cb4468af19a83daaff23db29b31a5ed16c7d0b59 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 16 Apr 2026 00:56:20 -0500 Subject: [PATCH 009/120] v03.09.16: brand-aside columns, offline page redesign, variable click-to-copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Brand-aside position now uses flex columns like top-a (card style, equal-width) - Offline page: external offline.css with theme variables, 3-column centered card layout, Osaka font loading, full-screen on mobile - CSS variable click-to-copy: scans text for --var patterns, wraps in clickable chips with toast notification on copy - Search button border matches input border (--input-border-color) - mod_stats override: converted from dl to table layout - Patch bump 03.09.15 → 03.09.16 Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 2 +- src/html/mod_stats/default.php | 16 ++- src/index.php | 2 +- src/joomla.asset.json | 2 +- src/media/css/offline.css | 237 +++++++++++++++++++++++++++++++++ src/media/css/template.css | 53 +++++++- src/media/js/template.js | 149 +++++++++++++++++++++ src/offline.php | 204 ++++++++++++++-------------- src/templateDetails.xml | 6 +- updates.xml | 32 ++--- 10 files changed, 568 insertions(+), 135 deletions(-) create mode 100644 src/media/css/offline.css diff --git a/README.md b/README.md index 5eeac79..6497f4f 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ INGROUP: MokoCassiopeia.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia FILE: ./README.md - VERSION: 03.09.15 + VERSION: 03.09.16 BRIEF: Documentation for MokoCassiopeia template --> diff --git a/src/html/mod_stats/default.php b/src/html/mod_stats/default.php index d6aa1fd..7b6fdcf 100644 --- a/src/html/mod_stats/default.php +++ b/src/html/mod_stats/default.php @@ -26,10 +26,14 @@ $headerClass = htmlspecialchars($params->get('header_class', ''), ENT_COMPAT, 'U showtitle) : ?> < class="mod-stats__title">title; ?>> -
- -
title; ?>
-
data; ?>
- -
+ + + + + + + + + +
title; ?>data; ?>
diff --git a/src/index.php b/src/index.php index 1198dca..e30eb97 100644 --- a/src/index.php +++ b/src/index.php @@ -406,7 +406,7 @@ $wa->useScript('user.js'); // js/user.js countModules('brand-aside', true)) : ?>
- +
diff --git a/src/joomla.asset.json b/src/joomla.asset.json index 37d692f..f51468b 100644 --- a/src/joomla.asset.json +++ b/src/joomla.asset.json @@ -17,7 +17,7 @@ "defgroup": "Joomla.Template.Site", "ingroup": "MokoCassiopeia.Template.Assets", "path": "./media/templates/site/mokocassiopeia/joomla.asset.json", - "version": "03.09.15", + "version": "03.09.16", "brief": "Joomla asset registry for MokoCassiopeia" } }, diff --git a/src/media/css/offline.css b/src/media/css/offline.css new file mode 100644 index 0000000..19c27f8 --- /dev/null +++ b/src/media/css/offline.css @@ -0,0 +1,237 @@ +/* Copyright (C) 2026 Moko Consulting + + This file is part of a Moko Consulting project. + + SPDX-License-Identifier: GPL-3.0-or-later + */ + +/* === Offline Page Layout === */ +.moko-offline-wrap { + min-height: 100vh; + display: grid; + grid-template-rows: auto 1fr auto; + background-color: var(--body-bg, #0e1318); + color: var(--body-font-color, #e6ebf1); + font-family: var(--body-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif); +} + +.moko-offline-main { + display: grid; + place-items: center; + padding: 2rem 1rem; +} + +/* === Centered Card Grid === */ +.moko-offline-grid { + display: grid; + grid-template-columns: 1fr minmax(0, 720px) 1fr; + width: 100%; + max-width: 1200px; + margin: 0 auto; +} + +.moko-offline-grid__side { + display: block; +} + +.moko-offline-grid__center { + min-width: 0; +} + +@media (max-width: 767.98px) { + .moko-offline-grid { + grid-template-columns: 1fr; + } + + .moko-offline-grid__side { + display: none; + } + + .moko-offline-main { + padding: 0; + } + + .moko-offline-card { + border-radius: 0; + border: none; + min-height: 100%; + } +} + +/* === Card === */ +.moko-offline-card { + background-color: var(--card-bg, var(--secondary-bg, #151b22)); + border: var(--card-border-width, 1px) solid var(--card-border-color, var(--border-color, #2b323b)); + border-radius: var(--border-radius, 0.25rem); + box-shadow: var(--box-shadow, 0 0.5rem 1rem rgba(0, 0, 0, 0.26)); + padding: 2.5rem; +} + +@media (min-width: 768px) { + .moko-offline-card { + padding: 3rem; + } +} + +/* === Brand / Logo === */ +.moko-offline-brand { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + gap: 0.75rem; + text-decoration: none; + color: var(--body-font-color, #e6ebf1); + margin-bottom: 2rem; +} + +.moko-offline-brand:hover { + color: var(--color-link, #8ab4f8); +} + +.moko-offline-brand img { + max-width: 200px; + max-height: 80px; + width: auto; + height: auto; +} + +.moko-offline-brand .site-title { + font-size: 1.75rem; + font-weight: 700; + font-family: 'Osaka', var(--body-font-family, sans-serif); +} + +.moko-offline-brand .brand-tagline { + display: block; + opacity: 0.75; + font-size: 0.875rem; + line-height: 1.2; +} + +/* === Header === */ +.moko-offline-header { + background-color: var(--header-bg, var(--color-primary, #112855)); + color: var(--mainmenu-nav-link-color, #fff); + padding: 1rem 0; +} + +.moko-offline-header .container { + display: flex; + align-items: center; + gap: 1rem; + max-width: 1200px; + margin: 0 auto; + padding: 0 1rem; +} + +.moko-offline-header .moko-brand { + display: flex; + align-items: center; + gap: 0.75rem; + text-decoration: none; + color: var(--mainmenu-nav-link-color, #fff); +} + +.moko-offline-header .moko-brand img { + max-width: 180px; + max-height: 60px; + width: auto; + height: auto; +} + +.moko-offline-header .moko-brand .brand-tagline { + display: block; + opacity: 0.75; + font-size: 0.875rem; + line-height: 1.2; +} + +/* === Content Typography === */ +.moko-offline-card h1 { + color: var(--heading-color, var(--body-font-color, #f1f5f9)); + font-weight: 700; +} + +.moko-offline-card .lead { + color: var(--gray-600, #48525d); + line-height: 1.6; +} + +/* === Accordion === */ +.moko-offline-card .accordion-item { + background-color: var(--card-bg, var(--secondary-bg, #151b22)); + border-color: var(--border-color, #2b323b); +} + +.moko-offline-card .accordion-button { + background-color: var(--card-bg, var(--secondary-bg, #151b22)); + color: var(--body-font-color, #e6ebf1); +} + +.moko-offline-card .accordion-button:not(.collapsed) { + background-color: var(--card-cap-bg, rgba(255, 255, 255, 0.03)); + color: var(--body-font-color, #e6ebf1); +} + +.moko-offline-card .accordion-body { + background-color: var(--card-bg, var(--secondary-bg, #151b22)); +} + +/* === Form Controls === */ +.moko-offline-card .form-control { + background-color: var(--input-bg, #1a2332); + border-color: var(--input-border-color, #3a4250); + color: var(--input-color, #e6ebf1); +} + +.moko-offline-card .form-control:focus { + border-color: var(--input-focus-border-color, #5472ff); + box-shadow: var(--input-focus-box-shadow, 0 0 0 0.25rem rgba(84, 114, 255, 0.25)); +} + +.moko-offline-card .form-label { + color: var(--body-font-color, #e6ebf1); +} + +.moko-offline-card .form-check-label { + color: var(--body-font-color, #e6ebf1); +} + +/* === Button === */ +.moko-offline-card .btn-primary { + background-color: var(--color-primary, #112855); + border-color: var(--color-primary, #112855); + color: var(--mainmenu-nav-link-color, #fff); +} + +.moko-offline-card .btn-primary:hover { + background-color: var(--color-hover, gray); + border-color: var(--color-hover, gray); +} + +/* === Links === */ +.moko-offline-card a { + color: var(--link-color, var(--color-link, #8ab4f8)); +} + +.moko-offline-card a:hover { + color: var(--link-hover-color, #c3d6ff); +} + +/* === Skip Link === */ +.skip-link { + position: absolute; + left: -9999px; + top: auto; + width: 1px; + height: 1px; + overflow: hidden; +} + +.skip-link:focus { + position: static; + width: auto; + height: auto; + padding: 0.5rem 1rem; +} diff --git a/src/media/css/template.css b/src/media/css/template.css index 889b8a3..40ffadd 100644 --- a/src/media/css/template.css +++ b/src/media/css/template.css @@ -14233,6 +14233,28 @@ fieldset>* { margin-inline-start: auto; display: flex; align-items: center; + gap: 1em; +} + +.container-brand-aside>* { + flex: 1; + margin: 0.5em 0; +} + +@media (max-width: 991.98px) { + .header-brand-wrap { + flex-direction: column; + align-items: stretch; + } + + .container-brand-aside { + margin-inline-start: 0; + flex-direction: column; + } + + .container-brand-aside>* { + flex: 0 1 auto; + } } .container-header .navbar-brand { @@ -18659,7 +18681,7 @@ nav[data-toggle=toc] .nav-link.active+ul{ flex: 0 0 auto; background-color: var(--color-primary, #112855); color: var(--mainmenu-nav-link-color, #fff); - border-color: var(--color-primary, #112855); + border: 1px solid var(--input-border-color, #3a4250); padding: 0.6rem 1rem; border-radius: 0 0.25rem 0.25rem 0; transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out; @@ -18668,7 +18690,7 @@ nav[data-toggle=toc] .nav-link.active+ul{ .mod-finder__search.input-group button:hover, .container-search button[type="submit"]:hover { background-color: var(--color-hover, gray); - border-color: var(--color-hover, gray); + border-color: var(--input-border-color, #3a4250); } .mod-finder__search.input-group button:focus, @@ -21637,6 +21659,33 @@ nav[data-toggle=toc] .nav-link.active+ul{ color: var(--gray-600, #48525d); } +/* === mod_stats === */ +.mod_stats__table { + width: 100%; + border-collapse: collapse; +} + +.mod_stats__table tr { + border-bottom: 1px solid var(--border-color, #2b323b); +} + +.mod_stats__table tr:last-child { + border-bottom: none; +} + +.mod_stats__label { + text-align: start; + font-weight: 600; + padding: 0.6rem 1rem 0.6rem 0; + color: var(--body-font-color, #e6ebf1); +} + +.mod_stats__data { + text-align: end; + padding: 0.6rem 0; + color: var(--gray-600, #48525d); +} + /* === Mobile Responsive Adjustments === */ @media (max-width: 575.98px) { .mod-kunena-login__input { diff --git a/src/media/js/template.js b/src/media/js/template.js index 94a6909..7d65301 100644 --- a/src/media/js/template.js +++ b/src/media/js/template.js @@ -641,6 +641,154 @@ }); } + // ======================================================================== + // CSS VARIABLE CLICK-TO-COPY + // ======================================================================== + + /** + * Inject toast + variable-chip styles once. + */ + function injectVarCopyStyles() { + if (doc.getElementById("moko-var-copy-styles")) return; + var style = doc.createElement("style"); + style.id = "moko-var-copy-styles"; + style.textContent = + ".moko-var-chip{cursor:pointer;font-family:var(--font-monospace,monospace);font-size:.875em;" + + "background:var(--secondary-bg,#151b22);color:var(--link-color,#8ab4f8);" + + "border:1px solid var(--border-color,#2b323b);border-radius:.25rem;padding:.1em .4em;" + + "transition:background .15s,border-color .15s;white-space:nowrap;display:inline}" + + ".moko-var-chip:hover{background:var(--color-primary,#112855);color:#fff;border-color:var(--color-primary,#112855)}" + + ".moko-toast{position:fixed;bottom:1.5rem;left:50%;transform:translateX(-50%);z-index:10000;" + + "background:var(--color-primary,#112855);color:#fff;padding:.6rem 1.25rem;" + + "border-radius:.375rem;font-size:.875rem;box-shadow:0 4px 12px rgba(0,0,0,.25);" + + "opacity:0;transition:opacity .2s;pointer-events:none}" + + ".moko-toast--show{opacity:1}"; + doc.head.appendChild(style); + } + + /** + * Show a brief "Copied to clipboard" toast. + * @param {string} text - The variable name that was copied + */ + function showCopyToast(text) { + var existing = doc.querySelector(".moko-toast"); + if (existing) existing.remove(); + + var toast = doc.createElement("div"); + toast.className = "moko-toast"; + toast.textContent = "Copied to clipboard: " + text; + doc.body.appendChild(toast); + + // Trigger reflow then show + void toast.offsetWidth; + toast.classList.add("moko-toast--show"); + + setTimeout(function () { + toast.classList.remove("moko-toast--show"); + setTimeout(function () { toast.remove(); }, 200); + }, 2000); + } + + /** + * Copy text to clipboard and show toast. + * @param {string} text + */ + function copyVariable(text) { + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(text).then(function () { + showCopyToast(text); + }); + } else { + // Fallback for older browsers using deprecated API + var ta = doc.createElement("textarea"); + ta.value = text; + ta.style.cssText = "position:fixed;left:-9999px"; + doc.body.appendChild(ta); + ta.select(); + try { doc.execCommand("copy"); } catch (e) { /* noop */ } + ta.remove(); + showCopyToast(text); + } + } + + /** + * Scan text nodes for CSS variable patterns (--variable-name) and wrap + * each match in a clickable chip that copies the variable to clipboard. + */ + function initVarCopy() { + injectVarCopyStyles(); + + // Pattern: --[a-zA-Z] followed by word/hyphen chars + var varPattern = /--[a-zA-Z][\w-]*/g; + + // Elements to skip (inputs, scripts, styles, already-processed, code editors) + var SKIP_TAGS = { SCRIPT: 1, STYLE: 1, TEXTAREA: 1, INPUT: 1, SELECT: 1, NOSCRIPT: 1 }; + + var walker = doc.createTreeWalker( + doc.body, + NodeFilter.SHOW_TEXT, + { + acceptNode: function (node) { + if (SKIP_TAGS[node.parentNode.tagName]) return NodeFilter.FILTER_REJECT; + if (node.parentNode.classList && node.parentNode.classList.contains("moko-var-chip")) return NodeFilter.FILTER_REJECT; + if (!varPattern.test(node.nodeValue)) return NodeFilter.FILTER_REJECT; + varPattern.lastIndex = 0; + return NodeFilter.FILTER_ACCEPT; + } + } + ); + + var textNodes = []; + while (walker.nextNode()) textNodes.push(walker.currentNode); + + textNodes.forEach(function (node) { + var text = node.nodeValue; + var frag = doc.createDocumentFragment(); + var lastIndex = 0; + var match; + + varPattern.lastIndex = 0; + while ((match = varPattern.exec(text)) !== null) { + // Text before the match + if (match.index > lastIndex) { + frag.appendChild(doc.createTextNode(text.slice(lastIndex, match.index))); + } + + // Clickable chip + var chip = doc.createElement("span"); + chip.className = "moko-var-chip"; + chip.textContent = match[0]; + chip.setAttribute("role", "button"); + chip.setAttribute("tabindex", "0"); + chip.setAttribute("title", "Click to copy " + match[0]); + chip.addEventListener("click", (function (varName) { + return function (e) { + e.preventDefault(); + copyVariable(varName); + }; + })(match[0])); + chip.addEventListener("keydown", (function (varName) { + return function (e) { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + copyVariable(varName); + } + }; + })(match[0])); + frag.appendChild(chip); + + lastIndex = match.index + match[0].length; + } + + // Remaining text after last match + if (lastIndex < text.length) { + frag.appendChild(doc.createTextNode(text.slice(lastIndex))); + } + + node.parentNode.replaceChild(frag, node); + }); + } + /** * Run all template JS initializations */ @@ -667,6 +815,7 @@ initBackTop(); initSearchToggle(); initSidebarAccordion(); + initVarCopy(); } if (doc.readyState === "loading") { diff --git a/src/offline.php b/src/offline.php index 004dabb..196eea3 100644 --- a/src/offline.php +++ b/src/offline.php @@ -38,6 +38,10 @@ $base = rtrim(Uri::root(true), '/') . '/templates/' . $this->template . ' $jsBase = rtrim(Uri::root(true), '/') . '/templates/' . $this->template . '/js/'; $doc->addStyleSheet($base . 'template' . $assetSuffix . '.css', ['version' => 'auto'], ['id' => 'moko-template']); +$doc->addStyleSheet($base . 'offline' . $assetSuffix . '.css', ['version' => 'auto'], ['id' => 'moko-offline']); + +/* Load Osaka font for site title */ +$doc->addStyleSheet($base . 'fonts/osaka.css', ['version' => 'auto'], ['id' => 'moko-font-osaka']); /* Load theme palettes */ $doc->addStyleSheet($base . 'theme/light.standard' . $assetSuffix . '.css', ['version' => 'auto'], ['id' => 'moko-light-standard']); @@ -168,15 +172,6 @@ if (class_exists('\Joomla\Component\Users\Site\Helper\RouteHelper')) { - -
-
- - - - - - - - - - - countModules('offline-header')) : ?> -
- -
- - -
-
+ countModules('offline-header')) : ?> +
+
+ +
+
+
-
- + -
- -
-

-

-
- -
-

-

- -

-
- +
+
+
+
+ + + + + + + - - countModules('offline')) : ?> -
- -
- + +
+

+

+
+ +
+

+

+ +

+
+ - -
-
-

- -

-
-
-
-
- + + countModules('offline')) : ?> +
+ +
+ -
- - -
+ +
+
+

+ +

+
+
+ +
+ -
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- -
+
+ + +
- - - - -
+
+ +
-
+ + -
+
  • + +
  • + +
  • + +
  • + + + + +
    +
    -
    +
    diff --git a/src/templateDetails.xml b/src/templateDetails.xml index 1706f11..5499cb6 100644 --- a/src/templateDetails.xml +++ b/src/templateDetails.xml @@ -39,13 +39,13 @@ MokoCassiopeia - 03.09.15 + 03.09.16 script.php - 2026-04-14 + 2026-04-15 Jonathan Miller || Moko Consulting hello@mokoconsulting.tech (C)GNU General Public License Version 3 - 2026 Moko Consulting - Version 03.09.15 License Joomla PHP

    MokoCassiopeia Template Description

    MokoCassiopeia continues Joomla's tradition of space-themed default templates— building on the legacy of Solarflare (Joomla 1.0), Milkyway (Joomla 1.5), and Protostar (Joomla 3.0).

    This template is a customized fork of the Cassiopeia template introduced in Joomla 4, preserving its modern, accessible, and mobile-first foundation while introducing new stylistic enhancements and structural refinements specifically tailored for use by Moko Consulting.

    Custom Colour Themes

    Starter palette files are included with the template. To create a custom colour scheme, copy templates/mokocassiopeia/templates/light.custom.css to media/templates/site/mokocassiopeia/css/theme/light.custom.css, or templates/mokocassiopeia/templates/dark.custom.css to media/templates/site/mokocassiopeia/css/theme/dark.custom.css. Customise the CSS variables to match your brand, then activate your palette in System → Site Templates → MokoCassiopeia → Theme tab by selecting "Custom" for the Light or Dark Mode Palette. A full variable reference is available in the CSS Variables tab in template options.

    Custom CSS & JavaScript

    For site-specific styles and scripts that should survive template updates, create the following files:

    • media/templates/site/mokocassiopeia/css/user.css — loaded on every page for custom CSS overrides.
    • media/templates/site/mokocassiopeia/js/user.js — loaded on every page for custom JavaScript.

    These files are gitignored and will not be overwritten by template updates.

    Code Attribution

    This template is based on the original Cassiopeia template developed by the Joomla! Project and released under the GNU General Public License.

    Modifications and enhancements have been made by Moko Consulting in accordance with open-source licensing standards.

    It includes integration with Bootstrap TOC, an open-source table of contents generator by A. Feld, licensed under the MIT License.

    All third-party libraries and assets remain the property of their respective authors and are credited within their source files where applicable.

    ]]>
    + Version 03.09.16 License Joomla PHP

    MokoCassiopeia Template Description

    MokoCassiopeia continues Joomla's tradition of space-themed default templates— building on the legacy of Solarflare (Joomla 1.0), Milkyway (Joomla 1.5), and Protostar (Joomla 3.0).

    This template is a customized fork of the Cassiopeia template introduced in Joomla 4, preserving its modern, accessible, and mobile-first foundation while introducing new stylistic enhancements and structural refinements specifically tailored for use by Moko Consulting.

    Custom Colour Themes

    Starter palette files are included with the template. To create a custom colour scheme, copy templates/mokocassiopeia/templates/light.custom.css to media/templates/site/mokocassiopeia/css/theme/light.custom.css, or templates/mokocassiopeia/templates/dark.custom.css to media/templates/site/mokocassiopeia/css/theme/dark.custom.css. Customise the CSS variables to match your brand, then activate your palette in System → Site Templates → MokoCassiopeia → Theme tab by selecting "Custom" for the Light or Dark Mode Palette. A full variable reference is available in the CSS Variables tab in template options.

    Custom CSS & JavaScript

    For site-specific styles and scripts that should survive template updates, create the following files:

    • media/templates/site/mokocassiopeia/css/user.css — loaded on every page for custom CSS overrides.
    • media/templates/site/mokocassiopeia/js/user.js — loaded on every page for custom JavaScript.

    These files are gitignored and will not be overwritten by template updates.

    Code Attribution

    This template is based on the original Cassiopeia template developed by the Joomla! Project and released under the GNU General Public License.

    Modifications and enhancements have been made by Moko Consulting in accordance with open-source licensing standards.

    It includes integration with Bootstrap TOC, an open-source table of contents generator by A. Feld, licensed under the MIT License.

    All third-party libraries and assets remain the property of their respective authors and are credited within their source files where applicable.

    ]]>
    1 component.php diff --git a/updates.xml b/updates.xml index 3fb3a81..76ab8b4 100644 --- a/updates.xml +++ b/updates.xml @@ -1,7 +1,7 @@ @@ -13,12 +13,12 @@ mokocassiopeia template site - 03.09.15 + 03.09.16 2026-04-14 https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/tag/development - https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/development/mokocassiopeia-03.09.15-dev.zip - https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/development/mokocassiopeia-03.09.15-dev.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/development/mokocassiopeia-03.09.16-dev.zip + https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/development/mokocassiopeia-03.09.16-dev.zip 310fc79b042a042c0b518a806018011d5243893f239d000224fb197943fee5e3 development @@ -35,12 +35,12 @@ mokocassiopeia template site - 03.09.15 + 03.09.16 2026-04-14 https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/tag/alpha - https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/alpha/mokocassiopeia-03.09.15-alpha.zip - https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/alpha/mokocassiopeia-03.09.15-alpha.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/alpha/mokocassiopeia-03.09.16-alpha.zip + https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/alpha/mokocassiopeia-03.09.16-alpha.zip c2660acdf7389244462485f7ab4c286e9f851366a148acc16739a184576f7932 alpha @@ -57,12 +57,12 @@ mokocassiopeia template site - 03.09.15 + 03.09.16 2026-04-14 https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/tag/beta - https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/beta/mokocassiopeia-03.09.15-beta.zip - https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/beta/mokocassiopeia-03.09.15-beta.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/beta/mokocassiopeia-03.09.16-beta.zip + https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/beta/mokocassiopeia-03.09.16-beta.zip 4cbe4fc379182ef17580396e7d12ce4ce95a90017ef364b922bdc2d04b0b3d97 beta @@ -79,12 +79,12 @@ mokocassiopeia template site - 03.09.15 + 03.09.16 2026-04-14 https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/tag/release-candidate - https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/release-candidate/mokocassiopeia-03.09.15-rc.zip - https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/release-candidate/mokocassiopeia-03.09.15-rc.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/release-candidate/mokocassiopeia-03.09.16-rc.zip + https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/release-candidate/mokocassiopeia-03.09.16-rc.zip c2660acdf7389244462485f7ab4c286e9f851366a148acc16739a184576f7932 rc @@ -101,12 +101,12 @@ mokocassiopeia template site - 03.09.15 + 03.09.16 2026-04-14 https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/tag/v03 - https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/v03/mokocassiopeia-03.09.15.zip - https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/v03/mokocassiopeia-03.09.15.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/v03/mokocassiopeia-03.09.16.zip + https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/v03/mokocassiopeia-03.09.16.zip c2660acdf7389244462485f7ab4c286e9f851366a148acc16739a184576f7932 stable From c35e76f554974916e4c8a0221e800def7d4d2ae4 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 16 Apr 2026 17:18:06 -0500 Subject: [PATCH 010/120] chore: update dev SHA-256 for 03.09.16 [skip ci] Co-Authored-By: Claude Opus 4.6 (1M context) --- updates.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/updates.xml b/updates.xml index 76ab8b4..865ffb9 100644 --- a/updates.xml +++ b/updates.xml @@ -14,13 +14,13 @@ template site 03.09.16 - 2026-04-14 + 2026-04-16 https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/tag/development https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/development/mokocassiopeia-03.09.16-dev.zip https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/development/mokocassiopeia-03.09.16-dev.zip - 310fc79b042a042c0b518a806018011d5243893f239d000224fb197943fee5e3 + sha256:781448d2d332a734fc5d0f7aec5f62127688b705b632deded2d9814e3897d991 development Moko Consulting https://mokoconsulting.tech From 5e5d6a4e897764ff8e948d4ad5447d4bc5dc08a8 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 16 Apr 2026 18:29:43 -0500 Subject: [PATCH 011/120] Offline page redesign, remove brand showcase tab, force a11y/theme to bottom-right - Offline page: full-viewport Joomla offline_image background, centered glass card overlay with logo glow, message, offline position, login accordion, copyright - Favicon: added to offline page, HTML-escaped paths in helper - Removed brand showcase tab, added footer CSS vars to CSS Variables tab - Forced a11y toolbar and theme FAB to bottom-right (removed position selectors) Co-Authored-By: Claude Opus 4.6 (1M context) --- src/helper/favicon.php | 2 +- src/index.php | 4 +- src/language/en-GB/tpl_mokocassiopeia.ini | 8 +- src/language/en-US/tpl_mokocassiopeia.ini | 8 +- src/media/css/offline.css | 273 +++++++++++----------- src/offline.php | 266 +++++++++++---------- src/templateDetails.xml | 13 +- 7 files changed, 298 insertions(+), 276 deletions(-) diff --git a/src/helper/favicon.php b/src/helper/favicon.php index 2567e86..028eaf2 100644 --- a/src/helper/favicon.php +++ b/src/helper/favicon.php @@ -162,7 +162,7 @@ class MokoFaviconHelper */ public static function getHeadTags(string $basePath): string { - $basePath = rtrim($basePath, '/'); + $basePath = htmlspecialchars(rtrim($basePath, '/'), ENT_QUOTES, 'UTF-8'); return '' . "\n" . '' . "\n" diff --git a/src/index.php b/src/index.php index e30eb97..70db6f6 100644 --- a/src/index.php +++ b/src/index.php @@ -41,7 +41,7 @@ $params_favicon_source = (string) $this->params->get('favicon_source', ''); $params_theme_enabled = $this->params->get('theme_enabled', 1); $params_theme_control_type = (string) $this->params->get('theme_control_type', 'radios'); $params_theme_fab_enabled = $this->params->get('theme_fab_enabled', 1); -$params_theme_fab_pos = $this->params->get('theme_fab_pos', 'br'); +$params_theme_fab_pos = 'br'; // Accessibility params $params_a11y_toolbar = $this->params->get('a11y_toolbar_enabled', 1); @@ -51,7 +51,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_pos = (string) $this->params->get('a11y_toolbar_pos', 'tl'); +$params_a11y_pos = 'br'; // Detecting Active Variables $option = $input->getCmd('option', ''); diff --git a/src/language/en-GB/tpl_mokocassiopeia.ini b/src/language/en-GB/tpl_mokocassiopeia.ini index 945950b..0116bc3 100644 --- a/src/language/en-GB/tpl_mokocassiopeia.ini +++ b/src/language/en-GB/tpl_mokocassiopeia.ini @@ -259,16 +259,14 @@ TPL_MOKOCASSIOPEIA_CSS_VARS_VM_DESC="Surfaces & text
    +/* Copyright (C) 2026 Moko Consulting This file is part of a Moko Consulting project. @@ -30,7 +30,7 @@ $params = $this->params ?: $app->getTemplate(true)->params; $direction = $this->direction ?: 'ltr'; /* ----------------------- - Load ONLY template.css + theme palettes (with min toggle) + Load CSS + theme palettes ------------------------ */ $useMin = !((int) $params->get('development_mode', 0) === 1); $assetSuffix = $useMin ? '.min' : ''; @@ -59,30 +59,30 @@ if ($params_DarkColorName === 'custom' && file_exists(JPATH_ROOT . '/media/templ $doc->addStyleSheet($base . 'theme/dark.custom' . $assetSuffix . '.css', ['version' => 'auto'], ['id' => 'moko-dark-custom']); } -/* Load user assets last (after all other styles and scripts) */ +/* Load user assets last */ $doc->addStyleSheet($base . 'user' . $assetSuffix . '.css', ['version' => 'auto'], ['id' => 'moko-user']); -/* Bootstrap CSS/JS for accordion behavior; safe to keep. */ +/* Bootstrap CSS/JS for accordion */ HTMLHelper::_('bootstrap.loadCss', true, $doc); HTMLHelper::_('bootstrap.framework'); -/* Load template.js for theme switcher and other functionality */ +/* Load template.js for theme switcher */ $doc->addScript($jsBase . 'template' . $assetSuffix . '.js', ['version' => 'auto', 'defer' => true], ['id' => 'moko-template-js']); -/* Load user.js last for custom user scripts */ +/* Load user.js last */ $doc->addScript($jsBase . 'user' . $assetSuffix . '.js', ['version' => 'auto', 'defer' => true], ['id' => 'moko-user-js']); /* ----------------------- - Title + Meta (Include Site Name in Page Titles) + Title + Meta ------------------------ */ $sitename = (string) $app->get('sitename'); $baseTitle = Text::_('JGLOBAL_OFFLINE') ?: 'Offline'; -$snSetting = (int) $app->get('sitename_pagetitles', 0); // 0=no, 1=before, 2=after +$snSetting = (int) $app->get('sitename_pagetitles', 0); if ($snSetting === 1) { - $doc->setTitle(Text::sprintf('JPAGETITLE', $sitename, $baseTitle)); // Site Name BEFORE + $doc->setTitle(Text::sprintf('JPAGETITLE', $sitename, $baseTitle)); } elseif ($snSetting === 2) { - $doc->setTitle(Text::sprintf('JPAGETITLE', $baseTitle, $sitename)); // Site Name AFTER + $doc->setTitle(Text::sprintf('JPAGETITLE', $baseTitle, $sitename)); } else { $doc->setTitle($baseTitle); } @@ -91,11 +91,21 @@ $doc->setMetaData('robots', 'noindex, nofollow'); /* ----------------------- Offline content from Global Config ------------------------ */ -$displayOfflineMessage = (int) $app->get('display_offline_message', 1); // 0|1|2 +$displayOfflineMessage = (int) $app->get('display_offline_message', 1); $offlineMessage = trim((string) $app->get('offline_message', '')); /* ----------------------- - Brand: logo from params OR siteTitle (matches index.php) + Offline image from Joomla Global Config (System > Global Configuration > Site > Offline Image) + Used as the full-viewport background image. +------------------------ */ +$offlineImage = trim((string) $app->get('offline_image', '')); +$bgStyle = ''; +if ($offlineImage !== '') { + $bgStyle = 'background-image: url(\'' . htmlspecialchars(Uri::root(false) . $offlineImage, ENT_QUOTES, 'UTF-8') . '\');'; +} + +/* ----------------------- + Brand: logo from template params OR siteTitle ------------------------ */ $brandHtml = ''; $logoFile = (string) $params->get('logoFile'); @@ -110,9 +120,8 @@ if ($logoFile !== '') { 0 ); } else { - // If no logo file, show the title (defaults to "MokoCassiopeia" if not set) $siteTitle = $params->get('siteTitle', 'MokoCassiopeia'); - $brandHtml = '' + $brandHtml = '' . htmlspecialchars($siteTitle, ENT_COMPAT, 'UTF-8') . ''; } @@ -120,6 +129,20 @@ if ($logoFile !== '') { $brandTagline = (string) ($params->get('brand_tagline') ?: $params->get('siteDescription') ?: ''); $showTagline = (int) $params->get('show_brand_tagline', 0); +// Favicon +$params_favicon_source = (string) $params->get('favicon_source', ''); +$faviconHeadTags = ''; +if ($params_favicon_source) { + require_once JPATH_ROOT . '/templates/' . $this->template . '/helper/favicon.php'; + $faviconSourceAbs = JPATH_ROOT . '/' . ltrim($params_favicon_source, '/'); + $faviconOutputDir = JPATH_ROOT . '/images/favicons'; + $faviconUrlBase = Uri::root(true) . '/images/favicons'; + + if (MokoFaviconHelper::generate($faviconSourceAbs, $faviconOutputDir)) { + $faviconHeadTags = MokoFaviconHelper::getHeadTags($faviconUrlBase); + } +} + // Theme params $params_theme_enabled = (int) $params->get('theme_enabled', 1); @@ -135,7 +158,7 @@ if (!empty($params_googlesitekey)) { } /* ----------------------- - Login routes & Users + Login routes ------------------------ */ $action = Route::_('index.php', true); $return = base64_encode(Uri::base()); @@ -156,10 +179,12 @@ if (class_exists('\Joomla\Component\Users\Site\Helper\RouteHelper')) { + + + - - - - - - - countModules('offline-header')) : ?> -
    -
    - -
    -
    - - -
    + +
    +
    -
    -
    -
    -
    - - - - - - - + +
    +
    - -
    -

    -

    -
    - -
    -

    -

    - -

    -
    - + + + + + + + - - countModules('offline')) : ?> -
    - -
    - + + +
    +

    +

    +
    + +
    +

    +

    +
    + - -
    -
    -

    - -

    -
    -
    -
    -
    - + + countModules('offline')) : ?> +
    + +
    + -
    - - -
    + +
    +
    +

    + +

    +
    +
    + +
    + -
    - - -
    +
    + + +
    -
    - - -
    +
    + + +
    -
    - - -
    +
    + + +
    -
    - -
    +
    + + +
    - - - - -
    +
    + +
    - - -
    -
    + + + + +
    + + +
    -
    -
    + + + +
    - + + countModules('offline-footer')) : ?> +
    + +
    + + diff --git a/src/templateDetails.xml b/src/templateDetails.xml index 5499cb6..c432c8c 100644 --- a/src/templateDetails.xml +++ b/src/templateDetails.xml @@ -295,7 +295,8 @@ - + @@ -312,7 +313,8 @@ - + @@ -365,6 +367,7 @@ + @@ -372,12 +375,6 @@ - - -
    - - -
    From 87b5326d1d8fb93320c9bce2b5d5c4d76c3a6935 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 16 Apr 2026 18:34:06 -0500 Subject: [PATCH 012/120] Use MOD_FOOTER_LINE2 for offline page copyright Co-Authored-By: Claude Opus 4.6 (1M context) --- src/offline.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/offline.php b/src/offline.php index ef0e668..6b5ff8a 100644 --- a/src/offline.php +++ b/src/offline.php @@ -352,7 +352,8 @@ if (class_exists('\Joomla\Component\Users\Site\Helper\RouteHelper')) {
    From d6383468cdc625cd40a968abfcc99ea16c5fc4b0 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 16 Apr 2026 18:36:23 -0500 Subject: [PATCH 013/120] Load Font Awesome 7 on offline page Co-Authored-By: Claude Opus 4.6 (1M context) --- src/offline.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/offline.php b/src/offline.php index 6b5ff8a..b11b436 100644 --- a/src/offline.php +++ b/src/offline.php @@ -43,6 +43,10 @@ $doc->addStyleSheet($base . 'offline' . $assetSuffix . '.css', ['version' => 'au /* Load Osaka font for site title */ $doc->addStyleSheet($base . 'fonts/osaka.css', ['version' => 'auto'], ['id' => 'moko-font-osaka']); +/* Load Font Awesome 7 Free (local) — Kit code not supported on offline page */ +$faBase = rtrim(Uri::root(true), '/') . '/templates/' . $this->template . '/vendor/fa7free/css/'; +$doc->addStyleSheet($faBase . 'all' . $assetSuffix . '.css', ['version' => 'auto'], ['id' => 'moko-fa7free']); + /* Load theme palettes */ $doc->addStyleSheet($base . 'theme/light.standard' . $assetSuffix . '.css', ['version' => 'auto'], ['id' => 'moko-light-standard']); $doc->addStyleSheet($base . 'theme/dark.standard' . $assetSuffix . '.css', ['version' => 'auto'], ['id' => 'moko-dark-standard']); From 6708569c6fc021d264ba502e7218b70a43748b63 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 16 Apr 2026 18:38:10 -0500 Subject: [PATCH 014/120] Rewrite offline page to use WebAssetManager - All CSS/JS loading now uses $wa->useStyle/useScript matching index.php - Added template.offline and template.offline.min to joomla.asset.json - Font Awesome, Osaka font, themes, user overrides all via WAM Co-Authored-By: Claude Opus 4.6 (1M context) --- src/joomla.asset.json | 12 ++++++++++ src/offline.php | 55 ++++++++++++++++++++++--------------------- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/src/joomla.asset.json b/src/joomla.asset.json index f51468b..b22ffd7 100644 --- a/src/joomla.asset.json +++ b/src/joomla.asset.json @@ -34,6 +34,18 @@ "uri": "media/templates/site/mokocassiopeia/css/template.min.css", "attributes": {"media": "all"} }, + { + "name": "template.offline", + "type": "style", + "uri": "media/templates/site/mokocassiopeia/css/offline.css", + "attributes": {"media": "all"} + }, + { + "name": "template.offline.min", + "type": "style", + "uri": "media/templates/site/mokocassiopeia/css/offline.min.css", + "attributes": {"media": "all"} + }, { "name": "template.user", "type": "style", diff --git a/src/offline.php b/src/offline.php index b11b436..bc260f8 100644 --- a/src/offline.php +++ b/src/offline.php @@ -26,56 +26,57 @@ use Joomla\CMS\Uri\Uri; $app = Factory::getApplication(); $doc = Factory::getDocument(); +$wa = $doc->getWebAssetManager(); $params = $this->params ?: $app->getTemplate(true)->params; $direction = $this->direction ?: 'ltr'; /* ----------------------- - Load CSS + theme palettes + Load assets via WebAssetManager (matches index.php pattern) ------------------------ */ -$useMin = !((int) $params->get('development_mode', 0) === 1); -$assetSuffix = $useMin ? '.min' : ''; -$base = rtrim(Uri::root(true), '/') . '/templates/' . $this->template . '/css/'; -$jsBase = rtrim(Uri::root(true), '/') . '/templates/' . $this->template . '/js/'; +$params_developmentmode = (bool) $params->get('developmentmode', false) || (bool) $app->get('debug', false); +$suffix = $params_developmentmode ? '' : '.min'; -$doc->addStyleSheet($base . 'template' . $assetSuffix . '.css', ['version' => 'auto'], ['id' => 'moko-template']); -$doc->addStyleSheet($base . 'offline' . $assetSuffix . '.css', ['version' => 'auto'], ['id' => 'moko-offline']); +// Core template CSS + offline overlay CSS +$wa->useStyle('template.base' . $suffix); +$wa->useStyle('template.offline' . $suffix); -/* Load Osaka font for site title */ -$doc->addStyleSheet($base . 'fonts/osaka.css', ['version' => 'auto'], ['id' => 'moko-font-osaka']); +// Osaka font +$wa->useStyle('template.font.osaka'); -/* Load Font Awesome 7 Free (local) — Kit code not supported on offline page */ -$faBase = rtrim(Uri::root(true), '/') . '/templates/' . $this->template . '/vendor/fa7free/css/'; -$doc->addStyleSheet($faBase . 'all' . $assetSuffix . '.css', ['version' => 'auto'], ['id' => 'moko-fa7free']); +// Font Awesome 7 Free +$wa->useStyle('vendor.fa7free.all' . $suffix); -/* Load theme palettes */ -$doc->addStyleSheet($base . 'theme/light.standard' . $assetSuffix . '.css', ['version' => 'auto'], ['id' => 'moko-light-standard']); -$doc->addStyleSheet($base . 'theme/dark.standard' . $assetSuffix . '.css', ['version' => 'auto'], ['id' => 'moko-dark-standard']); +// Theme palettes +$wa->useStyle('template.light.standard' . $suffix); +$wa->useStyle('template.dark.standard' . $suffix); -/* Load custom palettes only if selected in template configuration AND files exist */ +// Custom palettes (if selected and files exist) $params_LightColorName = (string) $params->get('colorLightName', 'standard'); $params_DarkColorName = (string) $params->get('colorDarkName', 'standard'); if ($params_LightColorName === 'custom' && file_exists(JPATH_ROOT . '/media/templates/site/mokocassiopeia/css/theme/light.custom.css')) { - $doc->addStyleSheet($base . 'theme/light.custom' . $assetSuffix . '.css', ['version' => 'auto'], ['id' => 'moko-light-custom']); + $wa->useStyle('template.light.custom' . $suffix); } if ($params_DarkColorName === 'custom' && file_exists(JPATH_ROOT . '/media/templates/site/mokocassiopeia/css/theme/dark.custom.css')) { - $doc->addStyleSheet($base . 'theme/dark.custom' . $assetSuffix . '.css', ['version' => 'auto'], ['id' => 'moko-dark-custom']); + $wa->useStyle('template.dark.custom' . $suffix); } -/* Load user assets last */ -$doc->addStyleSheet($base . 'user' . $assetSuffix . '.css', ['version' => 'auto'], ['id' => 'moko-user']); +// User overrides (loaded last) +$wa->useStyle('template.user'); -/* Bootstrap CSS/JS for accordion */ +// Template JS (theme switcher, var-copy, etc.) +if ($params_developmentmode) { + $wa->useScript('template.js'); +} else { + $wa->useScript('template.js.min'); +} +$wa->useScript('user.js'); + +// Bootstrap (accordion) HTMLHelper::_('bootstrap.loadCss', true, $doc); HTMLHelper::_('bootstrap.framework'); -/* Load template.js for theme switcher */ -$doc->addScript($jsBase . 'template' . $assetSuffix . '.js', ['version' => 'auto', 'defer' => true], ['id' => 'moko-template-js']); - -/* Load user.js last */ -$doc->addScript($jsBase . 'user' . $assetSuffix . '.js', ['version' => 'auto', 'defer' => true], ['id' => 'moko-user-js']); - /* ----------------------- Title + Meta ------------------------ */ From a62d40507749b60cb6ac7c46acf648393293cc51 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 16 Apr 2026 18:41:22 -0500 Subject: [PATCH 015/120] Add theme switcher, a11y toolbar, and high-contrast CSS to offline page - Load a11y-high-contrast.css via WAM - Add all data-theme-fab-* and data-a11y-* attributes to body - Theme switcher FAB and a11y toolbar now render on offline page Co-Authored-By: Claude Opus 4.6 (1M context) --- src/offline.php | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/offline.php b/src/offline.php index bc260f8..1d311f2 100644 --- a/src/offline.php +++ b/src/offline.php @@ -65,7 +65,10 @@ if ($params_DarkColorName === 'custom' && file_exists(JPATH_ROOT . '/media/templ // User overrides (loaded last) $wa->useStyle('template.user'); -// Template JS (theme switcher, var-copy, etc.) +// Accessibility high-contrast stylesheet +$wa->useStyle('template.a11y-high-contrast'); + +// Template JS (theme switcher, a11y toolbar, var-copy, etc.) if ($params_developmentmode) { $wa->useScript('template.js'); } else { @@ -149,7 +152,19 @@ if ($params_favicon_source) { } // Theme params -$params_theme_enabled = (int) $params->get('theme_enabled', 1); +$params_theme_enabled = (int) $params->get('theme_enabled', 1); +$params_theme_fab_enabled = (int) $params->get('theme_fab_enabled', 1); +$params_theme_fab_pos = 'br'; + +// Accessibility params +$params_a11y_toolbar = (int) $params->get('a11y_toolbar_enabled', 1); +$params_a11y_resize = (int) $params->get('a11y_text_resize', 1); +$params_a11y_invert = (int) $params->get('a11y_color_inversion', 1); +$params_a11y_contrast = (int) $params->get('a11y_high_contrast', 1); +$params_a11y_links = (int) $params->get('a11y_highlight_links', 1); +$params_a11y_font = (int) $params->get('a11y_readable_font', 1); +$params_a11y_animations = (int) $params->get('a11y_pause_animations', 1); +$params_a11y_pos = 'br'; // Analytics params $params_googletagmanager = $params->get('googletagmanager', false); @@ -203,7 +218,18 @@ if (class_exists('\Joomla\Component\Users\Site\Helper\RouteHelper')) { -style=""> +style="">