Repository navigation
Mega menu: the collapsed height holds before the script - #34
Merged
Merged
Conversation
Below its breakpoint the menu was served with the full list showing and the Menu button hidden; the script then folded the list away. On superherotech.ai at 412px the header went from 233px to 69px when the script ran (PageSpeed CLS 0.307 on every page, or a late LCP). - Each nav now opens with a <style> keyed on its data-breakpoint: @media (scripting: enabled) and (max-width: <breakpoint - 0.02>px), the script's own matchMedia question, shows the Menu button and folds the list until the script takes over (.mm:not(.mm--js)). Without JavaScript the full list shows and there is no Menu button, as before. - The Menu button is no longer served hidden; the stylesheet shows it in the collapsed layout only, and the script no longer unhides it. - Above the breakpoint a panel item's link carries the caret of the button that replaces it, and both are border-box with the same text settings, so the bar keeps its width when the script swaps them (it grew 40px on the site at 1280px). - A collapsed menu is set up the moment the browser reaches its script, so its Menu button is live as early as possible; a wide menu still waits for DOMContentLoaded. - check-mega-menu pins all of it (45/45 mutants killed). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
On phones the mega menu was served with every top link showing and the Menu button
hidden. The script then folded the list away. On superherotech.ai at 412px the header was 233px tall as served and 69px after the script, so the page below jumped up 164px. PageSpeed reported this as CLS 0.307 on every page, or as a late LCP when the hero started below the fold. Every site that uses this element has the same problem.What changed (
src/library/mega-menu/MegaMenu.astro)<style>keyed on its owndata-breakpoint:@media (scripting: enabled) and (max-width: <breakpoint − 0.02>px) { .mm:not(.mm--js)[data-breakpoint="N"] > .mm__toggle { display: inline-flex } … > .mm__bar { display: none } }It asks the same question as the script's
matchMedia. Each breakpoint gets its own rule, so two menus with different breakpoints on one page don't collide.:not(.mm--js)hands control to the script's classes once it runs. Breakpoint 0 emits nothing.hidden. The stylesheet hides.mm__toggleand shows it in the collapsed layout only (.mm--compact, or the pre-script rule). The script no longer unhides it. No new!important.scriptingmedia feature shows the full list until the script folds it, as before: it still shifts, but the menu works..mm__top, .mm__togglearebox-sizing: border-boxand the button inheritsletter-spacing,word-spacingandtext-transform, so the swap no longer widens the bar. Before, it grew 40px on the site at 1280px.breakpointnote plus one accessibility line).Check (
scripts/check-mega-menu.mjs, new stage "5. no shift")// <mm-prescript>block for breakpoints 960, 720, 1040 and "1200". Each must give one(scripting: enabled)rule at exactly the script'smatchMediathreshold, computed from the script's own source, keyed on that breakpoint. The rule must show the toggle and fold the list. 1040 and 720 must not name each other, and 0 must emit nothing.hidden.toggle.hidden..mm__toggleisdisplay: noneexcept under.mm--compact. Only.mm--compacthides.mm__bar..mm__top, .mm__toggleis border-box and inherits the text settings,start()runs at once, and everymatchMediain the script asks the same question.npm run build && npm run checkpasses.Measurements (headless Chromium,
layout-shiftentries collected with a buffered PerformanceObserver)Method. The menu's script is held back 1.5 s (its body wrapped in
setTimeout, whether it ships inline or as a CSP-moved/_astro/inline/*.js). The page as served is measured with scripting on, then again after the script./· 412/· 1280/elements/mega-menu/· 412/elements/mega-menu/· 800 (site menu at 1040 collapsed, demo menu at 720 wide)/elements/mega-menu/· 1280/mega-menu/· 412/mega-menu/· 1280Without JavaScript at 412px: the full list shows, no Menu button, site header 231.75px as before.
Unmodified pages with the CPU throttled 6×:
/: the horizontal shift from the link-to-button swap, present in every run before, is gone..header__actions(46.75px, after the nav in the markup) exists, and the header then grows from 69 to 71.75px. It also appears with the menu's script removed. Fixing it is a site change: give.header__bara minimum height of the button's height above 1040px.Functional (site build, Playwright):
Site (superherotech.ai)
Built from
Websiteorigin/main in a separate worktree withELEMENTS_SOURCE=<this branch>.npm run buildandnpm run checkboth pass. No site check pins thehiddentoggle or other markup this changes, so the site needs no update. Nothing was pushed or deployed. The site picks this up at its next build after merge.🤖 Generated with Claude Code