feat(nav): complete remaining zibll parity (search, i18n, senior menus, CSS)

- Remove obsolete dual-path nav CSS; add utilities + language dropdown styles
- Wire header/mobile translate switcher from i18n config
- Layout-2 inline search hot keywords panel; nav_folding on resize
- NavMenuOptions JSON editors for graphic_card / multi_column_links
- Section-navs zibll posts-nav-lists; page-navs enqueue; mobile article TOC box
- SPA/PHP user-card publish type; rebuild admin assets
This commit is contained in:
2026-07-22 11:40:17 +08:00
parent 259dbdbe04
commit fc50e57bc9
22 changed files with 580 additions and 1994 deletions
@@ -37,6 +37,7 @@ export const pageDisplaySchemas = {
header_nav_pay_vip: true,
header_nav_user_pay_vip: true,
header_newposts_types: ['post'],
header_user_newposts_btn: 'post',
header_user_newposts_text: '发布内容',
mobile_header_layout: 'center',
mobile_navbar_align: 'left',
@@ -124,6 +125,11 @@ export const pageDisplaySchemas = {
{ label: '帖子', value: 'topic' },
{ label: '商品', value: 'product' },
], dependsOn: { key: 'header_newposts_enabled', value: true }, help: '未开启的模块不会显示' },
{ key: 'header_user_newposts_btn', label: '用户卡发布类型', type: 'select', default: 'post', options: [
{ label: '文章', value: 'post' },
{ label: '帖子', value: 'topic' },
{ label: '商品', value: 'product' },
] },
{ key: 'header_user_newposts_text', label: '用户卡发布文案', type: 'input', default: '发布内容' },
{ key: 'header_nav_pay_vip', label: '导航栏开通会员按钮', type: 'switch', default: true, help: '顶部导航显示开通会员' },
{ key: 'header_nav_user_pay_vip', label: '用户框开通会员按钮', type: 'switch', default: true },
+2 -5
View File
@@ -2,10 +2,7 @@
@import url("nav/zib-navbar-core.css");
@import url("nav/zib-mobile-navbar.css");
@import url("nav/zib-senior-submenu.css");
@import url("nav/dark-mode-submenu.css");
@import url("nav/dark-mode-mega-menu.css");
@import url("nav/zib-utilities.css");
@import url("nav/header-menu-buttons.css");
@import url("nav/header-userbox.css");
@import url("nav/menu-item-icon.css");
@import url("nav/menu-item-badge.css");
@import url("nav/hover-effects.css");
@import url("nav/dark-mode-submenu.css");
-577
View File
@@ -1,577 +0,0 @@
/* Zibll body.nav-fixed: hard 88px reserve (main.css), not bare --nav-height.
* Nav bar itself is ~60px; extra space matches zibll chrome/shadow gap.
* --nav-fixed-reserve is shared with the hero pull-up so they cancel exactly
* (zibll: body padding-top 88px + .slide-header { margin-top: -90px }). */
body.nav-fixed {
--nav-fixed-reserve: 88px;
padding-top: var(--nav-fixed-reserve);
}
@media (max-width: 768px) {
body.nav-fixed {
--nav-fixed-reserve: 72px;
padding-top: var(--nav-fixed-reserve);
}
}
@media (max-width: 640px) {
body.nav-fixed {
--nav-fixed-reserve: 60px;
padding-top: var(--nav-fixed-reserve);
}
}
/* Fixed nav without hero overlay: take header out of flow.
body.nav-fixed already reserves space via padding-top. */
body.nav-fixed .header:not(.show-slide) {
position: fixed;
top: 0;
left: 0;
right: 0;
width: 100%;
z-index: 1000;
}
/* Nav lives inside the fixed header — don't keep sticky (double offset). */
body.nav-fixed .header:not(.show-slide) .site-nav,
body.nav-fixed .header:not(.show-slide) .navbar.navbar-top {
position: relative;
top: auto;
}
/*
* Non-home pages have no hero under the fixed header. Keep the first content
* (breadcrumb / article) clearly below the bar even if a fluid widget zone is
* empty or a child uses negative margin.
*/
body.nav-fixed:not(:has(.header.show-slide)) .fluid-widget-wrap:first-of-type:empty + main.container,
body.nav-fixed:not(:has(.header.show-slide)) main.container:first-of-type {
scroll-margin-top: calc(var(--nav-fixed-reserve, 88px) + 8px);
}
/* Hero sits under transparent fixed/absolute header.
* MUST cancel the full --nav-fixed-reserve (88/72/60), not just --nav-height (60).
* Using only -60px against 88px padding left a ~28px blank strip above the hero. */
body.nav-fixed .header-slider-container,
body.nav-fixed .header-components-container,
body.nav-fixed .header-components.slide-header {
margin-top: calc(var(--nav-fixed-reserve, 88px) * -1) !important;
position: relative;
z-index: 1;
}
/* ============================================================
* AKSRT Navigation — Zibll-aligned
*
* - 高度 60px (--nav-height)
* - 毛玻璃 blur-bg(saturate(5) blur(20px))
* - 透明叠 slider → 滚动后恢复实底
* - 滚动隐藏(scroll-down-hide 等价)
* ============================================================ */
.site-nav {
position: sticky;
top: 0;
z-index: 1000;
min-height: var(--nav-height, 60px);
background-color: var(--blur-bg);
border-bottom: 1px solid rgba(255, 255, 255, 0.45);
backdrop-filter: saturate(5) blur(20px);
-webkit-backdrop-filter: saturate(5) blur(20px);
transition: background-color 0.25s ease,
border-color 0.25s ease,
color 0.25s ease,
box-shadow 0.25s ease,
transform 0.3s ease; /* 滚动隐藏过渡 */
}
/* 滚动隐藏头部 — 对齐 zibll .scroll-down.nav-fixed .header.scroll-down-hide */
.site-nav.is-hidden {
transform: translateY(-105%);
}
.site-nav--static {
position: relative;
}
.site-nav--transparent {
background: transparent; /* 对齐 zibll .header.show-slide: 透明覆盖在 slider 上 */
border-bottom-color: transparent;
}
.site-nav--transparent:not(.is-scrolled) {
--header-color: #fff; /* 对齐 zibll --header-color:#fff */
color: #fff;
}
.site-nav--transparent:not(.is-scrolled) a {
color: inherit;
}
.site-nav--transparent.is-scrolled {
background: var(--blur-bg); /* 滚动后恢复毛玻璃背景 */
border-bottom-color: var(--color-line-soft);
}
.site-nav--blur {
background: var(--blur-bg);
backdrop-filter: saturate(5) blur(20px);
-webkit-backdrop-filter: saturate(5) blur(20px);
border-bottom-color: transparent;
}
html.dark .site-nav--transparent:not(.is-scrolled),
html.dark .site-nav--blur:not(.is-scrolled),
body.dark-theme .site-nav--transparent:not(.is-scrolled),
body.dark-theme .site-nav--blur:not(.is-scrolled) {
background: transparent;
border-bottom-color: transparent;
}
html.dark .site-nav--transparent.is-scrolled,
html.dark .site-nav--blur.is-scrolled,
body.dark-theme .site-nav--transparent.is-scrolled,
body.dark-theme .site-nav--blur.is-scrolled {
background: var(--blur-bg);
border-bottom-color: var(--color-line-soft);
}
/* 滚动后追加阴影 — 对齐 zibll .body-scroll .header */
.site-nav.is-scrolled {
box-shadow: 0 4px 10px var(--main-shadow, var(--shadow-sm));
}
/* WordPress Admin Bar 已全局禁用,无需偏移规则 */
/* ============================================================
* Inner / Row layout
* ============================================================ */
.site-nav__inner {
max-width: var(--nav-max-width, var(--container));
margin: 0 auto;
padding: 0 16px;
display: flex;
flex-direction: column;
gap: 0;
}
.site-nav__row {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 12px;
width: 100%;
min-height: var(--nav-height, 60px);
}
/* ============================================================
* Brand
* ============================================================ */
.site-brand {
display: inline-flex;
align-items: center;
gap: 10px;
min-width: 0;
max-width: min(48vw, 360px);
padding: 7px 10px;
color: var(--color-ink);
font-size: 17px;
font-weight: 700;
letter-spacing: -0.01em;
flex-shrink: 0;
text-decoration: none;
}
.site-brand:hover {
color: var(--color-ink);
text-decoration: none;
}
.site-brand--has-logo {
gap: 0;
}
.site-brand__logo {
width: 36px;
height: 36px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 800;
font-size: 13px;
color: var(--color-on-primary, #fff);
background: linear-gradient(135deg, var(--color-primary), var(--color-primary-active, var(--color-primary-dark)));
letter-spacing: 0.02em;
overflow: hidden;
}
.site-brand__media {
display: block;
flex: 0 1 auto;
max-width: 100%;
padding: 0 30px;
line-height: 0;
box-sizing: border-box;
background: transparent !important;
border: 0;
border-radius: 0;
box-shadow: none;
filter: none !important;
opacity: 1;
}
.site-brand__img {
display: block;
height: 36px;
width: auto;
max-width: 100%;
object-fit: contain;
object-position: left center;
background: transparent !important;
border-radius: 0;
box-shadow: none;
filter: none !important;
mix-blend-mode: normal !important;
opacity: 1;
transform: none !important;
}
.site-brand__name {
font-weight: 700;
letter-spacing: -0.01em;
white-space: nowrap;
}
/* Light/dark logo: single adaptive img (white-src/dark-src via theme.js) */
.navbar-logo > img,
.navbar-logo .site-brand__logo-img,
.site-brand .site-brand__logo-img,
.site-brand__logo-img,
.site-brand .navbar-logo img {
height: 36px !important;
width: auto !important;
max-width: min(180px, 40vw) !important;
max-height: 36px !important;
padding: 0 !important;
object-fit: contain !important;
display: inline-block;
vertical-align: middle;
}
/* Legacy dual-img classes (if any remain elsewhere) */
.site-brand__logo-img--dark {
display: none;
}
html.dark .site-brand__logo-img--light,
[data-theme="dark"] .site-brand__logo-img--light,
body.dark-theme .site-brand__logo-img--light {
display: none;
}
html.dark .site-brand__logo-img--dark,
[data-theme="dark"] .site-brand__logo-img--dark,
body.dark-theme .site-brand__logo-img--dark {
display: inline-block;
}
/* ============================================================
* Desktop menu
* ============================================================ */
ul.nav-desktop {
display: none;
list-style: none;
margin: 0;
padding: 0;
align-items: center;
flex: 1 1 auto;
min-width: 0;
}
.nav-desktop--fallback {
display: none;
align-items: center;
flex: 1 1 auto;
min-width: 0;
}
ul.nav-desktop a,
.nav-desktop--fallback a {
color: var(--color-ink-3);
font-size: 14px;
font-weight: 500;
padding: 0 12px; /* 对齐 zibll:仅水平 padding,垂直居中靠 flex 行高 */
border-radius: 4px;
transition: color var(--dur-fast, 0.15s) var(--ease),
background var(--dur-fast, 0.15s) var(--ease);
}
/* Hover — 对齐 zibll: 仅变色,无背景 */
ul.nav-desktop a:hover,
.nav-desktop--fallback a:hover {
color: var(--color-primary);
background: transparent;
}
/* Active page — 对齐 zibll: 仅变色(指示器走下方胶囊条) */
ul.nav-desktop .current-menu-item > a,
ul.nav-desktop .current_page_item > a {
color: var(--color-primary);
background: transparent;
}
/* ============================================================
* Desktop submenu (dropdown)
* ============================================================ */
ul.nav-desktop .menu-item {
position: relative;
}
/* 顶部 hover/current 胶囊指示器 — 对齐 zibll .navbar-nav>li:before
(5px 高、底部 15px 圆角,从 header 顶缘垂下)。
stretch 链(row→ul→li)让 li 充满 60px 行高,::before top:0 即锚定 header 顶部;
::before 为 absolute 不进入 flex 流,不挤压链接。 */
ul.nav-desktop {
align-self: stretch;
}
ul.nav-desktop > .menu-item {
align-self: stretch;
display: flex;
align-items: center;
}
ul.nav-desktop > .menu-item::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 5px;
background: var(--focus-color, var(--color-primary));
border-radius: 0 0 15px 15px;
opacity: 0;
transition: opacity 0.3s;
pointer-events: none;
}
ul.nav-desktop > .menu-item:hover::before,
ul.nav-desktop > .current-menu-item::before,
ul.nav-desktop > .current_page_item::before {
opacity: 1;
}
ul.nav-desktop .sub-menu {
position: absolute;
top: 100%;
left: 0;
z-index: 998; /* 保留较高 z-index(zibll 用 99,AKSRT 栈更深,避免被覆盖) */
min-width: 140px;
list-style: none;
margin: 8px 0 0;
padding: 8px 6px;
background: var(--main-bg-color, #fff);
border: none;
border-radius: var(--main-radius, var(--radius, 8px));
box-shadow: var(--shadow-lg);
opacity: 0;
visibility: hidden;
transform: translateY(6px);
transition: opacity 0.4s ease, transform 0.4s ease, visibility 0.4s ease;
}
ul.nav-desktop .menu-item:hover > .sub-menu {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
ul.nav-desktop .sub-menu .menu-item > a {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 15px;
font-size: 14px;
border-radius: 4px;
white-space: nowrap;
}
ul.nav-desktop .sub-menu .menu-item > a:hover {
background: var(--color-primary-soft, rgba(240, 68, 148, 0.08));
color: var(--color-primary);
}
/* Nested submenus fly out to the right — 对齐 zibll top:-10px,去 margin-left */
ul.nav-desktop .sub-menu .sub-menu {
top: -10px;
left: 100%;
margin: 0;
}
/* ============================================================
* Zibll .header.show-slide — transparent gradient over hero
* ============================================================ */
.header.show-slide {
--header-bg: linear-gradient(0, rgba(0, 0, 0, 0) 5%, rgba(0, 0, 0, 0.1) 80%);
--header-color: #fff;
-webkit-backdrop-filter: unset;
backdrop-filter: unset;
}
body.nav-fixed .header.show-slide {
position: absolute !important;
top: 0;
left: 0;
right: 0;
width: 100%;
z-index: 30;
background: transparent !important;
box-shadow: none !important;
border: 0 !important;
}
/* Nav inside absolute header must not use sticky (avoids double offset) */
body.nav-fixed .header.show-slide .site-nav,
body.nav-fixed .header.show-slide .navbar.navbar-top {
position: relative !important;
top: auto !important;
background: var(--header-bg, transparent) !important;
border-bottom-color: transparent !important;
box-shadow: none !important;
color: var(--header-color, #fff);
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
}
.header.show-slide .navbar.navbar-top,
.header.show-slide .site-nav {
background: var(--header-bg, transparent);
border-bottom-color: transparent;
box-shadow: none;
color: var(--header-color, #fff);
backdrop-filter: none;
-webkit-backdrop-filter: none;
}
.header.show-slide .site-nav:not(.is-scrolled) a,
.header.show-slide .site-nav:not(.is-scrolled) .icon-btn,
.header.show-slide .site-nav:not(.is-scrolled) .site-brand__name {
color: #fff;
}
.header.show-slide .site-nav.is-scrolled,
.site-nav.is-scrolled {
background: var(--blur-bg);
color: var(--header-color, var(--color-ink-2));
border-bottom-color: rgba(255, 255, 255, 0.45);
backdrop-filter: saturate(5) blur(20px);
-webkit-backdrop-filter: saturate(5) blur(20px);
box-shadow: 0 4px 10px var(--main-shadow, rgba(116, 116, 116, 0.08));
}
.navbar.navbar-top {
margin: 0;
border-radius: 0 !important;
font-size: 15px;
}
.container-header,
.container-footer {
max-width: calc(var(--mian-max-width, var(--container, 1200px)) + 180px);
margin-left: auto;
margin-right: auto;
width: 100%;
padding-left: 15px;
padding-right: 15px;
}
.navbar-top .navbar-brand {
padding: 7px 10px;
}
.navbar-logo > img,
.navbar-logo .site-brand__logo-img,
.site-brand .site-brand__logo-img,
.site-brand__logo-img {
height: 36px !important;
width: auto !important;
max-width: min(180px, 40vw) !important;
max-height: 36px !important;
padding: 0 !important;
object-fit: contain !important;
}
.navbar-top .navbar-nav,
.navbar-top ul.nav-desktop {
margin-left: 20px;
margin-right: 20px;
display: flex;
align-items: center;
gap: 0;
list-style: none;
padding: 0;
}
.navbar-top .navbar-nav > li > a,
.navbar-top ul.nav-desktop > li > a {
padding-left: 10px;
padding-right: 10px;
border-radius: 4px;
display: inline-flex !important;
align-items: center;
min-height: 36px;
font-size: 15px;
}
.navbar-top .site-nav__row {
display: flex;
align-items: center;
width: 100%;
min-height: var(--nav-height, 60px);
}
.navbar-top .nav-actions {
margin-left: auto;
display: flex;
align-items: center;
gap: 8px;
overflow: visible;
position: relative;
z-index: 25;
}
.scroll-down.nav-fixed .header.scroll-down-hide,
body.scroll-down.nav-fixed .header.scroll-down-hide {
transform: translate3d(0, -105%, 0);
-webkit-backdrop-filter: none;
backdrop-filter: none;
}
/* Layout-1 style: keep search as icon; hide expanded desktop form */
@media (min-width: 992px) {
.navbar-top .nav-searchbox--desktop {
display: none !important;
}
}
/* Transparent white controls on gradient hero */
.header.show-slide .site-nav:not(.is-scrolled) .btn-outline {
color: #fff;
border-color: rgba(255, 255, 255, 0.55);
background: transparent;
}
.header.show-slide .site-nav:not(.is-scrolled) .btn-outline:hover {
color: #fff;
border-color: #fff;
background: rgba(255, 255, 255, 0.12);
}
.header.show-slide .site-nav:not(.is-scrolled) .nav-user__guest-avatar,
.header.show-slide .site-nav:not(.is-scrolled) .icon-btn {
color: #fff;
}
-128
View File
@@ -1,128 +0,0 @@
/* Cards submenu */
.aksrt-nav__cards {
display: grid;
gap: 12px;
padding: 8px;
}
.aksrt-nav__card {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 12px;
border-radius: 6px;
text-decoration: none;
color: inherit;
transition: background 0.15s;
}
.aksrt-nav__card:hover {
background: var(--color-primary-soft, rgba(240, 68, 148, 0.06));
}
.aksrt-nav__card-icon {
width: 36px;
height: 36px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
background: var(--color-primary-soft, #e6f4ff);
color: var(--color-primary);
font-size: 16px;
flex-shrink: 0;
}
.aksrt-nav__card-text {
flex: 1;
min-width: 0;
}
.aksrt-nav__card-title {
font-size: 13px;
font-weight: 600;
color: var(--color-ink);
margin: 0;
}
.aksrt-nav__card-desc {
font-size: 11px;
color: var(--color-ink-4);
margin: 2px 0 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* ============================================================
* 导航溢出「更多」下拉
* ============================================================ */
.aksrt-nav-more {
position: relative;
}
.aksrt-nav-more__btn {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 6px 12px;
border: none;
background: none;
color: var(--color-ink-2);
font-size: 14px;
font-weight: 500;
cursor: pointer;
border-radius: var(--radius, 8px);
transition: background 0.15s, color 0.15s;
}
.aksrt-nav-more__btn:hover {
background: var(--color-primary-soft, rgba(240, 68, 148, 0.08));
color: var(--color-primary);
}
.aksrt-nav-more__dropdown {
display: none;
position: absolute;
top: 100%;
right: 0;
min-width: 160px;
padding: 6px 0;
background: var(--surface-1, #fff);
border: 1px solid var(--color-line, rgba(50, 50, 50, 0.06));
border-radius: var(--radius, 8px);
box-shadow: var(--shadow-lg, 0 0 10px rgba(116, 116, 116, 0.12));
z-index: var(--z-dropdown, 100);
list-style: none;
margin: 4px 0 0;
}
.aksrt-nav-more__dropdown.is-open {
display: block;
}
.aksrt-nav-more__dropdown li {
display: block;
}
.aksrt-nav-more__dropdown li a,
.aksrt-nav-more__dropdown li button {
display: block;
width: 100%;
padding: 8px 16px;
border: none;
background: none;
color: var(--color-ink-2);
font-size: 14px;
text-align: left;
text-decoration: none;
cursor: pointer;
transition: background 0.15s, color 0.15s;
}
.aksrt-nav-more__dropdown li a:hover,
.aksrt-nav-more__dropdown li button:hover {
background: var(--color-primary-soft, rgba(240, 68, 148, 0.08));
color: var(--color-primary);
}
@@ -1,12 +0,0 @@
/* Dark mode Mega Menu */
html.dark ul.nav-desktop .mega-menu-item > .mega-menu__panel,
body.dark-theme ul.nav-desktop .mega-menu-item > .mega-menu__panel {
background: var(--surface-page);
border-color: var(--color-line, #2d2d3f);
}
html.dark .mega-menu__panel > .menu-item:hover,
body.dark-theme .mega-menu__panel > .menu-item:hover {
background: var(--surface-2);
}
-35
View File
@@ -1,35 +0,0 @@
/* Hover effects */
.aksrt-nav__item--hover-underline > a::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
width: 0;
height: 2px;
background: var(--color-primary);
transition: width 0.2s ease, left 0.2s ease;
border-radius: 1px;
}
.aksrt-nav__item--hover-underline:hover > a::after {
width: 60%;
left: 20%;
}
.aksrt-nav__item--hover-background > a {
transition: background-color 0.2s;
border-radius: 4px;
}
.aksrt-nav__item--hover-background:hover > a {
background-color: var(--color-primary-soft, rgba(240, 68, 148, 0.08));
}
.aksrt-nav__item--hover-color > a {
transition: color 0.2s;
}
.aksrt-nav__item--hover-color:hover > a {
color: var(--color-primary);
}
-28
View File
@@ -1,28 +0,0 @@
/* Mega submenu */
.aksrt-nav__mega {
position: absolute;
top: 100%;
left: 0;
z-index: 1001;
background: var(--surface-1);
border: 1px solid var(--color-line);
border-radius: var(--radius);
box-shadow: var(--shadow-lg);
padding: 16px;
opacity: 0;
visibility: hidden;
transform: translateY(8px);
transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s ease;
}
.aksrt-nav__item:hover > .aksrt-nav__mega {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.aksrt-nav__mega--columns {
display: grid;
gap: 16px;
}
@@ -1,41 +0,0 @@
/* Menu item badge */
.aksrt-nav__badge {
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 10px;
font-weight: 600;
line-height: 1;
padding: 2px 5px;
border-radius: 3px;
margin-left: 4px;
white-space: nowrap;
vertical-align: middle;
transform: translateY(-1px);
}
.aksrt-nav__badge--new {
color: #fff;
background: var(--color-error);
}
.aksrt-nav__badge--hot {
color: #fff;
background: var(--color-warning);
}
.aksrt-nav__badge--info {
color: #fff;
background: var(--color-info);
}
.aksrt-nav__badge--success {
color: #fff;
background: var(--color-success);
}
.aksrt-nav__badge--vip {
color: #fff;
background: linear-gradient(135deg, var(--color-warning), var(--color-error));
}
@@ -1,20 +0,0 @@
/* Menu item icon */
.aksrt-nav__icon {
font-size: 0.85em;
width: 1.2em;
text-align: center;
margin-right: 4px;
opacity: 0.7;
transition: opacity 0.2s;
}
.aksrt-nav__item--has-icon > a {
display: inline-flex;
align-items: center;
gap: 4px;
}
.aksrt-nav__item--has-icon:hover .aksrt-nav__icon {
opacity: 1;
}
@@ -1,18 +0,0 @@
/* Mobile theme toggle in drawer */
.nav-drawer__theme-toggle {
padding: 0 20px 16px;
}
.nav-drawer__theme-toggle .theme-toggle-mobile {
width: 100%;
min-height: 44px;
justify-content: center;
gap: 8px;
display: flex;
align-items: center;
}
/* ============================================================
* Menu Decoration — Icons, Badges, Hover Effects
* ============================================================ */
-814
View File
@@ -1,814 +0,0 @@
/* Mobile toggle */
.nav-toggle {
display: flex;
}
@media (max-width: 991px) {
.site-brand {
max-width: min(46vw, 311px);
}
.site-brand__media {
padding: 0 8px;
}
.nav-publish-btn {
display: none;
}
}
/* Desktop search icon: hide on mobile (use drawer search instead) */
@media (max-width: 767px) {
.site-brand {
max-width: min(62vw, 260px);
padding: 7px 0;
}
.site-brand__media {
padding: 0;
}
#search-toggle {
display: none;
}
/* Search panel only makes sense alongside the desktop search icon */
.nav-search-panel {
display: none !important;
}
}
@media (min-width: 768px) {
ul.nav-desktop,
.nav-desktop--fallback {
display: flex;
}
.nav-auth {
display: flex;
}
.nav-auth-dropdown {
display: flex;
}
.nav-toggle {
display: none;
}
}
/* ============================================================
* Header Layout Variants (desktop ≥768px)
* Layout 1: Standard — brand left, nav flex-1, actions right
* Layout 2: Centered — brand left, nav centered with flex, actions right
* Layout 3: Compact — brand left, nav pushed right, actions right
* ============================================================ */
@media (min-width: 768px) {
/* Layout 2: center the nav between brand and actions */
body.header-layout-2 .nav-desktop {
flex: 1;
justify-content: center;
}
body.header-layout-2 .nav-actions {
flex-shrink: 0;
}
/* Layout 3: push nav to the right side */
body.header-layout-3 .site-nav__row {
justify-content: flex-start;
}
body.header-layout-3 .nav-desktop {
flex: 1;
justify-content: flex-end;
margin-left: 24px;
}
body.header-layout-3 .nav-actions {
margin-left: 8px;
}
}
.nav-mobile-strip {
display: none;
width: calc(100% + 8px);
margin: 0 -4px;
padding: 0 0 12px;
}
.nav-mobile-strip__menu,
.nav-mobile-strip__menu ul {
display: flex;
align-items: center;
gap: 8px;
overflow-x: auto;
margin: 0;
padding: 0;
list-style: none;
scrollbar-width: none;
-ms-overflow-style: none;
}
.nav-mobile-strip__menu::-webkit-scrollbar,
.nav-mobile-strip__menu ul::-webkit-scrollbar {
display: none;
}
.nav-mobile-strip__menu a {
display: inline-flex;
align-items: center;
min-height: 34px;
padding: 0 12px;
border-radius: var(--radius-pill, 999px);
background: var(--surface-2);
color: var(--color-ink-2);
white-space: nowrap;
text-decoration: none;
font-size: 0.875rem;
font-weight: 500;
}
.nav-mobile-strip__menu .current-menu-item > a,
.nav-mobile-strip__menu .current_page_item > a {
background: var(--color-primary);
color: var(--color-on-primary, #fff);
}
@media (max-width: 767px) {
.nav-mobile-strip {
display: block;
}
}
/* ============================================================
* User avatar + dropdown
* ============================================================ */
.nav-user {
position: relative;
}
.nav-user__toggle {
width: 36px;
height: 36px;
border-radius: var(--radius-avatar);
padding: 0;
border: none; /* 对齐 zibll:头像无边框 */
cursor: pointer;
/* Allow unread corner badge to paint outside the avatar circle. */
overflow: visible;
background: var(--surface-2);
transition: box-shadow var(--dur-fast, 0.15s) var(--ease);
}
.nav-user__toggle:hover {
box-shadow: 0 0 0 2px var(--color-primary-soft);
}
.nav-user__toggle img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
border-radius: inherit;
}
.user-dropdown {
position: absolute;
top: calc(100% + 8px);
right: 0;
width: 260px;
max-width: calc(100vw - 32px);
padding: 12px 0;
background: var(--surface-1);
border: 1px solid var(--color-line);
border-radius: var(--radius-lg, 8px);
box-shadow: var(--shadow-lg);
z-index: 1100;
opacity: 0;
visibility: hidden;
transform: translateY(-6px);
transition: opacity 0.2s, transform 0.2s, visibility 0.2s;
}
.user-dropdown.is-open {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.user-dropdown__profile {
display: flex;
align-items: center;
gap: 12px;
padding: 8px 16px 14px;
}
.user-dropdown__profile img {
width: 44px;
height: 44px;
border-radius: var(--radius-avatar);
object-fit: cover;
flex-shrink: 0;
}
.user-dropdown__name {
font-weight: 700;
font-size: 0.9375rem;
color: var(--color-ink);
}
.user-dropdown__email {
font-size: 0.75rem;
color: var(--color-ink-4);
word-break: break-all;
margin-top: 2px;
}
.user-dropdown__sep {
height: 1px;
background: var(--color-line);
margin: 6px 0;
}
.user-dropdown a,
.user-dropdown button {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
padding: 0 16px;
height: 44px;
border: none;
background: none;
font-size: 0.875rem;
color: var(--color-ink);
text-align: left;
cursor: pointer;
transition: background var(--dur-fast, 0.15s) var(--ease),
color var(--dur-fast, 0.15s) var(--ease);
}
.user-dropdown a:hover,
.user-dropdown button:hover {
background: var(--surface-3);
color: var(--color-primary-active);
}
/* dropdown 项内的 SVG 图标 */
.user-dropdown__item svg {
width: 16px;
height: 16px;
flex-shrink: 0;
opacity: 0.85;
}
.user-dropdown__item span:first-of-type:not(.user-dropdown__pill) {
flex: 1;
min-width: 0;
}
/* 未读消息数 badge */
.user-dropdown__badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 18px;
height: 18px;
padding: 0 5px;
margin-left: auto;
font-size: 11px;
font-weight: 600;
color: #fff;
background: var(--color-error, #ef4444);
border-radius: 9px;
line-height: 1;
}
.user-dropdown__credits {
display: flex;
align-items: center;
gap: 10px;
padding: 0 16px;
height: 44px;
font-size: 0.875rem;
color: var(--color-ink-2);
}
.user-dropdown__credits i {
color: var(--color-warning);
flex-shrink: 0;
}
.user-dropdown__credits-label {
flex: 1;
}
.user-dropdown__credits-value {
font-weight: 600;
color: var(--color-primary);
}
.user-dropdown__logout {
color: var(--color-error) !important;
}
.user-dropdown__logout svg {
color: var(--color-error);
}
.user-dropdown__logout:hover {
background: rgba(239, 68, 68, 0.08) !important;
color: var(--color-error) !important;
}
.user-dropdown .badge-dot {
margin-left: auto;
min-width: 8px;
height: 8px;
border-radius: var(--radius-pill);
background: var(--color-error);
}
/* ============================================================
* Search panel
* ============================================================ */
.nav-search-panel {
display: none;
padding: 12px 0 16px;
border-top: 1px solid var(--color-line);
}
.nav-search-panel.is-open {
display: block;
}
.nav-searchbox {
margin: 0;
}
.nav-searchbox__field {
display: flex;
align-items: center;
gap: 10px;
min-height: 42px;
padding: 0 6px 0 14px;
border: 1px solid var(--color-line);
border-radius: var(--radius-pill, 999px);
background: var(--surface-3);
box-sizing: border-box;
transition: border-color var(--dur-fast, 0.15s) var(--ease), box-shadow var(--dur-fast, 0.15s) var(--ease), background var(--dur-fast, 0.15s) var(--ease);
}
.nav-searchbox__icon {
order: 1;
flex: 0 0 16px;
width: 16px;
height: 16px;
color: var(--color-ink-4);
pointer-events: none;
}
.nav-searchbox__input {
order: 2;
flex: 1;
min-width: 0;
width: auto;
height: 40px;
padding: 0;
border: 0;
background: transparent;
color: var(--color-ink);
font-size: 0.9rem;
line-height: 1.2;
box-sizing: border-box;
}
.nav-searchbox__input:focus {
outline: none;
}
.nav-searchbox__input::placeholder {
color: var(--color-ink-4);
}
.nav-searchbox__submit {
order: 3;
flex: 0 0 auto;
height: 30px;
min-width: 64px;
padding: 0 14px;
border: 0;
border-radius: var(--radius-pill, 999px);
background: var(--color-primary);
color: #fff;
font-size: 0.8125rem;
font-weight: 600;
line-height: 1;
white-space: nowrap;
cursor: pointer;
}
.nav-searchbox__field:focus-within {
border-color: var(--color-primary);
box-shadow: 0 0 0 3px var(--color-primary-soft);
background: var(--surface-1);
}
.nav-searchbox__submit:hover {
background: var(--color-primary-active);
}
/* ============================================================
* Mobile drawer / mobile header
* ============================================================ */
.mobile-header.nav-drawer-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.45);
z-index: 1200;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s;
}
.mobile-header.nav-drawer-backdrop.is-open {
opacity: 1;
visibility: visible;
}
.mobile-header.nav-drawer {
position: fixed;
top: 0;
right: 0;
width: min(100vw, 360px);
height: 100vh;
max-height: 100dvh;
background: var(--surface-page);
z-index: 1210;
box-shadow: var(--shadow-drawer);
transform: translateX(100%);
transition: transform 0.32s var(--ease-out, ease);
display: flex;
flex-direction: column;
overflow: auto;
pointer-events: none;
visibility: hidden;
}
.mobile-header.nav-drawer.is-open {
transform: translateX(0);
pointer-events: auto;
visibility: visible;
}
.mobile-navbar {
display: flex;
flex-direction: column;
min-height: 100%;
}
.mobile-navbar .navbar-brand {
max-width: calc(100% - 52px);
padding: 7px 0;
}
.mobile-navbar .navbar-logo {
padding: 0;
}
.nav-drawer-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.45);
z-index: 1200;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s;
}
.nav-drawer-backdrop.is-open {
opacity: 1;
visibility: visible;
}
.nav-drawer {
position: fixed;
top: 0;
right: 0;
width: min(100vw, 360px);
height: 100vh;
max-height: 100dvh;
background: var(--surface-page);
z-index: 1210;
box-shadow: var(--shadow-drawer);
transform: translateX(100%);
transition: transform 0.32s var(--ease-out, ease);
display: flex;
flex-direction: column;
overflow: auto;
pointer-events: none;
visibility: hidden;
}
.nav-drawer.is-open {
transform: translateX(0);
pointer-events: auto;
visibility: visible;
}
.nav-drawer__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 16px 20px;
border-bottom: 1px solid var(--color-line);
font-weight: 700;
font-size: 1rem;
}
.site-brand--drawer {
max-width: calc(100% - 52px);
padding: 7px 0;
}
.site-brand--drawer .site-brand__media {
padding: 0;
}
.nav-drawer__search {
padding: 16px 20px 0;
}
.nav-drawer__search .nav-searchbox__field {
min-height: 40px;
}
.nav-drawer__search .nav-searchbox__input {
height: 38px;
}
.nav-drawer__search .nav-searchbox__submit {
height: 28px;
min-width: 58px;
padding: 0 12px;
font-size: 0.75rem;
}
.nav-searchbox__meta {
display: grid;
gap: 10px;
margin-top: 12px;
}
.nav-searchbox__types {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.nav-searchbox__type {
display: inline-flex;
align-items: center;
min-height: 32px;
padding: 0 12px;
border: 1px solid var(--color-line);
border-radius: var(--radius-pill, 999px);
background: var(--surface-2);
color: var(--color-ink-3);
font-size: 0.8125rem;
font-weight: 600;
cursor: pointer;
}
.nav-searchbox__type.is-active {
border-color: var(--color-primary);
background: var(--color-primary-soft);
color: var(--color-primary);
}
.nav-searchbox__category-wrap {
position: relative;
}
.nav-searchbox__category {
appearance: none;
width: 100%;
min-height: 36px;
padding: 0 34px 0 12px;
border: 1px solid var(--color-line);
border-radius: var(--radius-pill, 999px);
background: var(--surface-2);
color: var(--color-ink);
font: 500 0.875rem/1 var(--font-ui);
}
.nav-searchbox__category-wrap::after {
content: '';
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 5px solid var(--color-ink-3);
pointer-events: none;
}
.nav-searchbox__category:disabled {
opacity: 0.56;
cursor: not-allowed;
}
.nav-drawer__menu {
flex: 1;
padding: 16px 20px 32px;
}
.nav-drawer__menu a {
display: block;
padding: 14px 0;
font-size: 1rem;
font-weight: 500;
color: var(--color-ink);
border-bottom: 1px solid var(--color-line-soft);
}
.nav-drawer__menu a:hover {
color: var(--color-primary-active);
}
.nav-drawer__menu ul {
list-style: none;
margin: 0;
padding: 0;
}
.nav-drawer__auth {
padding: 20px;
display: flex;
flex-direction: column;
gap: 12px;
border-top: 1px solid var(--color-line);
}
.nav-drawer__auth .btn-outline,
.nav-drawer__auth .btn-primary {
width: 100%;
height: 44px;
}
.nav-drawer__publish {
padding: 0 20px 20px;
}
.nav-drawer__publish .btn-primary {
width: 100%;
min-height: 44px;
justify-content: center;
gap: 8px;
}
/* ============================================================
* Header user box (Zibll-like sub-user-box grid)
* ============================================================ */
.nav-user__toggle {
position: relative;
}
.nav-user__dot {
position: absolute;
top: -1px;
right: -1px;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--color-error, #ef4444);
border: 2px solid var(--surface-1, #fff);
box-sizing: content-box;
pointer-events: none;
z-index: 2;
}
.nav-user__guest-avatar {
width: 36px;
height: 36px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
background: var(--surface-2);
color: var(--color-ink-3);
}
.user-dropdown {
width: 300px;
padding: 14px 12px 12px;
}
.user-dropdown__profile.user-info {
position: relative;
padding-right: 42px;
}
.user-dropdown__meta {
min-width: 0;
flex: 1;
}
.user-dropdown__name {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
/* Beat generic `.user-dropdown a { width:100%; height:44px }` sizing. */
.user-dropdown a.user-dropdown__msg,
.user-dropdown__msg {
position: absolute;
top: 8px;
right: 8px;
width: 34px !important;
height: 34px !important;
min-width: 34px;
max-width: 34px;
border-radius: 50%;
display: inline-flex !important;
align-items: center;
justify-content: center;
gap: 0 !important;
padding: 0 !important;
background: var(--surface-2);
color: var(--color-ink-3);
box-sizing: border-box;
}
.user-dropdown a.user-dropdown__msg:hover,
.user-dropdown__msg:hover {
background: var(--surface-3);
color: var(--color-primary);
}
.user-dropdown__msg .user-dropdown__badge {
position: absolute;
top: -4px;
right: -4px;
margin: 0;
}
.user-dropdown__guest-title {
padding: 4px 8px 12px;
text-align: center;
font-weight: 700;
color: var(--color-ink);
}
.user-dropdown__actions {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 8px 4px;
padding: 4px 2px 2px;
}
.user-dropdown__actions--admin {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.user-dropdown__action {
display: flex !important;
flex-direction: column;
align-items: center;
justify-content: flex-start;
gap: 6px !important;
width: auto !important;
height: auto !important;
min-height: 0;
padding: 6px 4px !important;
text-align: center !important;
font-size: 12px !important;
line-height: 1.25;
color: var(--color-ink-3) !important;
background: transparent !important;
}
.user-dropdown__action:hover {
background: transparent !important;
color: var(--color-primary) !important;
}
.user-dropdown__action-icon {
width: 40px;
height: 40px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 15px;
color: #fff;
}
.user-dropdown__action-icon.is-blue { background: linear-gradient(135deg, #60a5fa, #3b82f6); }
.user-dropdown__action-icon.is-purple { background: linear-gradient(135deg, #a78bfa, #8b5cf6); }
.user-dropdown__action-icon.is-green { background: linear-gradient(135deg, #34d399, #10b981); }
.user-dropdown__action-icon.is-red { background: linear-gradient(135deg, #f87171, #ef4444); }
.user-dropdown__action-icon.is-yellow { background: linear-gradient(135deg, #fbbf24, #f59e0b); color: #1f2937; }
.user-dropdown__action.is-danger {
color: var(--color-error, #ef4444) !important;
}
.user-dropdown__credits {
margin-top: 2px;
border-radius: 12px;
background: var(--surface-2);
margin-left: 4px;
margin-right: 4px;
width: auto;
}
@@ -1,10 +0,0 @@
/* ===== nav_fixed mobile fallback ===== */
/* On mobile, static nav should remain sticky so the hamburger toggle stays accessible */
@media (max-width: 767px) {
.has-static-nav .site-nav--static {
position: sticky;
top: 0;
z-index: 1000;
}
}
@@ -1,109 +0,0 @@
/* Responsive Mega Menu */
@media (max-width: 767px) {
ul.nav-desktop .mega-menu-item > .mega-menu__panel {
position: static;
transform: none;
width: 100%;
max-width: none;
margin: 0;
box-shadow: none;
}
.mega-menu__columns-2,
.mega-menu__columns-3,
.mega-menu__columns-4,
.mega-menu__columns-5 {
grid-template-columns: 1fr;
}
}
/* ============================================================
* Right actions (icons + auth)
* ============================================================ */
.nav-actions {
display: flex;
align-items: center;
gap: 4px;
flex-shrink: 0;
margin-left: auto;
overflow: visible;
position: relative;
z-index: 25;
}
.nav-settings-link,
.nav-publish-btn {
flex-shrink: 0;
}
.nav-settings-link svg {
width: 18px;
height: 18px;
}
.nav-publish-btn {
display: inline-flex;
align-items: center;
gap: 8px;
height: 36px;
padding: 0 14px;
font-size: 0.875rem;
font-weight: 600;
}
.nav-publish-btn i {
font-size: 0.8125rem;
}
.theme-toggle.is-animating svg {
transform: rotate(360deg);
}
/* ============================================================
* Auth buttons (in nav, when logged out)
* ============================================================ */
.nav-auth {
display: none;
align-items: center;
gap: 8px;
}
/* btn-outline / btn-primary 复用 core/buttons.css 的定义,
* 这里仅做导航专用调整(高度对齐、字体) */
.nav-auth .btn-outline,
.nav-auth .btn-primary {
height: 36px;
font-size: 0.875rem;
padding: 0 16px;
}
/* Auth dropdown variant (layouts 1/3) — icon trigger + panel */
.nav-auth-dropdown {
position: relative;
display: none;
align-items: center;
}
.nav-auth-dropdown__trigger {
width: 36px;
height: 36px;
}
.nav-auth-dropdown__trigger svg {
width: 20px;
height: 20px;
}
.nav-auth-dropdown__panel {
display: none;
position: absolute;
top: calc(100% + 8px);
right: 0;
min-width: 180px;
z-index: 1100;
}
.nav-auth-dropdown.is-open .nav-auth-dropdown__panel {
display: block;
}
+132
View File
@@ -0,0 +1,132 @@
/* Utilities for zibll header chrome */
.css-icon.i-menu {
position: relative;
display: inline-block;
width: 1.15em;
height: 1em;
vertical-align: -0.1em;
}
.css-icon.i-menu > i,
.css-icon.i-menu::before,
.css-icon.i-menu::after {
content: "";
position: absolute;
left: 0;
right: 0;
height: 2px;
background: currentColor;
border-radius: 1px;
transition: 0.25s;
}
.css-icon.i-menu > i { top: 50%; margin-top: -1px; }
.css-icon.i-menu::before { top: 0; }
.css-icon.i-menu::after { bottom: 0; }
.list-inline { padding-left: 0; list-style: none; margin: 0; }
.list-inline > li { display: inline-block; vertical-align: middle; }
.list-inline.splitters > li + li { margin-left: 6px; }
.navbar-top .flex.ac { display: flex; align-items: center; }
.navbar-top .flex.jsb { display: flex; justify-content: space-between; }
.navbar-top .flex.jsa { display: flex; justify-content: space-around; }
.navbar-top .flex.jc { display: flex; justify-content: center; }
.mb6 { margin-bottom: 6px; }
.mb10 { margin-bottom: 10px; }
.mb20 { margin-bottom: 20px; }
.mt6 { margin-top: 6px; }
.mt10 { margin-top: 10px; }
.ml6 { margin-left: 6px; }
.ml10 { margin-left: 10px; }
.em09 { font-size: 0.9em; }
.em12 { font-size: 1.2em; }
.muted-2-color { opacity: 0.7; }
.muted-3-color { opacity: 0.55; }
.text-ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.padding-10 { padding: 10px; }
.relative { position: relative; }
.inline-block { display: inline-block; }
.hide { display: none !important; }
.pull-right { float: right; }
.null { background: none; border: 0; padding: 0; color: inherit; }
.abs-right { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); }
/* Mid breakpoint fold trigger (zibll ~991) */
@media (max-width: 991px) {
.navbar-top .show-nav-but { display: block !important; }
.navbar-top .nav.navbar-nav:not(.show) {
display: none;
}
.navbar-top .nav.navbar-nav.show {
display: block;
position: absolute;
left: 0; right: 0;
top: 100%;
background: var(--main-bg-color, #fff);
z-index: 1001;
padding: 10px;
box-shadow: 0 8px 20px rgba(0,0,0,.08);
}
}
/* Language dropdown */
.language-dropdown { position: relative; }
.language-dropdown .dropdown-menu,
.language-dropdown .hover-show-con {
min-width: 140px;
right: 0;
left: auto;
padding: 8px 0;
}
.language-dropdown .translate-button { display: flex; align-items: center; padding: 6px 14px; }
.language-dropdown .translate-button.active { color: var(--focus-color); }
/* Layout2 inline search focus panel */
.navbar-collapse .search-input {
min-width: 160px;
max-width: 240px;
height: 34px;
border-radius: 20px;
border: 0;
padding: 0 36px 0 14px;
}
.navbar-form.navbar-left.hover-show { margin: 0 8px; }
.navbar-form .form-group.relative { position: relative; }
.navbar-form .abs-right { right: 10px; }
/* Mobile drawer body scroll lock */
body.mobile-navbar-show { overflow: hidden; }
/* Ensure container-header flex row */
.container-header {
display: flex;
align-items: center;
flex-wrap: nowrap;
max-width: var(--nav-max-width, 100%);
margin: 0 auto;
width: 100%;
}
.navbar-collapse {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
flex-wrap: nowrap;
}
.navbar-header { display: flex; align-items: center; flex-shrink: 0; }
.navbar-top .navbar-toggle { display: none; background: none; border: 0; color: inherit; }
@media (max-width: 767px) {
.navbar-top .navbar-toggle { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; }
.navbar-collapse { display: none; }
}
/* Senior submenu full width under fixed header */
.navbar-top .sub-menu.senior-submenu {
position: fixed;
left: 0; right: 0;
top: 59px;
width: 100%;
min-width: 100%;
padding: 20px 0;
border-radius: 0;
z-index: 998;
}
+51 -24
View File
@@ -1,13 +1,15 @@
/**
* AKSRT nav chrome — zibll contract (body.mobile-navbar-show, folding, chevron).
* Drawer open/close is handled by aksrt-runtime [data-toggle-class].
* AKSRT nav chrome — zibll contract (folding, chevron, language, drawer a11y).
* Drawer open/close via aksrt-runtime [data-toggle-class] → body.mobile-navbar-show.
*/
(function () {
"use strict";
function appendChevrons() {
document.querySelectorAll(".navbar-top li.menu-item-has-children > a").forEach(function (a) {
if (a.querySelector(".fa-angle-down")) return;
var foldTimer = null;
function appendChevrons(root) {
(root || document).querySelectorAll(".navbar-top li.menu-item-has-children > a").forEach(function (a) {
if (a.querySelector(".fa-angle-down, .fas.fa-angle-down")) return;
var i = document.createElement("i");
i.className = "fa fa-angle-down ml6";
i.setAttribute("aria-hidden", "true");
@@ -15,18 +17,30 @@
});
}
function clearFolding() {
var more = document.querySelector(".navbar-nav > li.aksrt-nav-more");
if (!more) return;
var sub = more.querySelector(".sub-menu");
var nav = more.parentNode;
if (sub && nav) {
while (sub.firstChild) {
nav.insertBefore(sub.firstChild, more);
}
}
more.remove();
}
function navFolding() {
var collapse = document.querySelector(".navbar-collapse");
var nav = document.querySelector(".navbar-nav");
if (!collapse || !nav) return;
if (window.innerWidth <= 996) return;
clearFolding();
if (window.innerWidth <= 996) return;
var header = document.querySelector(".header");
if (header && header.offsetHeight <= 81) return;
// Already folded?
if (nav.querySelector("li.aksrt-nav-more")) return;
var surplus = collapse.clientWidth;
Array.prototype.forEach.call(collapse.children, function (child) {
if (!child.classList.contains("navbar-nav")) {
@@ -40,9 +54,7 @@
var foldItems = [];
Array.prototype.forEach.call(nav.children, function (li) {
navsWidth += li.offsetWidth || 0;
if (navsWidth > surplus) {
foldItems.push(li);
}
if (navsWidth > surplus) foldItems.push(li);
});
if (!foldItems.length) return;
@@ -51,14 +63,33 @@
more.innerHTML =
'<a href="javascript:void(0);"><i class="fas fa-bars" aria-hidden="true"></i></a><ul class="sub-menu"></ul>';
var sub = more.querySelector(".sub-menu");
foldItems.forEach(function (li) {
sub.appendChild(li);
});
foldItems.forEach(function (li) { sub.appendChild(li); });
nav.appendChild(more);
appendChevrons(more);
}
function scheduleFold() {
if (foldTimer) clearTimeout(foldTimer);
foldTimer = setTimeout(function () {
try { navFolding(); } catch (e) {}
}, 80);
}
function bindLanguage() {
document.addEventListener("click", function (e) {
var btn = e.target && e.target.closest ? e.target.closest(".translate-button[data-language]") : null;
if (!btn) return;
e.preventDefault();
var lang = btn.getAttribute("data-language");
if (!lang) return;
try { localStorage.setItem("aksrt_lang", lang); } catch (err) {}
var url = new URL(window.location.href);
url.searchParams.set("lang", lang);
window.location.href = url.toString();
});
}
function bindDrawerA11y() {
// Esc closes mobile nav via body class.
document.addEventListener("keydown", function (e) {
if (e.key === "Escape" && document.body.classList.contains("mobile-navbar-show")) {
document.body.classList.remove("mobile-navbar-show");
@@ -68,18 +99,14 @@
function init() {
appendChevrons();
try {
navFolding();
} catch (err) {
// ignore fold failures
}
scheduleFold();
bindLanguage();
bindDrawerA11y();
window.addEventListener("resize", scheduleFold, { passive: true });
}
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === "function") {
window.AksrtFrontend.register("aksrt-nav-chrome", function () {
init();
});
window.AksrtFrontend.register("aksrt-nav-chrome", function () { init(); });
} else if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
+127 -155
View File
@@ -1,9 +1,7 @@
/**
* AKSRT Section Navs — Article TOC / Scrollspy
* AKSRT Section Navs — zibll section_navs parity (posts-nav-lists + scrollspy).
*
* Auto-generates a table of contents from h1~h4 headings within
* [data-nav] content areas. Scrollspy highlights the current section.
* Collapsible sub-items when the TOC is tall.
* Builds TOC from [data-nav] h1–h4 into .posts-nav-box / .posts-nav-lists.
*
* @package AKSRT
*/
@@ -12,164 +10,138 @@
var HEADING_SEL = 'h1, h2, h3, h4';
var MIN_HEADINGS = 2;
var SCROLL_OFFSET = 105;
var MAX_HEIGHT_COLLAPSE = 380;
var NAVBOX = '.posts-nav-box';
var NAV_CLASS = 'posts-nav-lists';
function initSectionNavs() {
var navContainer = document.querySelector('[data-nav]');
if (!navContainer) return;
function isExcluded(el) {
return el.classList.contains('item-heading') || !!el.closest('.no-nav');
}
// Single-post TOC is owned by page-template.js (.posts-nav-box .toc-list).
// Do not invent a second .aksrt-section-nav tree on article singles.
if (document.querySelector('.posts-nav-box .toc-list, .posts-nav-box.toc-widget')) {
function ensureId(el, index) {
if (!el.id) {
el.id = 'section-nav-' + index;
}
return el.id;
}
function buildLists(root) {
var headings = root.querySelectorAll(HEADING_SEL);
var html = '';
var count = 0;
headings.forEach(function (h, i) {
if (isExcluded(h)) return;
var id = ensureId(h, i);
var tag = h.tagName.toUpperCase();
var text = (h.textContent || '').trim();
if (!text) return;
html += '<li class="n-' + tag + '"><a href="#' + id + '">' + text + '</a></li>';
count++;
});
return count >= MIN_HEADINGS ? html : '';
}
function fillBoxes(listsHtml) {
document.querySelectorAll(NAVBOX).forEach(function (box) {
var existing = box.querySelector('.' + NAV_CLASS);
if (existing) {
existing.innerHTML = listsHtml;
return;
}
var title = box.getAttribute('data-title') || '';
var titleHtml = title
? '<div class="box-body notop"><div class="title-theme">' + title + '</div></div>'
: '';
box.insertAdjacentHTML(
'beforeend',
titleHtml +
'<div class="zib-widget"><div class="' +
NAV_CLASS +
' scroll-y mini-scrollbar list-unstyled">' +
listsHtml +
'</div></div>'
);
});
}
function smoothTo(hash) {
var el = document.querySelector(hash);
if (!el) return;
var offset = 0;
var header = document.querySelector('.header');
if (document.body.classList.contains('nav-fixed') && header) {
offset = header.offsetHeight + 12;
}
var top = el.getBoundingClientRect().top + window.pageYOffset - offset;
window.scrollTo({ top: top, behavior: 'smooth' });
}
function bindClicks() {
document.addEventListener('click', function (e) {
var a = e.target && e.target.closest ? e.target.closest('.' + NAV_CLASS + ' a[href^="#"]') : null;
if (!a) return;
var href = a.getAttribute('href');
if (!href || href.length < 2) return;
e.preventDefault();
history.replaceState(null, '', href);
smoothTo(href);
});
}
function scrollspy() {
var links = document.querySelectorAll('.' + NAV_CLASS + ' a[href^="#"]');
if (!links.length) return;
var map = [];
links.forEach(function (a) {
var id = a.getAttribute('href').slice(1);
var el = document.getElementById(id);
if (el) map.push({ el: el, a: a, li: a.parentElement });
});
function update() {
var y = window.scrollY + 110;
var current = null;
for (var i = 0; i < map.length; i++) {
if (map[i].el.offsetTop <= y) current = map[i];
}
map.forEach(function (m) {
if (m.li) m.li.classList.toggle('active', m === current);
});
}
window.addEventListener('scroll', update, { passive: true });
update();
}
function autoHash() {
if (window.location.hash) {
setTimeout(function () {
smoothTo(window.location.hash);
}, 40);
}
}
function init() {
if (document.querySelector('.posts-nav-box .toc-list, .posts-nav-box.toc-widget .toc-list')) {
return;
}
var headings = navContainer.querySelectorAll(HEADING_SEL);
if (headings.length < MIN_HEADINGS) return;
var tocContainer = document.querySelector('.posts-nav-lists, .aksrt-section-nav');
if (!tocContainer) {
// Docs / page-navs only: create a dedicated container. Never inject into single sidebars.
if (document.body.classList.contains('single') || document.body.classList.contains('single-post')) {
return;
}
tocContainer = document.createElement('nav');
tocContainer.className = 'aksrt-section-nav';
tocContainer.setAttribute('aria-label', '文章目录');
var sidebar = document.querySelector('.content-layout__aside, .sidebar, .widget-area');
if (sidebar) {
sidebar.insertBefore(tocContainer, sidebar.firstChild);
} else if (navContainer.parentNode) {
navContainer.parentNode.insertBefore(tocContainer, navContainer);
} else {
return;
}
var root = document.querySelector('[data-nav], .article-content, .entry-content, main.article');
if (!root) return;
var listsHtml = buildLists(root);
if (!listsHtml) return;
if (!document.querySelector(NAVBOX) && !document.querySelector('.' + NAV_CLASS)) {
return;
}
fillBoxes(listsHtml);
bindClicks();
scrollspy();
autoHash();
}
var list = document.createElement('ul');
list.className = 'aksrt-section-nav__list';
var items = [];
headings.forEach(function (heading, i) {
if (!heading.id) {
heading.id = 'section-nav-' + i;
}
var level = parseInt(heading.tagName.charAt(1), 10);
var li = document.createElement('li');
li.className = 'aksrt-section-nav__item aksrt-section-nav__item--h' + level;
var link = document.createElement('a');
link.href = '#' + heading.id;
link.textContent = heading.textContent.trim();
link.className = 'aksrt-section-nav__link';
link.addEventListener('click', function (e) {
e.preventDefault();
smoothScrollTo(heading);
// Expand collapsed panels containing the target
var panel = heading.closest('[data-collapse], .collapse');
if (panel) {
panel.classList.add('show', 'is-open');
var readMore = panel.previousElementSibling;
if (readMore && readMore.classList.contains('read-more-open')) readMore.click();
}
});
li.appendChild(link);
list.appendChild(li);
items.push({ el: heading, link: link, li: li, level: level });
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') {
window.AksrtFrontend.register('aksrt-section-navs', function () {
init();
});
tocContainer.innerHTML = '';
tocContainer.appendChild(list);
// Collapse sub-items if TOC is tall
if (tocContainer.scrollHeight > MAX_HEIGHT_COLLAPSE) {
collapseSubItems(list, items);
}
// Scrollspy
var ticking = false;
function onScroll() {
if (ticking) return;
ticking = true;
requestAnimationFrame(function () {
ticking = false;
var scrollTop = window.pageYOffset + SCROLL_OFFSET;
var activeIdx = 0;
for (var i = items.length - 1; i >= 0; i--) {
if (items[i].el.offsetTop <= scrollTop) {
activeIdx = i;
break;
}
}
items.forEach(function (item, idx) {
item.link.classList.toggle('is-active', idx === activeIdx);
});
});
}
window.addEventListener('scroll', onScroll, { passive: true });
onScroll();
// URL hash auto-scroll
if (window.location.hash) {
var target = document.querySelector(window.location.hash);
if (target) setTimeout(function () { smoothScrollTo(target); }, 100);
}
}
function smoothScrollTo(el) {
if (window.aksrt && aksrt.scrollTopTo) {
aksrt.scrollTopTo(el);
} else {
var top = el.getBoundingClientRect().top + window.pageYOffset - SCROLL_OFFSET;
window.scrollTo({ top: Math.max(0, top), behavior: 'smooth' });
}
}
function collapseSubItems(list, items) {
var parentStack = [];
items.forEach(function (item) {
while (parentStack.length && parentStack[parentStack.length - 1].level >= item.level) {
parentStack.pop();
}
if (item.level >= 3 && parentStack.length) {
item.li.classList.add('yc');
// Add collapse toggle to parent if not already there
var parentLi = parentStack[parentStack.length - 1].li;
if (!parentLi.querySelector('.nav-toggle-collapse')) {
var toggle = document.createElement('span');
toggle.className = 'nav-toggle-collapse';
toggle.innerHTML = '<i class="fas fa-chevron-right"></i>';
toggle.addEventListener('click', function (e) {
e.stopPropagation();
toggle.classList.toggle('fa-rotate-90');
var next = parentLi.nextElementSibling;
while (next && next.classList.contains('yc')) {
next.classList.toggle('yc-open');
next = next.nextElementSibling;
}
});
parentLi.appendChild(toggle);
}
}
parentStack.push(item);
});
}
if (window.AksrtFrontend) {
AksrtFrontend.register('aksrt-section-navs', function () { initSectionNavs(); });
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initSectionNavs);
} else if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
initSectionNavs();
init();
}
})();