diff --git a/.vitepress/config.mts b/.vitepress/config.mts
index 20d09c0..1525518 100644
--- a/.vitepress/config.mts
+++ b/.vitepress/config.mts
@@ -63,6 +63,18 @@ export default defineConfig({
['link', { rel: 'icon', type: 'image/svg+xml', href: `${SITE_BASE}favicon.svg` }],
],
themeConfig: {
+ /**
+ * 导航左上角的品牌名 —— 主体固定为「雪羊科技」,不要替换成别的字。
+ *
+ * 后面那截 物联网卡与设备管理平台
+ * 是品牌定位副标题(样式见 portal.css 的 .site-subtitle),
+ * 与品牌名一起构成左上角的两行结构:上行品牌、下行定位。
+ *
+ * 注意区分:
+ * config.title → 浏览器标签页与 SEO 用的完整站名,不显示在导航
+ * siteTitle → 左上角实际渲染的内容
+ * 校验见 scripts/check-nav-brand.mjs(构建前自动执行)。
+ */
siteTitle: '雪羊科技物联网卡与设备管理平台',
logo: { src: '/snowgoat-logo.png', alt: '雪羊科技' },
nav: [
diff --git a/.vitepress/theme/styles/portal.css b/.vitepress/theme/styles/portal.css
index d47f3a2..0e2733e 100644
--- a/.vitepress/theme/styles/portal.css
+++ b/.vitepress/theme/styles/portal.css
@@ -1075,3 +1075,50 @@ html.portal-nav-home .VPNavBarMenuLink.active:not([data-anchor-active]) {
}
}
+/* ============================================================
+ 导航左上角品牌区:两行结构
+ 第一行:雪羊科技(品牌名)
+ 第二行:物联网卡与设备管理平台(定位副标题,更弱)
+ siteTitle 里的副标题是 ,由 v-html 插入。
+ 注意:它的父级是 VitePress 自己的裸 (无 class),
+ 所以选择器不能写 .title-text .site-subtitle —— 那个类根本不存在。
+ 直接用 .VPNavBarTitle .site-subtitle 即可。
+ ============================================================ */
+.VPNavBarTitle .title {
+ line-height: 1.2;
+ gap: 10px;
+}
+
+/* 品牌名与副标题的容器:两行紧凑排列,整体垂直居中 */
+.VPNavBarTitle .title > span {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ line-height: 1.15;
+ /* 品牌名与副标题作为一个整体在导航高度内垂直居中 */
+ height: var(--vp-nav-height);
+}
+
+.VPNavBarTitle .site-subtitle {
+ display: block;
+ margin-top: 3px;
+ font-size: 11px;
+ font-weight: 500;
+ letter-spacing: 0.03em;
+ line-height: 1.25;
+ color: var(--p-ink-3);
+}
+
+@media (max-width: 860px) {
+ .VPNavBarTitle .site-subtitle {
+ font-size: 10.5px;
+ margin-top: 0;
+ }
+}
+
+/* 窄屏下副标题可能挤压,超窄时隐藏,只留品牌名 */
+@media (max-width: 480px) {
+ .VPNavBarTitle .site-subtitle {
+ display: none;
+ }
+}
diff --git a/package.json b/package.json
index 994db3b..13ee4dd 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
"dev": "vitepress dev . --host 127.0.0.1",
"build": "vitepress build .",
"preview": "vitepress preview . --host 127.0.0.1",
- "check": "node scripts/check-registry.mjs",
+ "check": "node scripts/check-registry.mjs && node scripts/check-nav-brand.mjs",
"prebuild": "npm run check",
"predev": "npm run check"
},
diff --git a/scripts/check-nav-brand.mjs b/scripts/check-nav-brand.mjs
new file mode 100644
index 0000000..2de2c37
--- /dev/null
+++ b/scripts/check-nav-brand.mjs
@@ -0,0 +1,67 @@
+#!/usr/bin/env node
+/**
+ * 校验导航左上角品牌名。
+ *
+ * 约定:themeConfig.siteTitle 必须以「雪羊科技」为主体。
+ *
+ * 允许的形态:
+ * 雪羊科技
+ * 雪羊科技物联网卡与设备管理平台
+ *
+ * 不允许:
+ * - 主体被换成别的品牌名(如「雪羊科技开源」「雪羊开源」)
+ * - 整个删掉 siteTitle(VitePress 会回落到 config.title 的超长站名)
+ * - logo 缺失或指向不存在的文件
+ *
+ * config.title 是浏览器标签页/SEO 用的完整站名,和这里不是一回事。
+ */
+import { readFileSync, existsSync } from 'node:fs'
+import { fileURLToPath } from 'node:url'
+import { dirname, join } from 'node:path'
+
+const root = join(dirname(fileURLToPath(import.meta.url)), '..')
+const configPath = join(root, '.vitepress', 'config.mts')
+const src = readFileSync(configPath, 'utf-8')
+
+/** 品牌名主体,左上角必须以此开头 */
+const BRAND = '雪羊科技'
+
+// 取 siteTitle 的赋值(单引号或双引号,内容可含 HTML)
+const m = src.match(/siteTitle\s*:\s*(['"])([\s\S]*?)\1\s*,/)
+if (!m) {
+ console.error('[nav-brand] themeConfig.siteTitle 缺失。')
+ console.error(` 左上角会回落到 config.title(完整长名),应显式设为「${BRAND}」。`)
+ process.exit(1)
+}
+
+const raw = m[2].trim()
+
+// 去掉可选的副标题 span,只看品牌主体
+const withoutSubtitle = raw.replace(
+ /]*>[\s\S]*?<\/span>/g,
+ ''
+).trim()
+
+if (withoutSubtitle !== BRAND) {
+ console.error(`[nav-brand] siteTitle 的品牌主体是「${withoutSubtitle}」,应为「${BRAND}」。`)
+ console.error(' 左上角品牌名固定,副标题可保留但主体不要换。')
+ process.exit(1)
+}
+
+// logo 检查:既要配置在,也要文件真的存在
+const logoNeedle = 'snowgoat-logo.png'
+if (!src.includes(logoNeedle)) {
+ console.error(`[nav-brand] 未找到 logo 配置 ${logoNeedle}。`)
+ process.exit(1)
+}
+
+const logoFile = join(root, 'pages', 'public', logoNeedle)
+if (!existsSync(logoFile)) {
+ console.error(`[nav-brand] logo 文件不存在:pages/public/${logoNeedle}`)
+ process.exit(1)
+}
+
+const hasSubtitle = /site-subtitle/.test(raw)
+console.log(
+ `[nav-brand] OK:左上角「${BRAND}」${hasSubtitle ? ' + 定位副标题' : ''} + ${logoNeedle}`
+)