:root { --vp-nav-home-bg-color: var(--vp-nav-bg-color); --vp-sidebar-bg-color: var(--vp-c-bg); /* 三栏容器「内部」的顶部留白。 语义参考 Docus:容器本身紧贴 header,留白由容器自己的 pt-8 提供。 实测 docus.dev(页面顶部): 左栏 aside top=64(贴齐) padding-top=32px → 内容 90 右栏目录首块 top=64(贴齐) padding-top=32px → 内容 96 正文区块 top=64(贴齐) padding-top=32px → 内容 96 即三处的共同规律是「贴齐 + 32px」,不是「距 header 62px」。 改这一个值即可整体升降。 */ --g3-content-top-padding: 32px; /* 左栏内部自带的净垂直偏移:.nav 的 padding-top(8px) 与 .g3-list 的 margin-top(-6px) 相抵后为 +2px。 补偿它以让三栏内容落在同一水平线上(见下方 .VPSidebar[class])。 */ --g3-sidebar-inner-offset: 2px; } /* -------------------------------------------------------------------------- 内容区顶部留白(对齐 Docus:容器贴齐 header + 内部 32px) VitePress 三处的来源不同,需分别覆盖: 1) 正文:.VPDoc[data-v-*] { padding: 48px 32px 0 } → 顶部内边距改为 32px 2) 左侧边栏:.VPSidebar[data-v-*] { padding-top: var(--vp-nav-height) } 它的 padding-top 同时承担「避开固定 header」的职责,所以要保留 nav 高度,再叠加 32px 的呼吸空间。 3) 右侧目录:**不要**动 --vp-doc-top-height(原因见下方说明)。 ⚠ 关于 --vp-doc-top-height(踩过的坑): VitePress 的 .aside-container 计算式是 padding-top: calc(nav-height + layout-top-height + var(--vp-doc-top-height) + 48px) 它**已经内置了 48px 基线**,该变量只是额外增量。 我第一次把它设成 32px,得到 64 + 32 + 48 = 144px,右栏被推低了 80px。 Docus 的 32px 已被这个 48px 基线覆盖,所以保持默认 0(即不设该变量)。 ⚠ 特异性:VitePress 的规则带 [data-v-*] 属性选择器,权重高于单纯的 `.VPDoc` / `.VPSidebar`。实测只写类名不生效(计算值仍停在 48px/64px), 因此这里用 [class] 补齐权重,与 VitePress 打平后再靠顺序取胜。 -------------------------------------------------------------------------- */ @media (min-width: 960px) { /* 正文:VitePress 默认 48px → 32px */ .VPDoc[class] { padding-top: var(--g3-content-top-padding); } /* 左侧边栏:padding-top 本来就是 nav 高度(用于避让固定 header), 在其基础上叠加 32px。 额外减 2px 的原因(实测推算): .VPSidebar padding-top: 96px .nav padding-top: 8px → 104 .g3-list margin-top: -6px → 98 ← 内容实际落点 即左栏内部自带「+8 -6 = +2」的净偏移。减去它,三栏内容才都落在 96px。 (这两个值来自侧栏自身的复制样式,不宜改动,否则影响分组间距。) */ .VPSidebar[class] { padding-top: calc( var(--vp-nav-height) + var(--g3-content-top-padding) - var(--g3-sidebar-inner-offset) ); /* 底部留白:VitePress 默认 96px,Docus 是对称的 py-8 = 32px。 现在 .VPSidebar 自己就是滚动容器(与 Docus 的 overflow-y-auto 一致), 96px 在滚到底时会显得过厚。 */ padding-bottom: var(--g3-content-top-padding); } /* 右侧目录:.aside-container 是 position:fixed,其 padding-top 公式为 calc(nav-height + layout-top-height + var(--vp-doc-top-height) + 48px) 其中 48px 是 VitePress 写死、且**不与 --vp-doc-top-height 联动**的基线。 Docus 对应位置为 nav(64) + 32 = 96px,而公式给出 64 + 48 = 112px。 由于基线是硬编码的,只能整条覆盖(用 [class] 补齐特异性)。 */ .aside-container[class] { padding-top: calc(var(--vp-nav-height) + var(--g3-content-top-padding)); } } /* ========================================================================== 侧边栏 —— 照抄 Docus / @nuxt/ui 的 ContentNavigation -------------------------------------------------------------------------- 数据来源:从 https://docus.dev/en/getting-started/studio 用无头 Chrome 抓取的「真实计算样式」,不是从 Tailwind 类名反推。以下每一项都是实测值。 DOM(实测): aside > div.relative > nav > ul.isolate.-mx-2.5.-mt-1.5 li.flex.flex-col.data-[state=open]:mb-1.5 ← 有子项 button.group.relative.w-full.px-2.5.py-1.5... ← 分组标题 span.iconify.i-lucide:rocket.shrink-0.size-4.mx-0.5.text-dimmed span.truncate ← 标题文字 div[accordion content] ul.ms-5.border-s.border-default ← 子列表 li ← 无子项 a.group.relative.w-full... 实测值(1440px / light): ul.list margin: -6px -10px 0; list-style: none; isolation: isolate li(有子项) display:flex; flex-direction:column button/a.link display:flex; align-items:center; gap:6px padding:6px 10px; height:32px; font-size:14px border-radius:0 (圆角 6px 画在 ::before 上) 分组标题 font-weight:600; color: oklch(0.21 …) = text-highlighted 普通项 font-weight:400; color: oklch(0.552 …) = text-muted 激活项 font-weight:500; color: oklch(0.696 0.17 162.48) = primary background: transparent(无背景块) ::after 竖线:1px × 28px, left:-6px, top/bottom:2px 图标 16px × 16px;margin-inline: 2px;color: text-dimmed ul.listWithChildren margin-inline-start:20px; border-inline-start:1px; padding-inline-start:0 ::before position:absolute; inset-inline:0; inset-block:1px; border-radius:6px(仅用于 focus 轮廓,默认透明) 注意 variant 是 'link' 而非 'pill':激活项没有高亮底色, 只有「文字变品牌色 + 左侧 1px 竖线」。 ========================================================================== */ /* --- Nuxt UI token → VitePress 变量映射 --- */ :root { --g3-nav-text-dimmed: var(--vp-c-text-3); --g3-nav-text-muted: var(--vp-c-text-2); --g3-nav-text-highlighted: var(--vp-c-text-1); --g3-nav-border: var(--vp-c-divider); --g3-nav-radius: 6px; /* rounded-md */ } .VPSidebar { background-color: var(--vp-c-bg) !important; } /* 隐藏内置侧边栏 —— 改由 G3Sidebar.vue 渲染。 VPSidebarGroup 无包裹元素,直接产出并列的 .nav > .group, 只能用结构位置区分(:not 里必须排除自建节点)。 */ .VPSidebar > .nav > *:not(.g3-nav-root) { display: none; } /* 底部留白只由 .VPSidebar 容器提供(见文件开头 .VPSidebar[class] 的 padding-bottom)。这里把 .nav 自带的 32px 去掉,否则两者叠加成 64px, 比 Docus 的单份 py-8(32px) 厚一倍(实测滚到底时留白 70px → 应为 38px)。 顶部的 8px 保留:它与 .g3-list 的 -6px 相抵后构成左栏内部节奏, 已在 --g3-sidebar-inner-offset 中补偿。 */ .VPSidebar .nav { padding: 8px 0 0; } /* -------------------------------------------------------------------------- ul.list = isolate -mx-2.5 -mt-1.5 → 左右各 -10px、顶部 -6px,让条目的 hover 区比容器更宽 -------------------------------------------------------------------------- */ .g3-nav-root { width: 100%; } .g3-list { display: block; margin: -6px -10px 0; padding: 0; list-style: none; isolation: isolate; } /* li.itemWithChildren = flex flex-col */ .g3-item-with-children { display: flex; flex-direction: column; } .g3-item { display: block; } /* -------------------------------------------------------------------------- link = px-2.5 py-1.5 gap-1.5 text-sm flex items-center -------------------------------------------------------------------------- */ .g3-nav-link { position: relative; display: flex; align-items: center; gap: 6px; width: 100%; padding: 6px 10px; font-size: 14px; font-weight: 400; line-height: 20px; color: var(--g3-nav-text-muted); text-align: start; text-decoration: none; border: 0; background: transparent; cursor: pointer; transition: color 0.2s ease; } /* before:absolute before:rounded-md before:inset-x-0 before:inset-y-px link 变体下 ::before 本身不上色,仅承载 focus 轮廓的圆角形状 */ .g3-nav-link::before { content: ''; position: absolute; inset-inline: 0; inset-block: 1px; z-index: -1; border-radius: var(--g3-nav-radius); } /* 分组标题:trigger = font-semibold,颜色为 text-highlighted */ .g3-nav-trigger { font-weight: 600; color: var(--g3-nav-text-highlighted); } /* hover(link 变体 & 未激活)→ text-highlighted */ .g3-nav-link:hover { color: var(--g3-nav-text-highlighted); } /* 分组标题已展开态(Nuxt UI: data-[state=open]:text-highlighted) */ .g3-nav-trigger[aria-expanded='true'] { color: var(--g3-nav-text-highlighted); } /* -------------------------------------------------------------------------- 激活项:variant 'link' + active + highlight link → font-medium + text-primary(无背景块) after → 左侧 1px 竖线,inset-y-0.5(-2px),inset-inline-start:-6px 实测:width 1px, height 28px, left -6px, top 2px, bottom 2px -------------------------------------------------------------------------- */ .g3-nav-link.is-active { font-weight: 500; color: var(--vp-c-brand-1); } .g3-nav-link.is-active::after { content: ''; position: absolute; inset-inline-start: -6px; top: 2px; bottom: 2px; display: block; width: 1px; border-radius: 9999px; background-color: var(--vp-c-brand-1); transition: background-color 0.2s ease; } /* -------------------------------------------------------------------------- 文字:truncate -------------------------------------------------------------------------- */ .g3-nav-title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* -------------------------------------------------------------------------- 图标:shrink-0 size-4 mx-0.5 实测 16×16,左右各 2px(Docus 在 app.config.ts 里把 size-5 覆盖成 size-4) -------------------------------------------------------------------------- */ .g3-nav-icon { flex: 0 0 auto; width: 16px; height: 16px; margin-inline: 2px; color: var(--g3-nav-text-dimmed); transition: color 0.2s ease; } .g3-nav-link:hover .g3-nav-icon { color: var(--vp-c-text-1); } .g3-nav-link.is-active .g3-nav-icon { color: var(--vp-c-brand-1); } /* -------------------------------------------------------------------------- ul.listWithChildren = ms-5 border-s border-default 实测:margin-inline-start 20px;border-inline-start 1px;padding-inline-start 0 -------------------------------------------------------------------------- */ .g3-list-with-children { margin: 0 0 0 20px; margin-inline-start: 20px; padding: 0; list-style: none; border-inline-start: 1px solid var(--g3-nav-border); } /* -------------------------------------------------------------------------- level>0 的条目:Nuxt UI 的 item / itemWithChildren 在 level=true 时 带 "ps-1.5 -ms-px" → padding-inline-start:6px; margin-inline-start:-1px 关键:这两个值加在