Repository: adobecom/milo
Pull request #3865: MWPW-167717 Parallelize requests inside global-navigation.js
Author: login: sharmrj
Labels: name: verified
description: PR has been E2E tested by a reviewer
name: Ready for Stage
Created: 2025-03-26T09:24:36Z | Merged: 2025-04-28T16:10:26Z

<!-- Before submitting, please review all open PRs. -->
This is the third in a series of PRs to optimize the global-navigation load time.
1.  #3720 Load georoutingv2.json and georoutingv2.js in parallel
2. #3680 Remove await from both gnav and georouting

See the discussion #3558  for details, including the results of lab performance tests. Some discussion was had whether the parallelization of calls should go in utils.js instead of global-navigation.js, but it was ultimately decided that we did not want to add too much code to utils.js.
This is the first PR that directly touches gnav code
* Except for `utils.js`, which is expected to already have loaded in both Milo and in Standalone contexts, the requests for all required dependencies are kicked off concurrently instead of one at a time. This was done by changing the import statements at the top of the file into dynamic imports.
* Some imports are conditional, and those conditions were lifted to the top of the file where the imports are done. This is possible since these conditions depend on global state in one form or another
  * Breadcrumbs only loads if the `header` element has the class `has-breadcrumbs`, which is added in `decorateHeader` inside `utils.js` when a `breadcrumbs` block is in the document.
  * Aside only loads if the metadata `gnav-promo-source` is present in the `head`.
* The fetch of the initial gnav `plain.html` document was also lifted to the beginning of the file. `fetchAndProcessPlainHTML` was modified to optionally accept a promise.
* Global navigation testing was modified somewhat
  * This PR loads certain resources when the module is loaded rather than when the init is called. The main one we're concerned with is `getGnavSource`.
  * This means when a certain test requires a different file (even though it's mocked) to be loaded, this would not be possible since the gnav source was set when the module was loaded.
  * The solution is to 1) dynamically import `global-navigation.js` in tests where it is required rather than as a top level import, and 2) to add a unique query parameter to cause the module to reload completely.

