Merge branch 'version/03'
This commit is contained in:
4
.github/workflows/changelog-validation.yml
vendored
4
.github/workflows/changelog-validation.yml
vendored
@@ -19,9 +19,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
pull_request:
|
- version/*
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
|||||||
9
.github/workflows/ci-joomla.yml
vendored
9
.github/workflows/ci-joomla.yml
vendored
@@ -19,14 +19,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- dev/**
|
- version/*
|
||||||
- rc/**
|
|
||||||
- version/**
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- dev/**
|
|
||||||
- rc/**
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
|||||||
9
.github/workflows/codeql-analysis.yml
vendored
9
.github/workflows/codeql-analysis.yml
vendored
@@ -21,14 +21,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- dev/**
|
- version/*
|
||||||
- rc/**
|
|
||||||
- version/**
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- dev/**
|
|
||||||
- rc/**
|
|
||||||
schedule:
|
schedule:
|
||||||
# Weekly on Monday at 06:00 UTC
|
# Weekly on Monday at 06:00 UTC
|
||||||
- cron: '0 6 * * 1'
|
- cron: '0 6 * * 1'
|
||||||
|
|||||||
4
.github/workflows/standards-compliance.yml
vendored
4
.github/workflows/standards-compliance.yml
vendored
@@ -89,9 +89,7 @@ env:
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main, dev/**, rc/**, version/**]
|
branches: [main, version/*]
|
||||||
pull_request:
|
|
||||||
branches: [main, dev/**, rc/**]
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
|||||||
6
.github/workflows/update-server.yml
vendored
6
.github/workflows/update-server.yml
vendored
@@ -22,10 +22,8 @@ name: Update Joomla Update Server XML Feed
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- 'dev/**'
|
- main
|
||||||
- 'alpha/**'
|
- version/*
|
||||||
- 'beta/**'
|
|
||||||
- 'rc/**'
|
|
||||||
paths:
|
paths:
|
||||||
- 'src/**'
|
- 'src/**'
|
||||||
- 'htdocs/**'
|
- 'htdocs/**'
|
||||||
|
|||||||
@@ -10,49 +10,20 @@
|
|||||||
/**
|
/**
|
||||||
* Default layout override for mod_breadcrumbs.
|
* Default layout override for mod_breadcrumbs.
|
||||||
* Bootstrap 5 breadcrumb with schema.org BreadcrumbList markup.
|
* Bootstrap 5 breadcrumb with schema.org BreadcrumbList markup.
|
||||||
* Respects showHome, showLast, homeText module settings.
|
* Module settings (showHome, showLast, homeText) are handled by Joomla core
|
||||||
|
* before $list reaches this template.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defined('_JEXEC') or die;
|
defined('_JEXEC') or die;
|
||||||
|
|
||||||
use Joomla\CMS\Factory;
|
|
||||||
use Joomla\CMS\Language\Text;
|
use Joomla\CMS\Language\Text;
|
||||||
use Joomla\CMS\Router\Route;
|
|
||||||
use Joomla\CMS\Uri\Uri;
|
|
||||||
|
|
||||||
Factory::getApplication()->getLanguage()->load('mod_breadcrumbs', JPATH_SITE);
|
|
||||||
|
|
||||||
$suffix = htmlspecialchars($params->get('moduleclass_sfx', ''), ENT_COMPAT, 'UTF-8');
|
$suffix = htmlspecialchars($params->get('moduleclass_sfx', ''), ENT_COMPAT, 'UTF-8');
|
||||||
$headerTag = htmlspecialchars($params->get('header_tag', 'h3'), ENT_COMPAT, 'UTF-8');
|
$headerTag = htmlspecialchars($params->get('header_tag', 'h3'), ENT_COMPAT, 'UTF-8');
|
||||||
$headerClass = htmlspecialchars($params->get('header_class', ''), ENT_COMPAT, 'UTF-8');
|
$headerClass = htmlspecialchars($params->get('header_class', ''), ENT_COMPAT, 'UTF-8');
|
||||||
$showHome = $params->get('showHome', 1);
|
$showHere = $params->get('showHere', 1);
|
||||||
$showLast = $params->get('showLast', 1);
|
|
||||||
$homeText = $params->get('homeText', '') ?: Text::_('MOD_BREADCRUMBS_HOME');
|
|
||||||
|
|
||||||
// Build filtered list respecting module settings
|
if (empty($list)) {
|
||||||
$items = [];
|
|
||||||
$count = count($list);
|
|
||||||
|
|
||||||
foreach ($list as $key => $item) {
|
|
||||||
// Skip Home item if showHome is off
|
|
||||||
if ($key === 0 && !$showHome) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace Home text if custom homeText is set
|
|
||||||
if ($key === 0 && $showHome) {
|
|
||||||
$item->name = $homeText;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip last item if showLast is off
|
|
||||||
if ($key === $count - 1 && !$showLast) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$items[] = $item;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($items)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
@@ -60,9 +31,12 @@ if (empty($items)) {
|
|||||||
<?php if ($module->showtitle) : ?>
|
<?php if ($module->showtitle) : ?>
|
||||||
<<?php echo $headerTag; ?> class="mod-breadcrumbs__title<?php echo $headerClass ? ' ' . $headerClass : ''; ?>"><?php echo $module->title; ?></<?php echo $headerTag; ?>>
|
<<?php echo $headerTag; ?> class="mod-breadcrumbs__title<?php echo $headerClass ? ' ' . $headerClass : ''; ?>"><?php echo $module->title; ?></<?php echo $headerTag; ?>>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
<?php if ($showHere) : ?>
|
||||||
|
<span class="mod-breadcrumbs__here"><?php echo Text::_('MOD_BREADCRUMBS_HERE'); ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
<ol class="breadcrumb" itemscope itemtype="https://schema.org/BreadcrumbList">
|
<ol class="breadcrumb" itemscope itemtype="https://schema.org/BreadcrumbList">
|
||||||
<?php foreach ($items as $key => $item) : ?>
|
<?php foreach ($list as $key => $item) : ?>
|
||||||
<?php $isLast = ($key === array_key_last($items)); ?>
|
<?php $isLast = ($key === array_key_last($list)); ?>
|
||||||
<li class="breadcrumb-item<?php echo $isLast ? ' active' : ''; ?>" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem"
|
<li class="breadcrumb-item<?php echo $isLast ? ' active' : ''; ?>" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem"
|
||||||
<?php echo $isLast ? ' aria-current="page"' : ''; ?>>
|
<?php echo $isLast ? ' aria-current="page"' : ''; ?>>
|
||||||
<?php if (!$isLast && !empty($item->link)) : ?>
|
<?php if (!$isLast && !empty($item->link)) : ?>
|
||||||
|
|||||||
106
src/html/mod_menu/horizontal.php
Normal file
106
src/html/mod_menu/horizontal.php
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main Menu - Mobile responsive collapsible dropdown menu override
|
||||||
|
* Bootstrap 5 responsive navbar with hamburger menu
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('_JEXEC') or die;
|
||||||
|
|
||||||
|
use Joomla\CMS\Helper\ModuleHelper;
|
||||||
|
|
||||||
|
$id = '';
|
||||||
|
|
||||||
|
if ($tagId = $params->get('tag_id', '')) {
|
||||||
|
$id = ' id="' . $tagId . '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get module class suffix
|
||||||
|
$moduleclass_sfx = htmlspecialchars($params->get('moduleclass_sfx', ''), ENT_COMPAT, 'UTF-8');
|
||||||
|
|
||||||
|
// The menu class is deprecated. Use mod-menu instead
|
||||||
|
?>
|
||||||
|
<nav class="mod-menu mod-menu-main navbar navbar-expand-lg<?php echo $moduleclass_sfx; ?>"<?php echo $id; ?>>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<!-- Hamburger toggle button for mobile -->
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#horizontalMenuCollapse-<?php echo $module->id; ?>" aria-controls="horizontalMenuCollapse-<?php echo $module->id; ?>" aria-expanded="false" aria-label="Toggle Menu">
|
||||||
|
<span class="fa-solid fa-bars" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Collapsible menu content -->
|
||||||
|
<div class="collapse navbar-collapse" id="horizontalMenuCollapse-<?php echo $module->id; ?>">
|
||||||
|
<ul class="navbar-nav mod-menu-main__list">
|
||||||
|
<?php foreach ($list as $i => &$item) :
|
||||||
|
$itemParams = $item->getParams();
|
||||||
|
$class = 'nav-item mod-menu-main__item item-' . $item->id;
|
||||||
|
|
||||||
|
if ($item->id == $default_id) {
|
||||||
|
$class .= ' default';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($item->id == $active_id || ($item->type === 'alias' && $itemParams->get('aliasoptions') == $active_id)) {
|
||||||
|
$class .= ' current';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($item->id, $path)) {
|
||||||
|
$class .= ' active';
|
||||||
|
} elseif ($item->type === 'alias') {
|
||||||
|
$aliasToId = $itemParams->get('aliasoptions');
|
||||||
|
|
||||||
|
if (count($path) > 0 && $aliasToId == $path[count($path) - 1]) {
|
||||||
|
$class .= ' active';
|
||||||
|
} elseif (in_array($aliasToId, $path)) {
|
||||||
|
$class .= ' alias-parent-active';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($item->type === 'separator') {
|
||||||
|
$class .= ' divider';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($item->deeper) {
|
||||||
|
$class .= ' deeper dropdown';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($item->parent) {
|
||||||
|
$class .= ' parent';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<li class="' . $class . '">';
|
||||||
|
|
||||||
|
switch ($item->type) :
|
||||||
|
case 'separator':
|
||||||
|
case 'component':
|
||||||
|
case 'heading':
|
||||||
|
case 'url':
|
||||||
|
require ModuleHelper::getLayoutPath('mod_menu', 'horizontal_' . $item->type);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
require ModuleHelper::getLayoutPath('mod_menu', 'horizontal_url');
|
||||||
|
break;
|
||||||
|
endswitch;
|
||||||
|
|
||||||
|
// The next item is deeper.
|
||||||
|
if ($item->deeper) {
|
||||||
|
echo '<ul class="dropdown-menu mod-menu-main__dropdown">';
|
||||||
|
} elseif ($item->shallower) {
|
||||||
|
// The next item is shallower.
|
||||||
|
echo '</li>';
|
||||||
|
echo str_repeat('</ul></li>', $item->level_diff);
|
||||||
|
} else {
|
||||||
|
// The next item is on the same level.
|
||||||
|
echo '</li>';
|
||||||
|
}
|
||||||
|
endforeach;
|
||||||
|
?></ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
66
src/html/mod_menu/horizontal_component.php
Normal file
66
src/html/mod_menu/horizontal_component.php
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main Menu - Component item layout
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('_JEXEC') or die;
|
||||||
|
|
||||||
|
use Joomla\CMS\Filter\OutputFilter;
|
||||||
|
use Joomla\CMS\HTML\HTMLHelper;
|
||||||
|
|
||||||
|
$attributes = [];
|
||||||
|
|
||||||
|
if ($item->anchor_title) {
|
||||||
|
$attributes['title'] = $item->anchor_title;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($item->anchor_css) {
|
||||||
|
$attributes['class'] = $item->anchor_css;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($item->anchor_rel) {
|
||||||
|
$attributes['rel'] = $item->anchor_rel;
|
||||||
|
}
|
||||||
|
|
||||||
|
$linktype = $item->title;
|
||||||
|
|
||||||
|
if ($item->menu_icon) {
|
||||||
|
// The link is an icon
|
||||||
|
if ($itemParams->get('menu_text', 1)) {
|
||||||
|
// If the link text is to be displayed, the icon is added with aria-hidden
|
||||||
|
$linktype = '<span class="p-2 ' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||||
|
} else {
|
||||||
|
// If the icon itself is the link, it needs a visually hidden text
|
||||||
|
$linktype = '<span class="p-2 ' . $item->menu_icon . '" aria-hidden="true"></span><span class="visually-hidden">' . $item->title . '</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($item->browserNav == 1) {
|
||||||
|
$attributes['target'] = '_blank';
|
||||||
|
$attributes['rel'] = 'noopener noreferrer';
|
||||||
|
} elseif ($item->browserNav == 2) {
|
||||||
|
$options = 'toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,' . $params->get('window_open');
|
||||||
|
|
||||||
|
$attributes['onclick'] = "window.open(this.href, 'targetWindow', '" . $options . "'); return false;";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add dropdown toggle for items with children
|
||||||
|
$linkClass = 'nav-link mod-menu-main__link';
|
||||||
|
if ($item->deeper) {
|
||||||
|
$linkClass .= ' dropdown-toggle';
|
||||||
|
$attributes['data-bs-toggle'] = 'dropdown';
|
||||||
|
$attributes['role'] = 'button';
|
||||||
|
$attributes['aria-expanded'] = 'false';
|
||||||
|
}
|
||||||
|
|
||||||
|
$attributes['class'] = $linkClass;
|
||||||
|
|
||||||
|
echo HTMLHelper::_('link', OutputFilter::ampReplace(htmlspecialchars($item->flink, ENT_COMPAT, 'UTF-8', false)), $linktype, $attributes);
|
||||||
39
src/html/mod_menu/horizontal_heading.php
Normal file
39
src/html/mod_menu/horizontal_heading.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main Menu - Heading item layout
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('_JEXEC') or die;
|
||||||
|
|
||||||
|
$title = $item->anchor_title ? ' title="' . $item->anchor_title . '"' : '';
|
||||||
|
$anchor_css = $item->anchor_css ?: '';
|
||||||
|
|
||||||
|
$linktype = $item->title;
|
||||||
|
|
||||||
|
if ($item->menu_icon) {
|
||||||
|
// The link is an icon
|
||||||
|
if ($itemParams->get('menu_text', 1)) {
|
||||||
|
// If the link text is to be displayed, the icon is added with aria-hidden
|
||||||
|
$linktype = '<span class="p-2 ' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||||
|
} else {
|
||||||
|
// If the icon itself is the link, it needs a visually hidden text
|
||||||
|
$linktype = '<span class="p-2 ' . $item->menu_icon . '" aria-hidden="true"></span><span class="visually-hidden">' . $item->title . '</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add dropdown toggle for items with children
|
||||||
|
$headingClass = 'nav-link mod-menu-main__heading';
|
||||||
|
if ($item->deeper) {
|
||||||
|
$headingClass .= ' dropdown-toggle';
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
<span class="<?php echo $headingClass . ' ' . $anchor_css; ?>"<?php echo $title; ?>><?php echo $linktype; ?></span>
|
||||||
33
src/html/mod_menu/horizontal_separator.php
Normal file
33
src/html/mod_menu/horizontal_separator.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main Menu - Separator item layout
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('_JEXEC') or die;
|
||||||
|
|
||||||
|
$title = $item->anchor_title ? ' title="' . $item->anchor_title . '"' : '';
|
||||||
|
$anchor_css = $item->anchor_css ?: '';
|
||||||
|
|
||||||
|
$linktype = $item->title;
|
||||||
|
|
||||||
|
if ($item->menu_icon) {
|
||||||
|
// The link is an icon
|
||||||
|
if ($itemParams->get('menu_text', 1)) {
|
||||||
|
// If the link text is to be displayed, the icon is added with aria-hidden
|
||||||
|
$linktype = '<span class="p-2 ' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||||
|
} else {
|
||||||
|
// If the icon itself is the link, it needs a visually hidden text
|
||||||
|
$linktype = '<span class="p-2 ' . $item->menu_icon . '" aria-hidden="true"></span><span class="visually-hidden">' . $item->title . '</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
<span class="dropdown-divider mod-menu-main__separator <?php echo $anchor_css; ?>"<?php echo $title; ?>><?php echo $linktype; ?></span>
|
||||||
71
src/html/mod_menu/horizontal_url.php
Normal file
71
src/html/mod_menu/horizontal_url.php
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main Menu - URL item layout
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('_JEXEC') or die;
|
||||||
|
|
||||||
|
use Joomla\CMS\Filter\OutputFilter;
|
||||||
|
use Joomla\CMS\HTML\HTMLHelper;
|
||||||
|
|
||||||
|
$attributes = [];
|
||||||
|
|
||||||
|
if ($item->anchor_title) {
|
||||||
|
$attributes['title'] = $item->anchor_title;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($item->anchor_css) {
|
||||||
|
$attributes['class'] = $item->anchor_css;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($item->anchor_rel) {
|
||||||
|
$attributes['rel'] = $item->anchor_rel;
|
||||||
|
}
|
||||||
|
|
||||||
|
$linktype = $item->title;
|
||||||
|
|
||||||
|
if ($item->menu_icon) {
|
||||||
|
// The link is an icon
|
||||||
|
if ($itemParams->get('menu_text', 1)) {
|
||||||
|
// If the link text is to be displayed, the icon is added with aria-hidden
|
||||||
|
$linktype = '<span class="p-2 ' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||||
|
} else {
|
||||||
|
// If the icon itself is the link, it needs a visually hidden text
|
||||||
|
$linktype = '<span class="p-2 ' . $item->menu_icon . '" aria-hidden="true"></span><span class="visually-hidden">' . $item->title . '</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($item->browserNav == 1) {
|
||||||
|
$attributes['target'] = '_blank';
|
||||||
|
$attributes['rel'] = 'noopener noreferrer';
|
||||||
|
} elseif ($item->browserNav == 2) {
|
||||||
|
$options = 'toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,' . $params->get('window_open');
|
||||||
|
|
||||||
|
$attributes['onclick'] = "window.open(this.href, 'targetWindow', '" . $options . "'); return false;";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add dropdown toggle for items with children
|
||||||
|
$linkClass = 'nav-link mod-menu-main__link';
|
||||||
|
if ($item->deeper) {
|
||||||
|
$linkClass .= ' dropdown-toggle';
|
||||||
|
$attributes['data-bs-toggle'] = 'dropdown';
|
||||||
|
$attributes['role'] = 'button';
|
||||||
|
$attributes['aria-expanded'] = 'false';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge existing class with our class
|
||||||
|
if (isset($attributes['class'])) {
|
||||||
|
$attributes['class'] .= ' ' . $linkClass;
|
||||||
|
} else {
|
||||||
|
$attributes['class'] = $linkClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo HTMLHelper::_('link', OutputFilter::ampReplace(htmlspecialchars($item->flink, ENT_COMPAT, 'UTF-8', false)), $linktype, $attributes);
|
||||||
@@ -30,12 +30,12 @@ $moduleclass_sfx = htmlspecialchars($params->get('moduleclass_sfx', ''), ENT_COM
|
|||||||
<nav class="mod-menu mod-menu-main navbar navbar-expand-lg<?php echo $moduleclass_sfx; ?>"<?php echo $id; ?>>
|
<nav class="mod-menu mod-menu-main navbar navbar-expand-lg<?php echo $moduleclass_sfx; ?>"<?php echo $id; ?>>
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<!-- Hamburger toggle button for mobile -->
|
<!-- Hamburger toggle button for mobile -->
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainMenuCollapse" aria-controls="mainMenuCollapse" aria-expanded="false" aria-label="Toggle Main Menu">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainMenuCollapse-<?php echo $module->id; ?>" aria-controls="mainMenuCollapse-<?php echo $module->id; ?>" aria-expanded="false" aria-label="Toggle Main Menu">
|
||||||
<span class="fa-solid fa-bars" aria-hidden="true"></span>
|
<span class="fa-solid fa-bars" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Collapsible menu content -->
|
<!-- Collapsible menu content -->
|
||||||
<div class="collapse navbar-collapse" id="mainMenuCollapse">
|
<div class="collapse navbar-collapse" id="mainMenuCollapse-<?php echo $module->id; ?>">
|
||||||
<ul class="navbar-nav mod-menu-main__list">
|
<ul class="navbar-nav mod-menu-main__list">
|
||||||
<?php foreach ($list as $i => &$item) :
|
<?php foreach ($list as $i => &$item) :
|
||||||
$itemParams = $item->getParams();
|
$itemParams = $item->getParams();
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* IMPORTANT: Font files must be downloaded separately
|
|
||||||
*
|
|
||||||
* This CSS file references Fira Sans font files that must be manually downloaded
|
|
||||||
* and placed in the fonts directory. See GOOGLE_FONTS_README.md in the fonts
|
|
||||||
* directory for download instructions.
|
|
||||||
*
|
|
||||||
* Required files:
|
|
||||||
* - fira-sans-v17-latin-100.woff2
|
|
||||||
* - fira-sans-v17-latin-300.woff2
|
|
||||||
* - fira-sans-v17-latin-regular.woff2
|
|
||||||
* - fira-sans-v17-latin-700.woff2
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Fira Sans Thin (100) */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Fira Sans';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 100;
|
|
||||||
font-display: swap;
|
|
||||||
src: url('../../fonts/fira-sans-v17-latin-100.woff2') format('woff2');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fira Sans Light (300) */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Fira Sans';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: url('../../fonts/fira-sans-v17-latin-300.woff2') format('woff2');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fira Sans Regular (400) */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Fira Sans';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: url('../../fonts/fira-sans-v17-latin-regular.woff2') format('woff2');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fira Sans Bold (700) */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Fira Sans';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: swap;
|
|
||||||
src: url('../../fonts/fira-sans-v17-latin-700.woff2') format('woff2');
|
|
||||||
}
|
|
||||||
10
src/media/css/fonts/fredoka.css
Normal file
10
src/media/css/fonts/fredoka.css
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
* Fredoka — self-hosted from src/media/fonts/
|
||||||
|
*/
|
||||||
|
|
||||||
|
@font-face { font-family: 'Fredoka'; font-style: normal; font-weight: 300; font-display: swap; src: url('../../fonts/fredoka-v17-latin-300.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Fredoka'; font-style: normal; font-weight: 400; font-display: swap; src: url('../../fonts/fredoka-v17-latin-regular.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Fredoka'; font-style: normal; font-weight: 500; font-display: swap; src: url('../../fonts/fredoka-v17-latin-500.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Fredoka'; font-style: normal; font-weight: 600; font-display: swap; src: url('../../fonts/fredoka-v17-latin-600.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Fredoka'; font-style: normal; font-weight: 700; font-display: swap; src: url('../../fonts/fredoka-v17-latin-700.woff2') format('woff2'); }
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* IMPORTANT: Font files must be downloaded separately
|
|
||||||
*
|
|
||||||
* This CSS file references Noto Sans font files that must be manually downloaded
|
|
||||||
* and placed in the fonts directory. See GOOGLE_FONTS_README.md in the fonts
|
|
||||||
* directory for download instructions.
|
|
||||||
*
|
|
||||||
* Required files:
|
|
||||||
* - noto-sans-v36-latin-100.woff2
|
|
||||||
* - noto-sans-v36-latin-300.woff2
|
|
||||||
* - noto-sans-v36-latin-regular.woff2
|
|
||||||
* - noto-sans-v36-latin-700.woff2
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Noto Sans Thin (100) */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Noto Sans';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 100;
|
|
||||||
font-display: swap;
|
|
||||||
src: url('../../fonts/noto-sans-v36-latin-100.woff2') format('woff2');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Noto Sans Light (300) */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Noto Sans';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: url('../../fonts/noto-sans-v36-latin-300.woff2') format('woff2');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Noto Sans Regular (400) */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Noto Sans';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: url('../../fonts/noto-sans-v36-latin-regular.woff2') format('woff2');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Noto Sans Bold (700) */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Noto Sans';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: swap;
|
|
||||||
src: url('../../fonts/noto-sans-v36-latin-700.woff2') format('woff2');
|
|
||||||
}
|
|
||||||
6
src/media/css/fonts/pacifico.css
Normal file
6
src/media/css/fonts/pacifico.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
* Pacifico — self-hosted from src/media/fonts/
|
||||||
|
*/
|
||||||
|
|
||||||
|
@font-face { font-family: 'Pacifico'; font-style: normal; font-weight: 400; font-display: swap; src: url('../../fonts/pacifico-v23-latin-regular.woff2') format('woff2'); }
|
||||||
@@ -1,56 +1,23 @@
|
|||||||
/* Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
This file is part of a Moko Consulting project.
|
* Roboto — self-hosted from src/media/fonts/
|
||||||
|
|
||||||
SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
@font-face { font-family: 'Roboto'; font-style: normal; font-weight: 100; font-display: swap; src: url('../../fonts/roboto-v51-latin-100.woff2') format('woff2'); }
|
||||||
* IMPORTANT: Font files must be downloaded separately
|
@font-face { font-family: 'Roboto'; font-style: italic; font-weight: 100; font-display: swap; src: url('../../fonts/roboto-v51-latin-100italic.woff2') format('woff2'); }
|
||||||
*
|
@font-face { font-family: 'Roboto'; font-style: normal; font-weight: 200; font-display: swap; src: url('../../fonts/roboto-v51-latin-200.woff2') format('woff2'); }
|
||||||
* This CSS file references Roboto font files that must be manually downloaded
|
@font-face { font-family: 'Roboto'; font-style: italic; font-weight: 200; font-display: swap; src: url('../../fonts/roboto-v51-latin-200italic.woff2') format('woff2'); }
|
||||||
* and placed in the fonts directory. See GOOGLE_FONTS_README.md in the fonts
|
@font-face { font-family: 'Roboto'; font-style: normal; font-weight: 300; font-display: swap; src: url('../../fonts/roboto-v51-latin-300.woff2') format('woff2'); }
|
||||||
* directory for download instructions.
|
@font-face { font-family: 'Roboto'; font-style: italic; font-weight: 300; font-display: swap; src: url('../../fonts/roboto-v51-latin-300italic.woff2') format('woff2'); }
|
||||||
*
|
@font-face { font-family: 'Roboto'; font-style: normal; font-weight: 400; font-display: swap; src: url('../../fonts/roboto-v51-latin-regular.woff2') format('woff2'); }
|
||||||
* Required files:
|
@font-face { font-family: 'Roboto'; font-style: italic; font-weight: 400; font-display: swap; src: url('../../fonts/roboto-v51-latin-italic.woff2') format('woff2'); }
|
||||||
* - roboto-v30-latin-100.woff2
|
@font-face { font-family: 'Roboto'; font-style: normal; font-weight: 500; font-display: swap; src: url('../../fonts/roboto-v51-latin-500.woff2') format('woff2'); }
|
||||||
* - roboto-v30-latin-300.woff2
|
@font-face { font-family: 'Roboto'; font-style: italic; font-weight: 500; font-display: swap; src: url('../../fonts/roboto-v51-latin-500italic.woff2') format('woff2'); }
|
||||||
* - roboto-v30-latin-regular.woff2
|
@font-face { font-family: 'Roboto'; font-style: normal; font-weight: 600; font-display: swap; src: url('../../fonts/roboto-v51-latin-600.woff2') format('woff2'); }
|
||||||
* - roboto-v30-latin-700.woff2
|
@font-face { font-family: 'Roboto'; font-style: italic; font-weight: 600; font-display: swap; src: url('../../fonts/roboto-v51-latin-600italic.woff2') format('woff2'); }
|
||||||
*/
|
@font-face { font-family: 'Roboto'; font-style: normal; font-weight: 700; font-display: swap; src: url('../../fonts/roboto-v51-latin-700.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Roboto'; font-style: italic; font-weight: 700; font-display: swap; src: url('../../fonts/roboto-v51-latin-700italic.woff2') format('woff2'); }
|
||||||
/* Roboto Thin (100) */
|
@font-face { font-family: 'Roboto'; font-style: normal; font-weight: 800; font-display: swap; src: url('../../fonts/roboto-v51-latin-800.woff2') format('woff2'); }
|
||||||
@font-face {
|
@font-face { font-family: 'Roboto'; font-style: italic; font-weight: 800; font-display: swap; src: url('../../fonts/roboto-v51-latin-800italic.woff2') format('woff2'); }
|
||||||
font-family: 'Roboto';
|
@font-face { font-family: 'Roboto'; font-style: normal; font-weight: 900; font-display: swap; src: url('../../fonts/roboto-v51-latin-900.woff2') format('woff2'); }
|
||||||
font-style: normal;
|
@font-face { font-family: 'Roboto'; font-style: italic; font-weight: 900; font-display: swap; src: url('../../fonts/roboto-v51-latin-900italic.woff2') format('woff2'); }
|
||||||
font-weight: 100;
|
|
||||||
font-display: swap;
|
|
||||||
src: url('../../fonts/roboto-v30-latin-100.woff2') format('woff2');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Roboto Light (300) */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Roboto';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: url('../../fonts/roboto-v30-latin-300.woff2') format('woff2');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Roboto Regular (400) */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Roboto';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: url('../../fonts/roboto-v30-latin-regular.woff2') format('woff2');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Roboto Bold (700) */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Roboto';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: swap;
|
|
||||||
src: url('../../fonts/roboto-v30-latin-700.woff2') format('woff2');
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13987,21 +13987,10 @@ meter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
margin-top: 1em;
|
padding-top: 1rem;
|
||||||
color: var(--body-bg, #e6ebf1);
|
color: var(--body-bg, #e6ebf1);
|
||||||
background-color: var(--nav-bg-color);
|
background-color: var(--nav-bg-color);
|
||||||
padding-left: 100px;
|
padding-bottom: 80px;
|
||||||
padding-right: 60px;
|
|
||||||
padding-bottom: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Increase footer right padding when floating controls are present */
|
|
||||||
body[data-theme-fab-enabled="1"] .footer {
|
|
||||||
padding-right: 220px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body[data-theme-fab-enabled="1"][data-a11y-toolbar="1"] .footer {
|
|
||||||
padding-right: 420px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer .grid-child {
|
.footer .grid-child {
|
||||||
@@ -14624,6 +14613,10 @@ iframe {
|
|||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Module title alignment — apply via module class suffix */
|
||||||
|
.title-center [class*="__title"] { text-align: center; }
|
||||||
|
.title-right [class*="__title"] { text-align: right; }
|
||||||
|
|
||||||
/* ── MODULE: Statistics ── */
|
/* ── MODULE: Statistics ── */
|
||||||
.mod-stats__list {
|
.mod-stats__list {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -14760,6 +14753,21 @@ iframe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Breadcrumbs module */
|
/* Breadcrumbs module */
|
||||||
|
.mod-breadcrumbs {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod-breadcrumbs__here {
|
||||||
|
float: left;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
color: var(--body-font-color, #444);
|
||||||
|
padding-right: .15rem;
|
||||||
|
}
|
||||||
|
|
||||||
.mod-breadcrumbs .breadcrumb {
|
.mod-breadcrumbs .breadcrumb {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -14853,8 +14861,10 @@ iframe {
|
|||||||
.container-top-a,
|
.container-top-a,
|
||||||
.container-top-b,
|
.container-top-b,
|
||||||
.container-bottom-a,
|
.container-bottom-a,
|
||||||
.container-bottom-b {
|
.container-bottom-b,
|
||||||
|
.mod-breadcrumbs {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container-top-a>*,
|
.container-top-a>*,
|
||||||
@@ -15771,7 +15781,7 @@ body.wrapper-fluid header>.grid-child {
|
|||||||
}
|
}
|
||||||
|
|
||||||
footer .grid-child>div {
|
footer .grid-child>div {
|
||||||
padding: 1rem 4em;
|
padding: 1rem 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
header .grid-child .navbar-brand {
|
header .grid-child .navbar-brand {
|
||||||
|
|||||||
1143
src/media/css/theme/dark.custom.css
Normal file
1143
src/media/css/theme/dark.custom.css
Normal file
File diff suppressed because it is too large
Load Diff
1152
src/media/css/theme/light.custom.css
Normal file
1152
src/media/css/theme/light.custom.css
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,131 +1,31 @@
|
|||||||
<!--
|
<!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||||
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: GPL-3.0-or-later
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
VERSION: 03.09.10
|
||||||
# FILE INFORMATION
|
|
||||||
DEFGROUP: Joomla.Template.Site
|
|
||||||
INGROUP: MokoCassiopeia.Documentation
|
|
||||||
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
|
||||||
FILE: src/media/fonts/GOOGLE_FONTS_README.md
|
|
||||||
VERSION: 03.09.03
|
|
||||||
BRIEF: Instructions for downloading Google Fonts for self-hosting
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
# Google Fonts - Download Instructions
|
# Self-Hosted Google Fonts
|
||||||
|
|
||||||
This directory should contain self-hosted Google Font files to eliminate CDN dependencies.
|
Fonts are served locally to avoid external CDN dependencies and improve privacy/performance.
|
||||||
|
|
||||||
## ⚠️ Manual Download Required
|
## Available Fonts
|
||||||
|
|
||||||
The Google Font `.woff2` files are **NOT included** in the repository and must be downloaded manually before using non-default font schemes.
|
| Font | Weights | Styles | CSS File |
|
||||||
|
|------|---------|--------|----------|
|
||||||
|
| Roboto | 100–900 | Normal + Italic | `css/fonts/roboto.css` |
|
||||||
|
| Fredoka | 300–700 | Normal | `css/fonts/fredoka.css` |
|
||||||
|
| Pacifico | 400 | Normal | `css/fonts/pacifico.css` |
|
||||||
|
| Osaka | — | — | `css/fonts/osaka.css` |
|
||||||
|
|
||||||
**Currently Available:**
|
## Adding New Fonts
|
||||||
- ✅ Osaka font (local TTF file, included)
|
|
||||||
|
|
||||||
**Requires Manual Download:**
|
1. Run `php scripts/download-google-fonts.php` or manually download woff2 files
|
||||||
- ❌ Roboto fonts (4 weight variants)
|
2. Place files in `src/media/fonts/`
|
||||||
- ❌ Noto Sans fonts (4 weight variants)
|
3. Create a CSS file in `src/media/css/fonts/` with `@font-face` declarations
|
||||||
- ❌ Fira Sans fonts (4 weight variants)
|
4. Register the CSS in `joomla.asset.json`
|
||||||
|
5. Add the font as an option in `templateDetails.xml` font selector
|
||||||
|
|
||||||
## Required Font Files
|
## File Naming Convention
|
||||||
|
|
||||||
Download the following `.woff2` font files and place them in this directory:
|
`{font-name}-v{version}-latin-{weight}.woff2`
|
||||||
|
|
||||||
### Roboto Font Files
|
Examples: `roboto-v51-latin-regular.woff2`, `fredoka-v17-latin-700.woff2`
|
||||||
- `roboto-v30-latin-100.woff2` (Thin)
|
|
||||||
- `roboto-v30-latin-300.woff2` (Light)
|
|
||||||
- `roboto-v30-latin-regular.woff2` (Regular)
|
|
||||||
- `roboto-v30-latin-700.woff2` (Bold)
|
|
||||||
|
|
||||||
### Noto Sans Font Files
|
|
||||||
- `noto-sans-v36-latin-100.woff2` (Thin)
|
|
||||||
- `noto-sans-v36-latin-300.woff2` (Light)
|
|
||||||
- `noto-sans-v36-latin-regular.woff2` (Regular)
|
|
||||||
- `noto-sans-v36-latin-700.woff2` (Bold)
|
|
||||||
|
|
||||||
### Fira Sans Font Files
|
|
||||||
- `fira-sans-v17-latin-100.woff2` (Thin)
|
|
||||||
- `fira-sans-v17-latin-300.woff2` (Light)
|
|
||||||
- `fira-sans-v17-latin-regular.woff2` (Regular)
|
|
||||||
- `fira-sans-v17-latin-700.woff2` (Bold)
|
|
||||||
|
|
||||||
## How to Download
|
|
||||||
|
|
||||||
### Option 1: Using google-webfonts-helper (Recommended)
|
|
||||||
|
|
||||||
1. Visit https://gwfh.mranftl.com/
|
|
||||||
2. Search for each font (Roboto, Noto Sans, Fira Sans)
|
|
||||||
3. Select character sets: **latin** (or add latin-ext if needed)
|
|
||||||
4. Select styles:
|
|
||||||
- ☑ 100 (thin)
|
|
||||||
- ☑ 300 (light)
|
|
||||||
- ☑ 400 (regular)
|
|
||||||
- ☑ 700 (bold)
|
|
||||||
5. In step 3, ensure **Modern Browsers** is selected (woff2 format)
|
|
||||||
6. In step 4, click **Download files**
|
|
||||||
7. Extract the `.woff2` files to this directory
|
|
||||||
|
|
||||||
### Option 2: Using google-font-installer (Node.js)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install -g google-font-installer
|
|
||||||
cd src/media/fonts/
|
|
||||||
|
|
||||||
# Download Roboto
|
|
||||||
google-font-installer Roboto:100,300,400,700
|
|
||||||
|
|
||||||
# Download Noto Sans
|
|
||||||
google-font-installer "Noto Sans:100,300,400,700"
|
|
||||||
|
|
||||||
# Download Fira Sans
|
|
||||||
google-font-installer "Fira Sans:100,300,400,700"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option 3: Manual Download Script (Linux/macOS)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#!/bin/bash
|
|
||||||
# Run this from src/media/fonts/ directory
|
|
||||||
|
|
||||||
download_font() {
|
|
||||||
local font_url="$1"
|
|
||||||
local output_dir="."
|
|
||||||
|
|
||||||
# Download CSS
|
|
||||||
css=$(curl -s -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" "$font_url")
|
|
||||||
|
|
||||||
# Extract and download woff2 files
|
|
||||||
echo "$css" | grep -oP 'https://fonts\.gstatic\.com[^\)]*\.woff2' | while read url; do
|
|
||||||
filename=$(basename "$url")
|
|
||||||
echo "Downloading $filename..."
|
|
||||||
curl -s "$url" -o "$output_dir/$filename"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
download_font "https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;700&display=swap"
|
|
||||||
download_font "https://fonts.googleapis.com/css2?family=Noto+Sans:wght@100;300;400;700&display=swap"
|
|
||||||
download_font "https://fonts.googleapis.com/css2?family=Fira+Sans:wght@100;300;400;700&display=swap"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Font CSS Files
|
|
||||||
|
|
||||||
The corresponding CSS files with `@font-face` declarations are located in:
|
|
||||||
- `../css/fonts/roboto.css`
|
|
||||||
- `../css/fonts/noto-sans.css`
|
|
||||||
- `../css/fonts/fira-sans.css`
|
|
||||||
|
|
||||||
These CSS files reference the `.woff2` files in this directory.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
All Google Fonts are open source and licensed under the SIL Open Font License (OFL).
|
|
||||||
- Roboto: Apache License 2.0
|
|
||||||
- Noto Sans: SIL Open Font License 1.1
|
|
||||||
- Fira Sans: SIL Open Font License 1.1
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- Google Fonts: https://fonts.google.com/
|
|
||||||
- google-webfonts-helper: https://gwfh.mranftl.com/
|
|
||||||
- Font Licensing: https://fonts.google.com/attribution
|
|
||||||
|
|||||||
@@ -36,13 +36,13 @@
|
|||||||
</server>
|
</server>
|
||||||
</updateservers>
|
</updateservers>
|
||||||
<name>MokoCassiopeia</name>
|
<name>MokoCassiopeia</name>
|
||||||
<version>03.09.09</version>
|
<version>03.09.12</version>
|
||||||
<scriptfile>script.php</scriptfile>
|
<scriptfile>script.php</scriptfile>
|
||||||
<creationDate>2026-03-26</creationDate>
|
<creationDate>2026-03-26</creationDate>
|
||||||
<author>Jonathan Miller || Moko Consulting</author>
|
<author>Jonathan Miller || Moko Consulting</author>
|
||||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||||
<copyright>(C)GNU General Public License Version 3 - 2026 Moko Consulting</copyright>
|
<copyright>(C)GNU General Public License Version 3 - 2026 Moko Consulting</copyright>
|
||||||
<description><![CDATA[<h3>MokoCassiopeia Template Description</h3> <p> <strong>MokoCassiopeia</strong> continues Joomla's tradition of space-themed default templates— building on the legacy of <em>Solarflare</em> (Joomla 1.0), <em>Milkyway</em> (Joomla 1.5), and <em>Protostar</em> (Joomla 3.0). </p> <p> This template is a customized fork of the <strong>Cassiopeia</strong> template introduced in Joomla 4, preserving its modern, accessible, and mobile-first foundation while introducing new stylistic enhancements and structural refinements specifically tailored for use by Moko Consulting. </p> <h4>Custom Colour Themes</h4> <p> Starter palette files are included with the template. To create a custom colour scheme, copy <code>templates/mokocassiopeia/templates/light.custom.css</code> to <code>media/templates/site/mokocassiopeia/css/theme/light.custom.css</code>, or <code>templates/mokocassiopeia/templates/dark.custom.css</code> to <code>media/templates/site/mokocassiopeia/css/theme/dark.custom.css</code>. Customise the CSS variables to match your brand, then activate your palette in <em>System → Site Templates → MokoCassiopeia → Theme tab</em> by selecting "Custom" for the Light or Dark Mode Palette. A full variable reference is available in the <em>CSS Variables</em> tab in template options. </p> <h4>Custom CSS & JavaScript</h4> <p> For site-specific styles and scripts that should survive template updates, create the following files: </p> <ul> <li><code>media/templates/site/mokocassiopeia/css/user.css</code> — loaded on every page for custom CSS overrides.</li> <li><code>media/templates/site/mokocassiopeia/js/user.js</code> — loaded on every page for custom JavaScript.</li> </ul> <p> These files are gitignored and will not be overwritten by template updates. </p> <h4>Code Attribution</h4> <p> This template is based on the original <strong>Cassiopeia</strong> template developed by the <a href="https://www.joomla.org" target="_blank" rel="noopener">Joomla! Project</a> and released under the GNU General Public License. </p> <p> Modifications and enhancements have been made by Moko Consulting in accordance with open-source licensing standards. </p> <p> It includes integration with <a href="https://afeld.github.io/bootstrap-toc/" target="_blank" rel="noopener">Bootstrap TOC</a>, an open-source table of contents generator by A. Feld, licensed under the MIT License. </p> <p> All third-party libraries and assets remain the property of their respective authors and are credited within their source files where applicable. </p>]]></description>
|
<description><![CDATA[<p><img src="https://img.shields.io/badge/version-03.09.12-blue.svg?logo=v&logoColor=white" alt="Version 03.09.12" /> <img src="https://img.shields.io/badge/license-GPL--3.0--or--later-green.svg?logo=gnu&logoColor=white" alt="License" /> <img src="https://img.shields.io/badge/Joomla-5.x%20%7C%206.x-red.svg?logo=joomla&logoColor=white" alt="Joomla" /> <img src="https://img.shields.io/badge/PHP-8.1%2B-777BB4.svg?logo=php&logoColor=white" alt="PHP" /></p> <h3>MokoCassiopeia Template Description</h3> <p> <strong>MokoCassiopeia</strong> continues Joomla's tradition of space-themed default templates— building on the legacy of <em>Solarflare</em> (Joomla 1.0), <em>Milkyway</em> (Joomla 1.5), and <em>Protostar</em> (Joomla 3.0). </p> <p> This template is a customized fork of the <strong>Cassiopeia</strong> template introduced in Joomla 4, preserving its modern, accessible, and mobile-first foundation while introducing new stylistic enhancements and structural refinements specifically tailored for use by Moko Consulting. </p> <h4>Custom Colour Themes</h4> <p> Starter palette files are included with the template. To create a custom colour scheme, copy <code>templates/mokocassiopeia/templates/light.custom.css</code> to <code>media/templates/site/mokocassiopeia/css/theme/light.custom.css</code>, or <code>templates/mokocassiopeia/templates/dark.custom.css</code> to <code>media/templates/site/mokocassiopeia/css/theme/dark.custom.css</code>. Customise the CSS variables to match your brand, then activate your palette in <em>System → Site Templates → MokoCassiopeia → Theme tab</em> by selecting "Custom" for the Light or Dark Mode Palette. A full variable reference is available in the <em>CSS Variables</em> tab in template options. </p> <h4>Custom CSS & JavaScript</h4> <p> For site-specific styles and scripts that should survive template updates, create the following files: </p> <ul> <li><code>media/templates/site/mokocassiopeia/css/user.css</code> — loaded on every page for custom CSS overrides.</li> <li><code>media/templates/site/mokocassiopeia/js/user.js</code> — loaded on every page for custom JavaScript.</li> </ul> <p> These files are gitignored and will not be overwritten by template updates. </p> <h4>Code Attribution</h4> <p> This template is based on the original <strong>Cassiopeia</strong> template developed by the <a href="https://www.joomla.org" target="_blank" rel="noopener">Joomla! Project</a> and released under the GNU General Public License. </p> <p> Modifications and enhancements have been made by Moko Consulting in accordance with open-source licensing standards. </p> <p> It includes integration with <a href="https://afeld.github.io/bootstrap-toc/" target="_blank" rel="noopener">Bootstrap TOC</a>, an open-source table of contents generator by A. Feld, licensed under the MIT License. </p> <p> All third-party libraries and assets remain the property of their respective authors and are credited within their source files where applicable. </p>]]></description>
|
||||||
<inheritable>1</inheritable>
|
<inheritable>1</inheritable>
|
||||||
<files>
|
<files>
|
||||||
<filename>component.php</filename>
|
<filename>component.php</filename>
|
||||||
|
|||||||
Reference in New Issue
Block a user