feat: hero variants, block colors, theme preview tab, CSS var sync on upgrade
- Add .hero#primary / .hero#secondary CSS variant system to template.css - Add :nth-child() block color palette for top-a/b, bottom-a/b positions - Add named per-module overrides (#block-highlight, #block-cta, #block-alert) - Add all hero + block CSS variables to light/dark custom palette templates - Add theme-test.html with full visual CSS variable reference + color test - Add "Theme Preview" tab to admin config with embedded iframe of test sheet - Add script.php install/update script (Joomla 5/6 compatible) - Add sync_custom_vars.php — detects missing vars in user palettes on upgrade - Add en-GB and en-US language strings for new admin config fields - Update CSS_VARIABLES.md and CHANGELOG.md - Bump version to 03.09.02 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
28
CHANGELOG.md
28
CHANGELOG.md
@@ -19,6 +19,34 @@ All notable changes to the MokoCassiopeia Joomla template are documented in this
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [03.09.02] - 2026-03-26
|
||||
|
||||
### Added - Hero Variant System & Block Color System
|
||||
|
||||
#### Hero Variants
|
||||
- **`.hero#primary`** and **`.hero#secondary`** CSS variant system for visually distinct hero treatments
|
||||
- Shared `.hero` base class with `background-size: cover`, `border-radius: .5rem`, and `overflow: hidden`
|
||||
- Six new CSS variables (`--hero-primary-bg-color`, `--hero-primary-overlay`, `--hero-primary-color`, and secondary equivalents)
|
||||
- Light and dark mode defaults in custom palette templates
|
||||
|
||||
#### Block Color System
|
||||
- Automatic `:nth-child()` slot palette for `top-a`, `top-b`, `bottom-a`, `bottom-b` module positions
|
||||
- Four color slots (`--block-color-1` through `--block-color-4`) with matching text variables
|
||||
- Named per-module overrides: `#block-highlight`, `#block-cta`, `#block-alert`
|
||||
- ID specificity wins over `:nth-child()` — no `!important` needed
|
||||
|
||||
#### Files Modified
|
||||
- `src/media/css/template.css` — hero variant rules, block color `:nth-child()` rules, named override rules
|
||||
- `src/templates/light.custom.css` — hero and block color variables (light mode)
|
||||
- `src/templates/dark.custom.css` — hero and block color variables (dark mode)
|
||||
- `docs/CSS_VARIABLES.md` — full variable reference for both systems
|
||||
- `CHANGELOG.md` — this entry
|
||||
|
||||
#### Files Added
|
||||
- `src/templates/theme-test.html` — Bootstrap-style test page showing all CSS variables and new features
|
||||
|
||||
---
|
||||
|
||||
## [03.08.03] - 2026-02-27
|
||||
|
||||
### Added - Main Menu Collapsible Dropdown Override
|
||||
|
||||
@@ -38,6 +38,8 @@ This document provides a complete reference of all CSS variables available in th
|
||||
- [Responsive Tokens & Breakpoints](#responsive-tokens--breakpoints)
|
||||
- [VirtueMart Variables](#virtuemart-variables)
|
||||
- [Gable Variables](#gable-variables)
|
||||
- [Hero Variant Variables](#hero-variant-variables)
|
||||
- [Block Color Variables](#block-color-variables)
|
||||
|
||||
---
|
||||
|
||||
@@ -1357,6 +1359,81 @@ These ensure optimal readability for links within alert boxes.
|
||||
|
||||
---
|
||||
|
||||
## Hero Variant Variables
|
||||
|
||||
### `--hero-primary-bg-color`
|
||||
- **Description**: Fallback background color for the primary hero variant
|
||||
- **Light Mode Default**: `var(--color-primary)`
|
||||
- **Dark Mode Default**: `#0d1e3a`
|
||||
- **Usage**: `.hero#primary` background when no image loads
|
||||
|
||||
### `--hero-primary-overlay`
|
||||
- **Description**: Gradient overlay tint for primary hero
|
||||
- **Light Mode Default**: `linear-gradient(rgba(163, 205, 226, .45), rgba(163, 205, 226, .45))`
|
||||
- **Dark Mode Default**: `linear-gradient(rgba(13, 30, 58, .65), rgba(13, 30, 58, .65))`
|
||||
- **Usage**: Semi-transparent color wash over hero background image
|
||||
|
||||
### `--hero-primary-color`
|
||||
- **Description**: Text color for primary hero content
|
||||
- **Light Mode Default**: `var(--color-primary)`
|
||||
- **Dark Mode Default**: `#f1f5f9`
|
||||
- **Usage**: Headings and body text inside `.hero#primary`
|
||||
|
||||
### `--hero-secondary-bg-color`
|
||||
- **Description**: Fallback background color for the secondary hero variant
|
||||
- **Light Mode Default**: `var(--color-primary)`
|
||||
- **Dark Mode Default**: `#080f1e`
|
||||
- **Usage**: `.hero#secondary` background when no image loads
|
||||
|
||||
### `--hero-secondary-overlay`
|
||||
- **Description**: Gradient overlay tint for secondary hero
|
||||
- **Light Mode Default**: `linear-gradient(rgba(17, 40, 85, .75), rgba(17, 40, 85, .75))`
|
||||
- **Dark Mode Default**: `linear-gradient(rgba(8, 15, 30, .80), rgba(8, 15, 30, .80))`
|
||||
- **Usage**: Stronger overlay for inner-page heroes
|
||||
|
||||
### `--hero-secondary-color`
|
||||
- **Description**: Text color for secondary hero content
|
||||
- **Light Mode Default**: `#f1f5f9`
|
||||
- **Dark Mode Default**: `#f1f5f9`
|
||||
- **Usage**: Headings and body text inside `.hero#secondary`
|
||||
|
||||
---
|
||||
|
||||
## Block Color Variables
|
||||
|
||||
### Slot Palette (automatic by position order)
|
||||
|
||||
| Variable | Purpose | Light Default | Dark Default |
|
||||
|---|---|---|---|
|
||||
| `--block-color-1` | Background for 1st module | `var(--color-primary)` | `var(--secondary-bg)` |
|
||||
| `--block-text-1` | Text for 1st module | `var(--body-color)` | `var(--body-color)` |
|
||||
| `--block-color-2` | Background for 2nd module | `var(--accent-color-primary)` | `var(--accent-color-primary)` |
|
||||
| `--block-text-2` | Text for 2nd module | `#fff` | `#fff` |
|
||||
| `--block-color-3` | Background for 3rd module | `var(--warning, #eec234)` | `rgba(238, 194, 52, .15)` |
|
||||
| `--block-text-3` | Text for 3rd module | `var(--body-color)` | `var(--body-color)` |
|
||||
| `--block-color-4` | Background for 4th module | `var(--success-bg-subtle, #eef7f0)` | `rgba(74, 166, 100, .15)` |
|
||||
| `--block-text-4` | Text for 4th module | `var(--body-color)` | `var(--body-color)` |
|
||||
|
||||
### Named Per-Module Overrides
|
||||
|
||||
| Variable | Purpose |
|
||||
|---|---|
|
||||
| `--block-highlight-bg` | Background for `#block-highlight` module |
|
||||
| `--block-highlight-text` | Text color for `#block-highlight` module |
|
||||
| `--block-cta-bg` | Background for `#block-cta` module |
|
||||
| `--block-cta-text` | Text color for `#block-cta` module |
|
||||
| `--block-alert-bg` | Background for `#block-alert` module |
|
||||
| `--block-alert-text` | Text color for `#block-alert` module |
|
||||
|
||||
### Override Priority
|
||||
|
||||
| Priority | Method | How applied |
|
||||
|---|---|---|
|
||||
| 1 (highest) | Named module ID (`#block-highlight`) | ID in module HTML, named variable in palette |
|
||||
| 2 | Slot color (`--block-color-1` etc.) | Automatic by `:nth-child()` order |
|
||||
|
||||
---
|
||||
|
||||
## Metadata
|
||||
|
||||
* Document: docs/CSS_VARIABLES.md
|
||||
@@ -1372,5 +1449,6 @@ These ensure optimal readability for links within alert boxes.
|
||||
|
||||
| Date | Change Summary | Author |
|
||||
| ---------- | ----------------------------------------------------- | --------------- |
|
||||
| 2026-03-26 | Added hero variant and block color variable docs | Claude |
|
||||
| 2026-02-07 | Added missing CSS variable documentation | GitHub Copilot |
|
||||
| 2026-01-30 | Initial CSS variables reference documentation created | GitHub Copilot |
|
||||
|
||||
@@ -238,6 +238,11 @@ TPL_MOKOCASSIOPEIA_CSS_VARS_VM_DESC="<strong>Surfaces & text</strong><br><co
|
||||
TPL_MOKOCASSIOPEIA_CSS_VARS_GABLE_LABEL="Gable"
|
||||
TPL_MOKOCASSIOPEIA_CSS_VARS_GABLE_DESC="Colour tokens used by the Gable extension.<br><code>--gab-blue</code> — <code>#0066cc</code><br><code>--gab-green</code> — <code>#28a745</code><br><code>--gab-red</code> — <code>#dc3545</code><br><code>--gab-orange</code> — <code>#fd7e14</code><br><code>--gab-gray1</code> — <code>#495057</code><br><code>--gab-gray2</code> — <code>#6c757d</code><br><code>--gab-gray3</code> — <code>#adb5bd</code>"
|
||||
|
||||
; ===== Theme Preview tab =====
|
||||
TPL_MOKOCASSIOPEIA_THEME_PREVIEW_FIELDSET_LABEL="Theme Preview"
|
||||
TPL_MOKOCASSIOPEIA_THEME_PREVIEW_INTRO="<p>Live preview of all CSS variables, hero variants, block colours, and Bootstrap components rendered with your active theme. Use the <strong>Toggle Light / Dark</strong> button inside the preview to switch modes. This page is also available as a standalone file at <code>templates/mokocassiopeia/templates/theme-test.html</code>.</p>"
|
||||
TPL_MOKOCASSIOPEIA_THEME_PREVIEW_FRAME="<iframe src='../templates/mokocassiopeia/templates/theme-test.html' style='width:100%;height:80vh;border:1px solid #dee2e6;border-radius:.375rem;' loading='lazy' title='Theme test sheet preview'></iframe>"
|
||||
|
||||
; ===== Misc =====
|
||||
MOD_BREADCRUMBS_HERE="YOU ARE HERE:"
|
||||
|
||||
|
||||
@@ -148,6 +148,12 @@ TPL_MOKOCASSIOPEIA_CSS_VARS_COLORS_DESC="<strong>Named colors</strong><br><code>
|
||||
TPL_MOKOCASSIOPEIA_CSS_VARS_HERO_LABEL="Hero / Banner Overlay"
|
||||
TPL_MOKOCASSIOPEIA_CSS_VARS_HERO_DESC="Applied to the <code>.custom-hero</code> / <code>.banner-overlay</code> layout. Set on <code>:root[data-bs-theme]</code> so light and dark values are independent.<br><code>--hero-height</code> — Banner height (default: <code>70vh</code>)<br><code>--hero-color</code> — Base text color<br><code>--hero-bg-repeat</code> — Background repeat (default: <code>no-repeat</code>)<br><code>--hero-bg-attachment</code> — Background attachment (default: <code>fixed</code>)<br><code>--hero-bg-position</code> — Background position (default: <code>top center</code>)<br><code>--hero-bg-size</code> — Background size (default: <code>cover</code>)<br><code>--hero-border-bottom</code> — Bottom border (default: <code>solid var(--accent-color-secondary)</code>)<br><code>--hero-overlay-bg</code> — Overlay tint color (light default: <code>hsla(0,0%,0%,0.1)</code> / dark default: <code>hsla(0,0%,0%,0.3)</code>)<br><code>--hero-overlay-padding</code> — Overlay inner padding (default: <code>1em</code>)<br><code>--hero-overlay-text-align</code> — Overlay text alignment (default: <code>center</code>)<br><code>--hero-overlay-text-color</code> — Overlay text color"
|
||||
|
||||
TPL_MOKOCASSIOPEIA_CSS_VARS_HERO_VARIANTS_LABEL="Hero Variants (.hero#primary / .hero#secondary)"
|
||||
TPL_MOKOCASSIOPEIA_CSS_VARS_HERO_VARIANTS_DESC="Two-variant hero system using <code>.hero#primary</code> and <code>.hero#secondary</code>. Each variant resolves its own CSS variable set per theme.<br><br><strong>Primary variant</strong> — homepage, main landing pages (sky blue tint, softer overlay)<br><code>--hero-primary-bg-color</code> — Fallback background color<br><code>--hero-primary-overlay</code> — Gradient overlay tint<br><code>--hero-primary-color</code> — Text color<br><br><strong>Secondary variant</strong> — inner pages, events, about (navy overlay, lighter text)<br><code>--hero-secondary-bg-color</code> — Fallback background color<br><code>--hero-secondary-overlay</code> — Gradient overlay tint<br><code>--hero-secondary-color</code> — Text color<br><br><strong>HTML usage:</strong><br><code><div class="hero" id="primary" style="background-image:url(...)"></code>"
|
||||
|
||||
TPL_MOKOCASSIOPEIA_CSS_VARS_BLOCK_COLORS_LABEL="Block Color System (top-a / top-b / bottom-a / bottom-b)"
|
||||
TPL_MOKOCASSIOPEIA_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 > Slot color. No <code>!important</code> needed — specificity handles it."
|
||||
|
||||
TPL_MOKOCASSIOPEIA_CSS_VARS_HEADER_LABEL="Header Background"
|
||||
TPL_MOKOCASSIOPEIA_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>"
|
||||
|
||||
@@ -232,6 +238,11 @@ TPL_MOKOCASSIOPEIA_CSS_VARS_VM_DESC="<strong>Surfaces & text</strong><br><co
|
||||
TPL_MOKOCASSIOPEIA_CSS_VARS_GABLE_LABEL="Gable"
|
||||
TPL_MOKOCASSIOPEIA_CSS_VARS_GABLE_DESC="Color tokens used by the Gable extension.<br><code>--gab-blue</code> — <code>#0066cc</code><br><code>--gab-green</code> — <code>#28a745</code><br><code>--gab-red</code> — <code>#dc3545</code><br><code>--gab-orange</code> — <code>#fd7e14</code><br><code>--gab-gray1</code> — <code>#495057</code><br><code>--gab-gray2</code> — <code>#6c757d</code><br><code>--gab-gray3</code> — <code>#adb5bd</code>"
|
||||
|
||||
; ===== Theme Preview tab =====
|
||||
TPL_MOKOCASSIOPEIA_THEME_PREVIEW_FIELDSET_LABEL="Theme Preview"
|
||||
TPL_MOKOCASSIOPEIA_THEME_PREVIEW_INTRO="<p>Live preview of all CSS variables, hero variants, block colors, and Bootstrap components rendered with your active theme. Use the <strong>Toggle Light / Dark</strong> button inside the preview to switch modes. This page is also available as a standalone file at <code>templates/mokocassiopeia/templates/theme-test.html</code>.</p>"
|
||||
TPL_MOKOCASSIOPEIA_THEME_PREVIEW_FRAME="<iframe src='../templates/mokocassiopeia/templates/theme-test.html' style='width:100%;height:80vh;border:1px solid #dee2e6;border-radius:.375rem;' loading='lazy' title='Theme test sheet preview'></iframe>"
|
||||
|
||||
; ===== Misc =====
|
||||
MOD_BREADCRUMBS_HERE="YOU ARE HERE:"
|
||||
|
||||
|
||||
221
src/script.php
Normal file
221
src/script.php
Normal file
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Joomla.Site
|
||||
* @subpackage Templates.MokoCassiopeia
|
||||
*
|
||||
* @copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*
|
||||
* Template install/update/uninstall script.
|
||||
*
|
||||
* Joomla calls the methods in this class automatically during template
|
||||
* install, update, and uninstall via the <scriptfile> element in
|
||||
* templateDetails.xml.
|
||||
*
|
||||
* Joomla 5 and 6 compatible — uses the InstallerScriptInterface when
|
||||
* available, falls back to the legacy class-based approach otherwise.
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Installer\InstallerAdapter;
|
||||
use Joomla\CMS\Log\Log;
|
||||
|
||||
class Tpl_MokocassiopeiaInstallerScript
|
||||
{
|
||||
/**
|
||||
* Minimum PHP version required by this template.
|
||||
*/
|
||||
private const MIN_PHP = '8.1.0';
|
||||
|
||||
/**
|
||||
* Minimum Joomla version required by this template.
|
||||
*/
|
||||
private const MIN_JOOMLA = '4.4.0';
|
||||
|
||||
/**
|
||||
* Called before install/update/uninstall.
|
||||
*
|
||||
* @param string $type install, update, discover_install, or uninstall.
|
||||
* @param InstallerAdapter $parent The adapter calling this method.
|
||||
*
|
||||
* @return bool True to proceed, false to abort.
|
||||
*/
|
||||
public function preflight(string $type, InstallerAdapter $parent): bool
|
||||
{
|
||||
if (version_compare(PHP_VERSION, self::MIN_PHP, '<')) {
|
||||
Factory::getApplication()->enqueueMessage(
|
||||
sprintf(
|
||||
'MokoCassiopeia requires PHP %s or later. You are running PHP %s.',
|
||||
self::MIN_PHP,
|
||||
PHP_VERSION
|
||||
),
|
||||
'error'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version_compare(JVERSION, self::MIN_JOOMLA, '<')) {
|
||||
Factory::getApplication()->enqueueMessage(
|
||||
sprintf(
|
||||
'MokoCassiopeia requires Joomla %s or later. You are running Joomla %s.',
|
||||
self::MIN_JOOMLA,
|
||||
JVERSION
|
||||
),
|
||||
'error'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a successful install.
|
||||
*
|
||||
* @param InstallerAdapter $parent The adapter calling this method.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function install(InstallerAdapter $parent): bool
|
||||
{
|
||||
$this->logMessage('MokoCassiopeia template installed.');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a successful update.
|
||||
*
|
||||
* This is where the CSS variable sync runs — it detects variables that
|
||||
* were added in the new version and injects them into the user's custom
|
||||
* palette files without overwriting existing values.
|
||||
*
|
||||
* @param InstallerAdapter $parent The adapter calling this method.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function update(InstallerAdapter $parent): bool
|
||||
{
|
||||
$this->logMessage('MokoCassiopeia template updated.');
|
||||
|
||||
// Run CSS variable sync to inject any new variables into user's custom palettes.
|
||||
$synced = $this->syncCustomVariables($parent);
|
||||
|
||||
if ($synced > 0) {
|
||||
Factory::getApplication()->enqueueMessage(
|
||||
sprintf(
|
||||
'MokoCassiopeia: %d new CSS variable(s) were added to your custom palette files. '
|
||||
. 'Review them in your light.custom.css and/or dark.custom.css to customise the new defaults.',
|
||||
$synced
|
||||
),
|
||||
'notice'
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a successful uninstall.
|
||||
*
|
||||
* @param InstallerAdapter $parent The adapter calling this method.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function uninstall(InstallerAdapter $parent): bool
|
||||
{
|
||||
$this->logMessage('MokoCassiopeia template uninstalled.');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after install/update completes (regardless of type).
|
||||
*
|
||||
* @param string $type install, update, or discover_install.
|
||||
* @param InstallerAdapter $parent The adapter calling this method.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function postflight(string $type, InstallerAdapter $parent): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the CSS variable sync utility.
|
||||
*
|
||||
* Loads sync_custom_vars.php from the template directory and calls
|
||||
* MokoCssVarSync::run() to detect and inject missing variables.
|
||||
*
|
||||
* @param InstallerAdapter $parent The adapter calling this method.
|
||||
*
|
||||
* @return int Number of variables added across all files.
|
||||
*/
|
||||
private function syncCustomVariables(InstallerAdapter $parent): int
|
||||
{
|
||||
$templateDir = $parent->getParent()->getPath('source');
|
||||
|
||||
// The sync script lives alongside this script in the template root.
|
||||
$syncScript = $templateDir . '/sync_custom_vars.php';
|
||||
|
||||
if (!is_file($syncScript)) {
|
||||
$this->logMessage('CSS variable sync script not found at: ' . $syncScript, 'warning');
|
||||
return 0;
|
||||
}
|
||||
|
||||
require_once $syncScript;
|
||||
|
||||
if (!class_exists('MokoCssVarSync')) {
|
||||
$this->logMessage('MokoCssVarSync class not found after loading script.', 'warning');
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
$joomlaRoot = JPATH_ROOT;
|
||||
$results = MokoCssVarSync::run($joomlaRoot);
|
||||
|
||||
$totalAdded = 0;
|
||||
foreach ($results as $filePath => $result) {
|
||||
$totalAdded += count($result['added']);
|
||||
if (!empty($result['added'])) {
|
||||
$this->logMessage(
|
||||
sprintf(
|
||||
'CSS sync: added %d variable(s) to %s',
|
||||
count($result['added']),
|
||||
basename($filePath)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $totalAdded;
|
||||
} catch (\Throwable $e) {
|
||||
$this->logMessage('CSS variable sync failed: ' . $e->getMessage(), 'error');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message to Joomla's log system.
|
||||
*
|
||||
* @param string $message The log message.
|
||||
* @param string $priority Log priority (info, warning, error).
|
||||
*/
|
||||
private function logMessage(string $message, string $priority = 'info'): void
|
||||
{
|
||||
$priorities = [
|
||||
'info' => Log::INFO,
|
||||
'warning' => Log::WARNING,
|
||||
'error' => Log::ERROR,
|
||||
];
|
||||
|
||||
Log::addLogger(
|
||||
['text_file' => 'mokocassiopeia.log.php'],
|
||||
Log::ALL,
|
||||
['mokocassiopeia']
|
||||
);
|
||||
|
||||
Log::add($message, $priorities[$priority] ?? Log::INFO, 'mokocassiopeia');
|
||||
}
|
||||
}
|
||||
406
src/sync_custom_vars.php
Normal file
406
src/sync_custom_vars.php
Normal file
@@ -0,0 +1,406 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Joomla.Site
|
||||
* @subpackage Templates.MokoCassiopeia
|
||||
*
|
||||
* @copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*
|
||||
* CSS Variable Sync Utility
|
||||
*
|
||||
* Compares a user's custom palette file against the template starter file and
|
||||
* injects any missing CSS variable declarations. Existing user values are
|
||||
* never overwritten — only genuinely new variables are added.
|
||||
*
|
||||
* Usage (CLI):
|
||||
* php sync_custom_vars.php
|
||||
*
|
||||
* Usage (from Joomla script.php or plugin):
|
||||
* require_once __DIR__ . '/sync_custom_vars.php';
|
||||
* MokoCssVarSync::run();
|
||||
*
|
||||
* The script auto-detects Joomla's root by walking up from __DIR__.
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or define('MOKO_CLI', true);
|
||||
|
||||
final class MokoCssVarSync
|
||||
{
|
||||
/**
|
||||
* Template name used in Joomla's media path.
|
||||
*/
|
||||
private const TPL = 'mokocassiopeia';
|
||||
|
||||
/**
|
||||
* Palette pairs: [starter template path relative to this file, user file relative to Joomla root].
|
||||
*/
|
||||
private const PALETTES = [
|
||||
[
|
||||
'starter' => 'templates/light.custom.css',
|
||||
'user' => 'media/templates/site/%s/css/theme/light.custom.css',
|
||||
],
|
||||
[
|
||||
'starter' => 'templates/dark.custom.css',
|
||||
'user' => 'media/templates/site/%s/css/theme/dark.custom.css',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Run the sync for all palette pairs.
|
||||
*
|
||||
* @param string|null $joomlaRoot Absolute path to Joomla root (auto-detected if null).
|
||||
* @return array<string, array{added: string[], skipped: string[]}> Results keyed by file path.
|
||||
*/
|
||||
public static function run(?string $joomlaRoot = null): array
|
||||
{
|
||||
$tplDir = self::resolveTemplateDir();
|
||||
$root = $joomlaRoot ?? self::detectJoomlaRoot();
|
||||
|
||||
$results = [];
|
||||
|
||||
foreach (self::PALETTES as $pair) {
|
||||
$starterPath = $tplDir . '/' . $pair['starter'];
|
||||
$userPath = $root . '/' . sprintf($pair['user'], self::TPL);
|
||||
|
||||
if (!is_file($starterPath)) {
|
||||
self::log("SKIP starter not found: {$starterPath}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_file($userPath)) {
|
||||
self::log("SKIP user file not found (custom palette not deployed): {$userPath}");
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = self::syncFile($starterPath, $userPath);
|
||||
$results[$userPath] = $result;
|
||||
|
||||
$addedCount = count($result['added']);
|
||||
if ($addedCount > 0) {
|
||||
self::log("ADDED {$addedCount} variable(s) to {$userPath}");
|
||||
foreach ($result['added'] as $var) {
|
||||
self::log(" + {$var}");
|
||||
}
|
||||
} else {
|
||||
self::log("OK {$userPath} — all variables present");
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare a starter file against a user file and inject missing variables.
|
||||
*
|
||||
* @param string $starterPath Absolute path to the starter template CSS.
|
||||
* @param string $userPath Absolute path to the user's custom CSS.
|
||||
* @return array{added: string[], skipped: string[]}
|
||||
*/
|
||||
private static function syncFile(string $starterPath, string $userPath): array
|
||||
{
|
||||
$starterVars = self::extractVarsWithContext($starterPath);
|
||||
$userVars = self::extractVarNames($userPath);
|
||||
|
||||
$missing = [];
|
||||
foreach ($starterVars as $name => $declaration) {
|
||||
if (!isset($userVars[$name])) {
|
||||
$missing[$name] = $declaration;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($missing)) {
|
||||
return ['added' => [], 'skipped' => []];
|
||||
}
|
||||
|
||||
// Group missing variables by their section comment header.
|
||||
$sections = self::groupBySection($missing, $starterPath);
|
||||
|
||||
// Build the injection block.
|
||||
$injection = self::buildInjectionBlock($sections);
|
||||
|
||||
// Insert before the closing } of the :root rule.
|
||||
$userCss = file_get_contents($userPath);
|
||||
$userCss = self::injectBeforeRootClose($userCss, $injection);
|
||||
|
||||
// Write back (atomic: write to .tmp then rename).
|
||||
$tmpPath = $userPath . '.tmp';
|
||||
file_put_contents($tmpPath, $userCss);
|
||||
rename($tmpPath, $userPath);
|
||||
|
||||
return ['added' => array_keys($missing), 'skipped' => []];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract CSS custom property declarations with their full text (name: value).
|
||||
* Only extracts from the first :root block.
|
||||
*
|
||||
* @return array<string, string> Variable name => full declaration line.
|
||||
*/
|
||||
private static function extractVarsWithContext(string $filePath): array
|
||||
{
|
||||
$css = file_get_contents($filePath);
|
||||
$vars = [];
|
||||
|
||||
// Match --variable-name: value (possibly spanning multiple lines until ;)
|
||||
if (preg_match_all('/^\s*(--[\w-]+)\s*:\s*([^;]+);/m', $css, $matches, PREG_SET_ORDER)) {
|
||||
foreach ($matches as $m) {
|
||||
$name = trim($m[1]);
|
||||
$value = trim($m[2]);
|
||||
$vars[$name] = "{$name}: {$value};";
|
||||
}
|
||||
}
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract just the variable names present in a CSS file.
|
||||
*
|
||||
* @return array<string, true>
|
||||
*/
|
||||
private static function extractVarNames(string $filePath): array
|
||||
{
|
||||
$css = file_get_contents($filePath);
|
||||
$vars = [];
|
||||
|
||||
if (preg_match_all('/^\s*(--[\w-]+)\s*:/m', $css, $matches)) {
|
||||
foreach ($matches[1] as $name) {
|
||||
$vars[trim($name)] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Group missing variables by the section comment they appear under in the starter file.
|
||||
*
|
||||
* @param array<string, string> $missing Variable name => declaration.
|
||||
* @param string $starterPath Path to starter file.
|
||||
* @return array<string, string[]> Section header => list of declarations.
|
||||
*/
|
||||
private static function groupBySection(array $missing, string $starterPath): array
|
||||
{
|
||||
$lines = file($starterPath, FILE_IGNORE_NEW_LINES);
|
||||
$section = 'Uncategorised';
|
||||
$map = []; // variable name => section
|
||||
|
||||
foreach ($lines as $line) {
|
||||
// Detect section comment headers like /* ===== HERO VARIANTS ===== */
|
||||
if (preg_match('/\/\*\s*=+\s*(.+?)\s*=+\s*\*\//', $line, $m)) {
|
||||
$section = trim($m[1]);
|
||||
}
|
||||
// Detect variable declaration
|
||||
if (preg_match('/^\s*(--[\w-]+)\s*:/', $line, $m)) {
|
||||
$name = trim($m[1]);
|
||||
if (isset($missing[$name])) {
|
||||
$map[$name] = $section;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Group by section
|
||||
$sections = [];
|
||||
foreach ($missing as $name => $declaration) {
|
||||
$sec = $map[$name] ?? 'Uncategorised';
|
||||
$sections[$sec][] = $declaration;
|
||||
}
|
||||
|
||||
return $sections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a CSS block from grouped sections ready for injection.
|
||||
*/
|
||||
private static function buildInjectionBlock(array $sections): string
|
||||
{
|
||||
$lines = [];
|
||||
$lines[] = '';
|
||||
$lines[] = '/* ===== VARIABLES ADDED BY SYNC (' . date('Y-m-d') . ') ===== */';
|
||||
|
||||
foreach ($sections as $sectionName => $declarations) {
|
||||
$lines[] = '';
|
||||
$lines[] = "/* -- {$sectionName} -- */";
|
||||
foreach ($declarations as $decl) {
|
||||
$lines[] = $decl;
|
||||
}
|
||||
}
|
||||
|
||||
$lines[] = '';
|
||||
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject a block of CSS just before the closing } of the :root[data-bs-theme] rule.
|
||||
*/
|
||||
private static function injectBeforeRootClose(string $css, string $block): string
|
||||
{
|
||||
// Find the :root block's closing brace. The :root rule is the first major
|
||||
// rule in the file; its closing } is on its own line.
|
||||
// Strategy: find the LAST } that is preceded only by CSS variable content.
|
||||
// More robustly: find the first } that appears on its own line (possibly
|
||||
// with whitespace), which closes the :root block.
|
||||
|
||||
// Walk backwards from each } to see if it's inside the :root block.
|
||||
// Simple approach: the :root closing } is the first bare } on its own line.
|
||||
$pos = self::findRootClosingBrace($css);
|
||||
|
||||
if ($pos === false) {
|
||||
// Fallback: append before last }
|
||||
$pos = strrpos($css, '}');
|
||||
}
|
||||
|
||||
if ($pos === false) {
|
||||
// Last resort: append to end
|
||||
return $css . $block;
|
||||
}
|
||||
|
||||
return substr($css, 0, $pos) . $block . substr($css, $pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the byte position of the closing } for the :root rule.
|
||||
*/
|
||||
private static function findRootClosingBrace(string $css): int|false
|
||||
{
|
||||
// Find where :root starts
|
||||
$rootStart = preg_match('/:root\b/', $css, $m, PREG_OFFSET_CAPTURE);
|
||||
if (!$rootStart) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$offset = $m[0][1];
|
||||
$depth = 0;
|
||||
$len = strlen($css);
|
||||
|
||||
for ($i = $offset; $i < $len; $i++) {
|
||||
if ($css[$i] === '{') {
|
||||
$depth++;
|
||||
} elseif ($css[$i] === '}') {
|
||||
$depth--;
|
||||
if ($depth === 0) {
|
||||
return $i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the template source directory (where this file lives).
|
||||
*/
|
||||
private static function resolveTemplateDir(): string
|
||||
{
|
||||
return dirname(__FILE__);
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-detect Joomla root by walking up from template dir looking for
|
||||
* configuration.php or the media/templates directory structure.
|
||||
*/
|
||||
private static function detectJoomlaRoot(): string
|
||||
{
|
||||
$dir = dirname(__FILE__);
|
||||
|
||||
// Walk up max 10 levels
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
if (is_file($dir . '/configuration.php')) {
|
||||
return $dir;
|
||||
}
|
||||
// Also check for the media/templates structure (works in dev too)
|
||||
if (is_dir($dir . '/media/templates')) {
|
||||
return $dir;
|
||||
}
|
||||
$parent = dirname($dir);
|
||||
if ($parent === $dir) {
|
||||
break;
|
||||
}
|
||||
$dir = $parent;
|
||||
}
|
||||
|
||||
// Fallback for dev: if JPATH_ROOT is defined, use it
|
||||
if (defined('JPATH_ROOT')) {
|
||||
return JPATH_ROOT;
|
||||
}
|
||||
|
||||
self::log('WARNING: Could not auto-detect Joomla root. Pass it explicitly.');
|
||||
return dirname(__FILE__);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message (CLI: stdout, web: Joomla enqueueMessage if available).
|
||||
*/
|
||||
private static function log(string $message): void
|
||||
{
|
||||
if (defined('MOKO_CLI') || PHP_SAPI === 'cli') {
|
||||
echo $message . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dry-run mode: report what would be added without writing.
|
||||
*
|
||||
* @return array<string, string[]> File path => list of missing variable names.
|
||||
*/
|
||||
public static function dryRun(?string $joomlaRoot = null): array
|
||||
{
|
||||
$tplDir = self::resolveTemplateDir();
|
||||
$root = $joomlaRoot ?? self::detectJoomlaRoot();
|
||||
$report = [];
|
||||
|
||||
foreach (self::PALETTES as $pair) {
|
||||
$starterPath = $tplDir . '/' . $pair['starter'];
|
||||
$userPath = $root . '/' . sprintf($pair['user'], self::TPL);
|
||||
|
||||
if (!is_file($starterPath) || !is_file($userPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$starterVars = self::extractVarsWithContext($starterPath);
|
||||
$userVars = self::extractVarNames($userPath);
|
||||
|
||||
$missing = [];
|
||||
foreach ($starterVars as $name => $declaration) {
|
||||
if (!isset($userVars[$name])) {
|
||||
$missing[] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($missing)) {
|
||||
$report[$userPath] = $missing;
|
||||
}
|
||||
}
|
||||
|
||||
return $report;
|
||||
}
|
||||
}
|
||||
|
||||
// CLI entry point
|
||||
if (PHP_SAPI === 'cli' && realpath($argv[0] ?? '') === realpath(__FILE__)) {
|
||||
$dryRun = in_array('--dry-run', $argv, true);
|
||||
|
||||
echo "MokoCassiopeia CSS Variable Sync\n";
|
||||
echo str_repeat('─', 40) . "\n\n";
|
||||
|
||||
if ($dryRun) {
|
||||
echo "DRY RUN — no files will be modified\n\n";
|
||||
$report = MokoCssVarSync::dryRun();
|
||||
if (empty($report)) {
|
||||
echo "All custom palettes are up to date.\n";
|
||||
} else {
|
||||
foreach ($report as $file => $vars) {
|
||||
echo "MISSING in {$file}:\n";
|
||||
foreach ($vars as $var) {
|
||||
echo " - {$var}\n";
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MokoCssVarSync::run();
|
||||
}
|
||||
|
||||
echo "\nDone.\n";
|
||||
}
|
||||
@@ -36,7 +36,8 @@
|
||||
</server>
|
||||
</updateservers>
|
||||
<name>MokoCassiopeia</name>
|
||||
<version>03.09.01</version>
|
||||
<version>03.09.02</version>
|
||||
<scriptfile>script.php</scriptfile>
|
||||
<creationDate>2026-03-08</creationDate>
|
||||
<author>Jonathan Miller || Moko Consulting</author>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
@@ -49,6 +50,8 @@
|
||||
<filename>index.php</filename>
|
||||
<filename>joomla.asset.json</filename>
|
||||
<filename>offline.php</filename>
|
||||
<filename>script.php</filename>
|
||||
<filename>sync_custom_vars.php</filename>
|
||||
<filename>templateDetails.xml</filename>
|
||||
<folder>html</folder>
|
||||
<folder>language</folder>
|
||||
@@ -264,6 +267,8 @@
|
||||
<field name="css_vars_alert_list" type="note" label="TPL_MOKOCASSIOPEIA_CSS_VARS_ALERT_LIST_LABEL" description="TPL_MOKOCASSIOPEIA_CSS_VARS_ALERT_LIST_DESC" class="alert alert-light" />
|
||||
<field name="css_vars_colors" type="note" label="TPL_MOKOCASSIOPEIA_CSS_VARS_COLORS_LABEL" description="TPL_MOKOCASSIOPEIA_CSS_VARS_COLORS_DESC" class="alert alert-light" />
|
||||
<field name="css_vars_hero" type="note" label="TPL_MOKOCASSIOPEIA_CSS_VARS_HERO_LABEL" description="TPL_MOKOCASSIOPEIA_CSS_VARS_HERO_DESC" class="alert alert-light" />
|
||||
<field name="css_vars_hero_variants" type="note" label="TPL_MOKOCASSIOPEIA_CSS_VARS_HERO_VARIANTS_LABEL" description="TPL_MOKOCASSIOPEIA_CSS_VARS_HERO_VARIANTS_DESC" class="alert alert-info" />
|
||||
<field name="css_vars_block_colors" type="note" label="TPL_MOKOCASSIOPEIA_CSS_VARS_BLOCK_COLORS_LABEL" description="TPL_MOKOCASSIOPEIA_CSS_VARS_BLOCK_COLORS_DESC" class="alert alert-info" />
|
||||
<field name="css_vars_header" type="note" label="TPL_MOKOCASSIOPEIA_CSS_VARS_HEADER_LABEL" description="TPL_MOKOCASSIOPEIA_CSS_VARS_HEADER_DESC" class="alert alert-light" />
|
||||
<field name="css_vars_containers" type="note" label="TPL_MOKOCASSIOPEIA_CSS_VARS_CONTAINERS_LABEL" description="TPL_MOKOCASSIOPEIA_CSS_VARS_CONTAINERS_DESC" class="alert alert-light" />
|
||||
<field name="css_vars_borders" type="note" label="TPL_MOKOCASSIOPEIA_CSS_VARS_BORDERS_LABEL" description="TPL_MOKOCASSIOPEIA_CSS_VARS_BORDERS_DESC" class="alert alert-light" />
|
||||
@@ -293,6 +298,12 @@
|
||||
<field name="css_vars_virtuemart" type="note" label="TPL_MOKOCASSIOPEIA_CSS_VARS_VM_LABEL" description="TPL_MOKOCASSIOPEIA_CSS_VARS_VM_DESC" class="alert alert-light" />
|
||||
<field name="css_vars_gable" type="note" label="TPL_MOKOCASSIOPEIA_CSS_VARS_GABLE_LABEL" description="TPL_MOKOCASSIOPEIA_CSS_VARS_GABLE_DESC" class="alert alert-light" />
|
||||
</fieldset>
|
||||
|
||||
<!-- Theme Preview tab — embedded test sheet -->
|
||||
<fieldset name="theme_preview" label="TPL_MOKOCASSIOPEIA_THEME_PREVIEW_FIELDSET_LABEL">
|
||||
<field name="theme_preview_intro" type="note" description="TPL_MOKOCASSIOPEIA_THEME_PREVIEW_INTRO" />
|
||||
<field name="theme_preview_frame" type="note" description="TPL_MOKOCASSIOPEIA_THEME_PREVIEW_FRAME" />
|
||||
</fieldset>
|
||||
</fields>
|
||||
</config>
|
||||
</extension>
|
||||
|
||||
@@ -512,6 +512,40 @@ color-scheme: dark;
|
||||
--vm-vendor-menu-link-active: var(--primary);
|
||||
--vm-vendor-menu-hover-bg: var(--tertiary-bg);
|
||||
|
||||
/* ===== HERO VARIANTS ===== */
|
||||
/* Primary — deep navy, dark overlay */
|
||||
--hero-primary-bg-color: #0d1e3a;
|
||||
--hero-primary-overlay: linear-gradient(rgba(13, 30, 58, .65), rgba(13, 30, 58, .65));
|
||||
--hero-primary-color: #f1f5f9;
|
||||
|
||||
/* Secondary — darker navy, heavier overlay */
|
||||
--hero-secondary-bg-color: #080f1e;
|
||||
--hero-secondary-overlay: linear-gradient(rgba(8, 15, 30, .80), rgba(8, 15, 30, .80));
|
||||
--hero-secondary-color: #f1f5f9;
|
||||
|
||||
/* ===== BLOCK COLORS (top-a / top-b / bottom-a / bottom-b) ===== */
|
||||
--block-color-1: var(--secondary-bg);
|
||||
--block-text-1: var(--body-color);
|
||||
|
||||
--block-color-2: var(--accent-color-primary);
|
||||
--block-text-2: #fff;
|
||||
|
||||
--block-color-3: rgba(238, 194, 52, .15);
|
||||
--block-text-3: var(--body-color);
|
||||
|
||||
--block-color-4: rgba(74, 166, 100, .15);
|
||||
--block-text-4: var(--body-color);
|
||||
|
||||
/* ===== BLOCK COLOR OVERRIDES ===== */
|
||||
--block-highlight-bg: var(--accent-color-primary);
|
||||
--block-highlight-text: #fff;
|
||||
|
||||
--block-cta-bg: var(--color-primary);
|
||||
--block-cta-text: #f1f5f9;
|
||||
|
||||
--block-alert-bg: var(--danger, #c23a31);
|
||||
--block-alert-text: #fff;
|
||||
|
||||
/* ===== GABLE ===== */
|
||||
--gab-blue: #4d9fff;
|
||||
--gab-green: #5cb85c;
|
||||
|
||||
@@ -511,6 +511,40 @@ color-scheme: light;
|
||||
--vm-vendor-menu-link-active: var(--primary);
|
||||
--vm-vendor-menu-hover-bg: var(--secondary-bg);
|
||||
|
||||
/* ===== HERO VARIANTS ===== */
|
||||
/* Primary — sky blue, light overlay */
|
||||
--hero-primary-bg-color: var(--color-primary);
|
||||
--hero-primary-overlay: linear-gradient(rgba(163, 205, 226, .45), rgba(163, 205, 226, .45));
|
||||
--hero-primary-color: var(--color-primary);
|
||||
|
||||
/* Secondary — navy, stronger overlay */
|
||||
--hero-secondary-bg-color: var(--color-primary);
|
||||
--hero-secondary-overlay: linear-gradient(rgba(17, 40, 85, .75), rgba(17, 40, 85, .75));
|
||||
--hero-secondary-color: #f1f5f9;
|
||||
|
||||
/* ===== BLOCK COLORS (top-a / top-b / bottom-a / bottom-b) ===== */
|
||||
--block-color-1: var(--color-primary);
|
||||
--block-text-1: var(--body-color);
|
||||
|
||||
--block-color-2: var(--accent-color-primary);
|
||||
--block-text-2: #fff;
|
||||
|
||||
--block-color-3: var(--warning, #eec234);
|
||||
--block-text-3: var(--body-color);
|
||||
|
||||
--block-color-4: var(--success-bg-subtle, #eef7f0);
|
||||
--block-text-4: var(--body-color);
|
||||
|
||||
/* ===== BLOCK COLOR OVERRIDES ===== */
|
||||
--block-highlight-bg: var(--accent-color-primary);
|
||||
--block-highlight-text: #fff;
|
||||
|
||||
--block-cta-bg: var(--color-primary);
|
||||
--block-cta-text: #fff;
|
||||
|
||||
--block-alert-bg: var(--danger, #a51f18);
|
||||
--block-alert-text: #fff;
|
||||
|
||||
/* ===== GABLE ===== */
|
||||
--gab-blue: #0066cc;
|
||||
--gab-green: #28a745;
|
||||
|
||||
611
src/templates/theme-test.html
Normal file
611
src/templates/theme-test.html
Normal file
@@ -0,0 +1,611 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
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: Joomla.Template.Site
|
||||
INGROUP: MokaCassiopeia.Testing
|
||||
REPO: mokoconsulting-tech/MokoCassiopeia
|
||||
VERSION: 03.09.02
|
||||
PATH: ./src/templates/theme-test.html
|
||||
BRIEF: Bootstrap-style test page for MokoCassiopeia CSS variables, hero variants, and block color system
|
||||
-->
|
||||
<html lang="en" data-bs-theme="light">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>MokoCassiopeia — 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>MokoCassiopeia 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 & 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 & 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 & 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 & 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 */
|
||||
<div class="hero" id="primary"
|
||||
style="background-image:url('/images/hero/main.jpg')">
|
||||
<div class="col-12 py-5 px-4 text-center">
|
||||
...content...
|
||||
</div>
|
||||
</div></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>
|
||||
|
||||
<hr>
|
||||
<p style="color: var(--muted-color); font-size: .8rem; text-align: center; margin: 2rem 0;">
|
||||
MokoCassiopeia Theme Test Sheet — v03.09.02 — © 2026 Moko Consulting
|
||||
</p>
|
||||
|
||||
</div><!-- /.test-container -->
|
||||
|
||||
<script>
|
||||
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');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user