weg-shared-layout 0.0.41 → 0.0.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,7 @@ var appGlobals = require('./app-globals-V2Kpy_OQ.js');
6
6
  const defineCustomElements = async (win, options) => {
7
7
  if (typeof window === 'undefined') return undefined;
8
8
  await appGlobals.globalScripts();
9
- return index.bootstrapLazy([["weg-footer_2.cjs",[[513,"weg-footer",{"layout":[1],"resolved":[32]},null,{"layout":[{"watchLayout":0}]}],[513,"weg-header",{"layout":[1],"signedIn":[516,"signed-in"],"userName":[1,"user-name"],"accountBaseUrl":[1,"account-base-url"],"currentPath":[1,"current-path"],"resolved":[32],"menuOpen":[32],"openDropdown":[32]},[[8,"keydown","handleKeyDown"]],{"layout":[{"watchLayout":0}]}]]]], options);
9
+ return index.bootstrapLazy([["weg-footer_2.cjs",[[513,"weg-footer",{"layout":[1],"resolved":[32]},null,{"layout":[{"watchLayout":0}]}],[513,"weg-header",{"layout":[1],"signedIn":[516,"signed-in"],"userName":[1,"user-name"],"accountBaseUrl":[1,"account-base-url"],"currentPath":[1,"current-path"],"resolved":[32],"menuOpen":[32],"openDropdown":[32],"headerHidden":[32]},[[8,"keydown","handleKeyDown"]],{"layout":[{"watchLayout":0}]}]]]], options);
10
10
  };
11
11
 
12
12
  exports.setNonce = index.setNonce;
@@ -377,13 +377,28 @@ const LOGO_SRC = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA4QAAAFcCAYAAACQ
377
377
 
378
378
  const sharedCss = () => `:host{--weg-breakpoint-sm:640px;--weg-breakpoint-md:960px;--weg-breakpoint-lg:1280px;--weg-breakpoint-xl:1440px}:root,:host{--weg-container-breakpoint-sm:40rem;--weg-container-breakpoint-md:48rem;--weg-container-breakpoint-lg:64rem;--weg-container-breakpoint-xl:80rem;--weg-container-breakpoint-2xl:86rem;--weg-container-max-vw:95vw;--weg-container-padding-inline:1rem}@media (width >= 48rem){:root,:host{--weg-container-padding-inline:2rem}}.container{width:100%;max-width:var(--weg-container-max-vw);margin-inline:auto;padding-inline:var(--weg-container-padding-inline)}@media (width >= 40rem){.container{max-width:min(var(--weg-container-breakpoint-sm), var(--weg-container-max-vw))}}@media (width >= 48rem){.container{max-width:min(var(--weg-container-breakpoint-md), var(--weg-container-max-vw))}}@media (width >= 64rem){.container{max-width:min(var(--weg-container-breakpoint-lg), var(--weg-container-max-vw))}}@media (width >= 80rem){.container{max-width:min(var(--weg-container-breakpoint-xl), var(--weg-container-max-vw))}}@media (width >= 86rem){.container{max-width:min(var(--weg-container-breakpoint-xl), var(--weg-container-max-vw))}}*,*::before,*::after{box-sizing:border-box}:host{display:block;--weg-font-family:ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, 'Apple Color Emoji', 'Segoe UI Emoji';--weg-color-accent:#948eeb;--weg-purple-200:#cdcff8}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border:0}`;
379
379
 
