This commit is contained in:
oneao committed 2026-10-09 17:32:14 +08:00
1 parent e99a9fb274
commit 0be0b0767a
788 files changed
+112023 -14941

No files matched your search

@@ -0,0 +1,621 @@
: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
关键:这两个值加在 <li> 上,不是加在 <a>/<button> 上。
实测 docus.dev 的几何关系:
li left=42 padding-inline-start:6px margin-inline-start:-1px
a.link left=48 ← 被 li 的 6px padding 右推
::after left=-6px → 48-6 = 42 ← 正好落在子列表描边线上
若把 padding 加在 <a> 上,<a> 的 border box 左缘不变(仍是 42),
竖线会跑到 36px,比描边线左偏 6px(即「线跑到线的左边」)。
-------------------------------------------------------------------------- */
.g3-item,
.g3-item-with-children {
padding-inline-start: 6px;
margin-inline-start: -1px;
}
/* 顶层(level 0)不适用上面的偏移,保持与容器对齐 */
.g3-list > .g3-item,
.g3-list > .g3-item-with-children {
padding-inline-start: 0;
margin-inline-start: 0;
}
/* itemWithChildren 展开时底部留 6px(data-[state=open]:mb-1.5) */
.g3-item-with-children:has(> .g3-nav-content:not([style*='display: none'])) {
margin-bottom: 6px;
}
.g3-nav-content {
overflow: hidden;
}
@media (prefers-reduced-motion: reduce) {
.g3-nav-link,
.g3-nav-icon,
.g3-nav-link.is-active::after {
transition: none;
}
}
/* ==========================================================================
右侧目录(TOC)—— 照抄 Docus / @nuxt/ui ContentToc 的 circuit 指示器
--------------------------------------------------------------------------
依据:@nuxt/ui@4.11.2 dist/runtime/components/content/ContentToc.vue
+ https://docus.dev 实测计算样式
实测值:
标题 14px / 600(font-semibold)
链接 display:flex; align-items:center; padding:4px 0;
14px / 400; line-height:20px; height:28px
border-radius:4px; color: text-muted
链接 hover text-default
链接激活 text-primary;fontWeight 仍 400(注意:不是 500)
子层级 ul margin-inline-start:12px(ms-3)
指示器容器 position:absolute; width:12px; start-0; margin-inline-start:10px
轨道 absolute inset-0; 背景 border-default
活动块 width:100%(12px); height:var(--indicator-size);
translateY(var(--indicator-position)); 背景 primary
过渡 translate,height 0.2s ease-out
← 无圆角;「拐弯」由 SVG mask 裁出
========================================================================== */
/* 隐藏内置目录(改用 G3DocAside) */
.VPDocAsideOutline {
display: none !important;
}
.g3-toc {
padding: 0;
}
.g3-toc-trigger {
display: flex;
align-items: center;
gap: 6px;
margin: -6px 0 0;
padding: 6px 0;
font-size: 14px;
font-weight: 600;
line-height: 20px;
color: var(--vp-c-text-1);
}
.g3-toc-content {
position: relative;
display: flex;
padding-top: 12px;
}
/* --------------------------------------------------------------------------
指示器:整条 12px 宽色带,靠 mask-image 裁成折线
-------------------------------------------------------------------------- */
.g3-toc-indicator {
position: absolute;
inset-block: 12px 0; /* 与 content 的 padding-top 对齐 */
inset-inline-start: 0;
z-index: 0;
width: 12px;
margin-inline-start: 10px; /* ms-2.5 */
pointer-events: none;
/* mask 由组件的 circuitMask 计算后内联注入 */
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
}
/* 轨道:未激活部分的底色 */
.g3-toc-indicator-line {
position: absolute;
inset: 0;
background-color: var(--vp-c-divider);
}
/* 活动块:品牌色,高度与位移由 CSS 变量驱动 */
.g3-toc-indicator-active {
position: absolute;
inset-inline: 0;
top: 0;
width: 100%;
height: var(--indicator-size, 0);
background-color: var(--vp-c-brand-1);
transform: translateY(var(--indicator-position, 0));
transition:
transform 0.2s ease-out,
height 0.2s ease-out;
}
@media (prefers-reduced-motion: reduce) {
.g3-toc-indicator-active {
transition: none;
}
}
/* --------------------------------------------------------------------------
列表与链接
--------------------------------------------------------------------------
关键:ul.list 带 ps-6.5 = padding-inline-start:26px(Docus 实测)。
指示器(12px 宽 + ml 10px)是绝对定位在 content 上的,
这 26px 的 padding 把条目文字整体推到轨道右侧。
漏掉它文字就会压在轨道的 12px 上(实测差 26px:
文字左缘 − 指示器右缘 = -23px,正确值是 +3px)。
-------------------------------------------------------------------------- */
.g3-toc-list,
.g3-toc-list-with-children {
position: relative;
z-index: 1;
margin: 0;
padding: 0;
list-style: none;
}
.g3-toc-list {
padding-inline-start: 26px; /* ps-6.5 */
}
.g3-toc-list-with-children {
margin-inline-start: 12px; /* ms-3 */
padding-inline-start: 0;
}
.g3-toc-item {
min-width: 0;
margin-inline-start: -1px; /* -ms-px:与指示器轨道对齐 */
padding-inline-start: 0;
}
.g3-toc-link {
display: flex;
align-items: center;
padding: 4px 0; /* py-1 */
font-size: 14px;
font-weight: 400;
line-height: 20px;
height: 28px;
border-radius: 4px; /* rounded-sm */
color: var(--g3-nav-text-muted);
text-decoration: none;
transition: color 0.2s ease;
}
.g3-toc-link:hover {
color: var(--vp-c-text-1); /* hover:text-default */
}
.g3-toc-link.is-active {
color: var(--vp-c-brand-1); /* text-primary;字重保持 400 */
}
.g3-toc-link-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
@media (prefers-reduced-motion: reduce) {
.g3-toc-link {
transition: none;
}
}
.VPNavBar,
.VPNavBar.home,
.VPNavBar.home.top,
.VPNavBar:not(.home),
.VPNavBar .content-body,
.VPNavBar.home .content-body,
.VPNavBar.home.top .content-body,
.VPNavBar:not(.home) .content-body {
background-color: var(--vp-nav-bg-color) !important;
}
.VPNavBar .divider,
.VPNavBar.has-sidebar .divider {
padding-left: 0 !important;
background-color: var(--vp-c-gutter) !important;
}
.VPNavBar .divider-line,
.VPNavBar.home.top .divider-line,
.VPNavBar:not(.home) .divider-line {
background-color: var(--vp-c-gutter) !important;
}
.VPNavBarTitle.has-sidebar .title {
border-bottom-color: transparent !important;
}
@media (min-width: 960px) {
.VPNavBar .wrapper,
.VPNavBar.has-sidebar .wrapper {
padding: 0 32px !important;
}
.VPNavBar .container,
.VPNavBar.has-sidebar .container {
max-width: calc(var(--vp-layout-max-width) - 64px) !important;
}
.VPNavBar > .wrapper > .container > .title,
.VPNavBar.has-sidebar > .wrapper > .container > .title {
position: static !important;
z-index: auto !important;
flex-shrink: 0 !important;
padding: 0 !important;
width: auto !important;
height: calc(var(--vp-nav-height) - 1px) !important;
background-color: transparent !important;
}
.VPNavBar > .wrapper > .container > .content,
.VPNavBar.has-sidebar > .wrapper > .container > .content {
position: static !important;
z-index: auto !important;
flex-grow: 1 !important;
padding: 0 !important;
}
.VPNavBar .content-body,
.VPNavBar.has-sidebar .content-body {
position: static !important;
justify-content: flex-end !important;
padding: 0 !important;
}
}
.g3-header-sections {
display: flex;
align-items: center;
gap: 4px;
margin-left: 12px;
}
.g3-header-section {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 0 10px;
height: 32px;
border: 1px solid transparent;
border-radius: 6px;
color: var(--vp-c-text-2);
font-size: 13px;
font-weight: 500;
line-height: 1;
transition:
color 0.2s,
background-color 0.2s,
border-color 0.2s;
}
.g3-header-section:hover {
color: var(--vp-c-brand-1);
background-color: var(--vp-c-default-soft);
}
.g3-header-section.is-active {
color: var(--vp-c-brand-1);
background-color: var(--vp-c-brand-soft);
border-color: transparent;
}
.g3-header-section svg {
flex: 0 0 auto;
}
.VPNavScreen .g3-header-sections {
display: grid;
gap: 8px;
margin: 24px 0 0;
padding-top: 24px;
border-top: 1px solid var(--vp-c-divider);
}
.VPNavScreen .g3-header-section {
justify-content: flex-start;
width: 100%;
height: 44px;
padding: 0 12px;
font-size: 14px;
}
@media (max-width: 767px) {
.VPNavBar .g3-header-sections {
display: none;
}
}
@media (min-width: 768px) {
.VPNavScreen .g3-header-sections {
display: none;
}
}