Resolves: [MWPW-167717](https://jira.corp.adobe.com/browse/MWPW-167717)

**Test URLs:**
- Before: https://main--milo--adobecom.aem.page/?martech=off
- After: https://gnav-optimize-network-requests--milo--adobecom.aem.page/?martech=off

QA: 
A vanilla page: https://main--cc--adobecom.aem.live/products/photoshop?milolibs=gnav-optimize-network-requests
Standalone Dark Mode: https://adobecom.github.io/nav-consumer/navigation.html?env=prod&navbranch=gnav-optimize-network-requests&theme=dark
Standalone Light mode: https://adobecom.github.io/nav-consumer/navigation.html?env=prod&navbranch=gnav-optimize-network-requests

We should also test a page with a promo, and a page with the dynamic nav enabled.
If performance is being tested, it's best to use content overrides on prod and compare it to prod.

## GNav Test URLs

**Gnav + Footer + Region Picker modal:**
- Acrobat: https://main--dc--adobecom.hlx.live/acrobat?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom
- BACOM: https://main--bacom--adobecom.hlx.live/?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom
- CC: https://main--cc--adobecom.aem.live/creativecloud?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom
- Milo: https://gnav-optimize-network-requests--milo--adobecom.aem.page/drafts/blaishram/test-urls/page?martech=off
- Express: https://main--express-milo--adobecom.aem.live/express/?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom
- News: https://main--news--adobecom.aem.live/?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom
- Homepage: https://main--homepage--adobecom.aem.live/homepage/index-loggedout?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom

**Thin Gnav + ThinFooter + Region Picker dropup:**
- Acrobat: https://main--dc--adobecom.hlx.page/drafts/blaishram/test-urls/page-gnav-footer-thin?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom
- BACOM: https://main--bacom--adobecom.hlx.page/drafts/blaishram/test-urls/page-gnav-footer-thin?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom
- CC: https://main--cc--adobecom.hlx.page/drafts/blaishram/test-urls/page-gnav-footer-thin?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom
- Milo: https://gnav-optimize-network-requests--milo--adobecom.hlx.page/drafts/blaishram/test-urls/page-gnav-footer-thin?martech=off
- Express: https://main--express-milo--adobecom.hlx.page/drafts/blaishram/test-urls/page-gnav-footer-thin?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom
- News: https://main--news--adobecom.hlx.page/drafts/blaishram/test-urls/page-gnav-footer-thin?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom
- Homepage: https://main--homepage--adobecom.hlx.page/drafts/blaishram/test-urls/page-gnav-footer-thin?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom

**Localnav + Promo:**
- Acrobat: https://main--dc--adobecom.hlx.page/drafts/blaishram/test-urls/page-with-promo?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom
- BACOM: https://main--bacom--adobecom.hlx.page/drafts/blaishram/test-urls/page-with-promo?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom
- CC: https://main--cc--adobecom.hlx.page/drafts/blaishram/test-urls/page-with-promo?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom
- Milo: https://gnav-optimize-network-requests--milo--adobecom.hlx.page/drafts/blaishram/test-urls/page-with-promo?martech=off
- Express: https://main--express-milo--adobecom.hlx.page/drafts/blaishram/test-urls/page-with-promo?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom
- News: https://main--news--adobecom.hlx.page/drafts/blaishram/test-urls/page-with-promo?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom
- Homepage: https://main--homepage--adobecom.hlx.page/drafts/blaishram/test-urls/page-with-promo?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom

**Sticky Branch Banner:**
- URL: https://main--federal--adobecom.aem.page/drafts/blaishram/banner/branch-banner-sticky?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom
**Inline Branch Banner:**
- URL: https://main--federal--adobecom.aem.page/drafts/blaishram/banner/branch-banner-inline?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom

**Blog**
- URL: https://main--blog--adobecom.aem.page/?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom

**RTL Locale**
- URL: https://main--homepage--adobecom.aem.live/mena_ar/homepage/index-loggedout?martech=off&milolibs=gnav-optimize-network-requests--milo--adobecom


Diff:
diff --git a/libs/blocks/global-navigation/global-navigation.js b/libs/blocks/global-navigation/global-navigation.js
index a2d4d3f818b..d5948927ea1 100644
--- a/libs/blocks/global-navigation/global-navigation.js
+++ b/libs/blocks/global-navigation/global-navigation.js
@@ -4,12 +4,67 @@ import {
   getConfig,
   getMetadata,
   loadIms,
+  loadStyle,
+  loadLana,
   decorateLinks,
   loadScript,
   getGnavSource,
+  getFederatedUrl,
   getFedsPlaceholderConfig,
 } from '../../utils/utils.js';
-import {
+
+(async () => {
+  const { miloLibs, codeRoot, theme } = getConfig();
+  const url = `${miloLibs || codeRoot}/blocks/global-navigation/`;
+  const loadStylePromise = (u) => new Promise((resolve, reject) => {
+    loadStyle(u, (e) => {
+      if (e === 'error') return reject(u);
+      return resolve();
+    });
+  });
+  try {
+    await loadStylePromise(`${url}base.css`);
+    if (theme === 'dark') await loadStylePromise(`${url}dark-nav.css`);
+  } catch (e) {
+    const gnavSource = getMetadata('gnav-source');
+    if (!window.lana?.log) loadLana();
+    window.lana.log(`GNAV: Error in loadStyles | gnav-source: ${gnavSource} | href: ${window.location.href} | error loading style: ${e}`, {
+      clientId: 'feds-milo',
+      sampleRate: 1,
+      tags: 'utilities',
+      errorType: 'info',
+    });
+  }
+})();
+
+const plainHTMLPromise = (async () => {
+  const source = await getGnavSource();
+  const [url] = source.split('#');
+  let federatedURL = getFederatedUrl(url);
+  const mepGnav = getConfig()?.mep?.inBlock?.['global-navigation'];
+  const mepFragment = mepGnav?.fragments?.[federatedURL];
+  if (mepFragment && mepFragment.action === 'replace') {
+    federatedURL = mepFragment.content;
+  }
+  const res = await fetch(federatedURL.replace(/(\.html$|$)/, '.plain.html'));
+  return res;
+})();
+
+const asideJsPromise = getMetadata('gnav-promo-source') ? import('./features/aside/aside.js') : null;
+
+const breadCrumbsJsPromise = document.querySelector('header')?.classList.contains('has-breadcrumbs') ? import('./features/breadcrumbs/breadcrumbs.js') : null;
+
+const [utilities, placeholders, merch] = await Promise.all([
+  import('./utilities/utilities.js'),
+  import('../../features/placeholders.js'),
+  import('../merch/merch.js'),
+]);
+
+const { replaceKey, replaceKeyArray } = placeholders;
+
+const { getMiloLocaleSettings } = merch;
+
+const {
   closeAllDropdowns,
   decorateCta,
   fetchAndProcessPlainHtml,
@@ -21,7 +76,6 @@ import {
   isDesktop,
   isTangentToViewport,
   lanaLog,
-  loadBaseStyles,
   loadDecorateMenu,
   rootPath,
   loadStyles,
@@ -45,11 +99,7 @@ import {
   setAsyncDropdownCount,
   branchBannerLoadCheck,
   getBranchBannerInfo,
-} from './utilities/utilities.js';
-
-import { replaceKey, replaceKeyArray } from '../../features/placeholders.js';
-
-import { getMiloLocaleSettings } from '../merch/merch.js';
+} = utilities;
 
 const SIGNIN_CONTEXT = getConfig()?.signInContext;
 
@@ -116,9 +166,9 @@ export const CONFIG = {
               enableProfileSwitcher: true,
               miniAppContext: {
                 logger: {
-                  trace: () => {},
-                  debug: () => {},
-                  info: () => {},
+                  trace: () => { },
+                  debug: () => { },
+                  info: () => { },
                   warn: (e) => lanaLog({ message: 'Profile Menu warning', e, tags: 'universalnav', errorType: 'warn' }),
                   error: (e) => lanaLog({ message: 'Profile Menu error', e, tags: 'universalnav', errorType: 'error' }),
                 },
@@ -381,7 +431,6 @@ class Gnav {
       this.decorateMainNav,
       this.decorateTopNav,
       this.decorateTopnavWrapper,
-      loadBaseStyles,
       this.ims,
       this.addChangeEventListeners,
     ];
@@ -1012,13 +1061,13 @@ class Gnav {
     const promoPath = getMetadata('gnav-promo-source');
     const fedsPromoWrapper = document.querySelector('.feds-promo-aside-wrapper');
 
-    if (!promoPath) {
+    if (!promoPath || !asideJsPromise) {
       fedsPromoWrapper?.remove();
       this.block.classList.remove('has-promo');
       return this.elements.aside;
     }
 
-    const { default: decorate } = await import('./features/aside/aside.js');
+    const { default: decorate } = await asideJsPromise;
     if (!decorate) return this.elements.aside;
     this.elements.aside = await decorate({ headerElem: this.block, fedsPromoWrapper, promoPath });
     fedsPromoWrapper.append(this.elements.aside);
@@ -1367,7 +1416,8 @@ class Gnav {
     const breadcrumbsElem = this.block.querySelector('.breadcrumbs');
     // Breadcrumbs are not initially part of the nav, need to decorate the links
     if (breadcrumbsElem) decorateLinks(breadcrumbsElem);
-    const { default: createBreadcrumbs } = await import('./features/breadcrumbs/breadcrumbs.js');
+    if (!breadCrumbsJsPromise) return null;
+    const { default: createBreadcrumbs } = await breadCrumbsJsPromise;
     this.elements.breadcrumbsWrapper = await createBreadcrumbs(breadcrumbsElem);
     return this.elements.breadcrumbsWrapper;
   };
@@ -1412,7 +1462,7 @@ export default async function init(block) {
   if (hash === '_noActiveItem') {
     setDisableAEDState();
   }
-  const content = await fetchAndProcessPlainHtml({ url });
+  const content = await fetchAndProcessPlainHtml({ url, plainHTMLPromise });
   if (!content) {
     const error = new Error('Could not create global navigation. Content not found!');
     error.tags = 'gnav';
diff --git a/libs/blocks/global-navigation/utilities/utilities.js b/libs/blocks/global-navigation/utilities/utilities.js
index c30a9f7548b..cbcbd5a2ce0 100644
--- a/libs/blocks/global-navigation/utilities/utilities.js
+++ b/libs/blocks/global-navigation/utilities/utilities.js
@@ -378,20 +378,24 @@ export function trigger({
   if (isOpen) return false;
   element.setAttribute('aria-expanded', 'true');
   if (!isDesktop.matches && type === 'dropdown'
-      && !!document.querySelector('header.new-nav')) disableMobileScroll();
+    && !!document.querySelector('header.new-nav')) disableMobileScroll();
   return true;
 }
 
 export const yieldToMain = () => new Promise((resolve) => { setTimeout(resolve, 0); });
 
-export async function fetchAndProcessPlainHtml({ url, shouldDecorateLinks = true } = {}) {
+export async function fetchAndProcessPlainHtml({
+  url,
+  plainHTMLPromise = null,
+  shouldDecorateLinks = true,
+} = {}) {
   let path = getFederatedUrl(url);
   const mepGnav = getConfig()?.mep?.inBlock?.['global-navigation'];
   const mepFragment = mepGnav?.fragments?.[path];
   if (mepFragment && mepFragment.action === 'replace') {
     path = mepFragment.content;
   }
-  const res = await fetch(path.replace(/(\.html$|$)/, '.plain.html'));
+  const res = await (plainHTMLPromise ?? fetch(path.replace(/(\.html$|$)/, '.plain.html')));
   if (res.status !== 200) {
     lanaLog({
       message: 'Error in fetchAndProcessPlainHtml',
diff --git a/libs/utils/utils.js b/libs/utils/utils.js
index 3d7fe85a532..9c208682299 100644
--- a/libs/utils/utils.js
+++ b/libs/utils/utils.js
@@ -971,7 +971,7 @@ function decorateDefaults(el) {
 
 export async function getGnavSource() {
   const { locale, dynamicNavKey } = getConfig();
-  let url = getMetadata('gnav-source') || `${locale.contentRoot}/gnav`;
+  let url = getMetadata('gnav-source') || `${locale?.contentRoot ?? window.location.origin}/gnav`;
   if (dynamicNavKey) {
     const { default: dynamicNav } = await import('../features/dynamic-navigation/dynamic-navigation.js');
     url = dynamicNav(url, dynamicNavKey);
@@ -981,7 +981,7 @@ export async function getGnavSource() {
 
 export function isLocalNav() {
   const { locale = {} } = getConfig();
-  const gnavSource = getMetadata('gnav-source') || `${locale.contentRoot}/gnav`;
+  const gnavSource = getMetadata('gnav-source') || `${locale?.contentRoot ?? window.location.origin}/gnav`;
   let newNavEnabled = new URLSearchParams(window.location.search).get('newNav');
   newNavEnabled = newNavEnabled ? newNavEnabled !== 'false' : getMetadata('mobile-gnav-v2') !== 'off';
   return gnavSource.split('/').pop().startsWith('localnav-') && newNavEnabled;
diff --git a/test/blocks/global-navigation/global-navigation.test.js b/test/blocks/global-navigation/global-navigation.test.js
index 5cb31342c52..730b5896d6e 100644
--- a/test/blocks/global-navigation/global-navigation.test.js
+++ b/test/blocks/global-navigation/global-navigation.test.js
@@ -14,7 +14,6 @@ import {
   addMetaDataV2,
 } from './test-utilities.js';
 import { setConfig, getLocale } from '../../../libs/utils/utils.js';
-import initNav, { getUniversalNavLocale, osMap } from '../../../libs/blocks/global-navigation/global-navigation.js';
 import { isDesktop, isTangentToViewport, toFragment } from '../../../libs/blocks/global-navigation/utilities/utilities.js';
 import logoOnlyNav from './mocks/global-navigation-only-logo.plain.js';
 import longNav from './mocks/global-navigation-long.plain.js';
@@ -31,6 +30,10 @@ import { getConfig } from '../../../tools/send-to-caas/send-utils.js';
 
 async function initGnav(block) {
   try {
+    // After optimizing the gnav load, certain operations (e.g. getGnavSource) on load of
+    // the js file rather than on init. The queryparamenter supports this by reloading
+    // the module each time we get here.
+    const { default: initNav } = await import(`../../../libs/blocks/global-navigation/global-navigation.js?unique=${crypto.randomUUID()}`);
     await initNav(block);
   } catch (e) {
     // should throw error
@@ -238,7 +241,7 @@ describe('global navigation', () => {
 
         window.fetch = sinon.stub().callsFake(() => mockRes({
           payload:
-          { query_prefix: 'f', locale: 'en-US', suggested_completions: [{ name: 'framemaker', score: 578.15875, scope: 'learn' }, { name: 'fuse', score: 578.15875, scope: 'learn' }, { name: 'flash player', score: 578.15875, scope: 'learn' }, { name: 'framemaker publishing server', score: 578.15875, scope: 'learn' }, { name: 'fill & sign', score: 578.15875, scope: 'learn' }, { name: 'font folio', score: 578.15875, scope: 'learn' }, { name: 'free fonts for photoshop', score: 577.25055, scope: 'learn' }, { name: 'free lightroom presets', score: 577.25055, scope: 'learn' }, { name: 'frame', score: 577.25055, scope: 'learn' }, { name: 'frame for creative cloud', score: 577.25055, scope: 'learn' }], elastic_search_time: 1440.750028 },
+            { query_prefix: 'f', locale: 'en-US', suggested_completions: [{ name: 'framemaker', score: 578.15875, scope: 'learn' }, { name: 'fuse', score: 578.15875, scope: 'learn' }, { name: 'flash player', score: 578.15875, scope: 'learn' }, { name: 'framemaker publishing server', score: 578.15875, scope: 'learn' }, { name: 'fill & sign', score: 578.15875, scope: 'learn' }, { name: 'font folio', score: 578.15875, scope: 'learn' }, { name: 'free fonts for photoshop', score: 577.25055, scope: 'learn' }, { name: 'free lightroom presets', score: 577.25055, scope: 'learn' }, { name: 'frame', score: 577.25055, scope: 'learn' }, { name: 'frame for creative cloud', score: 577.25055, scope: 'learn' }], elastic_search_time: 1440.750028 },
         }));
 
         searchField.focus();
@@ -255,7 +258,7 @@ describe('global navigation', () => {
   describe('Gnav-logo', () => {
     describe('desktop', () => {
       it('renders the logo', async () => {
-        await createFullGlobalNavigation({ });
+        await createFullGlobalNavigation({});
 
         const logo = document.querySelector(selectors.logo);
         expect(isElementVisible(logo)).to.equal(true);
@@ -490,6 +493,7 @@ describe('global navigation', () => {
       });
 
       it('should send the correct device type', async () => {
+        const { osMap } = await import('../../../libs/blocks/global-navigation/global-navigation.js');
         const gnav = await createFullGlobalNavigation({ unavContent: 'on' });
         window.UniversalNav.resetHistory();
         const map = { Test: 'linux', ...osMap };
@@ -504,7 +508,8 @@ describe('global navigation', () => {
       });
 
       it('should send the correct locale to unav', async () => {
-        for (const data of unavLocalesTestData) {
+        const { LANGMAP, getUniversalNavLocale } = await import('../../../libs/blocks/global-navigation/global-navigation.js');
+        for (const data of unavLocalesTestData(LANGMAP)) {
           expect(getUniversalNavLocale({ prefix: data.prefix })).to.equal(data.expectedLocale);
         }
       });
diff --git a/test/blocks/global-navigation/test-utilities.js b/test/blocks/global-navigation/test-utilities.js
index 0cd594ed522..d21d887849d 100644
--- a/test/blocks/global-navigation/test-utilities.js
+++ b/test/blocks/global-navigation/test-utilities.js
@@ -2,7 +2,6 @@
 /* eslint-disable import/prefer-default-export */
 import sinon, { stub } from 'sinon';
 import { setViewport } from '@web/test-runner-commands';
-import initGnav, { LANGMAP } from '../../../libs/blocks/global-navigation/global-navigation.js';
 import { setConfig, loadStyle } from '../../../libs/utils/utils.js';
 import defaultPlaceholders from './mocks/placeholders.js';
 import defaultProfile from './mocks/profile.js';
@@ -89,7 +88,7 @@ export const addMetaDataV2 = (value) => {
   return metaTag;
 };
 
-export const unavLocalesTestData = Object.entries(LANGMAP).reduce((acc, curr) => {
+export const unavLocalesTestData = (LANGMAP) => Object.entries(LANGMAP).reduce((acc, curr) => {
   const result = [];
   const [locale, prefixes] = curr;
   prefixes.forEach((prefix) => (result.push({
@@ -227,6 +226,11 @@ export const createFullGlobalNavigation = async ({
       '../../../../libs/blocks/global-navigation/global-navigation.css',
     ),
   ]);
+  // After optimizing the gnav load, certain operations (e.g. getGnavSource) on load of
+  // the js file rather than on init. The queryparamenter supports this by reloading
+  // the module each time we get here.
+  const url = `../../../libs/blocks/global-navigation/global-navigation.js?reimport=${crypto.randomUUID()}`;
+  const { default: initGnav } = await import(url);
   const instancePromise = initGnav(document.body.querySelector('header'));
   await clock.runToLastAsync();
   clock.tick(1000);
diff --git a/test/navigation/bootstrapper.test.js b/test/navigation/bootstrapper.test.js
index 3f69a4f2a4d..b2efaf4142d 100644
--- a/test/navigation/bootstrapper.test.js
+++ b/test/navigation/bootstrapper.test.js
@@ -46,9 +46,9 @@ describe('Bootstrapper', async () => {
     });
     window.AdobeMessagingExperienceClient = window.AdobeMessagingExperienceClient
       || {
-        openMessagingWindow: () => {},
-        isAdobeMessagingClientInitialized: () => {},
-        getMessagingExperienceState: () => {},
+        openMessagingWindow: () => { },
+        isAdobeMessagingClientInitialized: () => { },
+        getMessagingExperienceState: () => { },
       };
     openMessagingWindowSpy = spy(window.AdobeMessagingExperienceClient, 'openMessagingWindow');
     setConfig({ miloLibs, contentRoot: '/federal/dev' });
@@ -98,7 +98,7 @@ describe('Bootstrapper', async () => {
     blockConfig.header.isLocalNav = true;
     blockConfig.header.mobileGnavV2 = true;
     setConfig({ contentRoot: '/federal/localnav' });
-    const { default: init } = await import('../../libs/blocks/global-navigation/global-navigation.js');
+    const { default: init } = await import('../../libs/blocks/global-navigation/global-navigation.js?unique=unique'); // eslint-disable-line
     await loadBlock(init, blockConfig.header);
     const el = document.querySelector('header');
     expect(el.nextElementSibling.classList.contains('feds-localnav')).to.be.true;


Reviews:
user: login: bandana147
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T12:31:14Z
user: login: bandana147
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T12:40:08Z
user: login: bandana147
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T06:06:37Z
user: login: nishantka
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T08:16:58Z
user: login: sharmrj
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T10:04:07Z
user: login: sharmrj
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T10:06:33Z
user: login: sharmrj
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T11:12:54Z
user: login: Deva309
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T13:52:06Z
user: login: chrischrischris
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T19:34:32Z
user: login: sharmrj
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T05:49:41Z
user: login: sharmrj
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T05:50:26Z
user: login: bandana147
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T08:49:22Z

Review comments:
diff_hunk: @@ -847,7 +847,7 @@ function decorateDefaults(el) {
 
 export async function getGnavSource() {
   const { locale, dynamicNavKey } = getConfig();
-  let url = getMetadata('gnav-source') || `${locale.contentRoot}/gnav`;
+  let url = getMetadata('gnav-source') || `${locale?.contentRoot ?? window.location.origin}/gnav`;
path: libs/utils/utils.js
user: login: bandana147
body: Why do we need `window.location.origin`, any page would always have locale.contentRoot, right?
created_at: 2025-03-27T12:31:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -994,13 +1026,13 @@ class Gnav {
     const promoPath = getMetadata('gnav-promo-source');
     const fedsPromoWrapper = document.querySelector('.feds-promo-aside-wrapper');
 
-    if (!promoPath) {
+    if (!promoPath || !asideJsPromise) {
path: libs/blocks/global-navigation/global-navigation.js
user: login: bandana147
body: Can we remove the `!promoPath` since it is already checked in asideJsPromise?
created_at: 2025-03-27T12:40:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,12 +4,50 @@ import {
   getConfig,
   getMetadata,
   loadIms,
+  loadStyle,
   decorateLinks,
   loadScript,
   getGnavSource,
+  getFederatedUrl,
   getFedsPlaceholderConfig,
 } from '../../utils/utils.js';
-import {
+
+(() => {
+  const { miloLibs, codeRoot } = getConfig();
+  const url = `${miloLibs || codeRoot}/blocks/global-navigation/`;
+  loadStyle(`${url}base.css`, () => {
+    if (getConfig().theme === 'dark') loadStyle(`${url}dark-nav.css`);
+  });
+})();
path: libs/blocks/global-navigation/global-navigation.js
user: login: bandana147
body: @nishantka Can you check this once? I remember you encountered an issue while implementing this and had to explicitly use a promise.
created_at: 2025-03-28T06:06:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,12 +4,50 @@ import {
   getConfig,
   getMetadata,
   loadIms,
+  loadStyle,
   decorateLinks,
   loadScript,
   getGnavSource,
+  getFederatedUrl,
   getFedsPlaceholderConfig,
 } from '../../utils/utils.js';
-import {
+
+(() => {
+  const { miloLibs, codeRoot } = getConfig();
+  const url = `${miloLibs || codeRoot}/blocks/global-navigation/`;
+  loadStyle(`${url}base.css`, () => {
+    if (getConfig().theme === 'dark') loadStyle(`${url}dark-nav.css`);
+  });
+})();
path: libs/blocks/global-navigation/global-navigation.js
user: login: nishantka
body: This will work, we just have to load `dark-nav.css` after `base.css`. We were using callback for adding Lana logs here - if we can catch errors also that will be great.
created_at: 2025-03-28T08:16:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -994,13 +1026,13 @@ class Gnav {
     const promoPath = getMetadata('gnav-promo-source');
     const fedsPromoWrapper = document.querySelector('.feds-promo-aside-wrapper');
 
-    if (!promoPath) {
+    if (!promoPath || !asideJsPromise) {
path: libs/blocks/global-navigation/global-navigation.js
user: login: sharmrj
body: I'd like to leave this code a bit defensive, even if it's just for the sake of unit tests. If ever there arises a situation where `asideJSPromise` is null but `promoPath` exists, we'll still get a promo.
created_at: 2025-04-02T10:04:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -847,7 +847,7 @@ function decorateDefaults(el) {
 
 export async function getGnavSource() {
   const { locale, dynamicNavKey } = getConfig();
-  let url = getMetadata('gnav-source') || `${locale.contentRoot}/gnav`;
+  let url = getMetadata('gnav-source') || `${locale?.contentRoot ?? window.location.origin}/gnav`;
path: libs/utils/utils.js
user: login: sharmrj
body: Not necessarily: some unit tests were failing because of this, which made me aware of the fact that it's possible, in certain situations, to have no `locale.contentRoot`. Defensive code, in general, is better than code that makes assumptions that can be wrong as it allows us to fail gracefully.
created_at: 2025-04-02T10:06:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,12 +4,50 @@ import {
   getConfig,
   getMetadata,
   loadIms,
+  loadStyle,
   decorateLinks,
   loadScript,
   getGnavSource,
+  getFederatedUrl,
   getFedsPlaceholderConfig,
 } from '../../utils/utils.js';
-import {
+
+(() => {
+  const { miloLibs, codeRoot } = getConfig();
+  const url = `${miloLibs || codeRoot}/blocks/global-navigation/`;
+  loadStyle(`${url}base.css`, () => {
+    if (getConfig().theme === 'dark') loadStyle(`${url}dark-nav.css`);
+  });
+})();
path: libs/blocks/global-navigation/global-navigation.js
user: login: sharmrj
body: @bandana147 @nishantka  I've added some error handling here. 
created_at: 2025-04-02T11:12:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,12 +4,67 @@ import {
   getConfig,
   getMetadata,
   loadIms,
+  loadStyle,
+  loadLana,
   decorateLinks,
   loadScript,
   getGnavSource,
+  getFederatedUrl,
   getFedsPlaceholderConfig,
 } from '../../utils/utils.js';
-import {
+
+(async () => {
+  const { miloLibs, codeRoot, theme } = getConfig();
+  const url = `${miloLibs || codeRoot}/blocks/global-navigation/`;
+  const loadStylePromise = (u) => new Promise((resolve, reject) => {
+    loadStyle(u, (e) => {
+      if (e === 'error') return reject(u);
+      return resolve();
+    });
+  });
+  try {
+    await loadStylePromise(`${url}base.css`);
+    if (theme === 'dark') await loadStylePromise(`${url}dark-nav.css`);
path: libs/blocks/global-navigation/global-navigation.js
user: login: chrischrischris
body: Does the css loading need to be `await`ed?  If so - probably better to parallel load these
```suggestion
    await Promise.all([
      loadStylePromise(`${url}base.css`),
      theme === 'dark' ? loadStylePromise(`${url}dark-nav.css`) : true,
    ]);
```
created_at: 2025-04-10T19:27:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,12 +4,67 @@ import {
   getConfig,
   getMetadata,
   loadIms,
+  loadStyle,
+  loadLana,
   decorateLinks,
   loadScript,
   getGnavSource,
+  getFederatedUrl,
   getFedsPlaceholderConfig,
 } from '../../utils/utils.js';
-import {
+
+(async () => {
+  const { miloLibs, codeRoot, theme } = getConfig();
+  const url = `${miloLibs || codeRoot}/blocks/global-navigation/`;
+  const loadStylePromise = (u) => new Promise((resolve, reject) => {
+    loadStyle(u, (e) => {
+      if (e === 'error') return reject(u);
+      return resolve();
+    });
+  });
+  try {
+    await loadStylePromise(`${url}base.css`);
+    if (theme === 'dark') await loadStylePromise(`${url}dark-nav.css`);
+  } catch (e) {
+    const gnavSource = getMetadata('gnav-source');
+    if (!window.lana?.log) loadLana();
+    window.lana.log(`GNAV: Error in loadStyles | gnav-source: ${gnavSource} | href: ${window.location.href} | error loading style: ${e}`, {
+      clientId: 'feds-milo',
+      sampleRate: 1,
+      tags: 'utilities',
+      errorType: 'info',
+    });
+  }
+})();
+
+const plainHTMLPromise = (async () => {
+  const source = await getGnavSource();
+  const [url] = source.split('#');
+  let federatedURL = getFederatedUrl(url);
+  const mepGnav = getConfig()?.mep?.inBlock?.['global-navigation'];
+  const mepFragment = mepGnav?.fragments?.[federatedURL];
+  if (mepFragment && mepFragment.action === 'replace') {
path: libs/blocks/global-navigation/global-navigation.js
user: login: chrischrischris
body: nit
```suggestion
  if (mepFragment?.action === 'replace') {
```
created_at: 2025-04-10T19:29:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,12 +4,67 @@ import {
   getConfig,
   getMetadata,
   loadIms,
+  loadStyle,
+  loadLana,
   decorateLinks,
   loadScript,
   getGnavSource,
+  getFederatedUrl,
   getFedsPlaceholderConfig,
 } from '../../utils/utils.js';
-import {
+
+(async () => {
+  const { miloLibs, codeRoot, theme } = getConfig();
+  const url = `${miloLibs || codeRoot}/blocks/global-navigation/`;
+  const loadStylePromise = (u) => new Promise((resolve, reject) => {
+    loadStyle(u, (e) => {
+      if (e === 'error') return reject(u);
+      return resolve();
+    });
+  });
+  try {
+    await loadStylePromise(`${url}base.css`);
+    if (theme === 'dark') await loadStylePromise(`${url}dark-nav.css`);
path: libs/blocks/global-navigation/global-navigation.js
user: login: sharmrj
body: `dark-nav.css` must load after base.css. If we load it in parallel, then if `dark-nav.css` loads first, the dark mode gnav will break (this is for a specific standalone consumer). In Milo consumers we don't have dark mode.
created_at: 2025-04-16T05:49:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,12 +4,67 @@ import {
   getConfig,
   getMetadata,
   loadIms,
+  loadStyle,
+  loadLana,
   decorateLinks,
   loadScript,
   getGnavSource,
+  getFederatedUrl,
   getFedsPlaceholderConfig,
 } from '../../utils/utils.js';
-import {
+
+(async () => {
+  const { miloLibs, codeRoot, theme } = getConfig();
+  const url = `${miloLibs || codeRoot}/blocks/global-navigation/`;
+  const loadStylePromise = (u) => new Promise((resolve, reject) => {
+    loadStyle(u, (e) => {
+      if (e === 'error') return reject(u);
+      return resolve();
+    });
+  });
+  try {
+    await loadStylePromise(`${url}base.css`);
+    if (theme === 'dark') await loadStylePromise(`${url}dark-nav.css`);
+  } catch (e) {
+    const gnavSource = getMetadata('gnav-source');
+    if (!window.lana?.log) loadLana();
+    window.lana.log(`GNAV: Error in loadStyles | gnav-source: ${gnavSource} | href: ${window.location.href} | error loading style: ${e}`, {
+      clientId: 'feds-milo',
+      sampleRate: 1,
+      tags: 'utilities',
+      errorType: 'info',
+    });
+  }
+})();
+
+const plainHTMLPromise = (async () => {
+  const source = await getGnavSource();
+  const [url] = source.split('#');
+  let federatedURL = getFederatedUrl(url);
+  const mepGnav = getConfig()?.mep?.inBlock?.['global-navigation'];
+  const mepFragment = mepGnav?.fragments?.[federatedURL];
+  if (mepFragment && mepFragment.action === 'replace') {
path: libs/blocks/global-navigation/global-navigation.js
user: login: sharmrj
body: I'll address this in the next PR, which will be raised soon after this one.
created_at: 2025-04-16T05:50:26Z
author_association: CONTRIBUTOR

PR comments:
user: login: aem-code-sync[bot]
created_at: 2025-03-26T09:24:39Z
body: <!-- aem-bot-dash -->
Hello, I'm the AEM Code Sync Bot and I will run some actions to deploy your branch and validate page speed.
In case there are problems, just click a checkbox below to rerun the respective action.

- [ ] <!-- action-psi --> Re-run PSI checks
- [ ] <!-- action-sync --> Re-sync branch

<details>
<summary>Commits</summary>
<!-- commit-history -->

* [ce17f18](https://github.com/adobecom/milo/commit/ce17f18f061460a842267247755d8a01cef3e619) :white_check_mark: (latest)
* [49e772e](https://github.com/adobecom/milo/commit/49e772e8cd7d04c87f29f7e25f693630ed0d3610) :white_check_mark:
* [d536f37](https://github.com/adobecom/milo/commit/d536f37cfe9f3b99c55e89e6afbc7525dd195fb0) :white_check_mark:
* [0b6ce6f](https://github.com/adobecom/milo/commit/0b6ce6fa06cfeee2fa7edb060cece1fadae3ed02) :white_check_mark:
* [bdb94d7](https://github.com/adobecom/milo/commit/bdb94d769a102cef42e0c9349e9af872282f574d) :white_check_mark:
* [0214479](https://github.com/adobecom/milo/commit/0214479596871887b899bfeefce9903cff9c307f) :white_check_mark:
* [0e65f7e](https://github.com/adobecom/milo/commit/0e65f7ea93be56337281c8826528e40552bfe5d0) :white_check_mark:
* [91c6202](https://github.com/adobecom/milo/commit/91c6202079376d130e27aa5ebefcc45b05af8620) :white_check_mark:
* [12e31a4](https://github.com/adobecom/milo/commit/12e31a4100600d643d82f5f2d96508d4ee21eadf) :white_check_mark:
* [39db841](https://github.com/adobecom/milo/commit/39db841016875450521aa1e1d9e467201ec0f91f) :white_check_mark:
* [89199cb](https://github.com/adobecom/milo/commit/89199cbaf355d2742ff5c5acbd741f74d7b26907) :white_check_mark:
* [d8ff070](https://github.com/adobecom/milo/commit/d8ff070b7ea2d9486bd5e28df3d564195c782068) :white_check_mark:
* [29147e7](https://github.com/adobecom/milo/commit/29147e784f181b1089597509b15df4427b6a2a51) :white_check_mark:
* [b3e4fff](https://github.com/adobecom/milo/commit/b3e4fff9b1e0348ed7251280efbe0c31ee82e715) :white_check_mark:
* [daa4849](https://github.com/adobecom/milo/commit/daa48497d6935090991352d630ff96219abae6c7) :white_check_mark:
* [d752143](https://github.com/adobecom/milo/commit/d752143e72af74dfcef4f4b1cf9878a2f368b22e) :white_check_mark:

<!-- commit-history-end -->
</details>
author_association: CONTRIBUTOR
user: login: aem-code-sync[bot]
created_at: 2025-03-26T09:24:55Z
body: <!-- aem-bot-psi -->
| | Page | Scores | Audits | Google |
|-|------|--------|--------|--------|
| :iphone: | [/?martech=off](https://gnav-optimize-network-requests--milo--adobecom.aem.page/?martech=off) | [![PERFORMANCE](https://img.shields.io/badge/PERFORMANCE-98-green?style=for-the-badge "Performance")](https://googlechrome.github.io/lighthouse/scorecalc/#device=mobile&version=12.4.0&SI=3777.8949861188817&FCP=844.8485449999998&LCP=1962.1111462499998&TBT=0&CLS=0 "See Calculator") ![A11Y](https://img.shields.io/badge/A11Y-96-green?style=for-the-badge "Accessibility") ![SEO](https://img.shields.io/badge/SEO-69-orange?style=for-the-badge "SEO") ![BEST PRACTICES](https://img.shields.io/badge/BEST%20PRACTICES-96-green?style=for-the-badge "Best Practices") | ![SI](https://img.shields.io/badge/SI-3.8s-orange?style=for-the-badge "Speed Index") ![FCP](https://img.shields.io/badge/FCP-0.8s-green?style=for-the-badge "First Contentful Paint") ![LCP](https://img.shields.io/badge/LCP-2.0s-green?style=for-the-badge "Largest Contentful Paint") ![TBT](https://img.shields.io/badge/TBT-0ms-green?style=for-the-badge "Total Blocking Time") ![CLS](https://img.shields.io/badge/CLS-0-green?style=for-the-badge "Cumulative Layout Shift") | [![PSI](https://www.gstatic.com/images/icons/material/product/2x/pagespeed_16dp.png)](https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Fgnav-optimize-network-requests--milo--adobecom.aem.page%2F%3Fmartech%3Doff "Run again in Google PSI") |
| :desktop_computer: | [/?martech=off](https://gnav-optimize-network-requests--milo--adobecom.aem.page/?martech=off) | [![PERFORMANCE](https://img.shields.io/badge/PERFORMANCE-99-green?style=for-the-badge "Performance")](https://googlechrome.github.io/lighthouse/scorecalc/#device=mobile&version=12.4.0&SI=1317.4589900421106&FCP=232.70782125&LCP=520.5631000000001&TBT=0&CLS=0 "See Calculator") ![A11Y](https://img.shields.io/badge/A11Y-96-green?style=for-the-badge "Accessibility") ![SEO](https://img.shields.io/badge/SEO-69-orange?style=for-the-badge "SEO") ![BEST PRACTICES](https://img.shields.io/badge/BEST%20PRACTICES-93-green?style=for-the-badge "Best Practices") | ![SI](https://img.shields.io/badge/SI-1.3s-orange?style=for-the-badge "Speed Index") ![FCP](https://img.shields.io/badge/FCP-0.2s-green?style=for-the-badge "First Contentful Paint") ![LCP](https://img.shields.io/badge/LCP-0.5s-green?style=for-the-badge "Largest Contentful Paint") ![TBT](https://img.shields.io/badge/TBT-0ms-green?style=for-the-badge "Total Blocking Time") ![CLS](https://img.shields.io/badge/CLS-0-green?style=for-the-badge "Cumulative Layout Shift") | [![PSI](https://www.gstatic.com/images/icons/material/product/2x/pagespeed_16dp.png)](https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Fgnav-optimize-network-requests--milo--adobecom.aem.page%2F%3Fmartech%3Doff "Run again in Google PSI") |
| :iphone: | [/drafts/blaishram/test-urls/page?martech=off](https://gnav-optimize-network-requests--milo--adobecom.aem.page/drafts/blaishram/test-urls/page?martech=off) | [![PERFORMANCE](https://img.shields.io/badge/PERFORMANCE-99-green?style=for-the-badge "Performance")](https://googlechrome.github.io/lighthouse/scorecalc/#device=mobile&version=12.4.0&SI=3400.530738019867&FCP=932.3008399999999&LCP=1676.6790399999998&TBT=0&CLS=0.0019038884489270369 "See Calculator") ![A11Y](https://img.shields.io/badge/A11Y-100-green?style=for-the-badge "Accessibility") ![SEO](https://img.shields.io/badge/SEO-69-orange?style=for-the-badge "SEO") ![BEST PRACTICES](https://img.shields.io/badge/BEST%20PRACTICES-93-green?style=for-the-badge "Best Practices") | ![SI](https://img.shields.io/badge/SI-3.4s-orange?style=for-the-badge "Speed Index") ![FCP](https://img.shields.io/badge/FCP-0.9s-green?style=for-the-badge "First Contentful Paint") ![LCP](https://img.shields.io/badge/LCP-1.7s-green?style=for-the-badge "Largest Contentful Paint") ![TBT](https://img.shields.io/badge/TBT-0ms-green?style=for-the-badge "Total Blocking Time") ![CLS](https://img.shields.io/badge/CLS-0.002-green?style=for-the-badge "Cumulative Layout Shift") | [![PSI](https://www.gstatic.com/images/icons/material/product/2x/pagespeed_16dp.png)](https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Fgnav-optimize-network-requests--milo--adobecom.aem.page%2Fdrafts%2Fblaishram%2Ftest-urls%2Fpage%3Fmartech%3Doff "Run again in Google PSI") |
| :desktop_computer: | [/drafts/blaishram/test-urls/page?martech=off](https://gnav-optimize-network-requests--milo--adobecom.aem.page/drafts/blaishram/test-urls/page?martech=off) | [![PERFORMANCE](https://img.shields.io/badge/PERFORMANCE-99-green?style=for-the-badge "Performance")](https://googlechrome.github.io/lighthouse/scorecalc/#device=mobile&version=12.4.0&SI=1421.9024099637902&FCP=261.97985&LCP=458.8705075&TBT=0&CLS=0.0004023435996063968 "See Calculator") ![A11Y](https://img.shields.io/badge/A11Y-99-green?style=for-the-badge "Accessibility") ![SEO](https://img.shields.io/badge/SEO-61-orange?style=for-the-badge "SEO") ![BEST PRACTICES](https://img.shields.io/badge/BEST%20PRACTICES-96-green?style=for-the-badge "Best Practices") | ![SI](https://img.shields.io/badge/SI-1.4s-orange?style=for-the-badge "Speed Index") ![FCP](https://img.shields.io/badge/FCP-0.3s-green?style=for-the-badge "First Contentful Paint") ![LCP](https://img.shields.io/badge/LCP-0.5s-green?style=for-the-badge "Largest Contentful Paint") ![TBT](https://img.shields.io/badge/TBT-0ms-green?style=for-the-badge "Total Blocking Time") ![CLS](https://img.shields.io/badge/CLS-0-green?style=for-the-badge "Cumulative Layout Shift") | [![PSI](https://www.gstatic.com/images/icons/material/product/2x/pagespeed_16dp.png)](https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Fgnav-optimize-network-requests--milo--adobecom.aem.page%2Fdrafts%2Fblaishram%2Ftest-urls%2Fpage%3Fmartech%3Doff "Run again in Google PSI") |
| :iphone: | [/drafts/blaishram/test-urls/page-gnav-footer-thin?martech=off](https://gnav-optimize-network-requests--milo--adobecom.hlx.page/drafts/blaishram/test-urls/page-gnav-footer-thin?martech=off) | [![PERFORMANCE](https://img.shields.io/badge/PERFORMANCE-97-green?style=for-the-badge "Performance")](https://googlechrome.github.io/lighthouse/scorecalc/#device=mobile&version=12.4.0&SI=4239.343762724741&FCP=945.5959400000002&LCP=1727.7157900000002&TBT=15&CLS=0.0019038884489270369 "See Calculator") ![A11Y](https://img.shields.io/badge/A11Y-98-green?style=for-the-badge "Accessibility") ![SEO](https://img.shields.io/badge/SEO-69-orange?style=for-the-badge "SEO") ![BEST PRACTICES](https://img.shields.io/badge/BEST%20PRACTICES-96-green?style=for-the-badge "Best Practices") | ![SI](https://img.shields.io/badge/SI-4.2s-orange?style=for-the-badge "Speed Index") ![FCP](https://img.shields.io/badge/FCP-0.9s-green?style=for-the-badge "First Contentful Paint") ![LCP](https://img.shields.io/badge/LCP-1.7s-green?style=for-the-badge "Largest Contentful Paint") ![TBT](https://img.shields.io/badge/TBT-20ms-green?style=for-the-badge "Total Blocking Time") ![CLS](https://img.shields.io/badge/CLS-0.002-green?style=for-the-badge "Cumulative Layout Shift") | [![PSI](https://www.gstatic.com/images/icons/material/product/2x/pagespeed_16dp.png)](https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Fgnav-optimize-network-requests--milo--adobecom.hlx.page%2Fdrafts%2Fblaishram%2Ftest-urls%2Fpage-gnav-footer-thin%3Fmartech%3Doff "Run again in Google PSI") |
| :desktop_computer: | [/drafts/blaishram/test-urls/page-gnav-footer-thin?martech=off](https://gnav-optimize-network-requests--milo--adobecom.hlx.page/drafts/blaishram/test-urls/page-gnav-footer-thin?martech=off) | [![PERFORMANCE](https://img.shields.io/badge/PERFORMANCE-98-green?style=for-the-badge "Performance")](https://googlechrome.github.io/lighthouse/scorecalc/#device=mobile&version=12.4.0&SI=1578.7999901395685&FCP=276.22121999999996&LCP=509.12190499999997&TBT=0&CLS=0.0013505605724132628 "See Calculator") ![A11Y](https://img.shields.io/badge/A11Y-98-green?style=for-the-badge "Accessibility") ![SEO](https://img.shields.io/badge/SEO-69-orange?style=for-the-badge "SEO") ![BEST PRACTICES](https://img.shields.io/badge/BEST%20PRACTICES-96-green?style=for-the-badge "Best Practices") | ![SI](https://img.shields.io/badge/SI-1.6s-orange?style=for-the-badge "Speed Index") ![FCP](https://img.shields.io/badge/FCP-0.3s-green?style=for-the-badge "First Contentful Paint") ![LCP](https://img.shields.io/badge/LCP-0.5s-green?style=for-the-badge "Largest Contentful Paint") ![TBT](https://img.shields.io/badge/TBT-0ms-green?style=for-the-badge "Total Blocking Time") ![CLS](https://img.shields.io/badge/CLS-0.001-green?style=for-the-badge "Cumulative Layout Shift") | [![PSI](https://www.gstatic.com/images/icons/material/product/2x/pagespeed_16dp.png)](https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Fgnav-optimize-network-requests--milo--adobecom.hlx.page%2Fdrafts%2Fblaishram%2Ftest-urls%2Fpage-gnav-footer-thin%3Fmartech%3Doff "Run again in Google PSI") |
| :iphone: | [/drafts/blaishram/test-urls/page-with-promo?martech=off](https://gnav-optimize-network-requests--milo--adobecom.hlx.page/drafts/blaishram/test-urls/page-with-promo?martech=off) | [![PERFORMANCE](https://img.shields.io/badge/PERFORMANCE-98-green?style=for-the-badge "Performance")](https://googlechrome.github.io/lighthouse/scorecalc/#device=mobile&version=12.4.0&SI=3648.7345568746564&FCP=910.05705&LCP=1661.2882175&TBT=0&CLS=0 "See Calculator") ![A11Y](https://img.shields.io/badge/A11Y-95-green?style=for-the-badge "Accessibility") ![SEO](https://img.shields.io/badge/SEO-61-orange?style=for-the-badge "SEO") ![BEST PRACTICES](https://img.shields.io/badge/BEST%20PRACTICES-96-green?style=for-the-badge "Best Practices") | ![SI](https://img.shields.io/badge/SI-3.6s-orange?style=for-the-badge "Speed Index") ![FCP](https://img.shields.io/badge/FCP-0.9s-green?style=for-the-badge "First Contentful Paint") ![LCP](https://img.shields.io/badge/LCP-1.7s-green?style=for-the-badge "Largest Contentful Paint") ![TBT](https://img.shields.io/badge/TBT-0ms-green?style=for-the-badge "Total Blocking Time") ![CLS](https://img.shields.io/badge/CLS-0-green?style=for-the-badge "Cumulative Layout Shift") | [![PSI](https://www.gstatic.com/images/icons/material/product/2x/pagespeed_16dp.png)](https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Fgnav-optimize-network-requests--milo--adobecom.hlx.page%2Fdrafts%2Fblaishram%2Ftest-urls%2Fpage-with-promo%3Fmartech%3Doff "Run again in Google PSI") |
| :desktop_computer: | [/drafts/blaishram/test-urls/page-with-promo?martech=off](https://gnav-optimize-network-requests--milo--adobecom.hlx.page/drafts/blaishram/test-urls/page-with-promo?martech=off) | [![PERFORMANCE](https://img.shields.io/badge/PERFORMANCE-97-green?style=for-the-badge "Performance")](https://googlechrome.github.io/lighthouse/scorecalc/#device=mobile&version=12.4.0&SI=1852.66860660614&FCP=275.03812&LCP=482.31066&TBT=0&CLS=0.000147320630469384 "See Calculator") ![A11Y](https://img.shields.io/badge/A11Y-99-green?style=for-the-badge "Accessibility") ![SEO](https://img.shields.io/badge/SEO-61-orange?style=for-the-badge "SEO") ![BEST PRACTICES](https://img.shields.io/badge/BEST%20PRACTICES-96-green?style=for-the-badge "Best Practices") | ![SI](https://img.shields.io/badge/SI-1.9s-orange?style=for-the-badge "Speed Index") ![FCP](https://img.shields.io/badge/FCP-0.3s-green?style=for-the-badge "First Contentful Paint") ![LCP](https://img.shields.io/badge/LCP-0.5s-green?style=for-the-badge "Largest Contentful Paint") ![TBT](https://img.shields.io/badge/TBT-0ms-green?style=for-the-badge "Total Blocking Time") ![CLS](https://img.shields.io/badge/CLS-0-green?style=for-the-badge "Cumulative Layout Shift") | [![PSI](https://www.gstatic.com/images/icons/material/product/2x/pagespeed_16dp.png)](https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Fgnav-optimize-network-requests--milo--adobecom.hlx.page%2Fdrafts%2Fblaishram%2Ftest-urls%2Fpage-with-promo%3Fmartech%3Doff "Run again in Google PSI") |

author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-27T01:43:14Z
body: This pull request is not passing all required checks. Please see [this discussion](https://github.com/orgs/adobecom/discussions/997) for information on how to get all checks passing. Inconsistent checks can be manually retried. If a test absolutely can not pass for a good reason, please add a comment with an explanation to the PR.
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-11T01:45:59Z
body: Reminder to set the `Ready for Stage` label - to queue this to get merged to stage & production.
author_association: CONTRIBUTOR
user: login: prativas22
created_at: 2025-04-25T13:07:24Z
body: Verified the PR to optimize the global-navigation load time. here below are the result from Desktop and Mobile mode

Performance reading without repo:
https://main--cc--adobecom.aem.live/products/photoshop
![image](https://github.com/user-attachments/assets/c68a46a2-8889-434e-96db-a8b5ec749d4f)

Performance reading with repo:
https://main--cc--adobecom.aem.live/products/photoshop?milolibs=gnav-optimize-network-requests
![image](https://github.com/user-attachments/assets/f4bc6ffa-3236-4d80-b048-1de899b2e9d4)
The five consecutive performance readings are shown below.
![image](https://github.com/user-attachments/assets/18b3800f-acb9-4d56-8e8a-695bc4d75970)

author_association: NONE