380
- const wegHeaderCss = () => `:host{--weg-breakpoint-sm:640px;--weg-breakpoint-md:960px;--weg-breakpoint-lg:1280px;--weg-breakpoint-xl:1440px}.header{width:100%;border-bottom:2px solid var(--weg-color-accent);background:#ffffff;color:#0f172a;font-family:var(--weg-font-family)}@media (width >= 960px){.header{border-bottom-width:0.25rem}}:host{position:relative;z-index:20}.header--menu-open{position:fixed;inset:0;z-index:1000;display:flex;flex-direction:column;overflow:hidden}.header--menu-open .header-inner{flex:1 1 auto;flex-wrap:wrap;align-content:flex-start;min-height:0}.header--menu-open .main-nav-panel{display:block;flex:1 1 100%;width:100%;min-height:0;overflow-y:auto;border-top:1px solid #d8d4f7}@media (width >= 960px){.header--menu-open{position:static;inset:auto;z-index:auto;display:block;overflow:visible}.header--menu-open .header-inner{flex:unset;flex-wrap:nowrap;align-content:unset;min-height:unset}}.logo-link{display:inline-flex;align-items:center;text-decoration:none;outline:none}.logo-link:focus-visible{outline:2px solid var(--weg-color-accent);outline-offset:4px;border-radius:4px}.logo{height:2.5rem;width:auto}@media (width >= 960px){.logo{height:3rem}}@media (width >= 1280px){.logo{height:5rem}}.header-inner{display:flex;width:100%;min-width:0;align-items:center;justify-content:space-between;gap:0.75rem;padding-block:0.75rem;padding-inline:var(--weg-container-padding-inline)}@media (width >= 960px){.header-inner{flex-wrap:nowrap;gap:1.5rem;padding-block:1.25rem}}@media (width >= 1440px){.header-inner{padding-block:1.75rem}}.header-inner .logo-link{flex:1 1 auto;min-width:0;justify-content:center}@media (width >= 960px){.header-inner .logo-link{flex:0 0 auto;justify-content:flex-start}}.header-inner .logo{max-width:100%}.header-actions{display:flex;align-items:center;flex-shrink:0}@media (width >= 960px){.header-actions{display:none}}.main-nav-panel{display:none;margin-inline:-0.5rem}@media (width >= 960px){.main-nav-panel{display:flex;flex:1 1 auto;width:auto;min-width:0;justify-content:flex-end;overflow:visible;border-top:none}}.main-nav{padding:0 1rem 1.5rem}@media (width >= 960px){.main-nav{display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:1rem;padding:0}}.main-nav__list{list-style:none;margin:0;padding:0}@media (width >= 960px){.main-nav__list{display:flex;align-items:center;flex-wrap:wrap;gap:0.5rem}}@media (width >= 1280px){.main-nav__list{gap:1.5rem}}.main-nav__item{border-bottom:1px solid #d8d4f7}@media (width >= 960px){.main-nav__item{border-bottom:none}}.main-nav__item--dropdown{position:static}.nav-dropdown{position:relative}.nav-link{display:block;width:100%;padding:1rem 0;font-weight:600;color:inherit;text-decoration:none;outline:none}.nav-link:hover{opacity:0.85}.nav-link:focus-visible{outline:2px solid var(--weg-color-accent);outline-offset:2px;border-radius:4px}.nav-link--active{background:var(--weg-purple-200)}.nav-dropdown__trigger{display:flex;align-items:center;justify-content:space-between;gap:0.75rem;width:100%;padding:1rem 0;border:none;background:transparent;font:inherit;font-weight:600;color:inherit;text-align:left;cursor:pointer;outline:none}.nav-dropdown__trigger:hover{opacity:0.85}.nav-dropdown__trigger:focus-visible{outline:2px solid var(--weg-color-accent);outline-offset:2px;border-radius:4px}.nav-dropdown__trigger--open{background:transparent}.nav-dropdown__trigger--active{background:var(--weg-purple-200)}.toggle-icon{display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;border-radius:999rem;background:#312e81;color:#ffffff;flex-shrink:0}.nav-dropdown__panel{padding:0 0 0.75rem}@media (width >= 960px){.nav-dropdown__panel{position:absolute;top:calc(100% + 0.5rem);left:0;z-index:500;min-width:17.5rem;padding:0.75rem 0 0.75rem 0.75rem;border:1px solid #e2e8f0;border-radius:0.5rem;background:#f8fafc;box-shadow:0 0.25rem 1rem rgba(15, 23, 42, 0.08)}.nav-dropdown__panel::before{content:'';position:absolute;top:-0.5rem;left:0;right:0;height:0.5rem}}.nav-dropdown__accent{border-left:0.25rem solid var(--weg-color-accent);padding-left:0.75rem}.nav-dropdown__links{display:flex;flex-direction:column;gap:0.25rem}.nav-dropdown__link{display:block;padding:0.625rem 0;font-weight:400;color:inherit;text-decoration:none;outline:none}.nav-dropdown__link:hover{opacity:0.85}.nav-dropdown__link:focus-visible{outline:0.125rem solid var(--weg-color-accent);outline-offset:0.125rem;border-radius:0.25rem}.nav-dropdown__link--active{background:var(--weg-purple-200)}@media (width >= 960px){.nav-dropdown__link{padding:0.5rem;margin-right:0.75rem;border-radius:0.5rem;font-weight:600}.nav-dropdown__link:hover,.nav-dropdown__link:focus-visible{background:var(--weg-purple-200);opacity:1}.nav-dropdown__link--active{opacity:1}}@media (width >= 1280px){.nav-dropdown__link{padding:0.5rem 0.75rem}}.main-nav__item--auth>a{display:flex;align-items:center;gap:0.5rem;width:100%;padding:1rem 0}@media (width >= 960px){.nav-link,.nav-dropdown__trigger,.main-nav__item--auth>a{display:inline-flex;align-items:center;width:auto;padding:0.5rem;border-radius:0.5rem}.nav-link--active,.main-nav__item--auth>a.nav-link--active{background:var(--weg-purple-200)}.nav-dropdown__trigger{gap:0.5rem;cursor:pointer}.nav-dropdown__trigger--open{background:#f1f5f9}.nav-dropdown__trigger--active{background:var(--weg-purple-200)}}@media (width >= 1280px){.main-nav{font-size:1.25rem}.main-nav__item--auth>a{padding:0.5rem 0.75rem}}.sign-in-link,.sign-out-link,.auth-button{display:inline-flex;align-items:center;gap:0.5rem;font-weight:600;color:inherit;text-decoration:none;outline:none}.auth-button{padding:0;border:none;background:transparent;font:inherit;cursor:pointer}.sign-in-link:hover,.sign-out-link:hover,.auth-button:hover{opacity:0.85}.sign-in-link:focus-visible,.sign-out-link:focus-visible,.auth-button:focus-visible{outline:2px solid var(--weg-color-accent);outline-offset:4px;border-radius:4px}.icon-button{display:inline-flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;padding:0;border:none;border-radius:0.5rem;background:transparent;color:inherit;cursor:pointer;text-decoration:none;outline:none}.icon-button:hover{background:#f1f5f9}.icon-button:focus-visible{outline:2px solid var(--weg-color-accent);outline-offset:2px}.menu-toggle{flex-shrink:0}@media (width >= 960px){.menu-toggle{display:none}}`;
380
+ const wegHeaderCss = () => `:host{--weg-breakpoint-sm:640px;--weg-breakpoint-md:960px;--weg-breakpoint-lg:1280px;--weg-breakpoint-xl:1440px}.weg-header-shell{display:block}.skip-to-content{position:fixed;top:-100%;left:0;z-index:10001;padding:0.75rem 1rem;background:#ffffff;color:#0f172a;font-family:var(--weg-font-family);font-weight:600;text-decoration:none;border:2px solid #312e81;border-radius:0 0 0.25rem 0;box-shadow:0 0.25rem 0.75rem rgba(17, 24, 39, 0.15)}.skip-to-content:focus,.skip-to-content:focus-visible{top:0;outline:2px solid var(--weg-color-accent);outline-offset:2px}.header{width:100%;border-bottom:2px solid var(--weg-color-accent);background:#ffffff;color:#0f172a;font-family:var(--weg-font-family)}@media (width >= 960px){.header{border-bottom-width:0.25rem}}:host{position:relative;z-index:20;display:block;height:var(--weg-header-height, auto)}.header--fixed{position:fixed;top:0;left:0;right:0;z-index:20;transform:translateY(0);transition:transform 0.25s ease}.header--fixed.header--hidden{transform:translateY(-100%)}@media (prefers-reduced-motion: reduce){.header--fixed{transition:none}}.header--menu-open{position:fixed;inset:0;z-index:1000;display:flex;flex-direction:column;overflow:hidden}.header--menu-open .header-inner{flex:1 1 auto;flex-wrap:wrap;align-content:flex-start;min-height:0}.header--menu-open .main-nav-panel{display:block;flex:1 1 100%;width:100%;min-height:0;overflow-y:auto;border-top:1px solid #d8d4f7}@media (width >= 960px){.header--menu-open{position:static;inset:auto;z-index:auto;display:block;overflow:visible}.header--menu-open .header-inner{flex:unset;flex-wrap:nowrap;align-content:unset;min-height:unset}}.logo-link{display:inline-flex;align-items:center;text-decoration:none;outline:none}.logo-link:focus-visible{outline:2px solid var(--weg-color-accent);outline-offset:4px;border-radius:4px}.logo{height:2.5rem;width:auto}@media (width >= 960px){.logo{height:3rem}}@media (width >= 1280px){.logo{height:5rem}}.header-inner{display:flex;width:100%;min-width:0;align-items:center;justify-content:space-between;gap:0.75rem;padding-block:0.75rem;padding-inline:var(--weg-container-padding-inline)}@media (width >= 960px){.header-inner{flex-wrap:nowrap;gap:1.5rem;padding-block:1.25rem}}@media (width >= 1440px){.header-inner{padding-block:1.75rem}}.header-inner .logo-link{flex:1 1 auto;min-width:0;justify-content:center}@media (width >= 960px){.header-inner .logo-link{flex:0 0 auto;justify-content:flex-start}}.header-inner .logo{max-width:100%}.header-actions{display:flex;align-items:center;flex-shrink:0}@media (width >= 960px){.header-actions{display:none}}.main-nav-panel{display:none;margin-inline:-0.5rem}@media (width >= 960px){.main-nav-panel{display:flex;flex:1 1 auto;width:auto;min-width:0;justify-content:flex-end;overflow:visible;border-top:none}}.main-nav{padding:0 1rem 1.5rem}@media (width >= 960px){.main-nav{display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:1rem;padding:0}}.main-nav__list{list-style:none;margin:0;padding:0}@media (width >= 960px){.main-nav__list{display:flex;align-items:center;flex-wrap:wrap;gap:0.5rem}}@media (width >= 1280px){.main-nav__list{gap:1.5rem}}.main-nav__item{border-bottom:1px solid #d8d4f7}@media (width >= 960px){.main-nav__item{border-bottom:none}}.main-nav__item--dropdown{position:static}.nav-dropdown{position:relative}.nav-link{display:block;width:100%;padding:1rem 0;font-weight:600;color:inherit;text-decoration:none;outline:none}.nav-link:hover{opacity:0.85}.nav-link:focus-visible{outline:2px solid var(--weg-color-accent);outline-offset:2px;border-radius:4px}.nav-link--active{background:var(--weg-purple-200)}.nav-dropdown__trigger{display:flex;align-items:center;justify-content:space-between;gap:0.75rem;width:100%;padding:1rem 0;border:none;background:transparent;font:inherit;font-weight:600;color:inherit;text-align:left;cursor:pointer;outline:none}.nav-dropdown__trigger:hover{opacity:0.85}.nav-dropdown__trigger:focus-visible{outline:2px solid var(--weg-color-accent);outline-offset:2px;border-radius:4px}.nav-dropdown__trigger--open{background:transparent}.nav-dropdown__trigger--active{background:var(--weg-purple-200)}.toggle-icon{display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;border-radius:999rem;background:#312e81;color:#ffffff;flex-shrink:0}.nav-dropdown__panel{padding:0 0 0.75rem}@media (width >= 960px){.nav-dropdown__panel{position:absolute;top:calc(100% + 0.5rem);left:0;z-index:500;min-width:17.5rem;padding:0.75rem 0 0.75rem 0.75rem;border:1px solid #e2e8f0;border-radius:0.5rem;background:#f8fafc;box-shadow:0 0.25rem 1rem rgba(15, 23, 42, 0.08)}.nav-dropdown__panel::before{content:'';position:absolute;top:-0.5rem;left:0;right:0;height:0.5rem}}.nav-dropdown__accent{border-left:0.25rem solid var(--weg-color-accent);padding-left:0.75rem}.nav-dropdown__links{display:flex;flex-direction:column;gap:0.25rem;list-style:none;margin:0;padding:0}.nav-dropdown__link{display:block;padding:0.625rem 0;font-weight:400;color:inherit;text-decoration:none;outline:none}.nav-dropdown__link:hover{opacity:0.85}.nav-dropdown__link:focus-visible{outline:0.125rem solid var(--weg-color-accent);outline-offset:0.125rem;border-radius:0.25rem}.nav-dropdown__link--active{background:var(--weg-purple-200)}@media (width >= 960px){.nav-dropdown__link{padding:0.5rem;margin-right:0.75rem;border-radius:0.5rem;font-weight:600}.nav-dropdown__link:hover,.nav-dropdown__link:focus-visible{background:var(--weg-purple-200);opacity:1}.nav-dropdown__link--active{opacity:1}}@media (width >= 1280px){.nav-dropdown__link{padding:0.5rem 0.75rem}}.main-nav__item--auth>a{display:flex;align-items:center;gap:0.5rem;width:100%;padding:1rem 0}@media (width >= 960px){.nav-link,.nav-dropdown__trigger,.main-nav__item--auth>a{display:inline-flex;align-items:center;width:auto;padding:0.5rem;border-radius:0.5rem}.nav-link--active,.main-nav__item--auth>a.nav-link--active{background:var(--weg-purple-200)}.nav-dropdown__trigger{gap:0.5rem;cursor:pointer}.nav-dropdown__trigger--open{background:#f1f5f9}.nav-dropdown__trigger--active{background:var(--weg-purple-200)}}@media (width >= 1280px){.main-nav{font-size:1.25rem}.main-nav__item--auth>a{padding:0.5rem 0.75rem}}.sign-in-link,.sign-out-link,.auth-button{display:inline-flex;align-items:center;gap:0.5rem;font-weight:600;color:inherit;text-decoration:none;outline:none}.auth-button{padding:0;border:none;background:transparent;font:inherit;cursor:pointer}.sign-in-link:hover,.sign-out-link:hover,.auth-button:hover{opacity:0.85}.sign-in-link:focus-visible,.sign-out-link:focus-visible,.auth-button:focus-visible{outline:2px solid var(--weg-color-accent);outline-offset:4px;border-radius:4px}.icon-button{display:inline-flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;padding:0;border:none;border-radius:0.5rem;background:transparent;color:inherit;cursor:pointer;text-decoration:none;outline:none}.icon-button:hover{background:#f1f5f9}.icon-button:focus-visible{outline:2px solid var(--weg-color-accent);outline-offset:2px}.menu-toggle{flex-shrink:0}@media (width >= 960px){.menu-toggle{display:none}}`;
381
381
 
