31 Commits

Author SHA1 Message Date
gitea-actions[bot]
96d7520fa8 chore(version): bump 01.00.24 → 01.00.25 [skip ci] 2026-04-23 23:34:20 +00:00
gitea-actions[bot]
598605c65c chore: update development SHA-256 for 01.00.24 [skip ci] 2026-04-23 23:30:51 +00:00
gitea-actions[bot]
f6ac970ec0 chore(version): bump 01.00.23 → 01.00.24 [skip ci] 2026-04-23 23:30:46 +00:00
3340c00e26 fix: drawer toggles override btn-outline-secondary with !important
Bootstrap .btn-outline-secondary was overriding the accent color
and border-radius. Use higher specificity + !important.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 23:29:39 +00:00
gitea-actions[bot]
89e255265d chore: update development SHA-256 for 01.00.23 [skip ci] 2026-04-23 23:11:22 +00:00
gitea-actions[bot]
484c223c7e chore(version): bump 01.00.22 → 01.00.23 [skip ci] 2026-04-23 23:11:18 +00:00
1be001993e feat: hero wrapper uses bg variables, default height 75vh
.hero now uses --hero-bg-image, --hero-bg-repeat, --hero-bg-attachment,
--hero-bg-position, --hero-bg-size CSS variables.
Default min-height changed from 100% to 75vh.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 23:10:04 +00:00
5b08fe53b5 feat: mod_custom background uses --hero-bg-image CSS variable
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 23:10:02 +00:00
gitea-actions[bot]
3b90a375aa chore: update development SHA-256 for 01.00.22 [skip ci] 2026-04-23 23:07:40 +00:00
gitea-actions[bot]
a0dc907f8d chore(version): bump 01.00.21 → 01.00.22 [skip ci] 2026-04-23 23:07:36 +00:00
6f15c3f4f0 refactor: remove CSS variables tab, link to repo docs instead
Replace the large inline CSS variables reference fieldset with a
single note linking to the MokoOnyx repo documentation.
Add docs link to template description.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 23:06:53 +00:00
489ad687af feat: brand buttons use black text by default
All .btn-primary variants now use color: #000 for text.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 23:05:28 +00:00
2e69795536 feat: drawer toggle buttons use accent color
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 23:04:10 +00:00
d6f6476454 fix: flat edge on drawer toggle buttons facing the drawer
.drawer-toggle-left gets border-radius 0 on left side.
.drawer-toggle-right gets border-radius 0 on right side.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 23:03:28 +00:00
gitea-actions[bot]
592b0ea640 chore: update development SHA-256 for 01.00.21 [skip ci] 2026-04-23 22:55:11 +00:00
gitea-actions[bot]
db584db4af chore(version): bump 01.00.20 → 01.00.21 [skip ci] 2026-04-23 22:55:07 +00:00
12a98762cd fix: hero wrapper full-width, card centering via CSS variables
.hero is the module wrapper — now a full-width flex container.
Card styling (max-width, bg, padding, radius) moves to .hero .card.
All dimensions driven by CSS variables with sensible defaults:
--hero-justify, --hero-align, --hero-min-height,
--hero-card-text-align, --hero-card-max-width, etc.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 22:53:17 +00:00
2f000c330e fix: remove GitHub mirror URLs from updates.xml (404) [skip ci] 2026-04-23 22:43:38 +00:00
gitea-actions[bot]
4977a8c6ff chore: update stable SHA-256 for 01.00.20 [skip ci] 2026-04-23 20:25:57 +00:00
gitea-actions[bot]
c379fab50d chore(version): bump 01.00.19 → 01.00.20 [skip ci] 2026-04-23 20:25:52 +00:00
6d09487aa7 chore: add package-lock.json for npm ci in release workflow
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 20:25:36 +00:00
gitea-actions[bot]
a292026966 chore(version): bump 01.00.18 → 01.00.19 [skip ci] 2026-04-23 20:21:35 +00:00
Jonathan Miller
9ce82bdacc fix: remove theme preview tab and theme-test.html
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 15:18:20 -05:00
ce01759455 feat: lock MokoOnyx extension on install and update
Sets locked=1 in #__extensions after install/update to prevent
accidental uninstallation via Extension Manager.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 20:09:29 +00:00
Jonathan Miller
7af3dc0b2d build: minify CSS/JS at release time instead of tracking .min files
All checks were successful
Auto-Assign Issues & PRs / Assign unassigned issues and PRs (pull_request_target) Successful in 1s
- Add scripts/minify.js using clean-css and terser
- Add "Setup Node.js" and "Minify CSS and JS" steps to both
  .gitea and .github release workflows (runs before packaging)
- Add src/media/{css,js}/*.min.{css,js} to .gitignore
- Remove tracked .min files from the repo (vendor/ untouched)

This eliminates noisy diffs on generated files and ensures the
release ZIP always contains freshly minified assets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 08:44:18 -05:00
Jonathan Miller
e98a1d125f docs: document --header-background-color in CSS Variables reference tab
Add the new variable to both en-GB and en-US language strings so it
appears in the template admin panel alongside the existing header
background variables.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 08:27:47 -05:00
gitea-actions[bot]
3e9379f300 chore: update development SHA-256 for 01.00.18 [skip ci] 2026-04-23 06:59:18 +00:00
gitea-actions[bot]
e7e71bf77e chore(version): bump 01.00.17 → 01.00.18 [skip ci] 2026-04-23 06:59:16 +00:00
Jonathan Miller
f438398e81 feat(theme): add --header-background-color CSS variable for image-free headers
Fix typo (backgroun-color → background-color) in .container-header that
silently broke the color fallback.  Split background shorthand into
discrete background-color / background-image properties so the color
renders when --header-background-image is set to none.

Add --header-background-color to all four theme files (dark.standard,
light.standard, dark.custom, light.custom).  Regenerate minified CSS.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 01:54:05 -05:00
Jonathan Miller
b158e7684c fix: direct API sync for updates.xml (PR blocked by reviews)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 00:00:33 -05:00
Jonathan Miller
765bb9b368 feat: sync updates.xml to main via PR instead of direct API push
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 03:22:22 -05:00
24 changed files with 375 additions and 957 deletions

View File

@@ -191,6 +191,16 @@ jobs:
composer install --no-dev --optimize-autoloader --no-interaction
fi
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Minify CSS and JS
run: |
npm ci --ignore-scripts
node scripts/minify.js
- name: Create package
run: |
mkdir -p build/package
@@ -479,39 +489,27 @@ jobs:
# Push to current branch
git push || true
# Also update updates.xml on main via Gitea API (git push blocked by branch protection)
if [ "$CURRENT_BRANCH" != "main" ]; then
GA_TOKEN="${{ secrets.GA_TOKEN }}"
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
# Sync updates.xml to main via direct API
GA_TOKEN="${{ secrets.GA_TOKEN }}"
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
# Get current file SHA on main (required for update)
FILE_SHA=$(curl -sf -H "Authorization: token ${GA_TOKEN}" \
"${API}/contents/updates.xml?ref=main" | jq -r '.sha // empty')
FILE_SHA=$(curl -sf -H "Authorization: token ${GA_TOKEN}" \
"${API}/contents/updates.xml?ref=main" | jq -r '.sha // empty')
if [ -n "$FILE_SHA" ]; then
# Base64-encode the updates.xml content from working tree (has updated SHA)
CONTENT=$(base64 -w0 updates.xml)
RESPONSE=$(curl -s -w "\n%{http_code}" -X PUT -H "Authorization: token ${GA_TOKEN}" \
-H "Content-Type: application/json" \
"${API}/contents/updates.xml" \
-d "$(jq -n \
--arg content "$CONTENT" \
--arg sha "$FILE_SHA" \
--arg msg "chore: update ${STABILITY} channel to ${VERSION} on main [skip ci]" \
--arg branch "main" \
'{content: $content, sha: $sha, message: $msg, branch: $branch}'
)")
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "201" ]; then
echo "updates.xml synced to main via API (HTTP ${HTTP_CODE})"
else
echo "WARNING: failed to sync updates.xml to main (HTTP ${HTTP_CODE})"
echo "$RESPONSE" | head -5
fi
else
echo "WARNING: could not get file SHA for updates.xml on main"
fi
if [ -n "$FILE_SHA" ]; then
CONTENT=$(base64 -w0 updates.xml)
curl -sf -X PUT -H "Authorization: token ${GA_TOKEN}" \
-H "Content-Type: application/json" \
"${API}/contents/updates.xml" \
-d "$(jq -n \
--arg content "$CONTENT" \
--arg sha "$FILE_SHA" \
--arg msg "chore: sync updates.xml ${STABILITY} ${VERSION} [skip ci]" \
--arg branch "main" \
'{content: $content, sha: $sha, message: $msg, branch: $branch}'
)" > /dev/null 2>&1 \
&& echo "updates.xml synced to main" \
|| echo "WARNING: failed to sync updates.xml to main"
fi
- name: Summary

View File

@@ -103,6 +103,16 @@ jobs:
composer install --no-dev --optimize-autoloader --no-interaction
fi
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Minify CSS and JS
run: |
npm ci --ignore-scripts
node scripts/minify.js
- name: Create package
run: |
mkdir -p build/package

8
.gitignore vendored
View File

@@ -119,6 +119,14 @@ site/
*.js.map
*.tsbuildinfo
# ============================================================
# Minified assets (generated at release time by scripts/minify.js)
# Vendor pre-minified files are excluded from this rule.
# ============================================================
src/media/css/*.min.css
src/media/css/theme/*.min.css
src/media/js/*.min.js
# ============================================================
# CI / test artifacts
# ============================================================

View File

@@ -9,7 +9,7 @@
INGROUP: MokoOnyx.Documentation
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
FILE: ./README.md
VERSION: 01.00.17
VERSION: 01.00.25
BRIEF: Documentation for MokoOnyx template
-->

144
package-lock.json generated Normal file
View File

@@ -0,0 +1,144 @@
{
"name": "mokoonyx-build",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mokoonyx-build",
"devDependencies": {
"clean-css": "^5.3.3",
"terser": "^5.39.0"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/source-map": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
"integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.31",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/acorn": {
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true,
"license": "MIT"
},
"node_modules/clean-css": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
"integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
"dev": true,
"license": "MIT",
"dependencies": {
"source-map": "~0.6.0"
},
"engines": {
"node": ">= 10.0"
}
},
"node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true,
"license": "MIT"
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"node_modules/terser": {
"version": "5.46.2",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.46.2.tgz",
"integrity": "sha512-uxfo9fPcSgLDYob/w1FuL0c99MWiJDnv+5qXSQc5+Ki5NjVNsYi66INnMFBjf6uFz6OnX12piJQPF4IpjJTNTw==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.15.0",
"commander": "^2.20.0",
"source-map-support": "~0.5.20"
},
"bin": {
"terser": "bin/terser"
},
"engines": {
"node": ">=10"
}
}
}
}

87
scripts/minify.js Normal file
View File

@@ -0,0 +1,87 @@
#!/usr/bin/env node
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* This file is part of a Moko Consulting project.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* FILE INFORMATION
* DEFGROUP: MokoOnyx.Build
* INGROUP: MokoOnyx
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
* PATH: /scripts/minify.js
* VERSION: 01.00.00
* BRIEF: Minify project CSS and JS assets (excludes vendor/)
*/
const fs = require('fs');
const path = require('path');
const CleanCSS = require('clean-css');
const { minify: terserMinify } = require('terser');
const SRC = path.resolve(__dirname, '..', 'src', 'media');
// Project-owned files only — vendor assets ship pre-minified
const CSS_FILES = [
'css/editor.css',
'css/template.css',
'css/theme/dark.standard.css',
'css/theme/light.standard.css',
];
const JS_FILES = [
'js/gtm.js',
'js/template.js',
];
async function minifyCSS(relPath) {
const src = path.join(SRC, relPath);
const dest = src.replace(/\.css$/, '.min.css');
const input = fs.readFileSync(src, 'utf8');
const result = new CleanCSS({ level: 1 }).minify(input);
if (result.errors.length) {
console.error(` FAIL ${relPath}:`, result.errors);
return false;
}
fs.writeFileSync(dest, result.styles);
const ratio = ((1 - result.styles.length / input.length) * 100).toFixed(0);
console.log(` ${relPath} → .min.css (${ratio}% smaller)`);
return true;
}
async function minifyJS(relPath) {
const src = path.join(SRC, relPath);
const dest = src.replace(/\.js$/, '.min.js');
const input = fs.readFileSync(src, 'utf8');
const result = await terserMinify(input, { compress: true, mangle: true });
if (result.error) {
console.error(` FAIL ${relPath}:`, result.error);
return false;
}
fs.writeFileSync(dest, result.code);
const ratio = ((1 - result.code.length / input.length) * 100).toFixed(0);
console.log(` ${relPath} → .min.js (${ratio}% smaller)`);
return true;
}
async function main() {
console.log('Minifying project assets...\n');
let ok = true;
for (const f of CSS_FILES) {
if (!await minifyCSS(f)) ok = false;
}
for (const f of JS_FILES) {
if (!await minifyJS(f)) ok = false;
}
console.log(ok ? '\nDone.' : '\nCompleted with errors.');
process.exit(ok ? 0 : 1);
}
main();

