diff --git a/assets/css/style.css b/assets/css/style.css index 79bef7e..b94bbd2 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -136,6 +136,57 @@ nav a { box-sizing: border-box; } +.portal-nav__menu-toggle { + display: none; + flex-shrink: 0; + margin-left: auto; + padding: 10px 12px; + border: 0; + border-radius: 10px; + background: transparent; + cursor: pointer; + color: var(--primary); +} + +.portal-nav__menu-toggle:focus-visible { + outline: 2px solid var(--primary); + outline-offset: 3px; +} + +.portal-nav__menu-toggle-icon { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 24px; + height: 18px; +} + +.portal-nav__menu-toggle-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; + background: currentColor; + transition: transform 0.2s ease, opacity 0.2s ease; +} + +.portal-nav__menu-toggle-bar+.portal-nav__menu-toggle-bar { + margin-top: 5px; +} + +.portal-nav.portal-nav--open .portal-nav__menu-toggle-bar:nth-child(1) { + transform: translateY(7px) rotate(45deg); +} + +.portal-nav.portal-nav--open .portal-nav__menu-toggle-bar:nth-child(2) { + opacity: 0; +} + +.portal-nav.portal-nav--open .portal-nav__menu-toggle-bar:nth-child(3) { + transform: translateY(-7px) rotate(-45deg); +} + .portal-nav>ul { display: flex; flex-wrap: wrap; @@ -205,7 +256,7 @@ nav a { color: #3a300a; } -/* 淪陷瞬間 · 全員發瘋:第一層向下;第二/三層向右飛出(僅 ≤767px 直向堆疊,避免 1024 平板也壓成直排) */ +/* 淪陷瞬間 · 全員發瘋:第一層向下;第二/三層向右飛出(≥992px);≤991px 漢堡+直向堆疊 */ .portal-nav .portal-submenu .portal-nav__subgroup { position: relative; z-index: 0; @@ -349,7 +400,7 @@ nav a { display: flex; } -@media (min-width: 768px) { +@media (min-width: 992px) { .portal-nav .portal-nav__subgroup>.portal-submenu-nested { position: absolute !important; @@ -396,7 +447,63 @@ nav a { } } -@media (max-width: 767px) { +@media (max-width: 991px) { + + .portal-nav__menu-toggle { + display: inline-flex; + align-items: center; + justify-content: center; + } + + .portal-nav>ul { + display: none; + flex: 1 1 100%; + flex-direction: column; + align-items: stretch; + justify-content: flex-start; + gap: 0; + width: 100%; + margin-top: 4px; + padding: 8px 0 12px; + border-top: 1px solid rgba(255, 204, 0, 0.2); + } + + .portal-nav.portal-nav--open>ul { + display: flex; + } + + .portal-nav>ul>li { + width: 100%; + position: relative; + } + + .portal-nav>ul>li>a { + justify-content: flex-start; + width: 100%; + min-height: 44px; + padding: 10px 8px; + box-sizing: border-box; + } + + .portal-nav>ul>li:hover>.portal-submenu, + .portal-nav>ul>li:focus-within>.portal-submenu { + display: none; + } + + .portal-nav>ul>li.open>.portal-submenu { + display: grid; + } + + .portal-nav .portal-submenu { + position: static; + width: 100%; + min-width: 0; + margin-top: 4px; + padding: 6px 8px 10px; + box-shadow: none; + border-radius: 10px; + background: rgba(255, 255, 255, 0.06); + } .portal-nav .portal-submenu .portal-nav__subgroup-head::after, .portal-nav .portal-nav__subgroup-head--nested::after { @@ -422,9 +529,17 @@ nav a { .portal-nav .portal-nav__subgroup:hover>.portal-submenu-nested, .portal-nav .portal-nav__subgroup:focus-within>.portal-submenu-nested { + display: none; + } + + .portal-nav .portal-nav__subgroup.open>.portal-submenu-nested { display: flex; } + .portal-nav .portal-submenu .portal-nav__subgroup-head { + cursor: pointer; + } + .portal-nav .portal-submenu-nested--tier3, .portal-nav .portal-nav__subgroup--nested:hover>.portal-submenu-nested--tier3, .portal-nav .portal-nav__subgroup--nested:focus-within>.portal-submenu-nested--tier3 { @@ -444,6 +559,10 @@ nav a { .portal-nav .portal-nav__subgroup--nested:hover>.portal-submenu-nested--tier3, .portal-nav .portal-nav__subgroup--nested:focus-within>.portal-submenu-nested--tier3 { + display: none; + } + + .portal-nav .portal-nav__subgroup--nested.open>.portal-submenu-nested--tier3 { display: flex; } } @@ -1566,7 +1685,7 @@ footer a .mark_b { padding-right: 10px; } -@media (min-width: 768px) { +@media (min-width: 992px) { .map-page .portal-nav .portal-submenu .portal-nav__subgroup-head::after { right: auto; @@ -1597,7 +1716,18 @@ footer a .mark_b { } } -@media (max-width: 767px) { +@media (max-width: 991px) { + + .map-page .portal-nav>ul>li>a { + justify-content: flex-end; + text-align: right; + } + + .map-page .portal-nav .portal-submenu a, + .map-page .portal-nav .portal-submenu .portal-nav__subgroup-head { + text-align: right; + justify-content: flex-end; + } .map-page .portal-nav .portal-nav__subgroup>.portal-submenu-nested, .map-page .portal-nav .portal-nav__subgroup:hover>.portal-submenu-nested, diff --git a/assets/css/tablet.css b/assets/css/tablet.css index 4211d79..21286fc 100644 --- a/assets/css/tablet.css +++ b/assets/css/tablet.css @@ -1,5 +1,20 @@ /* iPad/平板 RWD — max-width ≤1180px(含 iPad 直/橫與小平板/小筆電) */ +/* 頂欄漢堡:與 style.css 992 斷點一致,避免本檔 1180 規則把選單列壓回橫排 */ +@media (max-width: 991px) { + + .portal-nav>ul { + flex: 1 1 100%; + flex-wrap: nowrap; + justify-content: flex-start; + align-items: stretch; + } + + .portal-nav>ul>li>a { + flex-direction: row; + } +} + @media (max-width: 1180px) { html:has(.portal-nav) { @@ -45,7 +60,7 @@ justify-content: center; } - nav ul { + nav:not(.portal-nav):not(.bilivideo-episode-nav) ul { gap: clamp(12px, 2vw, 20px); } diff --git a/assets/js/portal-nav.js b/assets/js/portal-nav.js index 1e497df..09821ae 100644 --- a/assets/js/portal-nav.js +++ b/assets/js/portal-nav.js @@ -22,6 +22,27 @@ /** 主選單欄 `mouseleave` 後延遲關閉(ms),方便移入子選單或右側飛出層,避免略偏就關閉 */ const SUBMENU_CLOSE_DELAY_MS = 650; + /** 橫向+右側飛出子選單;≤991px 改漢堡+直向展開(對齊常見 992 斷點) */ + const PORTAL_NAV_DESKTOP_MIN_PX = 992; + + function isPortalNavDesktop() { + return window.matchMedia(`(min-width: ${PORTAL_NAV_DESKTOP_MIN_PX}px)`).matches; + } + + function setPortalNavMenuOpen(nav, open) { + nav.classList.toggle("portal-nav--open", open); + const toggle = nav.querySelector(".portal-nav__menu-toggle"); + if (toggle) { + toggle.setAttribute("aria-expanded", open ? "true" : "false"); + toggle.setAttribute("aria-label", open ? "關閉選單" : "開啟選單"); + } + if (!open) { + nav.querySelectorAll(":scope > ul > li.open, .portal-nav__subgroup.open").forEach((el) => { + el.classList.remove("open"); + }); + } + } + function pickSoonPhrase() { return NAV_SOON_PHRASES[Math.floor(Math.random() * NAV_SOON_PHRASES.length)]; } @@ -212,7 +233,14 @@ function buildPortalNavInnerHTML(rp) { return ` -