Hamburger menu
This commit is contained in:
parent
4ab06a69b5
commit
4649c56a58
7 changed files with 297 additions and 99 deletions
|
|
@ -10,7 +10,7 @@ services:
|
||||||
DB_NAME: drupal
|
DB_NAME: drupal
|
||||||
DB_USER: drupal
|
DB_USER: drupal
|
||||||
DB_PASS: drupal
|
DB_PASS: drupal
|
||||||
SITE_NAME: "Portfolio"
|
SITE_NAME: "Riverside Therapeutics"
|
||||||
ADMIN_PASS: "${ADMIN_PASS:-admin}"
|
ADMIN_PASS: "${ADMIN_PASS:-admin}"
|
||||||
HASH_SALT: "${HASH_SALT:-replace-this-in-production-with-a-long-random-string}"
|
HASH_SALT: "${HASH_SALT:-replace-this-in-production-with-a-long-random-string}"
|
||||||
POSTMARK_API_KEY: "${POSTMARK_API_KEY:-}"
|
POSTMARK_API_KEY: "${POSTMARK_API_KEY:-}"
|
||||||
|
|
|
||||||
|
|
@ -63,5 +63,7 @@ else
|
||||||
echo "[entrypoint] No config to import, continuing."
|
echo "[entrypoint] No config to import, continuing."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
$DRUSH cache:rebuild >/dev/null 2>&1 && echo "[entrypoint] Cache rebuilt."
|
||||||
|
|
||||||
echo "[entrypoint] Starting services..."
|
echo "[entrypoint] Starting services..."
|
||||||
exec supervisord -c /etc/supervisor/conf.d/supervisord.conf
|
exec supervisord -c /etc/supervisor/conf.d/supervisord.conf
|
||||||
|
|
|
||||||
|
|
@ -1,119 +1,194 @@
|
||||||
/* ── Hide Olivero's sticky-header toggle ─────────────────── */
|
/* ── Hide Olivero's built-in header entirely ─────────────── */
|
||||||
|
.site-header,
|
||||||
.sticky-header-toggle {
|
.sticky-header-toggle {
|
||||||
display: none;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Header: let the nav take all remaining width ────────── */
|
/* Push page content below the fixed header */
|
||||||
.site-header__inner__container {
|
body {
|
||||||
|
padding-top: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Custom header shell ─────────────────────────────────── */
|
||||||
|
.rpt-header {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
height: 80px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpt-header__inner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 1.5rem;
|
||||||
gap: 2rem;
|
gap: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-nav {
|
/* ── Brand / site name ───────────────────────────────────── */
|
||||||
|
.rpt-header__brand {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1e3a5f;
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpt-header__brand:hover {
|
||||||
|
color: #1e3a8a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Nav list ────────────────────────────────────────────── */
|
||||||
|
.rpt-header__nav {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Primary nav: flat horizontal list ──────────────────── */
|
.rpt-header__list {
|
||||||
.primary-nav__menu--level-1 {
|
display: flex;
|
||||||
display: flex !important;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpt-header__item--cta {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Nav links ───────────────────────────────────────────── */
|
||||||
|
.rpt-header__link {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.9375rem;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #374151;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 0.25rem 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpt-header__link:hover,
|
||||||
|
.rpt-header__link:focus {
|
||||||
|
color: #1e3a8a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpt-header__link.is-active {
|
||||||
|
color: #1e3a8a;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── CTA button ──────────────────────────────────────────── */
|
||||||
|
.rpt-header__cta {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.5rem 1.25rem;
|
||||||
|
background: #1e3a5f;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1.5px solid #1e3a5f;
|
||||||
|
transition: background 0.15s, border-color 0.15s;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpt-header__cta:hover,
|
||||||
|
.rpt-header__cta:focus {
|
||||||
|
background: #152a45;
|
||||||
|
border-color: #152a45;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Hamburger button (hidden on desktop) ────────────────── */
|
||||||
|
.rpt-header__hamburger {
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 6px;
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
padding: 8px;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpt-header__hamburger span {
|
||||||
|
display: block;
|
||||||
|
height: 4px;
|
||||||
|
background: #1e3a5f;
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: transform 0.25s ease, opacity 0.2s ease, width 0.25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpt-header__hamburger span:nth-child(1) { width: 45%; }
|
||||||
|
.rpt-header__hamburger span:nth-child(2) { width: 100%; }
|
||||||
|
.rpt-header__hamburger span:nth-child(3) { width: 45%; align-self: flex-end; }
|
||||||
|
|
||||||
|
/* Animate to X when open */
|
||||||
|
.rpt-header__hamburger[aria-expanded="true"] span:nth-child(1) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0 !important;
|
transform: translateY(10px) rotate(45deg);
|
||||||
padding: 0 !important;
|
}
|
||||||
border: none !important;
|
.rpt-header__hamburger[aria-expanded="true"] span:nth-child(2) {
|
||||||
background: none !important;
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.rpt-header__hamburger[aria-expanded="true"] span:nth-child(3) {
|
||||||
|
width: 100%;
|
||||||
|
transform: translateY(-10px) rotate(-45deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-nav__menu-item--level-1 {
|
/* ── Mobile layout ───────────────────────────────────────── */
|
||||||
border: none !important;
|
@media (max-width: 768px) {
|
||||||
background: none !important;
|
.rpt-header__hamburger {
|
||||||
padding: 0 !important;
|
display: flex !important;
|
||||||
margin: 0 !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Plain text nav links */
|
.rpt-header__nav {
|
||||||
.primary-nav__menu-link--level-1 {
|
position: absolute;
|
||||||
font-size: 0.9375rem !important;
|
top: 80px;
|
||||||
font-weight: 400 !important;
|
left: 0;
|
||||||
color: #374151 !important;
|
right: 0;
|
||||||
padding: 0.25rem 0.875rem !important;
|
background: #fff;
|
||||||
border: none !important;
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
background: none !important;
|
max-height: 0;
|
||||||
text-decoration: none !important;
|
overflow: hidden;
|
||||||
border-radius: 0 !important;
|
opacity: 0;
|
||||||
|
transition: max-height 0.3s ease, opacity 0.25s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-nav__menu-link--level-1:hover,
|
.rpt-header__nav.is-open {
|
||||||
.primary-nav__menu-link--level-1:focus {
|
max-height: 500px;
|
||||||
color: #1e3a8a !important;
|
opacity: 1;
|
||||||
background: none !important;
|
|
||||||
border: none !important;
|
|
||||||
text-decoration: none !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Active trail */
|
.rpt-header__list {
|
||||||
.primary-nav__menu-link--level-1.primary-nav__menu-link--active-trail {
|
flex-direction: column;
|
||||||
color: #1e3a8a !important;
|
align-items: stretch;
|
||||||
font-weight: 500 !important;
|
gap: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Push CTA group to the far right ────────────────────── */
|
.rpt-header__item--cta {
|
||||||
.primary-nav__menu-item--level-1:has(> .nav-cta) {
|
margin-left: 0;
|
||||||
margin-left: auto !important;
|
padding: 0.5rem 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-nav__menu-item--level-1:has(> .nav-cta)
|
.rpt-header__link {
|
||||||
~ .primary-nav__menu-item--level-1:has(> .nav-cta) {
|
padding: 0.75rem 1.5rem;
|
||||||
margin-left: 0.5rem !important;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── CTA buttons — rectangular ──────────────────────────── */
|
.rpt-header__cta {
|
||||||
.primary-nav__menu-link.nav-cta {
|
display: block;
|
||||||
display: inline-block !important;
|
text-align: center;
|
||||||
padding: 0.5rem 1.25rem !important;
|
|
||||||
border-radius: 4px !important;
|
|
||||||
font-size: 0.9rem !important;
|
|
||||||
font-weight: 500 !important;
|
|
||||||
text-decoration: none !important;
|
|
||||||
transition: background 0.15s, color 0.15s, border-color 0.15s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Contact: outlined */
|
|
||||||
.primary-nav__menu-link.nav-cta:not(.nav-cta--primary) {
|
|
||||||
background: transparent !important;
|
|
||||||
color: #1e3a5f !important;
|
|
||||||
border: 1.5px solid #1e3a5f !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.primary-nav__menu-link.nav-cta:not(.nav-cta--primary):hover,
|
|
||||||
.primary-nav__menu-link.nav-cta:not(.nav-cta--primary):focus {
|
|
||||||
background: #1e3a5f !important;
|
|
||||||
color: #fff !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Book An Appointment: filled */
|
|
||||||
.primary-nav__menu-link.nav-cta--primary {
|
|
||||||
background: #1e3a5f !important;
|
|
||||||
color: #fff !important;
|
|
||||||
border: 1.5px solid #1e3a5f !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.primary-nav__menu-link.nav-cta--primary:hover,
|
|
||||||
.primary-nav__menu-link.nav-cta--primary:focus {
|
|
||||||
background: #152a45 !important;
|
|
||||||
border-color: #152a45 !important;
|
|
||||||
color: #fff !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Inner span inside each link — remove Olivero padding ── */
|
|
||||||
.primary-nav__menu-link-inner--level-1 {
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Hide search, login, and site name from header ──────── */
|
|
||||||
.block-search-narrow,
|
|
||||||
.secondary-nav,
|
|
||||||
.site-branding__name,
|
|
||||||
.site-branding__slogan {
|
|
||||||
display: none !important;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
28
web/modules/custom/riverside_pt/js/nav.js
Normal file
28
web/modules/custom/riverside_pt/js/nav.js
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
var btn = document.querySelector('.rpt-header__hamburger');
|
||||||
|
var nav = document.getElementById('rpt-main-nav');
|
||||||
|
if (!btn || !nav) return;
|
||||||
|
|
||||||
|
btn.addEventListener('click', function () {
|
||||||
|
var open = nav.classList.toggle('is-open');
|
||||||
|
btn.setAttribute('aria-expanded', String(open));
|
||||||
|
});
|
||||||
|
|
||||||
|
nav.querySelectorAll('a').forEach(function (link) {
|
||||||
|
link.addEventListener('click', function () {
|
||||||
|
nav.classList.remove('is-open');
|
||||||
|
btn.setAttribute('aria-expanded', 'false');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('click', function (e) {
|
||||||
|
if (!e.target.closest('.rpt-header')) {
|
||||||
|
nav.classList.remove('is-open');
|
||||||
|
btn.setAttribute('aria-expanded', 'false');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
@ -7,6 +7,8 @@ navigation:
|
||||||
css:
|
css:
|
||||||
theme:
|
theme:
|
||||||
css/nav.css: {}
|
css/nav.css: {}
|
||||||
|
js:
|
||||||
|
js/nav.js: {}
|
||||||
|
|
||||||
schedule:
|
schedule:
|
||||||
css:
|
css:
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,61 @@ function riverside_pt_page_attachments(array &$attachments): void {
|
||||||
$attachments['#attached']['library'][] = 'riverside_pt/navigation';
|
$attachments['#attached']['library'][] = 'riverside_pt/navigation';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function riverside_pt_theme(): array {
|
||||||
|
return [
|
||||||
|
'riverside_pt_header' => [
|
||||||
|
'variables' => [
|
||||||
|
'site_name' => NULL,
|
||||||
|
'home_url' => NULL,
|
||||||
|
'menu_items' => [],
|
||||||
|
'current_path' => NULL,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function riverside_pt_page_top(array &$page_top): void {
|
||||||
|
$page_top['rpt_header'] = [
|
||||||
|
'#theme' => 'riverside_pt_header',
|
||||||
|
'#cache' => ['contexts' => ['url.path']],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function riverside_pt_preprocess_riverside_pt_header(array &$variables): void {
|
||||||
|
$variables['site_name'] = \Drupal::config('system.site')->get('name');
|
||||||
|
$variables['home_url'] = \Drupal\Core\Url::fromRoute('<front>')->toString();
|
||||||
|
|
||||||
|
$tree_service = \Drupal::service('menu.link_tree');
|
||||||
|
$params = new \Drupal\Core\Menu\MenuTreeParameters();
|
||||||
|
$params->setMaxDepth(1);
|
||||||
|
$tree = $tree_service->load('main', $params);
|
||||||
|
$tree = $tree_service->transform($tree, [
|
||||||
|
['callable' => 'menu.default_tree_manipulators:checkAccess'],
|
||||||
|
['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$seen = [];
|
||||||
|
$items = [];
|
||||||
|
foreach ($tree as $element) {
|
||||||
|
if (!$element->access->isAllowed()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$url = $element->link->getUrlObject()->toString();
|
||||||
|
if (in_array($url, $seen, TRUE)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$seen[] = $url;
|
||||||
|
$title = (string) $element->link->getTitle();
|
||||||
|
$items[] = [
|
||||||
|
'title' => $title,
|
||||||
|
'url' => $url,
|
||||||
|
'is_cta' => ($title === 'Book An Appointment'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$variables['menu_items'] = $items;
|
||||||
|
$variables['current_path'] = \Drupal::request()->getPathInfo();
|
||||||
|
}
|
||||||
|
|
||||||
function riverside_pt_system_breadcrumb_alter(Breadcrumb &$breadcrumb, RouteMatchInterface $route_match, array $context): void {
|
function riverside_pt_system_breadcrumb_alter(Breadcrumb &$breadcrumb, RouteMatchInterface $route_match, array $context): void {
|
||||||
if ($route_match->getRouteName() === 'riverside_pt.booking') {
|
if ($route_match->getRouteName() === 'riverside_pt.booking') {
|
||||||
$breadcrumb = new Breadcrumb();
|
$breadcrumb = new Breadcrumb();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<header class="rpt-header" role="banner">
|
||||||
|
<div class="rpt-header__inner">
|
||||||
|
|
||||||
|
<a class="rpt-header__brand" href="{{ home_url }}">{{ site_name }}</a>
|
||||||
|
|
||||||
|
<nav class="rpt-header__nav" id="rpt-main-nav" aria-label="Main navigation">
|
||||||
|
<ul class="rpt-header__list">
|
||||||
|
{% for item in menu_items %}
|
||||||
|
{% if not item.is_cta %}
|
||||||
|
<li class="rpt-header__item">
|
||||||
|
<a class="rpt-header__link{% if current_path == item.url %} is-active{% endif %}"
|
||||||
|
href="{{ item.url }}">{{ item.title }}</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for item in menu_items %}
|
||||||
|
{% if item.is_cta %}
|
||||||
|
<li class="rpt-header__item rpt-header__item--cta">
|
||||||
|
<a class="rpt-header__cta" href="{{ item.url }}">{{ item.title }}</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<button class="rpt-header__hamburger"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-controls="rpt-main-nav"
|
||||||
|
aria-label="Toggle navigation">
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
Loading…
Reference in a new issue