From 4649c56a5899e511c3869cc54315c96394c71101 Mon Sep 17 00:00:00 2001 From: Mork Swork Date: Thu, 14 May 2026 21:23:54 -0700 Subject: [PATCH] Hamburger menu --- docker-compose.yml | 2 +- docker/php/entrypoint.sh | 2 + web/modules/custom/riverside_pt/css/nav.css | 271 +++++++++++------- web/modules/custom/riverside_pt/js/nav.js | 28 ++ .../riverside_pt/riverside_pt.libraries.yml | 2 + .../custom/riverside_pt/riverside_pt.module | 55 ++++ .../templates/riverside-pt-header.html.twig | 36 +++ 7 files changed, 297 insertions(+), 99 deletions(-) create mode 100644 web/modules/custom/riverside_pt/js/nav.js create mode 100644 web/modules/custom/riverside_pt/templates/riverside-pt-header.html.twig diff --git a/docker-compose.yml b/docker-compose.yml index 4a3fcf1..ac5445c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,7 +10,7 @@ services: DB_NAME: drupal DB_USER: drupal DB_PASS: drupal - SITE_NAME: "Portfolio" + SITE_NAME: "Riverside Therapeutics" ADMIN_PASS: "${ADMIN_PASS:-admin}" HASH_SALT: "${HASH_SALT:-replace-this-in-production-with-a-long-random-string}" POSTMARK_API_KEY: "${POSTMARK_API_KEY:-}" diff --git a/docker/php/entrypoint.sh b/docker/php/entrypoint.sh index 43a286a..c0613dd 100644 --- a/docker/php/entrypoint.sh +++ b/docker/php/entrypoint.sh @@ -63,5 +63,7 @@ else echo "[entrypoint] No config to import, continuing." fi +$DRUSH cache:rebuild >/dev/null 2>&1 && echo "[entrypoint] Cache rebuilt." + echo "[entrypoint] Starting services..." exec supervisord -c /etc/supervisor/conf.d/supervisord.conf diff --git a/web/modules/custom/riverside_pt/css/nav.css b/web/modules/custom/riverside_pt/css/nav.css index cc2951a..23f226f 100644 --- a/web/modules/custom/riverside_pt/css/nav.css +++ b/web/modules/custom/riverside_pt/css/nav.css @@ -1,119 +1,194 @@ -/* ── Hide Olivero's sticky-header toggle ─────────────────── */ +/* ── Hide Olivero's built-in header entirely ─────────────── */ +.site-header, .sticky-header-toggle { - display: none; + display: none !important; } -/* ── Header: let the nav take all remaining width ────────── */ -.site-header__inner__container { +/* Push page content below the fixed header */ +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; } -.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; min-width: 0; } -/* ── Primary nav: flat horizontal list ──────────────────── */ -.primary-nav__menu--level-1 { - display: flex !important; +.rpt-header__list { + display: flex; 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%; - margin: 0 !important; - padding: 0 !important; - border: none !important; - background: none !important; + transform: translateY(10px) rotate(45deg); +} +.rpt-header__hamburger[aria-expanded="true"] span:nth-child(2) { + 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 { - border: none !important; - background: none !important; - padding: 0 !important; - margin: 0 !important; -} +/* ── Mobile layout ───────────────────────────────────────── */ +@media (max-width: 768px) { + .rpt-header__hamburger { + display: flex !important; + } -/* Plain text nav links */ -.primary-nav__menu-link--level-1 { - font-size: 0.9375rem !important; - font-weight: 400 !important; - color: #374151 !important; - padding: 0.25rem 0.875rem !important; - border: none !important; - background: none !important; - text-decoration: none !important; - border-radius: 0 !important; -} + .rpt-header__nav { + position: absolute; + top: 80px; + left: 0; + right: 0; + background: #fff; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + max-height: 0; + overflow: hidden; + opacity: 0; + transition: max-height 0.3s ease, opacity 0.25s ease; + } -.primary-nav__menu-link--level-1:hover, -.primary-nav__menu-link--level-1:focus { - color: #1e3a8a !important; - background: none !important; - border: none !important; - text-decoration: none !important; -} + .rpt-header__nav.is-open { + max-height: 500px; + opacity: 1; + } -/* Active trail */ -.primary-nav__menu-link--level-1.primary-nav__menu-link--active-trail { - color: #1e3a8a !important; - font-weight: 500 !important; -} + .rpt-header__list { + flex-direction: column; + align-items: stretch; + gap: 0; + } -/* ── Push CTA group to the far right ────────────────────── */ -.primary-nav__menu-item--level-1:has(> .nav-cta) { - margin-left: auto !important; -} + .rpt-header__item--cta { + margin-left: 0; + padding: 0.5rem 1.5rem; + } -.primary-nav__menu-item--level-1:has(> .nav-cta) -~ .primary-nav__menu-item--level-1:has(> .nav-cta) { - margin-left: 0.5rem !important; -} + .rpt-header__link { + padding: 0.75rem 1.5rem; + font-size: 1rem; + } -/* ── CTA buttons — rectangular ──────────────────────────── */ -.primary-nav__menu-link.nav-cta { - display: inline-block !important; - 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; + .rpt-header__cta { + display: block; + text-align: center; + } } diff --git a/web/modules/custom/riverside_pt/js/nav.js b/web/modules/custom/riverside_pt/js/nav.js new file mode 100644 index 0000000..13f2b23 --- /dev/null +++ b/web/modules/custom/riverside_pt/js/nav.js @@ -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'); + } + }); + }); +})(); diff --git a/web/modules/custom/riverside_pt/riverside_pt.libraries.yml b/web/modules/custom/riverside_pt/riverside_pt.libraries.yml index 4501c46..e483c43 100644 --- a/web/modules/custom/riverside_pt/riverside_pt.libraries.yml +++ b/web/modules/custom/riverside_pt/riverside_pt.libraries.yml @@ -7,6 +7,8 @@ navigation: css: theme: css/nav.css: {} + js: + js/nav.js: {} schedule: css: diff --git a/web/modules/custom/riverside_pt/riverside_pt.module b/web/modules/custom/riverside_pt/riverside_pt.module index d71d108..1417d9e 100644 --- a/web/modules/custom/riverside_pt/riverside_pt.module +++ b/web/modules/custom/riverside_pt/riverside_pt.module @@ -8,6 +8,61 @@ function riverside_pt_page_attachments(array &$attachments): void { $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('')->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 { if ($route_match->getRouteName() === 'riverside_pt.booking') { $breadcrumb = new Breadcrumb(); diff --git a/web/modules/custom/riverside_pt/templates/riverside-pt-header.html.twig b/web/modules/custom/riverside_pt/templates/riverside-pt-header.html.twig new file mode 100644 index 0000000..b11400d --- /dev/null +++ b/web/modules/custom/riverside_pt/templates/riverside-pt-header.html.twig @@ -0,0 +1,36 @@ +