Vor einiger Zeit wurde in der Joomla Community gefragt, ob man ein Offcanvas-Menü im Cassiopeia Template einbauen kann. Ich habe dann auf die Schnelle ein bisschen Code geschrieben (https://gist.github.com/drmenzelit/152a1954d73bcbe126194965e43c97f4), das prinzipiell funktioniert. Aber es kamen immer mehr Fragen, Wünsche und Verbesserungsideen dazu.

In diesem Beitrag stelle ich eine verbesserte Version des Offcanvas-Menüs vor, mit Logo und eine extra Modulposition. Es gibt zwei Varianten vom Menü:

  • offcanvas.php: für ein einfaches Menü ohne Unterpunkte
  • offcanvas-metismenü.php: für ein Dropdown Menü

In beiden Varianten ist jetzt die Besonderheit, dass das Logo aus dem Template und Module, die in der Position offcanvas veröffentlicht sind, im Offcanvas-Menü angezeigt werden. Die Klasse d-lg-none sorgt dafür, dass diese Elemente nur im Offcanvas Modus angezeigt werden. Die Klasse ist passend zur navbar-expand-lg gewählt. Diese Klassen können natürlich beliebig geändert werden.

Die Dateien werden unter templates/cassiopeia/html/mod_menu/ angelegt.

Einfaches Offcanvas

offcanvas.php

Im Menü Modul das Layout offcanvas wählen.

<?php
/**
 * @package     Joomla.Site
 * @subpackage  mod_menu
 *
 * @copyright   (C) 2021 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Helper\ModuleHelper;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Uri\Uri;

HTMLHelper::_('bootstrap.offcanvas');

$app = Factory::getApplication('site');
$template = $app->getTemplate(true);
$sitename = htmlspecialchars($app->get('sitename'), ENT_QUOTES, 'UTF-8');

if ($template->params->get('logoFile')) {
    $logo = HTMLHelper::_('image', Uri::root(false) . htmlspecialchars($template->params->get('logoFile'), ENT_QUOTES), $sitename, ['loading' => 'eager', 'decoding' => 'async'], false, 0);
} elseif ($template->params->get('siteTitle')) {
    $logo = '<span title="' . $sitename . '">' . htmlspecialchars($template->params->get('siteTitle'), ENT_COMPAT, 'UTF-8') . '</span>';
} else {
    $logo = HTMLHelper::_('image', 'logo.svg', $sitename, ['class' => 'logo d-inline-block', 'loading' => 'eager', 'decoding' => 'async'], true, 0);
}

?>

<nav class="navbar navbar-expand-lg">
    <button class="navbar-toggler navbar-toggler-right" type="button" data-bs-toggle="offcanvas" data-bs-target="#navbar<?php echo $module->id; ?>" aria-controls="navbar<?php echo $module->id; ?>" aria-expanded="false" aria-label="<?php echo Text::_('MOD_MENU_TOGGLE'); ?>">
        <span class="icon-menu" aria-hidden="true"></span>
    </button>
    <div class="offcanvas offcanvas-start" id="navbar<?php echo $module->id; ?>">
        <div class="offcanvas-header">
            <button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas" aria-label="Close"></button>
        </div>
        <div class="offcanvas-body">
            <div class="d-lg-none mb-3">
                <a class="brand-logo" href="/<?php echo Uri::root(true); ?>/">
                    <?php echo $logo; ?>
                </a>
            </div>

            <?php require ModuleHelper::getLayoutPath('mod_menu', 'default'); ?>

            <div class="d-lg-none mt-3">
                <?php
                    $modules = ModuleHelper::getModules('offcanvas');
                    foreach ($modules as $module) {
                        echo ModuleHelper::renderModule($module, []);
                    }
                ?>
            </div>
        </div>
    </div>
</nav>

Desktop Ansicht:

Normales Menü Desktop

Mobile Ansicht:

Mobile Ansicht des Menüs

Mobile Ansicht geöffnet:

Mobiles Menü geöffnet

In der Position offcanvas habe ich das Suchindex Modul veröffentlicht:

Suchmodul in Position offcanvas

Dropdown Offcanvas

offcanvas-metismenu.php

Im Menü Modul das Layout offcanvas-metismenu wählen.

<?php
/**
 * @package     Joomla.Site
 * @subpackage  mod_menu
 *
 * @copyright   (C) 2021 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Helper\ModuleHelper;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Uri\Uri;

HTMLHelper::_('bootstrap.offcanvas');

$app = Factory::getApplication('site');
$template = $app->getTemplate(true);
$sitename = htmlspecialchars($app->get('sitename'), ENT_QUOTES, 'UTF-8');

if ($template->params->get('logoFile')) {
    $logo = HTMLHelper::_('image', Uri::root(false) . htmlspecialchars($template->params->get('logoFile'), ENT_QUOTES), $sitename, ['loading' => 'eager', 'decoding' => 'async'], false, 0);
} elseif ($template->params->get('siteTitle')) {
    $logo = '<span title="' . $sitename . '">' . htmlspecialchars($template->params->get('siteTitle'), ENT_COMPAT, 'UTF-8') . '</span>';
} else {
    $logo = HTMLHelper::_('image', 'logo.svg', $sitename, ['class' => 'logo d-inline-block', 'loading' => 'eager', 'decoding' => 'async'], true, 0);
}

?>
<nav class="navbar navbar-expand-lg">
    <button class="navbar-toggler navbar-toggler-right" type="button" data-bs-toggle="offcanvas" data-bs-target="#navbar<?php echo $module->id; ?>" aria-controls="navbar<?php echo $module->id; ?>" aria-expanded="false" aria-label="<?php echo Text::_('MOD_MENU_TOGGLE'); ?>">
        <span class="icon-menu" aria-hidden="true"></span>
    </button>
    <div class="offcanvas offcanvas-start" id="navbar<?php echo $module->id; ?>">
        <div class="offcanvas-header">
            <button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas" aria-label="Close"></button>
        </div>
        <div class="offcanvas-body">
            <div class="d-lg-none mb-3">
                <a class="brand-logo" href="/<?php echo Uri::root(true); ?>/">
                    <?php echo $logo; ?>
                </a>
            </div>

            <?php require __DIR__ . '/dropdown-metismenu.php'; ?>

            <div class="d-lg-none mt-3">
                <?php
                    $modules = ModuleHelper::getModules('offcanvas');
                    foreach ($modules as $module) {
                        echo ModuleHelper::renderModule($module, []);
                    }
                ?>
            </div>
        </div>
    </div>
</nav>

Mobile Ansicht mit Dropdown:

Offcanvas mit Drowdown

Das passende CSS dazu:

.offcanvas.show {
    background-color: var(--cassiopeia-color-primary);
    background-image: linear-gradient(135deg,var(--cassiopeia-color-primary),var(--cassiopeia-color-hover));
}
@media (min-width: 992px) {
    .offcanvas-start {
        width: 100%;
    }
}
@media (max-width: 991.98px) {
    .offcanvas .metismenu.mod-menu .metismenu-item > ul {
        position: relative;
        width: 100%;
        margin-top: 1rem;
    }
    .offcanvas .metismenu.mod-menu .mm-collapse {
        background-color: transparent;
    }
    .offcanvas .metismenu.mod-menu .metismenu-item > a {
        color: #fff;
    }
}
.offcanvas-header > *:only-child {
    margin-left: auto;
}

Hier kann man auch beliebig Änderungen machen, z.B. andere Hindergrundfarbe für den Offcanvas-Bereich. Falls man einen helleren Hintergrund wählt, sollte man die Klasse btn-close-white entfernen.

Weitere Möglichkeiten können in der Dokumentation von Bootstrap gelesen werden:

https://getbootstrap.com/docs/5.3/components/offcanvas/

https://getbootstrap.com/docs/5.3/components/navbar/#offcanvas