382
382
  const WEG_HOME = 'https://www.warwickemploymentgroup.com';
383
383
  const DEFAULT_ACCOUNT_HOME = 'https://account.warwickemploymentgroup.com';
384
+ /** Stay visible while scrolling down through the first half of the viewport. */
385
+ const HEADER_SCROLL_HIDE_THRESHOLD_VH = 0.5;
386
+ const HEADER_SCROLL_DIRECTION_DELTA = 5;
384
387
  const DEFAULT_LOGO_HREF = `${WEG_HOME}/`;
385
388
  /** Placeholder href — sign-out is handled via `wegAuthClick`, not navigation. */
386
389
  const SIGN_OUT_HREF = '#';
390
+ const MAIN_NAV_LABEL = 'Main navigation';
391
+ const OPEN_MAIN_NAV_LABEL = 'Open main navigation';
392
+ const CLOSE_MAIN_NAV_LABEL = 'Close main navigation';
393
+ const SKIP_TO_CONTENT_LABEL = 'Skip to main content';
394
+ const SKIP_TO_CONTENT_TARGET_ID = 'main-content';
395
+ function focusScrollTarget(element) {
396
+ if (!element.hasAttribute('tabindex')) {
397
+ element.setAttribute('tabindex', '-1');
398
+ }
399
+ element.focus({ preventScroll: true });
400
+ element.scrollIntoView();
401
+ }
387
402
  function normalizeAccountBase(input) {
388
403
  const base = input?.trim() || DEFAULT_ACCOUNT_HOME;
389
404
  return base.replace(/\/$/, '');
@@ -519,9 +534,16 @@ const WegHeader = class {
519
534
  resolved = EMPTY_HEADER;
520
535
  menuOpen = false;
521
536
  openDropdown = null;
537
+ headerHidden = false;
522
538
  boundHandleDocumentClick = this.handleDocumentClick.bind(this);
523
539
  boundHandleViewportChange = this.handleViewportChange.bind(this);
524
540
  boundHandleHoverCapabilityChange = this.handleHoverCapabilityChange.bind(this);
541
+ boundHandleScroll = this.handleScroll.bind(this);
542
+ headerResizeObserver;
543
+ lastScrollY = 0;
544
+ scrollTicking = false;
545
+ bodyScrollLocked = false;
546
+ bodyScrollLockY = 0;
525
547
  hoverCloseTimer = null;
526
548
  hoverOpenEnabled = false;
527
549
  desktopMediaQuery;
@@ -542,15 +564,69 @@ const WegHeader = class {
542
564
  this.updateHoverDropdownEnabled();
543
565
  this.handleViewportChange();
544
566
  }
567
+ componentDidLoad() {
568
+ const header = this.el.shadowRoot?.querySelector('.header');
569
+ if (!header)
570
+ return;
571
+ this.headerResizeObserver = new ResizeObserver(() => this.syncHeaderHeight());
572
+ this.headerResizeObserver.observe(header);
573
+ this.syncHeaderHeight();
574
+ this.lastScrollY = window.scrollY;
575
+ this.updateHeaderVisibility();
576
+ }
545
577
  connectedCallback() {
546
578
  document.addEventListener('click', this.boundHandleDocumentClick);
579
+ window.addEventListener('scroll', this.boundHandleScroll, { passive: true });
547
580
  }
548
581
  disconnectedCallback() {
549
582
  document.removeEventListener('click', this.boundHandleDocumentClick);
583
+ window.removeEventListener('scroll', this.boundHandleScroll);
584
+ this.unlockBodyScroll();
585
+ this.headerResizeObserver?.disconnect();
550
586
  this.desktopMediaQuery?.removeEventListener('change', this.boundHandleViewportChange);
551
587
  this.hoverCapableMediaQuery?.removeEventListener('change', this.boundHandleHoverCapabilityChange);
552
588
  this.cancelScheduledDropdownClose();
553
589
  }
590
+ syncHeaderHeight() {
591
+ if (this.isMobileMenuActive())
592
+ return;
593
+ const header = this.el.shadowRoot?.querySelector('.header');
594
+ if (!header)
595
+ return;
596
+ this.el.style.setProperty('--weg-header-height', `${header.offsetHeight}px`);
597
+ }
598
+ handleScroll() {
599
+ if (this.scrollTicking)
600
+ return;
601
+ this.scrollTicking = true;
602
+ requestAnimationFrame(() => {
603
+ this.scrollTicking = false;
604
+ this.updateHeaderVisibility();
605
+ });
606
+ }
607
+ updateHeaderVisibility() {
608
+ if (this.isMobileMenuActive()) {
609
+ if (this.headerHidden)
610
+ this.headerHidden = false;
611
+ return;
612
+ }
613
+ const scrollY = window.scrollY;
614
+ const delta = scrollY - this.lastScrollY;
615
+ const threshold = window.innerHeight * HEADER_SCROLL_HIDE_THRESHOLD_VH;
616
+ let nextHidden = this.headerHidden;
617
+ if (scrollY <= threshold) {
618
+ nextHidden = false;
619
+ }
620
+ else if (Math.abs(delta) >= HEADER_SCROLL_DIRECTION_DELTA) {
621
+ nextHidden = delta > 0;
622
+ }
623
+ this.lastScrollY = scrollY;
624
+ if (nextHidden === this.headerHidden)
625
+ return;
626
+ if (nextHidden)
627
+ this.closeDropdown();
628
+ this.headerHidden = nextHidden;
629
+ }
554
630
  /**
555
631
  * Desktop dropdowns open on hover when viewport is md+ and the device reports hover support.
556
632
  * Uses (hover: hover) only — not (pointer: fine), which breaks in DevTools and hybrid devices.
@@ -568,10 +644,74 @@ const WegHeader = class {
568
644
  }
569
645
  handleViewportChange() {
570
646
  if (this.desktopMediaQuery?.matches) {
571
- this.closeMenu();
647
+ this.closeMenu({ announce: false, focusToggle: false });
572
648
  }
573
649
  this.updateHoverDropdownEnabled();
574
650
  }
651
+ announceToScreenReader(message) {
652
+ const region = this.el.shadowRoot?.querySelector('[data-weg-sr-live]');
653
+ if (!region)
654
+ return;
655
+ region.textContent = '';
656
+ requestAnimationFrame(() => {
657
+ region.textContent = message;
658
+ });
659
+ }
660
+ getMenuToggleButton() {
661
+ return this.el.shadowRoot?.querySelector('.menu-toggle');
662
+ }
663
+ getFirstNavFocusable() {
664
+ const panel = this.el.shadowRoot?.querySelector('#weg-header-main-nav');
665
+ if (!panel)
666
+ return null;
667
+ const focusables = panel.querySelectorAll('a[href], button:not([disabled])');
668
+ return focusables[0] ?? null;
669
+ }
670
+ focusFirstNavItem() {
671
+ setTimeout(() => {
672
+ const attemptFocus = (retries = 8) => {
673
+ const target = this.getFirstNavFocusable();
674
+ if (target) {
675
+ target.focus();
676
+ return;
677
+ }
678
+ if (retries > 0) {
679
+ requestAnimationFrame(() => attemptFocus(retries - 1));
680
+ }
681
+ };
682
+ attemptFocus();
683
+ }, 0);
684
+ }
685
+ focusMenuToggle() {
686
+ const attemptFocus = (retries = 8) => {
687
+ const toggle = this.getMenuToggleButton();
688
+ if (toggle) {
689
+ toggle.focus();
690
+ return;
691
+ }
692
+ if (retries > 0) {
693
+ requestAnimationFrame(() => attemptFocus(retries - 1));
694
+ }
695
+ };
696
+ attemptFocus();
697
+ }
698
+ getMenuGroupLinkCount(label) {
699
+ const group = this.getActiveMenu().find((item) => isMenuGroup(item) && item.label === label);
700
+ return group?.items?.length ?? 0;
701
+ }
702
+ formatLinkCount(count) {
703
+ return count === 1 ? '1 link' : `${count} links`;
704
+ }
705
+ formatItemCount(count) {
706
+ return count === 1 ? '1 item' : `${count} items`;
707
+ }
708
+ announceSubmenuExpanded(label) {
709
+ const linkCount = this.getMenuGroupLinkCount(label);
710
+ this.announceToScreenReader(`${label} submenu expanded. ${this.formatLinkCount(linkCount)}.`);
711
+ }
712
+ announceSubmenuCollapsed(label) {
713
+ this.announceToScreenReader(`${label} submenu collapsed.`);
714
+ }
575
715
  isMobileMenuActive() {
576
716
  return this.menuOpen && !this.desktopMediaQuery?.matches;
577
717
  }
@@ -581,8 +721,39 @@ const WegHeader = class {
581
721
  handleKeyDown(event) {
582
722
  if (event.key !== 'Escape')
583
723
  return;
584
- this.menuOpen = false;
585
- this.closeDropdown();
724
+ if (this.openDropdown) {
725
+ event.preventDefault();
726
+ this.closeDropdownAndFocusTrigger(this.openDropdown);
727
+ return;
728
+ }
729
+ if (this.menuOpen) {
730
+ event.preventDefault();
731
+ this.closeMenu({ focusToggle: true });
732
+ }
733
+ }
734
+ lockBodyScroll() {
735
+ if (this.bodyScrollLocked)
736
+ return;
737
+ this.bodyScrollLockY = window.scrollY;
738
+ document.body.style.overflow = 'hidden';
739
+ document.body.style.position = 'fixed';
740
+ document.body.style.top = `-${this.bodyScrollLockY}px`;
741
+ document.body.style.left = '0';
742
+ document.body.style.right = '0';
743
+ document.body.style.width = '100%';
744
+ this.bodyScrollLocked = true;
745
+ }
746
+ unlockBodyScroll() {
747
+ if (!this.bodyScrollLocked)
748
+ return;
749
+ document.body.style.overflow = '';
750
+ document.body.style.position = '';
751
+ document.body.style.top = '';
752
+ document.body.style.left = '';
753
+ document.body.style.right = '';
754
+ document.body.style.width = '';
755
+ window.scrollTo(0, this.bodyScrollLockY);
756
+ this.bodyScrollLocked = false;
586
757
  }
587
758
  handleDocumentClick(event) {
588
759
  const target = event.target;
@@ -613,10 +784,12 @@ const WegHeader = class {
613
784
  }
614
785
  toggleDropdown(label) {
615
786
  if (this.openDropdown === label) {
787
+ this.announceSubmenuCollapsed(label);
616
788
  this.closeDropdown();
617
789
  return;
618
790
  }
619
791
  this.openDropdownMenu(label);
792
+ this.announceSubmenuExpanded(label);
620
793
  }
621
794
  getDropdownPanelId(label) {
622
795
  return `weg-header-dropdown-${label.trim().toLowerCase().replace(/\s+/g, '-')}`;
@@ -669,29 +842,111 @@ const WegHeader = class {
669
842
  if (event.key !== 'ArrowDown')
670
843
  return;
671
844
  event.preventDefault();
672
- this.openDropdownMenu(label);
845
+ if (this.openDropdown !== label) {
846
+ this.openDropdownMenu(label);
847
+ this.announceSubmenuExpanded(label);
848
+ }
673
849
  this.focusFirstDropdownLink(itemEl);
674
850
  }
851
+ getDropdownLinks(container) {
852
+ return Array.from(container.querySelectorAll('.nav-dropdown__link'));
853
+ }
854
+ focusDropdownLink(container, index) {
855
+ const attemptFocus = (retries = 8) => {
856
+ const links = this.getDropdownLinks(container);
857
+ if (links[index]) {
858
+ links[index].focus();
859
+ return;
860
+ }
861
+ if (retries > 0) {
862
+ requestAnimationFrame(() => attemptFocus(retries - 1));
863
+ }
864
+ };
865
+ attemptFocus();
866
+ }
675
867
  focusFirstDropdownLink(itemEl) {
676
- setTimeout(() => {
677
- const firstLink = itemEl.querySelector('.nav-dropdown__link');
678
- firstLink?.focus();
679
- }, 0);
868
+ this.focusDropdownLink(itemEl, 0);
869
+ }
870
+ getDropdownContainer(label) {
871
+ const panelId = this.getDropdownPanelId(label);
872
+ return this.el.shadowRoot?.querySelector(`#${panelId}-trigger`)?.closest('.nav-dropdown') ?? null;
873
+ }
874
+ closeDropdownAndFocusTrigger(labelOrContainer) {
875
+ const container = typeof labelOrContainer === 'string'
876
+ ? this.getDropdownContainer(labelOrContainer)
877
+ : labelOrContainer;
878
+ const trigger = container?.querySelector('.nav-dropdown__trigger');
879
+ const label = typeof labelOrContainer === 'string'
880
+ ? labelOrContainer
881
+ : (trigger?.querySelector('.nav-dropdown__label')?.textContent?.trim() ?? '');
882
+ if (label && this.openDropdown === label) {
883
+ this.announceSubmenuCollapsed(label);
884
+ }
885
+ this.closeDropdown();
886
+ const attemptFocus = (retries = 8) => {
887
+ if (trigger && this.el.shadowRoot?.contains(trigger)) {
888
+ trigger.focus();
889
+ return;
890
+ }
891
+ if (retries > 0) {
892
+ requestAnimationFrame(() => attemptFocus(retries - 1));
893
+ }
894
+ };
895
+ attemptFocus();
896
+ }
897
+ handleDropdownLinkKeyDown(event, container) {
898
+ const links = this.getDropdownLinks(container);
899
+ const currentIndex = links.indexOf(event.currentTarget);
900
+ if (currentIndex === -1)
901
+ return;
902
+ if (event.key === 'ArrowDown') {
903
+ event.preventDefault();
904
+ links[(currentIndex + 1) % links.length]?.focus();
905
+ return;
906
+ }
907
+ if (event.key === 'ArrowUp') {
908
+ event.preventDefault();
909
+ links[(currentIndex - 1 + links.length) % links.length]?.focus();
910
+ return;
911
+ }
912
+ if (event.key === 'Escape') {
913
+ event.preventDefault();
914
+ event.stopPropagation();
915
+ this.closeDropdownAndFocusTrigger(container);
916
+ }
680
917
  }
681
918
  toggleMenu() {
682
919
  if (this.menuOpen) {
683
- this.closeMenu();
920
+ this.closeMenu({ focusToggle: false });
684
921
  return;
685
922
  }
686
923
  this.openMenu();
687
924
  }
688
925
  openMenu() {
689
926
  this.menuOpen = true;
927
+ this.headerHidden = false;
690
928
  this.closeDropdown();
929
+ if (!this.desktopMediaQuery?.matches) {
930
+ this.lockBodyScroll();
931
+ const itemCount = this.getActiveMenu().length;
932
+ this.announceToScreenReader(`${MAIN_NAV_LABEL} opened. ${this.formatItemCount(itemCount)}.`);
933
+ this.focusFirstNavItem();
934
+ }
691
935
  }
692
- closeMenu() {
936
+ closeMenu(options = {}) {
937
+ const { announce = true, focusToggle = false } = options;
938
+ const wasMobileActive = this.isMobileMenuActive();
693
939
  this.menuOpen = false;
694
940
  this.closeDropdown();
941
+ this.unlockBodyScroll();
942
+ this.syncHeaderHeight();
943
+ this.updateHeaderVisibility();
944
+ if (announce && wasMobileActive) {
945
+ this.announceToScreenReader(`${MAIN_NAV_LABEL} closed.`);
946
+ }
947
+ if (focusToggle) {
948
+ this.focusMenuToggle();
949
+ }
695
950
  }
696
951
  getAccountHome() {
697
952
  return normalizeAccountBase(this.accountBaseUrl);
@@ -762,13 +1017,17 @@ const WegHeader = class {
762
1017
  }, "aria-expanded": isOpen ? 'true' : 'false', "aria-haspopup": "true", "aria-controls": panelId, id: `${panelId}-trigger`, onClick: (event) => this.handleDropdownTriggerClick(event, item.label), onKeyDown: (event) => {
763
1018
  const container = event.currentTarget.closest('.nav-dropdown');
764
1019
  this.handleDropdownTriggerKeyDown(event, item.label, container);
765
- } }, index.h("span", { class: "nav-dropdown__label" }, item.label), index.h(ToggleIcon, { expanded: isOpen })), isOpen ? (index.h("div", { class: "nav-dropdown__panel", id: panelId, role: "region", "aria-labelledby": `${panelId}-trigger`, onMouseEnter: () => this.handleDropdownPanelPointerEnter(item.label), onMouseLeave: (event) => this.handleDropdownPanelPointerLeave(event) }, index.h("div", { class: "nav-dropdown__accent" }, index.h("div", { class: "nav-dropdown__links" }, items.map((child) => {
1020
+ } }, index.h("span", { class: "nav-dropdown__label" }, item.label), index.h(ToggleIcon, { expanded: isOpen })), isOpen ? (index.h("div", { class: "nav-dropdown__panel", id: panelId, role: "region", "aria-labelledby": `${panelId}-trigger`, onMouseEnter: () => this.handleDropdownPanelPointerEnter(item.label), onMouseLeave: (event) => this.handleDropdownPanelPointerLeave(event) }, index.h("div", { class: "nav-dropdown__accent" }, index.h("ul", { class: "nav-dropdown__links" }, items.map((child, index$1) => {
766
1021
  const childActive = this.isNavItemActive(child.href, 'exact');
767
1022
  const ariaCurrent = this.isAriaCurrentHref(child.href, ariaCurrentHref);
768
- return (index.h("a", { class: {
1023
+ const positionLabel = `${child.label}, link ${index$1 + 1} of ${items.length}`;
1024
+ return (index.h("li", { key: `${item.label}:${child.label}` }, index.h("a", { class: {
769
1025
  'nav-dropdown__link': true,
770
1026
  'nav-dropdown__link--active': childActive,
771
- }, href: child.href, key: `${item.label}:${child.label}`, "aria-current": ariaCurrent ? 'page' : undefined, onClick: () => onNavigate?.() }, child.label));
1027
+ }, href: child.href, "aria-label": positionLabel, "aria-current": ariaCurrent ? 'page' : undefined, onClick: () => onNavigate?.(), onKeyDown: (event) => {
1028
+ const dropdownContainer = event.currentTarget.closest('.nav-dropdown');
1029
+ this.handleDropdownLinkKeyDown(event, dropdownContainer);
1030
+ } }, child.label)));
772
1031
  }))))) : null)));
773
1032
  }
774
1033
  handleAuthClick(event, action, _href, onNavigate) {
@@ -831,7 +1090,7 @@ const WegHeader = class {
831
1090
  }
832
1091
  renderNav(onNavigate) {
833
1092
  const ariaCurrentHref = this.resolveAriaCurrentHref();
834
- return (index.h("nav", { class: "main-nav", "aria-label": "Main" }, index.h("ul", { class: "main-nav__list" }, this.getActiveMenu().map((item) => this.renderNavItem(item, onNavigate, ariaCurrentHref)))));
1093
+ return (index.h("nav", { class: "main-nav", "aria-label": MAIN_NAV_LABEL }, index.h("ul", { class: "main-nav__list" }, this.getActiveMenu().map((item) => this.renderNavItem(item, onNavigate, ariaCurrentHref)))));
835
1094
  }
836
1095
  renderCompactAuth(onNavigate) {
837
1096
  if (this.signedIn) {
@@ -847,15 +1106,28 @@ const WegHeader = class {
847
1106
  onNavigate,
848
1107
  });
849
1108
  }
1109
+ handleSkipToContent(event) {
1110
+ event.preventDefault();
1111
+ const mainContent = document.querySelector('main') ?? document.getElementById(SKIP_TO_CONTENT_TARGET_ID);
1112
+ if (mainContent instanceof HTMLElement) {
1113
+ focusScrollTarget(mainContent);
1114
+ }
1115
+ const headings = document.querySelectorAll('h1');
1116
+ if (headings.length > 0 && headings[0] instanceof HTMLElement) {
1117
+ focusScrollTarget(headings[0]);
1118
+ }
1119
+ }
850
1120
  render() {
851
1121
  const logoHref = this.getLogoHref();
852
1122
  const logoSrc = this.getLogoSrc();
853
1123
  const closeMenu = () => this.closeMenu();
854
1124
  const mobileMenuActive = this.isMobileMenuActive();
855
- return (index.h("header", { key: '6b932d0c3dc20b53292b61fa3403e26e35182d05', class: {
1125
+ return (index.h("div", { key: 'a769ab7a466cd90019570b8c3cf8e669b3df62c6', class: "weg-header-shell" }, index.h("a", { key: '43b11ec58387350df5ccf53c5a97cb94296357e5', class: "skip-to-content", href: `#${SKIP_TO_CONTENT_TARGET_ID}`, title: SKIP_TO_CONTENT_LABEL, onClick: (event) => this.handleSkipToContent(event) }, SKIP_TO_CONTENT_LABEL), index.h("header", { key: '84d893626e535525b235adc5997bac474d785732', class: {
856
1126
  header: true,
1127
+ 'header--fixed': !mobileMenuActive,
1128
+ 'header--hidden': this.headerHidden && !mobileMenuActive,
857
1129
  'header--menu-open': mobileMenuActive,
858
- } }, index.h("div", { key: '426395d6e1245188bc103c8a2f72ccfc3819ba7d', class: "header-inner" }, index.h("button", { key: 'fe6128407500f5b1a1899aa9e7fdd00222f177b3', type: "button", class: "menu-toggle icon-button", "aria-label": mobileMenuActive ? 'Close menu' : 'Open menu', "aria-expanded": mobileMenuActive ? 'true' : 'false', "aria-controls": "weg-header-main-nav", onClick: () => this.toggleMenu() }, mobileMenuActive ? index.h(CloseIcon, null) : index.h(HamburgerIcon, null)), index.h(Logo, { key: 'bb19f9430217686834c4aec615bd7306d276a969', href: logoHref, src: logoSrc }), index.h("div", { key: 'cebab248840b8412479a90a83c5fc3d0a601476b', class: "header-actions" }, this.renderCompactAuth(mobileMenuActive ? closeMenu : undefined)), index.h("div", { key: 'c029db0bcd71a9940d93bef5b32e59625f94b76c', class: "main-nav-panel", id: "weg-header-main-nav", role: mobileMenuActive ? 'dialog' : undefined, "aria-modal": mobileMenuActive ? 'true' : undefined, "aria-label": mobileMenuActive ? 'Menu' : undefined }, this.renderNav(mobileMenuActive ? closeMenu : undefined)))));
1130
+ } }, index.h("div", { key: '4cfaaf18575bf56d9c09ab6fe0db4383cc9cee94', class: "header-inner" }, index.h("div", { key: 'b9e2d18284db956c944a4e0669a6927586b6ee32', class: "sr-only", "aria-live": "polite", "aria-atomic": "true", "data-weg-sr-live": true }), index.h("button", { key: 'f019b74b76c22311c97ee3fc539f396e24ec98fd', type: "button", class: "menu-toggle icon-button", "aria-label": mobileMenuActive ? CLOSE_MAIN_NAV_LABEL : OPEN_MAIN_NAV_LABEL, "aria-expanded": mobileMenuActive ? 'true' : 'false', "aria-controls": "weg-header-main-nav", onClick: () => this.toggleMenu() }, mobileMenuActive ? index.h(CloseIcon, null) : index.h(HamburgerIcon, null)), index.h(Logo, { key: 'fe6d460021d059287b487fafb96be2a79a3253c8', href: logoHref, src: logoSrc }), index.h("div", { key: '2e59fe4e86e183aa7ad41402baba2ff6467967c1', class: "header-actions" }, this.renderCompactAuth(mobileMenuActive ? closeMenu : undefined)), index.h("div", { key: '90c8861c45b344862c03a3e2d5c2b6b6f37a6174', class: "main-nav-panel", id: "weg-header-main-nav", role: mobileMenuActive ? 'dialog' : undefined, "aria-modal": mobileMenuActive ? 'true' : undefined, "aria-label": mobileMenuActive ? MAIN_NAV_LABEL : undefined }, this.renderNav(mobileMenuActive ? closeMenu : undefined))))));
859
1131
  }
860
1132
  static get watchers() { return {
861
1133
  "layout": [{
@@ -19,7 +19,7 @@ var patchBrowser = () => {
19
19
 
20
20
  patchBrowser().then(async (options) => {
21
21
  await appGlobals.globalScripts();
22
- return index.bootstrapLazy([["weg-footer_2.cjs",[[513,"weg-footer",{"layout":[1],"resolved":[32]},null,{"layout":[{"watchLayout":0}]}],[513,"weg-header",{"layout":[1],"signedIn":[516,"signed-in"],"userName":[1,"user-name"],"accountBaseUrl":[1,"account-base-url"],"currentPath":[1,"current-path"],"resolved":[32],"menuOpen":[32],"openDropdown":[32]},[[8,"keydown","handleKeyDown"]],{"layout":[{"watchLayout":0}]}]]]], options);
22
+ return index.bootstrapLazy([["weg-footer_2.cjs",[[513,"weg-footer",{"layout":[1],"resolved":[32]},null,{"layout":[{"watchLayout":0}]}],[513,"weg-header",{"layout":[1],"signedIn":[516,"signed-in"],"userName":[1,"user-name"],"accountBaseUrl":[1,"account-base-url"],"currentPath":[1,"current-path"],"resolved":[32],"menuOpen":[32],"openDropdown":[32],"headerHidden":[32]},[[8,"keydown","handleKeyDown"]],{"layout":[{"watchLayout":0}]}]]]], options);
23
23
  });
24
24
 
25
25
  exports.setNonce = index.setNonce;
@@ -16,6 +16,33 @@
16
16
  }
17
17
 
18
18
 
19
+ .weg-header-shell {
20
+ display: block;
21
+ }
22
+
23
+ .skip-to-content {
24
+ position: fixed;
25
+ top: -100%;
26
+ left: 0;
27
+ z-index: 10001;
28
+ padding: 0.75rem 1rem;
29
+ background: #ffffff;
30
+ color: #0f172a;
31
+ font-family: var(--weg-font-family);
32
+ font-weight: 600;
33
+ text-decoration: none;
34
+ border: 2px solid #312e81;
35
+ border-radius: 0 0 0.25rem 0;
36
+ box-shadow: 0 0.25rem 0.75rem rgba(17, 24, 39, 0.15);
37
+ }
38
+
39
+ .skip-to-content:focus,
40
+ .skip-to-content:focus-visible {
41
+ top: 0;
42
+ outline: 2px solid var(--weg-color-accent);
43
+ outline-offset: 2px;
44
+ }
45
+
19
46
  .header {
20
47
  width: 100%;
21
48
  border-bottom: 2px solid var(--weg-color-accent);
@@ -33,6 +60,28 @@
33
60
  :host {
34
61
  position: relative;
35
62
  z-index: 20;
63
+ display: block;
64
+ height: var(--weg-header-height, auto);
65
+ }
66
+
67
+ .header--fixed {
68
+ position: fixed;
69
+ top: 0;
70
+ left: 0;
71
+ right: 0;
72
+ z-index: 20;
73
+ transform: translateY(0);
74
+ transition: transform 0.25s ease;
75
+ }
76
+
77
+ .header--fixed.header--hidden {
78
+ transform: translateY(-100%);
79
+ }
80
+
81
+ @media (prefers-reduced-motion: reduce) {
82
+ .header--fixed {
83
+ transition: none;
84
+ }
36
85
  }
37
86
 
38
87
  .header--menu-open {
@@ -340,6 +389,9 @@
340
389
  display: flex;
341
390
  flex-direction: column;
342
391
  gap: 0.25rem;
392
+ list-style: none;
393
+ margin: 0;
394
+ padding: 0;
343
395
  }
344
396
 
345
397
  .nav-dropdown__link {