View File

@@ -25,8 +25,9 @@ $headerClass = htmlspecialchars($params->get('header_class', ''), ENT_COMPAT, 'U
if ($params->get('backgroundimage')) {
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $app->getDocument()->getWebAssetManager();
$bgUrl = Uri::root(true) . '/' . HTMLHelper::_('cleanImageURL', $params->get('backgroundimage'))->url;
$wa->addInlineStyle(
'#' . $modId . '{background-image: url("' . Uri::root(true) . '/' . HTMLHelper::_('cleanImageURL', $params->get('backgroundimage'))->url . '");}',
'#' . $modId . '{--hero-bg-image: url("' . $bgUrl . '"); background-image: var(--hero-bg-image);}',
['name' => $modId]
);
}

View File

@@ -176,7 +176,7 @@ TPL_MOKOONYX_CSS_VARS_BLOCK_COLORS_LABEL="Block Colour System (top-a / top-b / b
TPL_MOKOONYX_CSS_VARS_BLOCK_COLORS_DESC="Automatic brand colour palette for modules in <code>top-a</code>, <code>top-b</code>, <code>bottom-a</code>, and <code>bottom-b</code> positions. Colours assigned by <code>:nth-child()</code> order — no classes needed.<br><br><strong>Slot palette</strong><br><code>--block-color-1</code> / <code>--block-text-1</code> — 1st module<br><code>--block-color-2</code> / <code>--block-text-2</code> — 2nd module<br><code>--block-color-3</code> / <code>--block-text-3</code> — 3rd module<br><code>--block-color-4</code> / <code>--block-text-4</code> — 4th module<br><br><strong>Named overrides</strong> (add an ID to the module HTML to bypass slot colour)<br><code>--block-highlight-bg</code> / <code>--block-highlight-text</code> — for <code>#block-highlight</code><br><code>--block-cta-bg</code> / <code>--block-cta-text</code> — for <code>#block-cta</code><br><code>--block-alert-bg</code> / <code>--block-alert-text</code> — for <code>#block-alert</code><br><br><strong>Priority:</strong> Named ID &gt; Slot colour. No <code>!important</code> needed — specificity handles it."
TPL_MOKOONYX_CSS_VARS_HEADER_LABEL="Header Background"
TPL_MOKOONYX_CSS_VARS_HEADER_DESC="Controls the background of the topbar/header area.<br><code>--header-background-image</code> — CSS <code>background-image</code> value (default: built-in SVG pattern)<br><code>--header-background-attachment</code> — <code>fixed</code> or <code>scroll</code><br><code>--header-background-repeat</code> — e.g. <code>repeat</code>, <code>no-repeat</code><br><code>--header-background-size</code> — e.g. <code>auto</code>, <code>cover</code>, <code>contain</code>"
TPL_MOKOONYX_CSS_VARS_HEADER_DESC="Controls the background of the topbar/header area.<br><code>--header-background-color</code> — fallback colour when no image is set (default: <code>#adadad</code> light / <code>#1a1f2b</code> dark). Set <code>--header-background-image: none;</code> to use a solid colour.<br><code>--header-background-image</code> — CSS <code>background-image</code> value (default: built-in SVG pattern)<br><code>--header-background-attachment</code> — <code>fixed</code> or <code>scroll</code><br><code>--header-background-repeat</code> — e.g. <code>repeat</code>, <code>no-repeat</code><br><code>--header-background-size</code> — e.g. <code>auto</code>, <code>cover</code>, <code>contain</code>"
TPL_MOKOONYX_CSS_VARS_CONTAINERS_LABEL="Container Backgrounds"
TPL_MOKOONYX_CSS_VARS_CONTAINERS_DESC="Each layout container has its own background variables. Replace <code>{pos}</code> with: <code>below-topbar</code>, <code>top-a</code>, <code>top-b</code>, <code>sidebar</code>, <code>bottom-a</code>, or <code>bottom-b</code>.<br><br><code>--container-{pos}-bg-image</code> — Background image (default: <code>none</code>)<br><code>--container-{pos}-bg-color</code> — Background colour (default: <code>transparent</code>)<br><code>--container-{pos}-bg-position</code> — Background position<br><code>--container-{pos}-bg-attachment</code> — <code>fixed</code> or <code>scroll</code><br><code>--container-{pos}-bg-repeat</code> — Repeat behaviour<br><code>--container-{pos}-bg-size</code> — e.g. <code>cover</code>, <code>auto</code><br><code>--container-{pos}-border</code> — Border shorthand<br><code>--container-{pos}-border-radius</code> — Border radius<br><br>Also: <code>--container-toc-bg</code> / <code>--container-toc-color</code> for the TOC sidebar."

View File

@@ -176,7 +176,7 @@ TPL_MOKOONYX_CSS_VARS_BLOCK_COLORS_LABEL="Block Color System (top-a / top-b / bo
TPL_MOKOONYX_CSS_VARS_BLOCK_COLORS_DESC="Automatic brand color palette for modules in <code>top-a</code>, <code>top-b</code>, <code>bottom-a</code>, and <code>bottom-b</code> positions. Colors assigned by <code>:nth-child()</code> order — no classes needed.<br><br><strong>Slot palette</strong><br><code>--block-color-1</code> / <code>--block-text-1</code> — 1st module<br><code>--block-color-2</code> / <code>--block-text-2</code> — 2nd module<br><code>--block-color-3</code> / <code>--block-text-3</code> — 3rd module<br><code>--block-color-4</code> / <code>--block-text-4</code> — 4th module<br><br><strong>Named overrides</strong> (add an ID to the module HTML to bypass slot color)<br><code>--block-highlight-bg</code> / <code>--block-highlight-text</code> — for <code>#block-highlight</code><br><code>--block-cta-bg</code> / <code>--block-cta-text</code> — for <code>#block-cta</code><br><code>--block-alert-bg</code> / <code>--block-alert-text</code> — for <code>#block-alert</code><br><br><strong>Priority:</strong> Named ID &gt; Slot color. No <code>!important</code> needed — specificity handles it."
TPL_MOKOONYX_CSS_VARS_HEADER_LABEL="Header Background"
TPL_MOKOONYX_CSS_VARS_HEADER_DESC="Controls the background of the topbar/header area.<br><code>--header-background-image</code> — CSS <code>background-image</code> value (default: built-in SVG pattern)<br><code>--header-background-attachment</code> — <code>fixed</code> or <code>scroll</code><br><code>--header-background-repeat</code> — e.g. <code>repeat</code>, <code>no-repeat</code><br><code>--header-background-size</code> — e.g. <code>auto</code>, <code>cover</code>, <code>contain</code>"
TPL_MOKOONYX_CSS_VARS_HEADER_DESC="Controls the background of the topbar/header area.<br><code>--header-background-color</code> — fallback color when no image is set (default: <code>#adadad</code> light / <code>#1a1f2b</code> dark). Set <code>--header-background-image: none;</code> to use a solid color.<br><code>--header-background-image</code> — CSS <code>background-image</code> value (default: built-in SVG pattern)<br><code>--header-background-attachment</code> — <code>fixed</code> or <code>scroll</code><br><code>--header-background-repeat</code> — e.g. <code>repeat</code>, <code>no-repeat</code><br><code>--header-background-size</code> — e.g. <code>auto</code>, <code>cover</code>, <code>contain</code>"
TPL_MOKOONYX_CSS_VARS_CONTAINERS_LABEL="Container Backgrounds"
TPL_MOKOONYX_CSS_VARS_CONTAINERS_DESC="Each layout container has its own background variables. Replace <code>{pos}</code> with: <code>below-topbar</code>, <code>top-a</code>, <code>top-b</code>, <code>sidebar</code>, <code>bottom-a</code>, or <code>bottom-b</code>.<br><br><code>--container-{pos}-bg-image</code> — Background image (default: <code>none</code>)<br><code>--container-{pos}-bg-color</code> — Background color (default: <code>transparent</code>)<br><code>--container-{pos}-bg-position</code> — Background position<br><code>--container-{pos}-bg-attachment</code> — <code>fixed</code> or <code>scroll</code><br><code>--container-{pos}-bg-repeat</code> — Repeat behavior<br><code>--container-{pos}-bg-size</code> — e.g. <code>cover</code>, <code>auto</code><br><code>--container-{pos}-border</code> — Border shorthand<br><code>--container-{pos}-border-radius</code> — Border radius<br><br>Also: <code>--container-toc-bg</code> / <code>--container-toc-color</code> for the TOC sidebar."

View File

@@ -1 +0,0 @@
@charset "UTF-8";body{font-size:1rem;font-weight:400;line-height:1.5;color:#22262a;background-color:#fff}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:700;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}h2{font-size:calc(1.325rem + .9vw)}h3{font-size:calc(1.3rem + .6vw)}h4{font-size:calc(1.275rem + .3vw)}h5{font-size:1.25rem}h6{font-size:1rem}a{text-decoration:none}a:link{color:#224faa}a:hover{color:#424077}p{margin-top:0;margin-bottom:1rem}hr#system-readmore{color:red;border:1px dashed red}span[lang]{padding:2px;border:1px dashed #bbb}span[lang]:after{font-size:smaller;color:red;vertical-align:super;content:attr(lang)}

View File

@@ -101,26 +101,34 @@ form {
width: 280px;
}
.drawer-toggle-left{
button.drawer-toggle-left,
.drawer-toggle-left.btn {
position: fixed;
top: 50%;
left: 0px;
z-index: 1050;
background-color: var(--nav-bg-color);
color: var(--nav-text-color, gray);
background-color: var(--accent-color, var(--color-primary, #2a69b8)) !important;
color: var(--accent-color-text, #fff) !important;
border-color: var(--accent-color, var(--color-primary, #2a69b8)) !important;
padding-left: .5rem;
padding-right: .5rem;
border-top-left-radius: 0 !important;
border-bottom-left-radius: 0 !important;
}
.drawer-toggle-right{
button.drawer-toggle-right,
.drawer-toggle-right.btn {
position: fixed;
top: 50%;
right: 0px;
z-index: 1050;
background-color: var(--nav-bg-color);
color: var(--nav-text-color, gray);
background-color: var(--accent-color, var(--color-primary, #2a69b8)) !important;
color: var(--accent-color-text, #fff) !important;
border-color: var(--accent-color, var(--color-primary, #2a69b8)) !important;
padding-left: .5rem;
padding-right: .5rem;
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
hr {
@@ -13623,12 +13631,14 @@ a.navbar-brand {
.btn-primary {
background-color: var(--color-primary, #112855);
border-color: var(--color-primary, #112855);
color: #000;
}
.btn-primary:active,
.btn-primary:focus {
background-color: var(--color-active);
border-color: var(--color-active);
color: #000;
}
.btn-group {
@@ -13925,8 +13935,22 @@ meter {
}
}
/* ── HERO CARD BASE ── */
/* ── HERO WRAPPER (module wrapper, full-width centering container) ── */
.hero {
display: flex;
justify-content: var(--hero-justify, center);
align-items: var(--hero-align, center);
width: 100%;
min-height: var(--hero-min-height, 75vh);
background-image: var(--hero-bg-image, none);
background-repeat: var(--hero-bg-repeat, no-repeat);
background-attachment: var(--hero-bg-attachment, fixed);
background-position: var(--hero-bg-position, center);
background-size: var(--hero-bg-size, cover);
}
/* ── HERO CARD BASE (the .card inside the hero wrapper) ── */
.hero .card {
max-width: var(--hero-card-max-width, 600px);
padding: var(--hero-card-padding-y, 3rem) var(--hero-card-padding-x, 2rem);
background-color: var(--hero-card-bg, var(--hero-primary-bg-color, #0d1e3a));
@@ -13935,17 +13959,21 @@ meter {
background-position: center;
color: var(--hero-card-color, var(--hero-primary-color, #f1f5f9));
border-radius: var(--hero-card-border-radius, .5rem);
text-align: var(--hero-card-text-align, center);
margin-left: auto;
margin-right: auto;
border: none;
}
/* ── PRIMARY VARIANT (uses default card vars) ── */
.hero#primary {
/* ── PRIMARY VARIANT ── */
.hero#primary .card {
background-color: var(--hero-card-bg, #0d1e3a);
background-image: var(--hero-card-overlay, none);
color: var(--hero-card-color, #f1f5f9);
}
/* ── SECONDARY / ALTERNATIVE VARIANT ── */
.hero#secondary {
.hero#secondary .card {
max-width: var(--hero-alt-card-max-width, 600px);
padding: var(--hero-alt-card-padding-y, 3rem) var(--hero-alt-card-padding-x, 2rem);
background-color: var(--hero-alt-card-bg, #080f1e);
@@ -13967,14 +13995,14 @@ meter {
background-image: none;
}
.hero {
max-width: 100%;
width: 100%;
min-height: 100vh;
min-height: 100dvh;
border-radius: 0;
display: flex;
flex-direction: column;
justify-content: center;
}
.hero .card {
max-width: 100%;
width: 100%;
border-radius: 0;
}
}
@@ -14193,10 +14221,11 @@ fieldset>* {
.container-header {
z-index: 100;
background: var(--header-background-image, url('../../../../../../media/templates/site/mokoonyx/images/bg.svg'));
background-color: var(--header-background-color, #adadad);
background-image: var(--header-background-image, url('../../../../../../media/templates/site/mokoonyx/images/bg.svg'));
background-size: var(--header-background-size, auto);
box-shadow: 0 5px 5px hsla(0, 0%, 0%, 0.03) inset;
background-repeat: var(--header-background-repeat, repeat);
box-shadow: 0 5px 5px hsla(0, 0%, 0%, 0.03) inset;
}
/* Sticky header: override z-index to stay above all content */
@@ -16959,11 +16988,14 @@ body:not(.has-sidebar-right) .site-grid .container-component {
}
.btn-primary {
--bs-btn-color: #000;
--bs-btn-bg: #333;
--bs-btn-border-color: #333;
--bs-btn-hover-color: #000;
--bs-btn-hover-bg: #555;
--bs-btn-hover-border-color: #555;
--bs-btn-focus-shadow-rgb: 49, 132, 253;
--bs-btn-active-color: #000;
--bs-btn-active-bg: #555;
--bs-btn-active-border-color: #555;
--bs-btn-disabled-bg: #A0A0A0;
@@ -18949,7 +18981,7 @@ nav[data-toggle=toc] .nav-link.active+ul{
/* ===== BOOTSTRAP & JOOMLA BUTTONS ===== */
.btn-primary {
--btn-color: white;
--btn-color: #000;
--btn-bg: var(--color-primary, #112855);
--btn-border-color: var(--color-primary, #112855);
--btn-hover-bg: color-mix(in srgb, var(--color-primary, #112855) 85%, black);

File diff suppressed because one or more lines are too long

View File

@@ -210,6 +210,7 @@ color-scheme: dark;
/* ===== HEADER BACKGROUND ===== */
--header-background-color: #1a1f2b;
--header-background-image: url('../../../../../../media/templates/site/mokoonyx/images/bg.svg');
--header-background-attachment: fixed;
--header-background-repeat: repeat;

File diff suppressed because one or more lines are too long

View File

@@ -209,6 +209,7 @@ color-scheme: light;
/* ===== HEADER BACKGROUND ===== */
--header-background-color: #adadad;
--header-background-image: url('../../../../../../media/templates/site/mokoonyx/images/bg.svg');
--header-background-attachment: fixed;
--header-background-repeat: repeat;

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
(()=>{"use strict";const e=window,t={},n=e=>{const t=(()=>{const e=document.currentScript;return e||(Array.from(document.getElementsByTagName("script")).reverse().find(e=>(e.getAttribute("src")||"").includes("/gtm.js"))||null)})(),n=document.documentElement,o=document.body,a=document.querySelector(`meta[name="moko:gtm-${e}"]`);return t&&t.dataset&&t.dataset[e]||n&&n.dataset&&n.dataset[e]||o&&o.dataset&&o.dataset[e]||a&&a.getAttribute("content")||null},o=(e,t=!1)=>{if(null==e)return t;const n=String(e).trim().toLowerCase();return!!["1","true","yes","y","on"].includes(n)||!["0","false","no","n","off"].includes(n)&&t},a=(...e)=>{if(r.debug)try{console.info("[moko-gtm]",...e)}catch(e){}},r={id:"",dataLayerName:"dataLayer",debug:!1,ignoreDNT:!1,blockOnDev:!0,envAuth:"",envPreview:"",consentDefault:{analytics_storage:"granted",functionality_storage:"granted",security_storage:"granted",ad_storage:"denied",ad_user_data:"denied",ad_personalization:"denied"},pageVars:()=>({})},d=(e,t={})=>{const n={...e};for(const e in t){if(!Object.prototype.hasOwnProperty.call(t,e))continue;const o=t[e];o&&"object"==typeof o&&!Array.isArray(o)?n[e]={...n[e]||{},...o}:void 0!==o&&(n[e]=o)}return n},i=()=>{const t=e.MOKO_GTM_OPTIONS&&"object"==typeof e.MOKO_GTM_OPTIONS?e.MOKO_GTM_OPTIONS:{},a=n("id")||e.MOKO_GTM_ID||"",r=n("dataLayer")||"",d=n("debug"),i=n("ignoreDnt"),c=n("blockOnDev"),s=n("envAuth")||"",u=n("envPreview")||"";return{id:a||t.id||"",dataLayerName:r||t.dataLayerName||void 0,debug:o(d,!!t.debug),ignoreDNT:o(i,!!t.ignoreDNT),blockOnDev:o(c,t.blockOnDev??!0),envAuth:s||t.envAuth||"",envPreview:u||t.envPreview||"",consentDefault:t.consentDefault||void 0,pageVars:"function"==typeof t.pageVars?t.pageVars:void 0}},c=()=>{const t=r.dataLayerName;return e[t]=e[t]||[],e[t]},s=(...e)=>{c().push(arguments.length>1?e:e[0]),a("gtag push:",e)};t.push=(...e)=>s(...e),t.setConsent=e=>{s("consent","update",e||{})},t.isLoaded=()=>!!document.querySelector('script[src*="googletagmanager.com/gtm.js"]'),t.config=()=>({...r});const u=()=>{if(!r.id)return void a("GTM ID missing; aborting load.");if(t.isLoaded())return void a("GTM already loaded; skipping duplicate injection.");c().push({"gtm.start":(new Date).getTime(),event:"gtm.js"});const e=document.getElementsByTagName("script")[0],n=document.createElement("script");n.async=!0,n.src=`https://www.googletagmanager.com/gtm.js?id=${encodeURIComponent(r.id)}${"dataLayer"!==r.dataLayerName?`&l=${encodeURIComponent(r.dataLayerName)}`:""}${(()=>{const e=[];return r.envAuth&&e.push(`gtm_auth=${encodeURIComponent(r.envAuth)}`),r.envPreview&&e.push(`gtm_preview=${encodeURIComponent(r.envPreview)}`,"gtm_cookies_win=x"),e.length?`&${e.join("&")}`:""})()}`,e&&e.parentNode?e.parentNode.insertBefore(n,e):(document.head||document.documentElement).appendChild(n),a("Injected GTM script:",n.src)},g=()=>!r.ignoreDNT&&(()=>{const e=navigator,t=(e.doNotTrack||e.msDoNotTrack||e.navigator&&e.navigator.doNotTrack||"").toString().toLowerCase();return"1"===t||"yes"===t})()?(a("DNT is enabled; blocking GTM load (set ignoreDNT=true to override)."),!1):!r.blockOnDev||!(()=>{const t=e.location&&e.location.hostname||"";return"localhost"===t||"127.0.0.1"===t||t.endsWith(".local")||t.endsWith(".test")})()||(a("Development host detected; blocking GTM load (set blockOnDev=false to override)."),!1);t.init=(e={})=>{const t=i(),n=d(r,d(t,e));Object.assign(r,n),a("Config:",r),c(),s("consent","default",r.consentDefault),a("Applied default consent:",r.consentDefault),(()=>{const e={event:"moko.page_init",page_title:document.title||"",page_language:document.documentElement&&document.documentElement.lang||"",..."function"==typeof r.pageVars&&r.pageVars()||{}};s(e)})(),g()?u():a("GTM load prevented by configuration or environment.")};const m=()=>{!(!i().id&&!e.MOKO_GTM_ID)?t.init():a("No GTM ID detected; awaiting manual init via window.mokoGTM.init({ id: 'GTM-XXXXXXX' }).")};"complete"===document.readyState||"interactive"===document.readyState?setTimeout(m,0):document.addEventListener("DOMContentLoaded",m,{once:!0}),e.mokoGTM=t;try{const e=i();o(e.debug,!1)&&(r.debug=!0,a("Ready. You can call window.mokoGTM.init({ id: 'GTM-XXXXXXX' })."))}catch(e){}})();

View File

@@ -1 +0,0 @@
!function(e,t){"use strict";var a="theme",n=e.matchMedia("(prefers-color-scheme: dark)"),r=t.documentElement;function o(e){r.setAttribute("data-bs-theme",e),r.setAttribute("data-aria-theme",e);try{localStorage.setItem(a,e)}catch(e){}}function d(){try{localStorage.removeItem(a)}catch(e){}}function i(){return n.matches?"dark":"light"}function c(){try{return localStorage.getItem(a)}catch(e){return null}}function l(){if(!t.getElementById("mokoThemeFab")){var a,l=t.createElement("div");l.id="mokoThemeFab",l.className=(a=(t.body.getAttribute("data-theme-fab-pos")||"br").toLowerCase(),/^(br|bl|tr|tl)$/.test(a)||(a="br"),"pos-"+a);var s=t.createElement("span");s.className="label",s.textContent="Light";var u=t.createElement("button");u.id="mokoThemeSwitch",u.type="button",u.setAttribute("role","switch"),u.setAttribute("aria-label","Toggle dark mode"),u.setAttribute("aria-checked","false");var m=t.createElement("span");m.className="switch";var h=t.createElement("span");h.className="knob",m.appendChild(h),u.appendChild(m);var f=t.createElement("span");f.className="label",f.textContent="Dark";var b=t.createElement("button");b.id="mokoThemeAuto",b.type="button",b.className="btn btn-sm btn-link text-decoration-none px-2",b.setAttribute("aria-label","Follow system theme"),b.textContent="Auto",u.addEventListener("click",function(){var e="dark"===(r.getAttribute("data-bs-theme")||"light").toLowerCase()?"light":"dark";o(e),u.setAttribute("aria-checked","dark"===e?"true":"false");var a=t.querySelector('meta[name="theme-color"]');a&&a.setAttribute("content","dark"===e?"#0f1115":"#ffffff")}),b.addEventListener("click",function(){d();var e=i();o(e),u.setAttribute("aria-checked","dark"===e?"true":"false")});var g=function(){if(!c()){var e=i();o(e),u.setAttribute("aria-checked","dark"===e?"true":"false")}};"function"==typeof n.addEventListener?n.addEventListener("change",g):"function"==typeof n.addListener&&n.addListener(g);var p=c()||i();u.setAttribute("aria-checked","dark"===p?"true":"false"),l.appendChild(s),l.appendChild(u),l.appendChild(f),l.appendChild(b),t.body.appendChild(l),e.mokoThemeFabStatus=function(){var a=t.getElementById("mokoThemeFab");if(!a)return{mounted:!1};var n=a.getBoundingClientRect();return{mounted:!0,rect:{top:n.top,left:n.left,width:n.width,height:n.height},zIndex:e.getComputedStyle(a).zIndex,posClass:a.className}},setTimeout(function(){var e=l.getBoundingClientRect();(e.width<10||e.height<10)&&(l.classList.add("debug-outline"),console.warn("[moko] Theme FAB mounted but appears too small — check CSS collisions."))},50)}}function s(){e.scrollY>50?t.body.classList.add("scrolled"):t.body.classList.remove("scrolled")}function u(){var a=t.getElementById("back-top");a&&a.addEventListener("click",function(t){t.preventDefault(),e.scrollTo({top:0,behavior:"smooth"})})}function m(){!function(){var e=c()||i();o(e);var a=function(){c()||o(i())};"function"==typeof n.addEventListener?n.addEventListener("change",a):"function"==typeof n.addListener&&n.addListener(a);var r=t.getElementById("themeSwitch"),l=t.getElementById("themeAuto");r&&(r.checked="dark"===e,r.addEventListener("change",function(){o(r.checked?"dark":"light")})),l&&l.addEventListener("click",function(){d(),o(i())})}(),"1"===t.body.getAttribute("data-theme-fab-enabled")&&l(),s(),e.addEventListener("scroll",s),t.querySelector(".drawer-toggle-left")||t.querySelector(".drawer-toggle-right"),u()}"loading"===t.readyState?t.addEventListener("DOMContentLoaded",m):m()}(window,document);

View File

@@ -93,6 +93,7 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface
$this->migrateFromCassiopeia();
$this->replaceCassiopeiaReferences();
$this->clearFaviconStamp();
$this->lockExtension();
}
return true;
@@ -378,6 +379,29 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface
}
}
/**
* Lock the extension to prevent uninstallation via Extension Manager.
*/
private function lockExtension(): void
{
$db = Factory::getDbo();
try {
$query = $db->getQuery(true)
->update('#__extensions')
->set($db->quoteName('locked') . ' = 1')
->where($db->quoteName('element') . ' = ' . $db->quote(self::NEW_NAME))
->where($db->quoteName('type') . ' = ' . $db->quote('template'));
$db->setQuery($query)->execute();
if ($db->getAffectedRows() > 0) {
$this->logMessage('MokoOnyx extension locked.');
}
} catch (\Throwable $e) {
$this->logMessage('Failed to lock extension: ' . $e->getMessage(), 'warning');
}
}
private function logMessage(string $message, string $priority = 'info'): void
{
$priorities = [

View File

@@ -39,13 +39,13 @@
</server>
</updateservers>
<name>MokoOnyx</name>
<version>01.00.17</version>
<version>01.00.25</version>
<scriptfile>script.php</scriptfile>
<creationDate>2026-04-15</creationDate>
<author>Jonathan Miller || Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<copyright>(C)GNU General Public License Version 3 - 2026 Moko Consulting</copyright>
<description><![CDATA[<div style="padding:1rem;border:2px solid #059669;border-radius:8px;background:#ecfdf5;margin-bottom:1rem;"> <h3 style="color:#065f46;margin:0 0 .5rem;">Migrating from MokoCassiopeia?</h3> <p style="margin:0 0 .75rem;">MokoOnyx automatically imports your MokoCassiopeia settings on first use. To trigger the migration:</p> <ol style="margin:0 0 .75rem 1.5rem;"> <li>Install MokoOnyx via <strong>System → Install → Extensions</strong></li> <li>Go to <strong>System → Site Templates</strong> and set <strong>MokoOnyx</strong> as your default template</li> <li><strong>Visit any page on your site</strong> — the migration runs automatically on first page load</li> <li>Check <strong>administrator/logs/mokoonyx_migrate.log.php</strong> to confirm migration completed</li> <li>Uninstall MokoCassiopeia from <strong>Extensions → Manage</strong></li> </ol> <p style="margin:0;font-size:.9em;color:#065f46;"> <strong>What gets migrated:</strong> template style params, custom colour palettes (light.custom.css, dark.custom.css), user.css, user.js, and update server URL. To re-run the migration, delete <code>templates/mokoonyx/.migrated</code> and reload any page. </p></div> <p><img src="https://img.shields.io/gitea/v/release/MokoConsulting/MokoOnyx?gitea_url=https%3A%2F%2Fgit.mokoconsulting.tech&amp;logo=gitea&amp;logoColor=white&amp;label=version" alt="Version" /> <img src="https://img.shields.io/badge/license-GPL--3.0--or--later-green.svg?logo=gnu&amp;logoColor=white" alt="License" /> <img src="https://img.shields.io/badge/Joomla-5.x%20%7C%206.x-red.svg?logo=joomla&amp;logoColor=white" alt="Joomla" /> <img src="https://img.shields.io/badge/PHP-8.1%2B-777BB4.svg?logo=php&amp;logoColor=white" alt="PHP" /></p> <h3>MokoOnyx</h3> <p> <strong>MokoOnyx</strong> (formerly MokoCassiopeia) is a modern, lightweight enhancement layer built on Joomla's Cassiopeia template. It adds Font Awesome 7, Bootstrap 5 helpers, automatic Table of Contents, advanced Dark Mode theming, and optional Google Tag Manager / GA4 integration — all with minimal core overrides for maximum upgrade compatibility. </p> <h4>Custom Colour Themes</h4> <p> Copy <code>templates/mokoonyx/templates/light.custom.css</code> to <code>media/templates/site/mokoonyx/css/theme/light.custom.css</code> (or dark equivalent), customise the CSS variables, then select "Custom" in the Theme tab. </p> <h4>Custom CSS &amp; JavaScript</h4> <ul> <li><code>media/templates/site/mokoonyx/css/user.css</code> — custom CSS overrides</li> <li><code>media/templates/site/mokoonyx/js/user.js</code> — custom JavaScript</li> </ul> <p>These files survive template updates.</p>]]></description>
<description><![CDATA[<div style="padding:1rem;border:2px solid #059669;border-radius:8px;background:#ecfdf5;margin-bottom:1rem;"> <h3 style="color:#065f46;margin:0 0 .5rem;">Migrating from MokoCassiopeia?</h3> <p style="margin:0 0 .75rem;">MokoOnyx automatically imports your MokoCassiopeia settings on first use. To trigger the migration:</p> <ol style="margin:0 0 .75rem 1.5rem;"> <li>Install MokoOnyx via <strong>System → Install → Extensions</strong></li> <li>Go to <strong>System → Site Templates</strong> and set <strong>MokoOnyx</strong> as your default template</li> <li><strong>Visit any page on your site</strong> — the migration runs automatically on first page load</li> <li>Check <strong>administrator/logs/mokoonyx_migrate.log.php</strong> to confirm migration completed</li> <li>Uninstall MokoCassiopeia from <strong>Extensions → Manage</strong></li> </ol> <p style="margin:0;font-size:.9em;color:#065f46;"> <strong>What gets migrated:</strong> template style params, custom colour palettes (light.custom.css, dark.custom.css), user.css, user.js, and update server URL. To re-run the migration, delete <code>templates/mokoonyx/.migrated</code> and reload any page. </p></div> <p><img src="https://img.shields.io/gitea/v/release/MokoConsulting/MokoOnyx?gitea_url=https%3A%2F%2Fgit.mokoconsulting.tech&amp;logo=gitea&amp;logoColor=white&amp;label=version" alt="Version" /> <img src="https://img.shields.io/badge/license-GPL--3.0--or--later-green.svg?logo=gnu&amp;logoColor=white" alt="License" /> <img src="https://img.shields.io/badge/Joomla-5.x%20%7C%206.x-red.svg?logo=joomla&amp;logoColor=white" alt="Joomla" /> <img src="https://img.shields.io/badge/PHP-8.1%2B-777BB4.svg?logo=php&amp;logoColor=white" alt="PHP" /></p> <h3>MokoOnyx</h3> <p> <strong>MokoOnyx</strong> (formerly MokoCassiopeia) is a modern, lightweight enhancement layer built on Joomla's Cassiopeia template. It adds Font Awesome 7, Bootstrap 5 helpers, automatic Table of Contents, advanced Dark Mode theming, and optional Google Tag Manager / GA4 integration — all with minimal core overrides for maximum upgrade compatibility. </p> <h4>Custom Colour Themes</h4> <p> Copy <code>templates/mokoonyx/templates/light.custom.css</code> to <code>media/templates/site/mokoonyx/css/theme/light.custom.css</code> (or dark equivalent), customise the CSS variables, then select "Custom" in the Theme tab. </p> <h4>Custom CSS &amp; JavaScript</h4> <ul> <li><code>media/templates/site/mokoonyx/css/user.css</code> — custom CSS overrides</li> <li><code>media/templates/site/mokoonyx/js/user.js</code> — custom JavaScript</li> </ul> <p>These files survive template updates.</p> <h4>CSS Variables Reference</h4> <p>For a complete list of CSS variables, see the <a href="https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx" target="_blank">MokoOnyx repository documentation</a>.</p>]]></description>
<inheritable>1</inheritable>
<files>
<filename>component.php</filename>
@@ -327,57 +327,11 @@
</field>
</fieldset>
<!-- CSS Variables reference tab -->
<!-- CSS Variables: see repo documentation -->
<fieldset name="css_variables" label="TPL_MOKOONYX_CSS_VARS_FIELDSET_LABEL">
<field name="css_vars_intro" type="note" description="TPL_MOKOONYX_CSS_VARS_INTRO" />
<field name="css_vars_brand" type="note" label="TPL_MOKOONYX_CSS_VARS_BRAND_LABEL" description="TPL_MOKOONYX_CSS_VARS_BRAND_DESC" class="alert alert-light" />
<field name="css_vars_links" type="note" label="TPL_MOKOONYX_CSS_VARS_LINKS_LABEL" description="TPL_MOKOONYX_CSS_VARS_LINKS_DESC" class="alert alert-light" />
<field name="css_vars_typography" type="note" label="TPL_MOKOONYX_CSS_VARS_TYPO_LABEL" description="TPL_MOKOONYX_CSS_VARS_TYPO_DESC" class="alert alert-light" />
<field name="css_vars_nav" type="note" label="TPL_MOKOONYX_CSS_VARS_NAV_LABEL" description="TPL_MOKOONYX_CSS_VARS_NAV_DESC" class="alert alert-light" />
<field name="css_vars_layout" type="note" label="TPL_MOKOONYX_CSS_VARS_LAYOUT_LABEL" description="TPL_MOKOONYX_CSS_VARS_LAYOUT_DESC" class="alert alert-light" />
<field name="css_vars_breakpoints" type="note" label="TPL_MOKOONYX_CSS_VARS_BP_LABEL" description="TPL_MOKOONYX_CSS_VARS_BP_DESC" class="alert alert-light" />
<field name="css_vars_bootstrap" type="note" label="TPL_MOKOONYX_CSS_VARS_BS_LABEL" description="TPL_MOKOONYX_CSS_VARS_BS_DESC" class="alert alert-light" />
<field name="css_vars_bootstrap_states" type="note" label="TPL_MOKOONYX_CSS_VARS_BS_STATES_LABEL" description="TPL_MOKOONYX_CSS_VARS_BS_STATES_DESC" class="alert alert-light" />
<field name="css_vars_alert_list" type="note" label="TPL_MOKOONYX_CSS_VARS_ALERT_LIST_LABEL" description="TPL_MOKOONYX_CSS_VARS_ALERT_LIST_DESC" class="alert alert-light" />
<field name="css_vars_colors" type="note" label="TPL_MOKOONYX_CSS_VARS_COLORS_LABEL" description="TPL_MOKOONYX_CSS_VARS_COLORS_DESC" class="alert alert-light" />
<field name="css_vars_hero" type="note" label="TPL_MOKOONYX_CSS_VARS_HERO_LABEL" description="TPL_MOKOONYX_CSS_VARS_HERO_DESC" class="alert alert-light" />
<field name="css_vars_hero_variants" type="note" label="TPL_MOKOONYX_CSS_VARS_HERO_VARIANTS_LABEL" description="TPL_MOKOONYX_CSS_VARS_HERO_VARIANTS_DESC" class="alert alert-info" />
<field name="css_vars_block_colors" type="note" label="TPL_MOKOONYX_CSS_VARS_BLOCK_COLORS_LABEL" description="TPL_MOKOONYX_CSS_VARS_BLOCK_COLORS_DESC" class="alert alert-info" />
<field name="css_vars_header" type="note" label="TPL_MOKOONYX_CSS_VARS_HEADER_LABEL" description="TPL_MOKOONYX_CSS_VARS_HEADER_DESC" class="alert alert-light" />
<field name="css_vars_containers" type="note" label="TPL_MOKOONYX_CSS_VARS_CONTAINERS_LABEL" description="TPL_MOKOONYX_CSS_VARS_CONTAINERS_DESC" class="alert alert-light" />
<field name="css_vars_borders" type="note" label="TPL_MOKOONYX_CSS_VARS_BORDERS_LABEL" description="TPL_MOKOONYX_CSS_VARS_BORDERS_DESC" class="alert alert-light" />
<field name="css_vars_shadows" type="note" label="TPL_MOKOONYX_CSS_VARS_SHADOWS_LABEL" description="TPL_MOKOONYX_CSS_VARS_SHADOWS_DESC" class="alert alert-light" />
<field name="css_vars_forms" type="note" label="TPL_MOKOONYX_CSS_VARS_FORMS_LABEL" description="TPL_MOKOONYX_CSS_VARS_FORMS_DESC" class="alert alert-light" />
<field name="css_vars_buttons" type="note" label="TPL_MOKOONYX_CSS_VARS_BUTTONS_LABEL" description="TPL_MOKOONYX_CSS_VARS_BUTTONS_DESC" class="alert alert-light" />
<field name="css_vars_cards" type="note" label="TPL_MOKOONYX_CSS_VARS_CARDS_LABEL" description="TPL_MOKOONYX_CSS_VARS_CARDS_DESC" class="alert alert-light" />
<field name="css_vars_accordion" type="note" label="TPL_MOKOONYX_CSS_VARS_ACCORDION_LABEL" description="TPL_MOKOONYX_CSS_VARS_ACCORDION_DESC" class="alert alert-light" />
<field name="css_vars_alert_base" type="note" label="TPL_MOKOONYX_CSS_VARS_ALERT_BASE_LABEL" description="TPL_MOKOONYX_CSS_VARS_ALERT_BASE_DESC" class="alert alert-light" />
<field name="css_vars_badge" type="note" label="TPL_MOKOONYX_CSS_VARS_BADGE_LABEL" description="TPL_MOKOONYX_CSS_VARS_BADGE_DESC" class="alert alert-light" />
<field name="css_vars_breadcrumb" type="note" label="TPL_MOKOONYX_CSS_VARS_BREADCRUMB_LABEL" description="TPL_MOKOONYX_CSS_VARS_BREADCRUMB_DESC" class="alert alert-light" />
<field name="css_vars_dropdown_menu" type="note" label="TPL_MOKOONYX_CSS_VARS_DROPDOWN_MENU_LABEL" description="TPL_MOKOONYX_CSS_VARS_DROPDOWN_MENU_DESC" class="alert alert-light" />
<field name="css_vars_list_group" type="note" label="TPL_MOKOONYX_CSS_VARS_LIST_GROUP_LABEL" description="TPL_MOKOONYX_CSS_VARS_LIST_GROUP_DESC" class="alert alert-light" />
<field name="css_vars_modal" type="note" label="TPL_MOKOONYX_CSS_VARS_MODAL_LABEL" description="TPL_MOKOONYX_CSS_VARS_MODAL_DESC" class="alert alert-light" />
<field name="css_vars_backdrop" type="note" label="TPL_MOKOONYX_CSS_VARS_BACKDROP_LABEL" description="TPL_MOKOONYX_CSS_VARS_BACKDROP_DESC" class="alert alert-light" />
<field name="css_vars_nav_tabs" type="note" label="TPL_MOKOONYX_CSS_VARS_NAV_TABS_LABEL" description="TPL_MOKOONYX_CSS_VARS_NAV_TABS_DESC" class="alert alert-light" />
<field name="css_vars_nav_pills" type="note" label="TPL_MOKOONYX_CSS_VARS_NAV_PILLS_LABEL" description="TPL_MOKOONYX_CSS_VARS_NAV_PILLS_DESC" class="alert alert-light" />
<field name="css_vars_pagination" type="note" label="TPL_MOKOONYX_CSS_VARS_PAGINATION_LABEL" description="TPL_MOKOONYX_CSS_VARS_PAGINATION_DESC" class="alert alert-light" />
<field name="css_vars_popover" type="note" label="TPL_MOKOONYX_CSS_VARS_POPOVER_LABEL" description="TPL_MOKOONYX_CSS_VARS_POPOVER_DESC" class="alert alert-light" />
<field name="css_vars_progress" type="note" label="TPL_MOKOONYX_CSS_VARS_PROGRESS_LABEL" description="TPL_MOKOONYX_CSS_VARS_PROGRESS_DESC" class="alert alert-light" />
<field name="css_vars_spinner" type="note" label="TPL_MOKOONYX_CSS_VARS_SPINNER_LABEL" description="TPL_MOKOONYX_CSS_VARS_SPINNER_DESC" class="alert alert-light" />
<field name="css_vars_table" type="note" label="TPL_MOKOONYX_CSS_VARS_TABLE_LABEL" description="TPL_MOKOONYX_CSS_VARS_TABLE_DESC" class="alert alert-light" />
<field name="css_vars_toast" type="note" label="TPL_MOKOONYX_CSS_VARS_TOAST_LABEL" description="TPL_MOKOONYX_CSS_VARS_TOAST_DESC" class="alert alert-light" />
<field name="css_vars_tooltip" type="note" label="TPL_MOKOONYX_CSS_VARS_TOOLTIP_LABEL" description="TPL_MOKOONYX_CSS_VARS_TOOLTIP_DESC" class="alert alert-light" />
<field name="css_vars_components" type="note" label="TPL_MOKOONYX_CSS_VARS_COMPONENTS_LABEL" description="TPL_MOKOONYX_CSS_VARS_COMPONENTS_DESC" class="alert alert-light" />
<field name="css_vars_offcanvas" type="note" label="TPL_MOKOONYX_CSS_VARS_OFFCANVAS_LABEL" description="TPL_MOKOONYX_CSS_VARS_OFFCANVAS_DESC" class="alert alert-light" />
<field name="css_vars_virtuemart" type="note" label="TPL_MOKOONYX_CSS_VARS_VM_LABEL" description="TPL_MOKOONYX_CSS_VARS_VM_DESC" class="alert alert-light" />
<field name="css_vars_gable" type="note" label="TPL_MOKOONYX_CSS_VARS_GABLE_LABEL" description="TPL_MOKOONYX_CSS_VARS_GABLE_DESC" class="alert alert-light" />
<field name="css_vars_footer" type="note" label="TPL_MOKOONYX_CSS_VARS_FOOTER_LABEL" description="TPL_MOKOONYX_CSS_VARS_FOOTER_DESC" class="alert alert-light" />
</fieldset>
<!-- Theme Preview tab — embedded test sheet -->
<fieldset name="theme_preview" label="TPL_MOKOONYX_THEME_PREVIEW_FIELDSET_LABEL">
<field name="theme_preview_intro" type="note" description="TPL_MOKOONYX_THEME_PREVIEW_INTRO" />
<field name="theme_preview_frame" type="note" description="TPL_MOKOONYX_THEME_PREVIEW_FRAME" />
<field name="css_vars_docs_link" type="note"
description="For a full list of CSS variables and theming options, see the &lt;a href='https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx' target='_blank'&gt;MokoOnyx documentation&lt;/a&gt;."
class="alert alert-info" />
</fieldset>
</fields>
</config>

View File

@@ -210,6 +210,7 @@ color-scheme: dark;
/* ===== HEADER BACKGROUND ===== */
--header-background-color: #1a1f2b;
--header-background-image: url('../../../../../../media/templates/site/mokoonyx/images/bg.svg');
--header-background-attachment: fixed;
--header-background-repeat: repeat;

View File

@@ -209,6 +209,7 @@ color-scheme: light;
/* ===== HEADER BACKGROUND ===== */
--header-background-color: #adadad;
--header-background-image: url('../../../../../../media/templates/site/mokoonyx/images/bg.svg');
--header-background-attachment: fixed;
--header-background-repeat: repeat;

View File

@@ -1,836 +0,0 @@
<!DOCTYPE html>
<!-- Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
-->
<html lang="en" data-bs-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MokoOnyx — Theme Test Sheet</title>
<!-- Load the template CSS -->
<link rel="stylesheet" href="../media/css/template.css">
<!-- Load the light custom palette (swap to dark.custom.css for dark mode testing) -->
<link rel="stylesheet" href="light.custom.css">
<style>
/* ── Test Page Layout ── */
body { font-family: var(--body-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif); color: var(--body-color, #22262a); background: var(--body-bg, #fff); margin: 0; padding: 0; }
.test-container { max-width: 1200px; margin: 0 auto; padding: 1rem 1.5rem; }
h1, h2, h3, h4, h5, h6 { color: var(--heading-color, inherit); }
h1 { font-size: 2.25rem; margin-bottom: .25rem; }
h2 { font-size: 1.75rem; margin-top: 2.5rem; border-bottom: 2px solid var(--border-color, #dfe3e7); padding-bottom: .5rem; }
h3 { font-size: 1.25rem; margin-top: 1.5rem; }
p.lead { font-size: 1.15rem; color: var(--muted-color, #6d757e); }
hr { border: 0; border-top: 1px solid var(--border-color, #dfe3e7); margin: 2rem 0; }
a { color: var(--link-color, #224faa); text-decoration: var(--link-decoration, underline); }
a:hover { color: var(--link-hover-color, #424077); }
code { color: var(--code-color, #e93f8e); background: var(--secondary-bg, #eaedf0); padding: .15em .4em; border-radius: .2rem; font-size: .875em; }
pre { background: var(--secondary-bg, #eaedf0); color: var(--body-color); padding: 1rem; border-radius: var(--border-radius, .25rem); overflow-x: auto; font-size: .875rem; }
/* ── Swatch Grid ── */
.swatch-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: .75rem; margin: 1rem 0; }
.swatch { border-radius: var(--border-radius, .25rem); border: 1px solid var(--border-color, #dfe3e7); overflow: hidden; }
.swatch-color { height: 60px; }
.swatch-label { padding: .4rem .6rem; font-size: .75rem; background: var(--body-bg, #fff); }
.swatch-label code { font-size: .7rem; }
/* ── Variable Table ── */
.var-table { width: 100%; border-collapse: collapse; margin: 1rem 0; font-size: .875rem; }
.var-table th, .var-table td { padding: .5rem .75rem; border: 1px solid var(--border-color, #dfe3e7); text-align: left; }
.var-table th { background: var(--secondary-bg, #eaedf0); font-weight: 600; }
.var-table tr:nth-child(even) td { background: var(--tertiary-bg, #f9fafb); }
/* ── Flex row helper ── */
.row { display: flex; flex-wrap: wrap; gap: 1rem; }
.col { flex: 1; min-width: 200px; }
/* ── Theme Toggle ── */
.theme-toggle { position: fixed; top: 1rem; right: 1.5rem; z-index: 1000; }
.theme-toggle button { padding: .5rem 1rem; border: 1px solid var(--border-color); border-radius: var(--border-radius); background: var(--body-bg); color: var(--body-color); cursor: pointer; font-size: .875rem; }
/* ── Block Color Demo ── */
.block-demo { display: flex; gap: .75rem; flex-wrap: wrap; margin: 1rem 0; }
.block-demo .card { flex: 1; min-width: 180px; padding: 1.25rem; border-radius: var(--border-radius, .25rem); border: 1px solid var(--border-color, #dfe3e7); }
</style>
</head>
<body>
<div class="theme-toggle">
<button onclick="toggleTheme()">Toggle Light / Dark</button>
</div>
<div class="test-container">
<!-- ══════════════════════════════════════════════
HEADER
══════════════════════════════════════════════ -->
<h1>MokoOnyx Theme Test Sheet</h1>
<p class="lead">Visual reference for CSS variables, Bootstrap components, hero variants, and block color system. Toggle light/dark mode with the button in the top-right corner.</p>
<hr>
<!-- ══════════════════════════════════════════════
1. BRAND COLORS
══════════════════════════════════════════════ -->
<h2>1. Brand &amp; Theme Colors</h2>
<div class="swatch-grid">
<div class="swatch">
<div class="swatch-color" style="background: var(--color-primary)"></div>
<div class="swatch-label"><code>--color-primary</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--accent-color-primary)"></div>
<div class="swatch-label"><code>--accent-color-primary</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--accent-color-secondary)"></div>
<div class="swatch-label"><code>--accent-color-secondary</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--body-bg)"></div>
<div class="swatch-label"><code>--body-bg</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--body-color)"></div>
<div class="swatch-label"><code>--body-color</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--secondary-bg)"></div>
<div class="swatch-label"><code>--secondary-bg</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--tertiary-bg)"></div>
<div class="swatch-label"><code>--tertiary-bg</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--border-color)"></div>
<div class="swatch-label"><code>--border-color</code></div>
</div>
</div>
<!-- ══════════════════════════════════════════════
2. BOOTSTRAP PALETTE
══════════════════════════════════════════════ -->
<h2>2. Bootstrap Color Palette</h2>
<div class="swatch-grid">
<div class="swatch">
<div class="swatch-color" style="background: var(--primary)"></div>
<div class="swatch-label"><code>--primary</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--secondary)"></div>
<div class="swatch-label"><code>--secondary</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--success)"></div>
<div class="swatch-label"><code>--success</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--info)"></div>
<div class="swatch-label"><code>--info</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--warning)"></div>
<div class="swatch-label"><code>--warning</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--danger)"></div>
<div class="swatch-label"><code>--danger</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--light)"></div>
<div class="swatch-label"><code>--light</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--dark)"></div>
<div class="swatch-label"><code>--dark</code></div>
</div>
</div>
<!-- ══════════════════════════════════════════════
3. GRAY SCALE
══════════════════════════════════════════════ -->
<h2>3. Gray Scale</h2>
<div class="swatch-grid">
<div class="swatch">
<div class="swatch-color" style="background: var(--gray-100)"></div>
<div class="swatch-label"><code>--gray-100</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--gray-200)"></div>
<div class="swatch-label"><code>--gray-200</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--gray-300)"></div>
<div class="swatch-label"><code>--gray-300</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--gray-400)"></div>
<div class="swatch-label"><code>--gray-400</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--gray-500)"></div>
<div class="swatch-label"><code>--gray-500</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--gray-600)"></div>
<div class="swatch-label"><code>--gray-600</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--gray-700)"></div>
<div class="swatch-label"><code>--gray-700</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--gray-800)"></div>
<div class="swatch-label"><code>--gray-800</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--gray-900)"></div>
<div class="swatch-label"><code>--gray-900</code></div>
</div>
</div>
<!-- ══════════════════════════════════════════════
4. STANDARD COLORS
══════════════════════════════════════════════ -->
<h2>4. Standard Colors</h2>
<div class="swatch-grid">
<div class="swatch">
<div class="swatch-color" style="background: var(--blue)"></div>
<div class="swatch-label"><code>--blue</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--indigo)"></div>
<div class="swatch-label"><code>--indigo</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--purple)"></div>
<div class="swatch-label"><code>--purple</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--pink)"></div>
<div class="swatch-label"><code>--pink</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--red)"></div>
<div class="swatch-label"><code>--red</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--orange)"></div>
<div class="swatch-label"><code>--orange</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--yellow)"></div>
<div class="swatch-label"><code>--yellow</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--green)"></div>
<div class="swatch-label"><code>--green</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--teal)"></div>
<div class="swatch-label"><code>--teal</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--cyan)"></div>
<div class="swatch-label"><code>--cyan</code></div>
</div>
</div>
<!-- ══════════════════════════════════════════════
5. TYPOGRAPHY
══════════════════════════════════════════════ -->
<h2>5. Typography</h2>
<div>
<h1>Heading 1 <small style="color: var(--muted-color); font-size: .5em;">h1</small></h1>
<h2 style="border: none; padding: 0; margin-top: .5rem;">Heading 2 <small style="color: var(--muted-color); font-size: .5em;">h2</small></h2>
<h3>Heading 3 <small style="color: var(--muted-color); font-size: .6em;">h3</small></h3>
<h4>Heading 4 <small style="color: var(--muted-color); font-size: .6em;">h4</small></h4>
<h5>Heading 5 <small style="color: var(--muted-color); font-size: .6em;">h5</small></h5>
<h6>Heading 6 <small style="color: var(--muted-color); font-size: .6em;">h6</small></h6>
</div>
<p>This is regular body text using <code>--body-color</code> on <code>--body-bg</code>. Font family: <code>--body-font-family</code>. Size: <code>--body-font-size</code> (1rem).</p>
<p><strong>Bold text.</strong> <em>Italic text.</em> <a href="#">This is a link</a>. <code>Inline code</code>. <mark style="background: var(--highlight-bg); color: var(--highlight-color);">Highlighted text</mark>.</p>
<p class="lead">This is lead text styled with <code>--muted-color</code>.</p>
<!-- ══════════════════════════════════════════════
6. LINKS
══════════════════════════════════════════════ -->
<h2>6. Link Colors</h2>
<table class="var-table">
<tr><th>Variable</th><th>Preview</th></tr>
<tr><td><code>--link-color</code></td><td><a href="#" style="color: var(--link-color)">Sample link</a></td></tr>
<tr><td><code>--link-hover-color</code></td><td><span style="color: var(--link-hover-color); text-decoration: underline; cursor: pointer;">Hover state</span></td></tr>
<tr><td><code>--color-link</code></td><td><span style="color: var(--color-link)">color-link value</span></td></tr>
<tr><td><code>--color-hover</code></td><td><span style="color: var(--color-hover)">color-hover value</span></td></tr>
</table>
<!-- ══════════════════════════════════════════════
7. BUTTONS (Bootstrap-style)
══════════════════════════════════════════════ -->
<h2>7. Buttons</h2>
<div style="display: flex; flex-wrap: wrap; gap: .5rem; margin: 1rem 0;">
<button class="btn btn-primary">Primary</button>
<button class="btn btn-secondary">Secondary</button>
<button class="btn btn-success">Success</button>
<button class="btn btn-danger">Danger</button>
<button class="btn btn-warning">Warning</button>
<button class="btn btn-info">Info</button>
<button class="btn btn-light">Light</button>
<button class="btn btn-dark">Dark</button>
</div>
<div style="display: flex; flex-wrap: wrap; gap: .5rem; margin: 1rem 0;">
<button class="btn btn-outline-primary">Outline Primary</button>
<button class="btn btn-outline-secondary">Outline Secondary</button>
<button class="btn btn-outline-success">Outline Success</button>
<button class="btn btn-outline-danger">Outline Danger</button>
</div>
<!-- ══════════════════════════════════════════════
8. CARDS
══════════════════════════════════════════════ -->
<h2>8. Cards</h2>
<div class="row">
<div class="col">
<div class="card" style="background: var(--card-bg); border: var(--card-border-width) solid var(--card-border-color); border-radius: var(--card-border-radius); padding: 0;">
<div style="padding: var(--card-cap-padding-y) var(--card-cap-padding-x); background: var(--card-cap-bg); color: var(--card-cap-color); border-bottom: 1px solid var(--card-border-color); font-weight: 600;">Card Header</div>
<div style="padding: var(--card-spacer-y) var(--card-spacer-x); color: var(--card-color);">
<h5 style="margin-top: 0;">Card Title</h5>
<p style="margin-bottom: .5rem;">Card body using <code>--card-bg</code>, <code>--card-color</code>, and <code>--card-border-color</code>.</p>
<button class="btn btn-primary" style="font-size: .875rem;">Action</button>
</div>
</div>
</div>
<div class="col">
<div class="card" style="background: var(--card-bg); border: var(--card-border-width) solid var(--card-border-color); border-radius: var(--card-border-radius); padding: var(--card-spacer-y) var(--card-spacer-x); color: var(--card-color);">
<h5 style="margin-top: 0;">Simple Card</h5>
<p>No header, just body content. Uses the same card variables.</p>
</div>
</div>
</div>
<!-- ══════════════════════════════════════════════
9. FORMS
══════════════════════════════════════════════ -->
<h2>9. Form Elements</h2>
<div style="max-width: 480px;">
<div style="margin-bottom: 1rem;">
<label style="display: block; margin-bottom: .25rem; font-weight: 500;">Text Input</label>
<input type="text" placeholder="Placeholder text" style="width: 100%; padding: .375rem .75rem; border: 1px solid var(--input-border-color, #ced4da); border-radius: var(--border-radius); background: var(--input-bg, #fff); color: var(--input-color, #22262a); font-size: 1rem;">
</div>
<div style="margin-bottom: 1rem;">
<label style="display: block; margin-bottom: .25rem; font-weight: 500;">Select</label>
<select style="width: 100%; padding: .375rem .75rem; border: 1px solid var(--input-border-color, #ced4da); border-radius: var(--border-radius); background: var(--input-bg, #fff); color: var(--input-color, #22262a); font-size: 1rem;">
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
</select>
</div>
<div style="margin-bottom: 1rem;">
<label style="display: block; margin-bottom: .25rem; font-weight: 500;">Textarea</label>
<textarea rows="3" style="width: 100%; padding: .375rem .75rem; border: 1px solid var(--input-border-color, #ced4da); border-radius: var(--border-radius); background: var(--input-bg, #fff); color: var(--input-color, #22262a); font-size: 1rem;">Sample text content</textarea>
</div>
</div>
<!-- ══════════════════════════════════════════════
10. ALERTS (Bootstrap-style)
══════════════════════════════════════════════ -->
<h2>10. Alerts</h2>
<div style="padding: .75rem 1rem; margin-bottom: .75rem; border-radius: var(--border-radius); background: var(--primary-bg-subtle); color: var(--primary-text-emphasis); border: 1px solid var(--primary-border-subtle);">
<strong>Primary alert.</strong> Uses <code>--primary-bg-subtle</code> and <code>--primary-text-emphasis</code>.
</div>
<div style="padding: .75rem 1rem; margin-bottom: .75rem; border-radius: var(--border-radius); background: var(--success-bg-subtle); color: var(--success-text-emphasis); border: 1px solid var(--success-border-subtle);">
<strong>Success alert.</strong> Uses <code>--success-bg-subtle</code> and <code>--success-text-emphasis</code>.
</div>
<div style="padding: .75rem 1rem; margin-bottom: .75rem; border-radius: var(--border-radius); background: var(--warning-bg-subtle); color: var(--warning-text-emphasis); border: 1px solid var(--warning-border-subtle);">
<strong>Warning alert.</strong> Uses <code>--warning-bg-subtle</code> and <code>--warning-text-emphasis</code>.
</div>
<div style="padding: .75rem 1rem; margin-bottom: .75rem; border-radius: var(--border-radius); background: var(--danger-bg-subtle); color: var(--danger-text-emphasis); border: 1px solid var(--danger-border-subtle);">
<strong>Danger alert.</strong> Uses <code>--danger-bg-subtle</code> and <code>--danger-text-emphasis</code>.
</div>
<div style="padding: .75rem 1rem; margin-bottom: .75rem; border-radius: var(--border-radius); background: var(--info-bg-subtle); color: var(--info-text-emphasis); border: 1px solid var(--info-border-subtle);">
<strong>Info alert.</strong> Uses <code>--info-bg-subtle</code> and <code>--info-text-emphasis</code>.
</div>
<!-- ══════════════════════════════════════════════
11. BORDERS & SHADOWS
══════════════════════════════════════════════ -->
<h2>11. Borders &amp; Shadows</h2>
<div class="row">
<div class="col" style="padding: 1.5rem; border: var(--border-width) var(--border-style) var(--border-color); border-radius: var(--border-radius); margin-bottom: 1rem;">
Default border: <code>--border-width</code> / <code>--border-color</code> / <code>--border-radius</code>
</div>
<div class="col" style="padding: 1.5rem; border-radius: var(--border-radius); box-shadow: var(--box-shadow-sm); margin-bottom: 1rem;">
<code>--box-shadow-sm</code>
</div>
<div class="col" style="padding: 1.5rem; border-radius: var(--border-radius); box-shadow: var(--box-shadow); margin-bottom: 1rem;">
<code>--box-shadow</code>
</div>
<div class="col" style="padding: 1.5rem; border-radius: var(--border-radius); box-shadow: var(--box-shadow-lg); margin-bottom: 1rem;">
<code>--box-shadow-lg</code>
</div>
</div>
<!-- ══════════════════════════════════════════════
12. NAVIGATION COLORS
══════════════════════════════════════════════ -->
<h2>12. Navigation Colors</h2>
<div class="swatch-grid">
<div class="swatch">
<div class="swatch-color" style="background: var(--nav-bg-color)"></div>
<div class="swatch-label"><code>--nav-bg-color</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--nav-text-color); border: 1px solid var(--border-color)"></div>
<div class="swatch-label"><code>--nav-text-color</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--mainmenu-nav-link-color); border: 1px solid var(--border-color)"></div>
<div class="swatch-label"><code>--mainmenu-nav-link-color</code></div>
</div>
</div>
<!-- ══════════════════════════════════════════════
13. CONTAINER BACKGROUNDS
══════════════════════════════════════════════ -->
<h2>13. Container Background Variables</h2>
<table class="var-table">
<tr><th>Container</th><th>BG Color</th><th>BG Image</th><th>Border</th></tr>
<tr><td>below-topbar</td><td><code>--container-below-topbar-bg-color</code></td><td><code>--container-below-topbar-bg-image</code></td><td><code>--container-below-topbar-border</code></td></tr>
<tr><td>top-a</td><td><code>--container-top-a-bg-color</code></td><td><code>--container-top-a-bg-image</code></td><td><code>--container-top-a-border</code></td></tr>
<tr><td>top-b</td><td><code>--container-top-b-bg-color</code></td><td><code>--container-top-b-bg-image</code></td><td><code>--container-top-b-border</code></td></tr>
<tr><td>bottom-a</td><td><code>--container-bottom-a-bg-color</code></td><td><code>--container-bottom-a-bg-image</code></td><td><code>--container-bottom-a-border</code></td></tr>
<tr><td>bottom-b</td><td><code>--container-bottom-b-bg-color</code></td><td><code>--container-bottom-b-bg-image</code></td><td><code>--container-bottom-b-border</code></td></tr>
<tr><td>sidebar</td><td><code>--container-sidebar-bg-color</code></td><td><code>--container-sidebar-bg-image</code></td><td><code>--container-sidebar-border</code></td></tr>
</table>
<!-- ══════════════════════════════════════════════
14. HERO VARIANTS (NEW)
══════════════════════════════════════════════ -->
<h2>14. Hero Variants <span style="font-size: .65em; color: var(--success); font-weight: normal;">NEW</span></h2>
<p>The <code>.hero#primary</code> and <code>.hero#secondary</code> variants use CSS variables for background color, overlay gradient, and text color. Each adapts automatically with the active theme.</p>
<h3>Primary Variant — <code>.hero#primary</code></h3>
<div class="hero" id="primary" style="background-image: url('data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%22400%22 height=%22200%22><rect fill=%22%23a3cde2%22 width=%22400%22 height=%22200%22/><text x=%2250%25%22 y=%2250%25%22 text-anchor=%22middle%22 dy=%22.3em%22 font-family=%22sans-serif%22 font-size=%2216%22 fill=%22%23112855%22>Hero Background Image Area</text></svg>'); padding: 0;">
<div style="padding: 3rem 2rem; text-align: center;">
<h2 style="border: none; padding: 0; margin: 0 0 .5rem 0; font-size: 2rem;">Primary Hero</h2>
<p style="margin: 0; font-size: 1.1rem;">Homepage &amp; main landing pages — sky blue tint, softer overlay</p>
</div>
</div>
<h3>Secondary Variant — <code>.hero#secondary</code></h3>
<div class="hero" id="secondary" style="background-image: url('data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%22400%22 height=%22200%22><rect fill=%22%23112855%22 width=%22400%22 height=%22200%22/><text x=%2250%25%22 y=%2250%25%22 text-anchor=%22middle%22 dy=%22.3em%22 font-family=%22sans-serif%22 font-size=%2216%22 fill=%22%23f1f5f9%22>Hero Background Image Area</text></svg>'); padding: 0;">
<div style="padding: 3rem 2rem; text-align: center;">
<h2 style="border: none; padding: 0; margin: 0 0 .5rem 0; font-size: 2rem;">Secondary Hero</h2>
<p style="margin: 0; font-size: 1.1rem;">Inner pages, events, about — navy overlay, lighter text</p>
</div>
</div>
<h3>Hero Variable Reference</h3>
<table class="var-table">
<tr><th>Variable</th><th>Variant</th><th>Purpose</th></tr>
<tr><td><code>--hero-primary-bg-color</code></td><td>Primary</td><td>Fallback background color</td></tr>
<tr><td><code>--hero-primary-overlay</code></td><td>Primary</td><td>Gradient overlay tint</td></tr>
<tr><td><code>--hero-primary-color</code></td><td>Primary</td><td>Text color</td></tr>
<tr><td><code>--hero-secondary-bg-color</code></td><td>Secondary</td><td>Fallback background color</td></tr>
<tr><td><code>--hero-secondary-overlay</code></td><td>Secondary</td><td>Gradient overlay tint</td></tr>
<tr><td><code>--hero-secondary-color</code></td><td>Secondary</td><td>Text color</td></tr>
</table>
<!-- ══════════════════════════════════════════════
15. BLOCK COLOR SYSTEM (NEW)
══════════════════════════════════════════════ -->
<h2>15. Block Color System <span style="font-size: .65em; color: var(--success); font-weight: normal;">NEW</span></h2>
<p>Modules in <code>top-a</code>, <code>top-b</code>, <code>bottom-a</code>, and <code>bottom-b</code> positions automatically receive brand colors based on their order. No classes needed — <code>:nth-child()</code> handles assignment.</p>
<h3>Slot Palette Preview</h3>
<div class="block-demo">
<div class="card" style="background-color: var(--block-color-1); color: var(--block-text-1);">
<strong>Slot 1</strong><br>
<code style="color: inherit; background: rgba(255,255,255,.15);">--block-color-1</code>
</div>
<div class="card" style="background-color: var(--block-color-2); color: var(--block-text-2);">
<strong>Slot 2</strong><br>
<code style="color: inherit; background: rgba(255,255,255,.15);">--block-color-2</code>
</div>
<div class="card" style="background-color: var(--block-color-3); color: var(--block-text-3);">
<strong>Slot 3</strong><br>
<code style="color: inherit; background: rgba(255,255,255,.15);">--block-color-3</code>
</div>
<div class="card" style="background-color: var(--block-color-4); color: var(--block-text-4);">
<strong>Slot 4</strong><br>
<code style="color: inherit; background: rgba(255,255,255,.15);">--block-color-4</code>
</div>
</div>
<h3>Named Override Preview</h3>
<div class="block-demo">
<div class="card" style="background-color: var(--block-highlight-bg); color: var(--block-highlight-text);">
<strong>#block-highlight</strong><br>
<code style="color: inherit; background: rgba(255,255,255,.15);">--block-highlight-bg</code>
</div>
<div class="card" style="background-color: var(--block-cta-bg); color: var(--block-cta-text);">
<strong>#block-cta</strong><br>
<code style="color: inherit; background: rgba(255,255,255,.15);">--block-cta-bg</code>
</div>
<div class="card" style="background-color: var(--block-alert-bg); color: var(--block-alert-text);">
<strong>#block-alert</strong><br>
<code style="color: inherit; background: rgba(255,255,255,.15);">--block-alert-bg</code>
</div>
</div>
<h3>Block Variable Reference</h3>
<table class="var-table">
<tr><th>Variable</th><th>Purpose</th></tr>
<tr><td><code>--block-color-1</code> / <code>--block-text-1</code></td><td>1st module in position (automatic)</td></tr>
<tr><td><code>--block-color-2</code> / <code>--block-text-2</code></td><td>2nd module in position (automatic)</td></tr>
<tr><td><code>--block-color-3</code> / <code>--block-text-3</code></td><td>3rd module in position (automatic)</td></tr>
<tr><td><code>--block-color-4</code> / <code>--block-text-4</code></td><td>4th module in position (automatic)</td></tr>
<tr><td><code>--block-highlight-bg</code> / <code>--block-highlight-text</code></td><td>Named override for <code>#block-highlight</code></td></tr>
<tr><td><code>--block-cta-bg</code> / <code>--block-cta-text</code></td><td>Named override for <code>#block-cta</code></td></tr>
<tr><td><code>--block-alert-bg</code> / <code>--block-alert-text</code></td><td>Named override for <code>#block-alert</code></td></tr>
</table>
<h3>Override Priority</h3>
<table class="var-table">
<tr><th>Priority</th><th>Method</th><th>How Applied</th></tr>
<tr><td>1 (highest)</td><td>Named module ID (<code>#block-highlight</code>)</td><td>ID in module HTML + named variable</td></tr>
<tr><td>2 (default)</td><td>Slot color (<code>--block-color-N</code>)</td><td>Automatic by <code>:nth-child()</code> order</td></tr>
</table>
<!-- ══════════════════════════════════════════════
16. VIRTUEMART COLORS
══════════════════════════════════════════════ -->
<h2>16. VirtueMart Surface Colors</h2>
<div class="swatch-grid">
<div class="swatch">
<div class="swatch-color" style="background: var(--vm-surface, #fff)"></div>
<div class="swatch-label"><code>--vm-surface</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--vm-surface-2, #f8f9fa)"></div>
<div class="swatch-label"><code>--vm-surface-2</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--vm-price-color, #448344)"></div>
<div class="swatch-label"><code>--vm-price-color</code></div>
</div>
</div>
<!-- ══════════════════════════════════════════════
17. GABLE COLORS
══════════════════════════════════════════════ -->
<h2>17. Gable Colors</h2>
<div class="swatch-grid">
<div class="swatch">
<div class="swatch-color" style="background: var(--gab-blue)"></div>
<div class="swatch-label"><code>--gab-blue</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--gab-green)"></div>
<div class="swatch-label"><code>--gab-green</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--gab-red)"></div>
<div class="swatch-label"><code>--gab-red</code></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--gab-orange)"></div>
<div class="swatch-label"><code>--gab-orange</code></div>
</div>
</div>
<!-- ══════════════════════════════════════════════
18. CODE SAMPLES
══════════════════════════════════════════════ -->
<h2>18. Code &amp; Preformatted Text</h2>
<p>Inline code: <code>var(--color-primary)</code></p>
<pre>/* Example: overriding block slot 1 in colors_custom.css */
--block-color-1: var(--accent-color-primary);
--block-text-1: #fff;
/* Hero variant usage in module HTML */
&lt;div class="hero" id="primary"
style="background-image:url('/images/hero/main.jpg')"&gt;
&lt;div class="col-12 py-5 px-4 text-center"&gt;
...content...
&lt;/div&gt;
&lt;/div&gt;</pre>
<!-- ══════════════════════════════════════════════
19. OPACITY UTILITIES
══════════════════════════════════════════════ -->
<h2>19. Opacity Scale</h2>
<div style="display: flex; gap: .5rem; flex-wrap: wrap;">
<div style="width: 60px; height: 60px; background: var(--color-primary); opacity: var(--opacity-5); border-radius: var(--border-radius); display: flex; align-items: center; justify-content: center; font-size: .7rem;">5%</div>
<div style="width: 60px; height: 60px; background: var(--color-primary); opacity: var(--opacity-10); border-radius: var(--border-radius); display: flex; align-items: center; justify-content: center; font-size: .7rem;">10%</div>
<div style="width: 60px; height: 60px; background: var(--color-primary); opacity: var(--opacity-15); border-radius: var(--border-radius); display: flex; align-items: center; justify-content: center; font-size: .7rem;">15%</div>
<div style="width: 60px; height: 60px; background: var(--color-primary); opacity: var(--opacity-25); border-radius: var(--border-radius); display: flex; align-items: center; justify-content: center; font-size: .7rem; color: #fff;">25%</div>
<div style="width: 60px; height: 60px; background: var(--color-primary); opacity: var(--opacity-50); border-radius: var(--border-radius); display: flex; align-items: center; justify-content: center; font-size: .7rem; color: #fff;">50%</div>
<div style="width: 60px; height: 60px; background: var(--color-primary); opacity: var(--opacity-75); border-radius: var(--border-radius); display: flex; align-items: center; justify-content: center; font-size: .7rem; color: #fff;">75%</div>
<div style="width: 60px; height: 60px; background: var(--color-primary); opacity: var(--opacity-100); border-radius: var(--border-radius); display: flex; align-items: center; justify-content: center; font-size: .7rem; color: #fff;">100%</div>
</div>
<!-- ══════════════════════════════════════════════
21. BRANDED BOOTSTRAP 5 SHOWCASE
══════════════════════════════════════════════ -->
<h2>21. Branded Bootstrap 5 Showcase</h2>
<p>Comprehensive component demos using MokoOnyx's brand variables. Mirrors the live Joomla article at <code>/style/branded-bootstrap5</code>.</p>
<!-- BRAND HEADER (gradient + border via variables) -->
<div class="text-center p-4 mb-4" style="background: var(--header-background-image); background-position: center; background-attachment: fixed; background-repeat: repeat; background-size: auto; border-bottom: var(--border, 5px) solid var(--accent-color-primary); color: var(--color-primary);">
<h3 class="mb-1" style="border: none; padding: 0; margin: 0;">Brand + Bootstrap Showcase</h3>
<p class="lead mb-0" style="color: inherit;">Comprehensive components with toggleable code samples</p>
</div>
<!-- NAV SAMPLE (brand colors) -->
<nav class="d-flex align-items-center gap-3 px-3 py-2 mb-4" style="background: var(--nav-bg-color); border-radius: var(--border-radius, .25rem);">
<span class="fw-bold" style="color: var(--nav-text-color);">Brand Nav</span>
<a href="#" class="text-decoration-none" style="color: var(--mainmenu-nav-link-color);">Home</a>
<a href="#" class="text-decoration-none" style="color: var(--mainmenu-nav-link-color);">About</a>
<a href="#" class="text-decoration-none ms-auto fw-semibold" style="color: var(--accent-color-secondary);">Contact</a>
</nav>
<!-- BREADCRUMB -->
<nav aria-label="Breadcrumbs" style="margin-bottom: 1.5rem;">
<ol class="breadcrumb px-3 py-2" style="background: var(--secondary-bg, #eaedf0); border-radius: var(--border-radius, .25rem); margin: 0; list-style: none; display: flex; flex-wrap: wrap; padding: .5rem 1rem; font-size: .875rem;">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item"><a href="#">Style</a></li>
<li class="breadcrumb-item active" style="color: var(--muted-color);">Branded Bootstrap5</li>
</ol>
</nav>
<!-- TYPOGRAPHY SECTION -->
<h3>Typography</h3>
<div class="row" style="margin-bottom: 2rem;">
<div class="col">
<h1 style="border: none; padding: 0; margin: .5rem 0;">H1 Heading</h1>
<h2 style="border: none; padding: 0; margin: .5rem 0;">H2 Heading</h2>
<h3 style="margin: .5rem 0;">H3 Heading</h3>
<h4>H4 Heading</h4>
<h5>H5 Heading</h5>
<h6>H6 Heading</h6>
<p class="lead">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.</p>
<p>Curabitur <strong>ullamcorper</strong> nec <em>nisi</em> a <a href="#">themed link</a>. Nulla vitae <code>&lt;section&gt;</code> purus.</p>
<blockquote style="border-left: var(--border, 5px) solid var(--accent-color-primary); padding-left: 1rem; margin: 1rem 0;">
<p style="margin-bottom: .25rem;">"Design is intelligence made visible."</p>
<footer style="color: var(--muted-color); font-size: .875rem;">— Alina Wheeler</footer>
</blockquote>
</div>
</div>
<!-- BUTTONS & GROUPS -->
<h3>Buttons &amp; Button Groups</h3>
<div style="display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: 1rem;">
<button class="btn btn-primary">Primary</button>
<button class="btn btn-secondary">Secondary</button>
<button class="btn btn-success">Success</button>
<button class="btn btn-info">Info</button>
<button class="btn btn-warning">Warning</button>
<button class="btn btn-danger">Danger</button>
<button class="btn btn-light">Light</button>
<button class="btn btn-dark">Dark</button>
<button class="btn" style="background: var(--accent-color-primary); color: var(--color-primary); border: none;">Accent</button>
</div>
<div style="display: inline-flex; margin-bottom: 1.5rem;">
<button class="btn btn-outline-primary" style="border-radius: var(--border-radius, .25rem) 0 0 var(--border-radius, .25rem);">Left</button>
<button class="btn btn-outline-primary" style="border-radius: 0;">Middle</button>
<button class="btn btn-outline-primary" style="border-radius: 0 var(--border-radius, .25rem) var(--border-radius, .25rem) 0;">Right</button>
</div>
<!-- BADGES & ALERTS -->
<h3>Badges &amp; Alerts</h3>
<div style="display: flex; flex-wrap: wrap; gap: .35rem; margin-bottom: 1rem;">
<span style="display: inline-block; padding: .25em .5em; font-size: .75em; font-weight: 700; border-radius: var(--border-radius, .25rem); background: var(--primary); color: #fff;">Primary</span>
<span style="display: inline-block; padding: .25em .5em; font-size: .75em; font-weight: 700; border-radius: var(--border-radius, .25rem); background: var(--secondary); color: #fff;">Secondary</span>
<span style="display: inline-block; padding: .25em .5em; font-size: .75em; font-weight: 700; border-radius: var(--border-radius, .25rem); background: var(--success); color: #fff;">Success</span>
<span style="display: inline-block; padding: .25em .5em; font-size: .75em; font-weight: 700; border-radius: var(--border-radius, .25rem); background: var(--warning); color: var(--body-color);">Warning</span>
<span style="display: inline-block; padding: .25em .5em; font-size: .75em; font-weight: 700; border-radius: var(--border-radius, .25rem); background: var(--danger); color: #fff;">Danger</span>
<span style="display: inline-block; padding: .25em .5em; font-size: .75em; font-weight: 700; border-radius: var(--border-radius, .25rem); background: var(--accent-color-primary); color: var(--color-primary);">Accent</span>
</div>
<div style="padding: .75rem 1rem; margin-bottom: .75rem; border-radius: var(--border-radius); background: var(--primary-bg-subtle); color: var(--primary-text-emphasis); border: 1px solid var(--primary-border-subtle);">
<strong>Primary:</strong> Vivamus sagittis lacus vel augue.
</div>
<div style="padding: .75rem 1rem; margin-bottom: .75rem; border-radius: var(--border-radius); background: var(--warning-bg-subtle); color: var(--warning-text-emphasis); border: 1px solid var(--warning-border-subtle);">
Cras mattis consectetur purus sit amet fermentum.
</div>
<div style="padding: .75rem 1rem; margin-bottom: 1.5rem; border-radius: var(--border-radius); background: var(--accent-color-primary); color: var(--color-primary); border: var(--border, 5px) solid var(--accent-color-secondary);">
Brand alert — Aenean lacinia bibendum nulla sed consectetur.
</div>
<!-- TABLES -->
<h3>Tables</h3>
<div style="overflow-x: auto; margin-bottom: 1.5rem;">
<table class="var-table">
<thead><tr style="background: var(--dark, #212529); color: #fff;"><th>#</th><th>Name</th><th>Status</th><th>Notes</th></tr></thead>
<tbody>
<tr><td>1</td><td>Alpha</td><td><span style="display: inline-block; padding: .15em .4em; font-size: .75em; font-weight: 700; border-radius: var(--border-radius); background: var(--success); color: #fff;">Active</span></td><td>Lorem ipsum dolor sit amet.</td></tr>
<tr><td>2</td><td>Beta</td><td><span style="display: inline-block; padding: .15em .4em; font-size: .75em; font-weight: 700; border-radius: var(--border-radius); background: var(--warning); color: var(--body-color);">Pending</span></td><td>Integer posuere erat a ante.</td></tr>
<tr><td>3</td><td>Gamma</td><td><span style="display: inline-block; padding: .15em .4em; font-size: .75em; font-weight: 700; border-radius: var(--border-radius); background: var(--danger); color: #fff;">Blocked</span></td><td>Donec id elit non mi porta.</td></tr>
</tbody>
</table>
</div>
<!-- FORMS (BRANDED) -->
<h3>Branded Forms</h3>
<div style="max-width: 600px; margin-bottom: 1.5rem;">
<div style="display: flex; flex-wrap: wrap; gap: 1rem; margin-bottom: 1rem;">
<div style="flex: 1; min-width: 200px;">
<label style="display: block; margin-bottom: .25rem; font-weight: 500;">Email</label>
<input type="email" placeholder="name@example.com" style="width: 100%; padding: .375rem .75rem; border: 1px solid var(--input-border-color, #ced4da); border-radius: var(--border-radius); background: var(--input-bg, #fff); color: var(--input-color, #22262a);">
</div>
<div style="flex: 1; min-width: 200px;">
<label style="display: block; margin-bottom: .25rem; font-weight: 500;">Password</label>
<input type="password" placeholder="••••••••" style="width: 100%; padding: .375rem .75rem; border: 1px solid var(--input-border-color, #ced4da); border-radius: var(--border-radius); background: var(--input-bg, #fff); color: var(--input-color, #22262a);">
</div>
</div>
<div style="margin-bottom: 1rem;">
<label style="display: block; margin-bottom: .25rem; font-weight: 500;">Input Group</label>
<div style="display: flex;">
<span style="display: flex; align-items: center; padding: .375rem .75rem; background: var(--secondary-bg, #eaedf0); border: 1px solid var(--input-border-color, #ced4da); border-right: none; border-radius: var(--border-radius) 0 0 var(--border-radius);">@</span>
<input type="text" placeholder="username" style="flex: 1; padding: .375rem .75rem; border: 1px solid var(--input-border-color, #ced4da); background: var(--input-bg, #fff); color: var(--input-color, #22262a);">
<button class="btn btn-outline-secondary" style="border-radius: 0 var(--border-radius) var(--border-radius) 0;">Search</button>
</div>
</div>
<button class="btn btn-primary">Submit</button>
</div>
<!-- CARDS & LIST GROUPS -->
<h3>Branded Cards &amp; List Groups</h3>
<div class="row" style="margin-bottom: 1.5rem;">
<div class="col">
<div style="border: 1px solid var(--card-border-color, var(--border-color)); border-radius: var(--card-border-radius, var(--border-radius)); overflow: hidden; background: var(--card-bg, var(--body-bg));">
<div style="padding: .5rem 1rem; background: var(--accent-color-primary); color: var(--color-primary); font-weight: 600;">Featured</div>
<div style="padding: var(--card-spacer-y, 1rem) var(--card-spacer-x, 1rem); color: var(--card-color, var(--body-color));">
<h5 style="margin-top: 0;">Card title</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p>
<button class="btn btn-primary" style="font-size: .875rem;">Go somewhere</button>
</div>
</div>
</div>
<div class="col">
<div style="border: 1px solid var(--border-color); border-radius: var(--border-radius); overflow: hidden;">
<div style="padding: .5rem 1rem; background: var(--primary); color: #fff; font-weight: 500;">Active item</div>
<div style="padding: .5rem 1rem; border-bottom: 1px solid var(--border-color);">Second item</div>
<div style="padding: .5rem 1rem; display: flex; justify-content: space-between; align-items: center;">With badge <span style="display: inline-block; padding: .15em .5em; font-size: .75em; font-weight: 700; border-radius: 50rem; background: var(--primary); color: #fff;">4</span></div>
</div>
</div>
</div>
<!-- BREADCRUMB & PAGINATION -->
<h3>Breadcrumb &amp; Pagination</h3>
<nav aria-label="breadcrumb" style="margin-bottom: 1rem;">
<ol style="list-style: none; display: flex; flex-wrap: wrap; padding: .5rem 1rem; margin: 0; background: var(--secondary-bg, #eaedf0); border-radius: var(--border-radius); font-size: .875rem; gap: .5rem;">
<li><a href="#">Home</a> /</li>
<li><a href="#">Library</a> /</li>
<li style="color: var(--muted-color);">Data</li>
</ol>
</nav>
<nav aria-label="Pagination" style="margin-bottom: 1.5rem;">
<ul style="list-style: none; display: flex; padding: 0; margin: 0; gap: 0;">
<li><span style="display: block; padding: .375rem .75rem; border: 1px solid var(--border-color); color: var(--muted-color); border-radius: var(--border-radius) 0 0 var(--border-radius);">Previous</span></li>
<li><a href="#" style="display: block; padding: .375rem .75rem; border: 1px solid var(--border-color); border-left: none; text-decoration: none;">1</a></li>
<li><a href="#" style="display: block; padding: .375rem .75rem; border: 1px solid var(--border-color); border-left: none; text-decoration: none;">2</a></li>
<li><a href="#" style="display: block; padding: .375rem .75rem; border: 1px solid var(--border-color); border-left: none; text-decoration: none;">3</a></li>
<li><a href="#" style="display: block; padding: .375rem .75rem; border: 1px solid var(--border-color); border-left: none; text-decoration: none; border-radius: 0 var(--border-radius) var(--border-radius) 0;">Next</a></li>
</ul>
</nav>
<!-- PROGRESS BARS -->
<h3>Progress Bars</h3>
<div style="margin-bottom: 1.5rem;">
<div style="height: 20px; background: var(--secondary-bg, #eaedf0); border-radius: var(--border-radius); margin-bottom: .5rem; overflow: hidden;">
<div style="width: 25%; height: 100%; background: var(--accent-color-primary); color: var(--color-primary); display: flex; align-items: center; justify-content: center; font-size: .75rem; font-weight: 600;">25%</div>
</div>
<div style="height: 20px; background: var(--secondary-bg, #eaedf0); border-radius: var(--border-radius); overflow: hidden;">
<div style="width: 65%; height: 100%; background: var(--success); color: #fff; display: flex; align-items: center; justify-content: center; font-size: .75rem; font-weight: 600;">65%</div>
</div>
</div>
<!-- CSS VARIABLE SWATCHES (COMPUTED VALUES) -->
<h3>CSS Variable Swatches (Computed)</h3>
<p style="color: var(--muted-color);">Visual preview of key variables with their resolved values displayed via JavaScript.</p>
<div class="swatch-grid" id="computed-swatches">
<div class="swatch">
<div class="swatch-color" style="background: var(--color-primary)"></div>
<div class="swatch-label"><code>--color-primary</code><br><small class="computed-val" data-var="--color-primary"></small></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--color-link)"></div>
<div class="swatch-label"><code>--color-link</code><br><small class="computed-val" data-var="--color-link"></small></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--color-hover)"></div>
<div class="swatch-label"><code>--color-hover</code><br><small class="computed-val" data-var="--color-hover"></small></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--accent-color-primary)"></div>
<div class="swatch-label"><code>--accent-color-primary</code><br><small class="computed-val" data-var="--accent-color-primary"></small></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--accent-color-secondary)"></div>
<div class="swatch-label"><code>--accent-color-secondary</code><br><small class="computed-val" data-var="--accent-color-secondary"></small></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--nav-bg-color)"></div>
<div class="swatch-label"><code>--nav-bg-color</code><br><small class="computed-val" data-var="--nav-bg-color"></small></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--body-bg); border: 1px solid var(--border-color);"></div>
<div class="swatch-label"><code>--body-bg</code><br><small class="computed-val" data-var="--body-bg"></small></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--body-color)"></div>
<div class="swatch-label"><code>--body-color</code><br><small class="computed-val" data-var="--body-color"></small></div>
</div>
<div class="swatch">
<div class="swatch-color" style="background: var(--border-color)"></div>
<div class="swatch-label"><code>--border-color</code><br><small class="computed-val" data-var="--border-color"></small></div>
</div>
</div>
<hr>
<p style="color: var(--muted-color); font-size: .8rem; text-align: center; margin: 2rem 0;">
MokoOnyx Theme Test Sheet &mdash; v03.09.02 &mdash; &copy; 2026 Moko Consulting
</p>
</div><!-- /.test-container -->
<script>
// Populate computed CSS variable values
function updateComputedValues() {
const root = document.documentElement;
const style = getComputedStyle(root);
document.querySelectorAll('.computed-val').forEach(el => {
const varName = el.dataset.var;
if (varName) {
el.textContent = style.getPropertyValue(varName).trim() || '(not set)';
}
});
}
updateComputedValues();
function toggleTheme() {
const html = document.documentElement;
const current = html.getAttribute('data-bs-theme');
const next = current === 'light' ? 'dark' : 'light';
html.setAttribute('data-bs-theme', next);
// Swap stylesheet
const links = document.querySelectorAll('link[rel="stylesheet"]');
links.forEach(link => {
if (link.href.includes('light.custom.css')) {
link.href = link.href.replace('light.custom.css', 'dark.custom.css');
} else if (link.href.includes('dark.custom.css')) {
link.href = link.href.replace('dark.custom.css', 'light.custom.css');
}
});
// Refresh computed values after stylesheet loads
setTimeout(updateComputedValues, 200);
}
</script>
</body>
</html>

View File

@@ -13,13 +13,13 @@
<element>mokoonyx</element>
<type>template</type>
<client>site</client>
<version>01.00.16</version>
<creationDate>2026-04-22</creationDate>
<version>01.00.24</version>
<creationDate>2026-04-23</creationDate>
<infourl title='MokoOnyx Dev'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/development</infourl>
<downloads>
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/development/mokoonyx-01.00.16-dev.zip</downloadurl>
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/development/mokoonyx-01.00.24-dev.zip</downloadurl>
</downloads>
<sha256>6d0e690995eacd1b2e9193d847abd21369d2eb574406b27515d23f8d5a428c51</sha256>
<sha256>a2d215c19b3487eeeae62735a618e183c2e665ff75f9031bdb5df4b3c8f299a6</sha256>
<tags><tag>development</tag></tags>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
@@ -81,7 +81,6 @@
<infourl title='MokoOnyx RC'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/release-candidate</infourl>
<downloads>
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/release-candidate/mokoonyx-01.00.07-rc.zip</downloadurl>
<downloadurl type='full' format='zip'>https://github.com/mokoconsulting-tech/MokoOnyx/releases/download/release-candidate/mokoonyx-01.00.07-rc.zip</downloadurl>
</downloads>
<sha256></sha256>
<tags><tag>rc</tag></tags>
@@ -98,14 +97,13 @@
<element>mokoonyx</element>
<type>template</type>
<client>site</client>
<version>01.00.17</version>
<creationDate>2026-04-22</creationDate>
<version>01.00.20</version>
<creationDate>2026-04-23</creationDate>
<infourl title='MokoOnyx'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable</infourl>
<downloads>
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/mokoonyx-01.00.17.zip</downloadurl>
<downloadurl type='full' format='zip'>https://github.com/mokoconsulting-tech/MokoOnyx/releases/download/stable/mokoonyx-01.00.17.zip</downloadurl>
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/mokoonyx-01.00.20.zip</downloadurl>
</downloads>
<sha256>f4811005774b99e54513cd2cae052b815ef085e2a04f3a91cd5c0bc9cff7ffc2</sha256>
<sha256>0e490ad750ba4fd65eebb8412de6a17e94872fde63be0827f7b8679d400d8130</sha256>
<tags><tag>stable</tag></tags>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>